summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:40:13 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:40:13 +0000
commite9be59e1502a41bab9891d96d753102a7dafef0b (patch)
treec3b2da87c414881f4b53d0964f407c83492d813e /lib
parentInitial commit. (diff)
downloadcluster-glue-e9be59e1502a41bab9891d96d753102a7dafef0b.tar.xz
cluster-glue-e9be59e1502a41bab9891d96d753102a7dafef0b.zip
Adding upstream version 1.0.12.upstream/1.0.12upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--lib/Makefile.am20
-rw-r--r--lib/clplumbing/GSource.c1864
-rw-r--r--lib/clplumbing/Makefile.am99
-rw-r--r--lib/clplumbing/base64.c422
-rw-r--r--lib/clplumbing/base64_md5_test.c113
-rw-r--r--lib/clplumbing/cl_compress.c500
-rw-r--r--lib/clplumbing/cl_log.c1261
-rw-r--r--lib/clplumbing/cl_malloc.c1044
-rw-r--r--lib/clplumbing/cl_misc.c179
-rw-r--r--lib/clplumbing/cl_msg.c2537
-rw-r--r--lib/clplumbing/cl_msg_types.c1736
-rw-r--r--lib/clplumbing/cl_netstring.c570
-rw-r--r--lib/clplumbing/cl_pidfile.c294
-rw-r--r--lib/clplumbing/cl_plugin.c140
-rw-r--r--lib/clplumbing/cl_poll.c809
-rw-r--r--lib/clplumbing/cl_random.c164
-rw-r--r--lib/clplumbing/cl_reboot.c59
-rw-r--r--lib/clplumbing/cl_signal.c209
-rw-r--r--lib/clplumbing/cl_syslog.c149
-rw-r--r--lib/clplumbing/cl_uuid.c180
-rw-r--r--lib/clplumbing/coredumps.c309
-rw-r--r--lib/clplumbing/cpulimits.c219
-rw-r--r--lib/clplumbing/ipcsocket.c2767
-rw-r--r--lib/clplumbing/ipctest.c1377
-rw-r--r--lib/clplumbing/ipctransient.h50
-rw-r--r--lib/clplumbing/ipctransientclient.c222
-rw-r--r--lib/clplumbing/ipctransientlib.c97
-rw-r--r--lib/clplumbing/ipctransientserver.c204
-rw-r--r--lib/clplumbing/longclock.c275
-rw-r--r--lib/clplumbing/md5.c335
-rw-r--r--lib/clplumbing/mkstemp_mode.c56
-rw-r--r--lib/clplumbing/netstring_test.c255
-rw-r--r--lib/clplumbing/ocf_ipc.c594
-rw-r--r--lib/clplumbing/proctrack.c515
-rw-r--r--lib/clplumbing/realtime.c354
-rw-r--r--lib/clplumbing/replytrack.c643
-rw-r--r--lib/clplumbing/setproctitle.c235
-rw-r--r--lib/clplumbing/timers.c119
-rwxr-xr-xlib/clplumbing/transient-test.sh120
-rw-r--r--lib/clplumbing/uids.c140
-rw-r--r--lib/lrm/Makefile.am36
-rw-r--r--lib/lrm/clientlib.c1612
-rw-r--r--lib/lrm/lrm_msg.c212
-rw-r--r--lib/lrm/racommon.c178
-rw-r--r--lib/pils/Makefile.am57
-rw-r--r--lib/pils/main.c122
-rw-r--r--lib/pils/pils.c2152
-rw-r--r--lib/pils/test.c107
-rw-r--r--lib/plugins/InterfaceMgr/HBauth.c171
-rw-r--r--lib/plugins/InterfaceMgr/Makefile.am33
-rw-r--r--lib/plugins/InterfaceMgr/generic.c452
-rw-r--r--lib/plugins/Makefile.am20
-rw-r--r--lib/plugins/compress/Makefile.am52
-rw-r--r--lib/plugins/compress/bz2.c142
-rw-r--r--lib/plugins/compress/zlib.c135
-rw-r--r--lib/plugins/lrm/Makefile.am58
-rw-r--r--lib/plugins/lrm/dbus/Makefile.am16
-rw-r--r--lib/plugins/lrm/dbus/com.ubuntu.Upstart.Instance.xml45
-rw-r--r--lib/plugins/lrm/dbus/com.ubuntu.Upstart.Job.xml71
-rw-r--r--lib/plugins/lrm/dbus/com.ubuntu.Upstart.xml57
-rw-r--r--lib/plugins/lrm/raexechb.c416
-rw-r--r--lib/plugins/lrm/raexeclsb.c609
-rw-r--r--lib/plugins/lrm/raexecocf.c496
-rw-r--r--lib/plugins/lrm/raexecupstart.c222
-rw-r--r--lib/plugins/lrm/upstart-dbus.c406
-rw-r--r--lib/plugins/lrm/upstart-dbus.h36
-rw-r--r--lib/plugins/stonith/Makefile.am216
-rw-r--r--lib/plugins/stonith/apcmaster.c822
-rw-r--r--lib/plugins/stonith/apcmastersnmp.c890
-rw-r--r--lib/plugins/stonith/apcmastersnmp.cfg.example39
-rw-r--r--lib/plugins/stonith/apcsmart.c1028
-rw-r--r--lib/plugins/stonith/apcsmart.cfg.example1
-rw-r--r--lib/plugins/stonith/baytech.c924
-rw-r--r--lib/plugins/stonith/bladehpi.c1101
-rw-r--r--lib/plugins/stonith/cyclades.c650
-rw-r--r--lib/plugins/stonith/drac3.c359
-rw-r--r--lib/plugins/stonith/drac3_command.c342
-rw-r--r--lib/plugins/stonith/drac3_command.h29
-rw-r--r--lib/plugins/stonith/drac3_hash.c106
-rw-r--r--lib/plugins/stonith/drac3_hash.h28
-rw-r--r--lib/plugins/stonith/external.c868
-rw-r--r--lib/plugins/stonith/external/Makefile.am33
-rw-r--r--lib/plugins/stonith/external/drac5.in113
-rw-r--r--lib/plugins/stonith/external/dracmc-telnet377
-rwxr-xr-xlib/plugins/stonith/external/hetzner139
-rw-r--r--lib/plugins/stonith/external/hmchttp218
-rw-r--r--lib/plugins/stonith/external/ibmrsa157
-rw-r--r--lib/plugins/stonith/external/ibmrsa-telnet320
-rw-r--r--lib/plugins/stonith/external/ipmi276
-rwxr-xr-xlib/plugins/stonith/external/ippower9258.in316
-rw-r--r--lib/plugins/stonith/external/kdumpcheck.in274
-rw-r--r--lib/plugins/stonith/external/libvirt298
-rw-r--r--lib/plugins/stonith/external/nut302
-rw-r--r--lib/plugins/stonith/external/rackpdu280
-rw-r--r--lib/plugins/stonith/external/riloe530
-rw-r--r--lib/plugins/stonith/external/ssh.in176
-rwxr-xr-xlib/plugins/stonith/external/vcenter280
-rw-r--r--lib/plugins/stonith/external/vmware216
-rw-r--r--lib/plugins/stonith/external/xen0253
-rwxr-xr-xlib/plugins/stonith/external/xen0-ha-dom0-stonith-helper72
-rwxr-xr-xlib/plugins/stonith/external/xen0-ha.in96
-rw-r--r--lib/plugins/stonith/ibmhmc.c1261
-rw-r--r--lib/plugins/stonith/ipmi_os_handler.c257
-rw-r--r--lib/plugins/stonith/ipmilan.c587
-rw-r--r--lib/plugins/stonith/ipmilan.h41
-rw-r--r--lib/plugins/stonith/ipmilan_command.c399
-rw-r--r--lib/plugins/stonith/ipmilan_test.c63
-rw-r--r--lib/plugins/stonith/meatware.c351
-rw-r--r--lib/plugins/stonith/null.c260
-rw-r--r--lib/plugins/stonith/nw_rpc100s.c779
-rw-r--r--lib/plugins/stonith/rcd_serial.c602
-rw-r--r--lib/plugins/stonith/rhcs.c1035
-rw-r--r--lib/plugins/stonith/ribcl.py.in101
-rw-r--r--lib/plugins/stonith/riloe.c338
-rw-r--r--lib/plugins/stonith/rps10.c1070
-rw-r--r--lib/plugins/stonith/ssh.c351
-rw-r--r--lib/plugins/stonith/stonith_config_xml.h157
-rw-r--r--lib/plugins/stonith/stonith_expect_helpers.h120
-rw-r--r--lib/plugins/stonith/stonith_plugin_common.h127
-rw-r--r--lib/plugins/stonith/stonith_signal.h68
-rw-r--r--lib/plugins/stonith/suicide.c274
-rw-r--r--lib/plugins/stonith/vacm.c485
-rw-r--r--lib/plugins/stonith/wti_mpc.c856
-rw-r--r--lib/plugins/stonith/wti_nps.c813
-rw-r--r--lib/stonith/Makefile.am54
-rw-r--r--lib/stonith/README31
-rw-r--r--lib/stonith/expect.c539
-rwxr-xr-xlib/stonith/ha_log.sh114
-rw-r--r--lib/stonith/main.c727
-rw-r--r--lib/stonith/meatclient.c152
-rw-r--r--lib/stonith/st_ttylock.c225
-rw-r--r--lib/stonith/stonith.c636
132 files changed, 54150 insertions, 0 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
new file mode 100644
index 0000000..5047e1d
--- /dev/null
+++ b/lib/Makefile.am
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2008 Andrew Beekhof
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+
+MAINTAINERCLEANFILES = Makefile.in
+SUBDIRS = pils clplumbing lrm stonith plugins
diff --git a/lib/clplumbing/GSource.c b/lib/clplumbing/GSource.c
new file mode 100644
index 0000000..48bb198
--- /dev/null
+++ b/lib/clplumbing/GSource.c
@@ -0,0 +1,1864 @@
+/*
+ * Copyright (c) 2002 Alan Robertson <alanr@unix.sh>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <lha_internal.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <clplumbing/cl_log.h>
+#include <clplumbing/cl_signal.h>
+#include <clplumbing/GSource_internal.h>
+#include <clplumbing/proctrack.h>
+#include <clplumbing/Gmain_timeout.h>
+#include <clplumbing/timers.h>
+
+#ifdef events
+# undef events
+#endif
+#ifdef revents
+# undef revents
+#endif
+
+#define DEFAULT_MAXDISPATCH 0
+#define DEFAULT_MAXDELAY 0
+#define OTHER_MAXDELAY 100
+
+/*
+ * On architectures with alignment constraints, our casting between
+ * "(GSource*)" and "(GFDSource_s*)" etc. causes trouble, because of
+ * the massive alignment requirements of "longclock_t".
+ *
+ * Use the following to store and fetch.
+ */
+static
+void
+lc_store(char *destptr, longclock_t value) {
+ longclock_t _ltt;
+ _ltt = value;
+ memcpy((destptr), &_ltt, sizeof(longclock_t));
+}
+
+static
+longclock_t
+lc_fetch(char *ptr) {
+ longclock_t _ltt;
+ memcpy(&_ltt, (ptr), sizeof(longclock_t));
+ return _ltt;
+}
+
+#define ERR_EVENTS (G_IO_ERR|G_IO_NVAL)
+#define INPUT_EVENTS (G_IO_IN|G_IO_PRI|G_IO_HUP)
+#define OUTPUT_EVENTS (G_IO_OUT)
+#define DEF_EVENTS (INPUT_EVENTS|ERR_EVENTS)
+
+#define WARN_DELAY(ms, mx, input) cl_log(LOG_WARNING \
+ , "%s: Dispatch function for %s was delayed" \
+ " %lu ms (> %lu ms) before being called (GSource: 0x%lx)" \
+ , __FUNCTION__, (input)->description, ms, mx \
+ , POINTER_TO_ULONG(input))
+
+#define EXPLAINDELAY(started, detected) cl_log(LOG_INFO \
+ , "%s: started at %llu should have started at %llu" \
+ , __FUNCTION__, (unsigned long long)started \
+ , (unsigned long long)detected)
+
+
+#define WARN_TOOLONG(ms, mx, input) cl_log(LOG_WARNING \
+ , "%s: Dispatch function for %s took too long to execute" \
+ ": %lu ms (> %lu ms) (GSource: 0x%lx)" \
+ , __FUNCTION__, (input)->description, ms, mx \
+ , POINTER_TO_ULONG(input))
+
+#define CHECK_DISPATCH_DELAY(i) { \
+ unsigned long ms; \
+ longclock_t dettime; \
+ dispstart = time_longclock(); \
+ dettime = lc_fetch((i)->detecttime); \
+ ms = longclockto_ms(sub_longclock(dispstart,dettime)); \
+ if ((i)->maxdispatchdelayms > 0 \
+ && ms > (i)->maxdispatchdelayms) { \
+ WARN_DELAY(ms, (i)->maxdispatchdelayms, (i)); \
+ EXPLAINDELAY(dispstart, dettime); \
+ } \
+}
+
+#define CHECK_DISPATCH_TIME(i) { \
+ unsigned long ms; \
+ longclock_t dispend = time_longclock(); \
+ ms = longclockto_ms(sub_longclock(dispend, dispstart)); \
+ if ((i)->maxdispatchms > 0 && ms > (i)->maxdispatchms) { \
+ WARN_TOOLONG(ms, (i)->maxdispatchms, (i)); \
+ } \
+ lc_store(((i)->detecttime), zero_longclock); \
+}
+
+#define WARN_TOOMUCH(ms, mx, input) cl_log(LOG_WARNING \
+ , "%s: working on %s took %ld ms (> %ld ms)" \
+ , __FUNCTION__, (input)->description, ms, mx);
+
+#define SAVESTART {funstart = time_longclock();}
+
+#define CHECKEND(input) { \
+ longclock_t funend = time_longclock(); \
+ long ms; \
+ ms = longclockto_ms(sub_longclock(funend, funstart)); \
+ if (ms > OTHER_MAXDELAY){ \
+ WARN_TOOMUCH(ms, ((long) OTHER_MAXDELAY), input); \
+ } \
+} \
+
+
+#ifndef _NSIG
+# define _NSIG 2*NSIG
+#endif
+
+static gboolean G_fd_prepare(GSource* source,
+ gint* timeout);
+static gboolean G_fd_check(GSource* source);
+static gboolean G_fd_dispatch(GSource* source,
+ GSourceFunc callback,
+ gpointer user_data);
+static void G_fd_destroy(GSource* source);
+
+static GSourceFuncs G_fd_SourceFuncs = {
+ G_fd_prepare,
+ G_fd_check,
+ G_fd_dispatch,
+ G_fd_destroy,
+};
+
+GSource*
+G_main_add_input(int priority,
+ gboolean can_recurse,
+ GSourceFuncs* funcs)
+{
+ GSource * input_source = g_source_new(funcs, sizeof(GSource));
+ if (input_source == NULL){
+ cl_log(LOG_ERR, "create glib source for input failed!");
+ }else {
+ g_source_set_priority(input_source, priority);
+ g_source_set_can_recurse(input_source, can_recurse);
+ if(g_source_attach(input_source, NULL) == 0){
+ cl_log(LOG_ERR, "attaching input_source to main context"
+ " failed!! ");
+ }
+ }
+
+ return input_source;
+}
+
+
+/*
+ * Add the given file descriptor to the gmainloop world.
+ */
+
+
+GFDSource*
+G_main_add_fd(int priority, int fd, gboolean can_recurse
+, gboolean (*dispatch)(int fd, gpointer user_data)
+, gpointer userdata
+, GDestroyNotify notify)
+{
+
+ GSource* source = g_source_new(&G_fd_SourceFuncs,
+ sizeof(GFDSource));
+ GFDSource* ret = (GFDSource*)source;
+
+ ret->magno = MAG_GFDSOURCE;
+ ret->maxdispatchdelayms = DEFAULT_MAXDELAY;
+ ret->maxdispatchms = DEFAULT_MAXDISPATCH;
+ ret->udata = userdata;
+ ret->dispatch = dispatch;
+ ret->gpfd.fd = fd;
+ ret->gpfd.events = DEF_EVENTS;
+ ret->gpfd.revents = 0;
+ ret->dnotify = notify;
+ lc_store((ret->detecttime), zero_longclock);
+
+ g_source_add_poll(source, &ret->gpfd);
+
+
+ g_source_set_priority(source, priority);
+
+ g_source_set_can_recurse(source, can_recurse);
+
+ ret->gsourceid = g_source_attach(source, NULL);
+ ret->description = "file descriptor";
+
+ if (ret->gsourceid == 0) {
+ g_source_remove_poll(source, &ret->gpfd);
+ memset(ret, 0, sizeof(GFDSource));
+ g_source_unref(source);
+ source = NULL;
+ ret = NULL;
+ }
+ return ret;
+}
+
+gboolean
+G_main_del_fd(GFDSource* fdp)
+{
+ GSource * source = (GSource*) fdp;
+
+
+ if (fdp->gsourceid <= 0) {
+ return FALSE;
+ }
+
+ g_source_remove_poll(source, &fdp->gpfd);
+ g_source_remove(fdp->gsourceid);
+ fdp->gsourceid = 0;
+ g_source_unref(source);
+
+ return TRUE;
+
+}
+
+void
+g_main_output_is_blocked(GFDSource* fdp)
+{
+ fdp->gpfd.events |= OUTPUT_EVENTS;
+}
+
+
+/*
+ * For pure file descriptor events, return FALSE because we
+ * have to poll to get events.
+ *
+ * Note that we don't modify 'timeout' either.
+ */
+static gboolean
+G_fd_prepare(GSource* source,
+ gint* timeout)
+{
+ GFDSource* fdp = (GFDSource*)source;
+ g_assert(IS_FDSOURCE(fdp));
+ return FALSE;
+}
+
+/*
+ * Did we notice any I/O events?
+ */
+
+static gboolean
+G_fd_check(GSource* source)
+
+{
+ GFDSource* fdp = (GFDSource*)source;
+ g_assert(IS_FDSOURCE(fdp));
+ if (fdp->gpfd.revents) {
+ lc_store((fdp->detecttime), time_longclock());
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * Some kind of event occurred - notify the user.
+ */
+static gboolean
+G_fd_dispatch(GSource* source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+
+ GFDSource* fdp = (GFDSource*)source;
+ longclock_t dispstart;
+ g_assert(IS_FDSOURCE(fdp));
+ CHECK_DISPATCH_DELAY(fdp);
+
+
+ /*
+ * Is output now unblocked?
+ *
+ * If so, turn off OUTPUT_EVENTS to avoid going into
+ * a tight poll(2) loop.
+ */
+ if (fdp->gpfd.revents & OUTPUT_EVENTS) {
+ fdp->gpfd.events &= ~OUTPUT_EVENTS;
+ }
+
+ if(fdp->dispatch) {
+ if(!(fdp->dispatch(fdp->gpfd.fd, fdp->udata))){
+ g_source_remove_poll(source,&fdp->gpfd);
+ g_source_unref(source);
+ CHECK_DISPATCH_TIME(fdp);
+ return FALSE;
+ }
+ CHECK_DISPATCH_TIME(fdp);
+ }
+
+ return TRUE;
+}
+
+/*
+ * Free up our data, and notify the user process...
+ */
+static void
+G_fd_destroy(GSource* source)
+{
+ GFDSource* fdp = (GFDSource*)source;
+ g_assert(IS_FDSOURCE(fdp));
+ fdp->gsourceid = 0;
+ if (fdp->dnotify) {
+ fdp->dnotify(fdp->udata);
+ }
+}
+
+
+/************************************************************
+ * Functions for IPC_Channels
+ ***********************************************************/
+gboolean G_CH_prepare_int(GSource* source,
+ gint* timeout);
+gboolean G_CH_check_int(GSource* source);
+
+gboolean G_CH_dispatch_int(GSource* source,
+ GSourceFunc callback,
+ gpointer user_data);
+void G_CH_destroy_int(GSource* source);
+
+
+static GSourceFuncs G_CH_SourceFuncs = {
+ G_CH_prepare_int,
+ G_CH_check_int,
+ G_CH_dispatch_int,
+ G_CH_destroy_int,
+};
+
+
+
+
+void
+set_IPC_Channel_dnotify(GCHSource* chp,
+ GDestroyNotify notify){
+ chp->dnotify = notify;
+}
+
+/*
+ * Add an IPC_channel to the gmainloop world...
+ */
+GCHSource*
+G_main_IPC_Channel_constructor(GSource* source, IPC_Channel* ch
+ , gpointer userdata
+ , GDestroyNotify notify)
+{
+ int rfd, wfd;
+ GCHSource* chp;
+
+ if( !source ) {
+ cl_log(LOG_WARNING, "%s:%d: got null source", __FUNCTION__,__LINE__);
+ return NULL;
+ }
+ if( !ch ) {
+ cl_log(LOG_WARNING, "%s:%d: got null channel", __FUNCTION__,__LINE__);
+ return NULL;
+ }
+ chp = (GCHSource*)source;
+
+ chp->magno = MAG_GCHSOURCE;
+ chp->maxdispatchdelayms = DEFAULT_MAXDELAY;
+ chp->maxdispatchms = DEFAULT_MAXDISPATCH;
+ lc_store((chp->detecttime), zero_longclock);
+ ch->refcount++;
+ chp->ch = ch;
+ chp->udata=userdata;
+ chp->dnotify = notify;
+ chp->dontread = FALSE;
+
+ rfd = ch->ops->get_recv_select_fd(ch);
+ wfd = ch->ops->get_send_select_fd(ch);
+
+ chp->fd_fdx = (rfd == wfd);
+
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG, "%s(sock=%d,%d)",__FUNCTION__, rfd,wfd);
+ }
+ chp->infd.fd = rfd;
+ chp->infd.events = DEF_EVENTS;
+ g_source_add_poll(source, &chp->infd);
+ if (!chp->fd_fdx) {
+ chp->outfd.fd = wfd;
+ chp->outfd.events = DEF_EVENTS;
+ g_source_add_poll(source, &chp->outfd);
+ }
+ chp->dispatch = NULL;
+ chp->description = "IPC channel(base)";
+ chp->gsourceid = 0;
+ return chp;
+}
+
+GCHSource*
+G_main_add_IPC_Channel(int priority, IPC_Channel* ch
+ , gboolean can_recurse
+ , gboolean (*dispatch)(IPC_Channel* source_data,
+ gpointer user_data)
+ , gpointer userdata
+ , GDestroyNotify notify)
+{
+ GCHSource *chp;
+ GSource *source;
+
+ if( !ch ) {
+ cl_log(LOG_WARNING, "%s:%d: got null channel", __FUNCTION__,__LINE__);
+ return NULL;
+ }
+ source = g_source_new(&G_CH_SourceFuncs,
+ sizeof(GCHSource));
+ G_main_IPC_Channel_constructor(source,ch,userdata,notify);
+
+ chp = (GCHSource*)source;
+ chp->dispatch = dispatch;
+
+ g_source_set_priority(source, priority);
+ g_source_set_can_recurse(source, can_recurse);
+
+ chp->gsourceid = g_source_attach(source, NULL);
+ chp->description = "IPC channel";
+
+
+ if (chp->gsourceid == 0) {
+ g_source_remove_poll(source, &chp->infd);
+ if (!chp->fd_fdx) {
+ g_source_remove_poll(source, &chp->outfd);
+ }
+ g_source_unref(source);
+ source = NULL;
+ chp = NULL;
+ }
+ return chp;
+}
+
+
+void /* Suspend reading from far end writer (flow control) */
+G_main_IPC_Channel_pause(GCHSource* chp)
+{
+ if (chp == NULL){
+ cl_log(LOG_ERR, "%s: invalid input", __FUNCTION__);
+ return;
+ }
+
+ chp->dontread = TRUE;
+ return;
+}
+
+
+void /* Resume reading from far end writer (un-flow-control) */
+G_main_IPC_Channel_resume(GCHSource* chp)
+{
+ if (chp == NULL){
+ cl_log(LOG_ERR, "%s: invalid input", __FUNCTION__);
+ return;
+ }
+
+ chp->dontread = FALSE;
+ return;
+
+}
+
+/*
+ * Delete an IPC_channel from the gmainloop world...
+ */
+gboolean
+G_main_del_IPC_Channel(GCHSource* chp)
+{
+ GSource* source = (GSource*) chp;
+
+ if (chp == NULL || chp->gsourceid <= 0) {
+ return FALSE;
+ }
+
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG, "%s(sock=%d)",__FUNCTION__, chp->infd.fd);
+ }
+ g_source_remove(chp->gsourceid);
+ chp->gsourceid = 0;
+ /* chp should (may) now be undefined */
+ g_source_unref(source);
+
+ return TRUE;
+}
+
+/*
+ * For IPC_CHANNEL events, enable output checking when needed
+ * and note when unread input is already queued.
+ *
+ * Note that we don't modify 'timeout' either.
+ */
+gboolean
+G_CH_prepare_int(GSource* source,
+ gint* timeout)
+{
+ GCHSource* chp = (GCHSource*)source;
+ longclock_t funstart;
+ gboolean ret;
+
+ g_assert(IS_CHSOURCE(chp));
+ SAVESTART;
+
+
+ if (chp->ch->ops->is_sending_blocked(chp->ch)) {
+ if (chp->fd_fdx) {
+ chp->infd.events |= OUTPUT_EVENTS;
+ }else{
+ chp->outfd.events |= OUTPUT_EVENTS;
+ }
+ }
+
+ if (chp->ch->recv_queue->current_qlen < chp->ch->recv_queue->max_qlen) {
+ chp->infd.events |= INPUT_EVENTS;
+ }else{
+ /*
+ * This also disables EOF events - until we
+ * read some of the packets we've already gotten
+ * This prevents a tight loop in poll(2).
+ */
+ chp->infd.events &= ~INPUT_EVENTS;
+ }
+
+ if (chp->dontread){
+ return FALSE;
+ }
+ ret = chp->ch->ops->is_message_pending(chp->ch);
+ if (ret) {
+ lc_store((chp->detecttime), time_longclock());
+ }
+ CHECKEND(chp);
+ return ret;
+}
+
+/*
+ * Did we notice any I/O events?
+ */
+
+gboolean
+G_CH_check_int(GSource* source)
+{
+
+ GCHSource* chp = (GCHSource*)source;
+ gboolean ret;
+ longclock_t funstart;
+
+ g_assert(IS_CHSOURCE(chp));
+ SAVESTART;
+
+
+ if (chp->dontread){
+ /* Make sure output gets unblocked */
+ chp->ch->ops->resume_io(chp->ch);
+ return FALSE;
+ }
+
+ ret = (chp->infd.revents != 0
+ || (!chp->fd_fdx && chp->outfd.revents != 0)
+ || chp->ch->ops->is_message_pending(chp->ch));
+ if (ret) {
+ lc_store((chp->detecttime), time_longclock());
+ }
+ CHECKEND(chp);
+ return ret;
+}
+
+/*
+ * Some kind of event occurred - notify the user.
+ */
+gboolean
+G_CH_dispatch_int(GSource * source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ GCHSource* chp = (GCHSource*)source;
+ longclock_t dispstart;
+ longclock_t resume_start = zero_longclock;
+
+ g_assert(IS_CHSOURCE(chp));
+ CHECK_DISPATCH_DELAY(chp);
+
+
+ if (chp->dontread){
+ return TRUE;
+ }
+
+ /* Is output now unblocked?
+ *
+ * If so, turn off OUTPUT_EVENTS to avoid going into
+ * a tight poll(2) loop.
+ */
+ if (chp->fd_fdx) {
+ if (chp->infd.revents & OUTPUT_EVENTS) {
+ chp->infd.events &= ~OUTPUT_EVENTS;
+ }
+ }else if (chp->outfd.revents & OUTPUT_EVENTS) {
+ chp->outfd.events &= ~OUTPUT_EVENTS;
+ }
+
+ if (ANYDEBUG) {
+ resume_start = time_longclock();
+ }
+
+ chp->ch->ops->resume_io(chp->ch);
+
+ if (ANYDEBUG) {
+ longclock_t resume_end = time_longclock();
+ unsigned long ms;
+ ms = longclockto_ms(sub_longclock(resume_end
+ , resume_start));
+ if (ms > 10) {
+ cl_log(LOG_WARNING
+ , "%s: resume_io() for %s took %lu ms"
+ , __FUNCTION__
+ , chp->description, ms);
+ }
+ }
+
+
+ if(chp->dispatch && chp->ch->ops->is_message_pending(chp->ch)) {
+ if(!(chp->dispatch(chp->ch, chp->udata))){
+ g_source_remove_poll(source, &chp->infd);
+ if (!chp->fd_fdx) {
+ g_source_remove_poll(source, &chp->outfd);
+ }
+ CHECK_DISPATCH_TIME(chp);
+ g_source_unref(source);
+ return FALSE;
+ }
+ }
+ CHECK_DISPATCH_TIME(chp);
+
+ if (chp->ch->ch_status == IPC_DISCONNECT){
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ * Free up our data, and notify the user process...
+ */
+void
+G_CH_destroy_int(GSource* source)
+{
+ GCHSource* chp = (GCHSource*)source;
+
+ g_assert(IS_CHSOURCE(chp));
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG, "%s(chp=0x%lx, sock=%d) {", __FUNCTION__
+ , (unsigned long)chp, chp->infd.fd);
+ }
+
+ if (chp->dnotify) {
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG
+ , "%s: Calling dnotify(sock=%d, arg=0x%lx) function"
+ , __FUNCTION__, chp->infd.fd, (unsigned long)chp->udata);
+ }
+ chp->dnotify(chp->udata);
+ }else{
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG
+ , "%s: NOT calling dnotify(sock=%d) function"
+ , __FUNCTION__, chp->infd.fd);
+ }
+ }
+ if (chp->ch) {
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG
+ , "%s: calling IPC destroy (chp->ch=0x%lx, sock=%d)"
+ , __FUNCTION__ , (unsigned long)chp->ch, chp->infd.fd);
+ }
+ chp->ch->ops->destroy(chp->ch);
+ chp->ch = NULL;
+ }
+ /*chp->gsourceid = 0; ?*/
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG, "}/*%s(sock=%d)*/", __FUNCTION__, chp->infd.fd);
+ }
+}
+
+
+/************************************************************
+ * Functions for IPC_WaitConnections
+ ***********************************************************/
+static gboolean G_WC_prepare(GSource * source,
+ gint* timeout);
+static gboolean G_WC_check(GSource* source);
+static gboolean G_WC_dispatch(GSource* source,
+ GSourceFunc callback,
+ gpointer user_data);
+static void G_WC_destroy(GSource* source);
+
+static GSourceFuncs G_WC_SourceFuncs = {
+ G_WC_prepare,
+ G_WC_check,
+ G_WC_dispatch,
+ G_WC_destroy,
+};
+
+
+/*
+ * Add an IPC_WaitConnection to the gmainloop world...
+ */
+GWCSource*
+G_main_add_IPC_WaitConnection(int priority
+, IPC_WaitConnection* wch
+, IPC_Auth* auth_info
+, gboolean can_recurse
+, gboolean (*dispatch)(IPC_Channel* wch
+, gpointer user_data)
+, gpointer userdata
+, GDestroyNotify notify)
+{
+
+ GWCSource* wcp;
+ GSource * source = g_source_new(&G_WC_SourceFuncs,
+ sizeof(GWCSource));
+
+ wcp = (GWCSource*)source;
+
+ wcp->magno = MAG_GWCSOURCE;
+ wcp->maxdispatchdelayms = DEFAULT_MAXDELAY;
+ wcp->maxdispatchms = DEFAULT_MAXDISPATCH;
+ lc_store((wcp->detecttime), zero_longclock);
+ wcp->udata = userdata;
+ wcp->gpfd.fd = wch->ops->get_select_fd(wch);
+ wcp->gpfd.events = DEF_EVENTS;
+ wcp->gpfd.revents = 0;
+ wcp->wch = wch;
+ wcp->dnotify = notify;
+ wcp->auth_info = auth_info;
+ wcp->dispatch = dispatch;
+
+ g_source_add_poll(source, &wcp->gpfd);
+
+ g_source_set_priority(source, priority);
+
+ g_source_set_can_recurse(source, can_recurse);
+
+ wcp->gsourceid = g_source_attach(source, NULL);
+ wcp->description = "IPC wait for connection";
+
+ if (wcp->gsourceid == 0) {
+ g_source_remove_poll(source, &wcp->gpfd);
+ g_source_unref(source);
+ source = NULL;
+ wcp = NULL;
+ }
+ return wcp;
+}
+
+
+/* Delete the given IPC_WaitConnection from the gmainloop world */
+gboolean
+G_main_del_IPC_WaitConnection(GWCSource* wcp)
+{
+
+ GSource* source = (GSource*) wcp;
+
+
+ if (wcp->gsourceid <= 0) {
+ return FALSE;
+ }
+
+ g_source_remove(wcp->gsourceid);
+ wcp->gsourceid = 0;
+ g_source_unref(source);
+
+ return TRUE;
+}
+
+
+
+/*
+ * For IPC_WaitConnection events, return FALSE because we
+ * have to poll to get events.
+ *
+ * We don't modify 'timeout' either.
+ */
+static gboolean
+G_WC_prepare(GSource* source,
+ gint* timeout)
+{
+ GWCSource* wcp = (GWCSource*)source;
+ g_assert(IS_WCSOURCE(wcp));
+ return FALSE;
+}
+
+/*
+ * Did we notice any I/O (connection pending) events?
+ */
+
+static gboolean
+G_WC_check(GSource * source)
+{
+ GWCSource* wcp = (GWCSource*)source;
+ g_assert(IS_WCSOURCE(wcp));
+
+ if (wcp->gpfd.revents != 0) {
+ lc_store((wcp->detecttime), time_longclock());
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * Someone is trying to connect.
+ * Try to accept the connection and notify the user.
+ */
+static gboolean
+G_WC_dispatch(GSource* source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ GWCSource* wcp = (GWCSource*)source;
+ IPC_Channel* ch;
+ gboolean rc = TRUE;
+ int count = 0;
+ longclock_t dispstart;
+
+ g_assert(IS_WCSOURCE(wcp));
+ CHECK_DISPATCH_DELAY(wcp);
+
+ while(1) {
+ ch = wcp->wch->ops->accept_connection(wcp->wch, wcp->auth_info);
+ if (ch == NULL) {
+ if (errno == EBADF) {
+ cl_perror("%s: Stopping accepting connections(socket=%d)!!"
+ , __FUNCTION__, wcp->gpfd.fd);
+ rc = FALSE;
+ }
+ break;
+ }
+ ++count;
+
+ if(!wcp->dispatch) {
+ continue;
+ }
+
+ rc = wcp->dispatch(ch, wcp->udata);
+ if(!rc) {
+ g_source_remove_poll(source, &wcp->gpfd);
+ g_source_unref(source);
+ break;
+ }
+ }
+ CHECK_DISPATCH_TIME(wcp);
+ return rc;
+}
+
+/*
+ * Free up our data, and notify the user process...
+ */
+static void
+G_WC_destroy(GSource* source)
+{
+
+ GWCSource* wcp = (GWCSource*)source;
+ wcp->gsourceid = 0;
+ g_assert(IS_WCSOURCE(wcp));
+ wcp->wch->ops->destroy(wcp->wch);
+ if (wcp->dnotify) {
+ wcp->dnotify(wcp->udata);
+ }
+}
+
+
+/************************************************************
+ * Functions for Signals
+ ***********************************************************/
+static gboolean G_SIG_prepare(GSource* source,
+ gint* timeout);
+static gboolean G_SIG_check(GSource* source);
+
+static gboolean G_SIG_dispatch(GSource* source,
+ GSourceFunc callback,
+ gpointer user_data);
+static void G_SIG_destroy(GSource* source);
+
+static void G_main_signal_handler(int nsig);
+
+static GSourceFuncs G_SIG_SourceFuncs = {
+ G_SIG_prepare,
+ G_SIG_check,
+ G_SIG_dispatch,
+ G_SIG_destroy,
+};
+
+static GSIGSource *G_main_signal_list[_NSIG];
+
+void
+set_SignalHandler_dnotify(GSIGSource* sig_src, GDestroyNotify notify)
+{
+ sig_src->dnotify = notify;
+}
+
+/*
+ * Add an Signal to the gmainloop world...
+ */
+GSIGSource*
+G_main_add_SignalHandler(int priority, int signal,
+ gboolean (*dispatch)(int nsig, gpointer user_data),
+ gpointer userdata, GDestroyNotify notify)
+{
+ GSIGSource* sig_src;
+ GSource * source = g_source_new(&G_SIG_SourceFuncs, sizeof(GSIGSource));
+ gboolean failed = FALSE;
+
+ sig_src = (GSIGSource*)source;
+
+ sig_src->magno = MAG_GSIGSOURCE;
+ sig_src->maxdispatchdelayms = DEFAULT_MAXDELAY;
+ sig_src->maxdispatchms = DEFAULT_MAXDISPATCH;
+ sig_src->signal = signal;
+ sig_src->dispatch = dispatch;
+ sig_src->udata = userdata;
+ sig_src->dnotify = notify;
+
+ sig_src->signal_triggered = FALSE;
+
+ g_source_set_priority(source, priority);
+ g_source_set_can_recurse(source, FALSE);
+
+ if(G_main_signal_list[signal] != NULL) {
+ cl_log(LOG_ERR
+ , "%s: Handler already present for signal %d"
+ , __FUNCTION__, signal);
+ failed = TRUE;
+ }
+ if(!failed) {
+ sig_src->gsourceid = g_source_attach(source, NULL);
+ sig_src->description = "signal";
+ if (sig_src->gsourceid < 1) {
+ cl_log(LOG_ERR
+ , "%s: Could not attach source for signal %d (%d)"
+ , __FUNCTION__
+ , signal, sig_src->gsourceid);
+ failed = TRUE;
+ }
+ }
+
+ if(failed) {
+ cl_log(LOG_ERR
+ , "%s: Signal handler for signal %d NOT added"
+ , __FUNCTION__, signal);
+ g_source_remove(sig_src->gsourceid);
+ g_source_unref(source);
+ source = NULL;
+ sig_src = NULL;
+ } else {
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG
+ , "%s: Added signal handler for signal %d"
+ , __FUNCTION__, signal);
+ }
+ G_main_signal_list[signal] = sig_src;
+ CL_SIGNAL(signal, G_main_signal_handler);
+ /*
+ * If we don't set this on, then the mainloop poll(2) call
+ * will never be interrupted by this signal - which sort of
+ * defeats the whole purpose of a signal handler in a
+ * mainloop program
+ */
+ cl_signal_set_interrupt(signal, TRUE);
+ }
+ return sig_src;
+}
+
+
+/*
+ * Delete a Signal from the gmainloop world...
+ */
+gboolean
+G_main_del_SignalHandler(GSIGSource* sig_src)
+{
+ GSource* source = (GSource*) sig_src;
+
+ if (sig_src->gsourceid <= 0) {
+ return FALSE;
+ }
+ if(_NSIG <= sig_src->signal) {
+ g_assert(_NSIG > sig_src->signal);
+ return FALSE;
+ }
+
+ CL_SIGNAL(sig_src->signal, NULL);
+
+ sig_src->signal_triggered = FALSE;
+ g_source_remove(sig_src->gsourceid);
+ G_main_signal_list[sig_src->signal] = NULL;
+ sig_src->gsourceid = 0;
+ g_source_unref(source);
+
+ return TRUE;
+}
+
+static gboolean
+G_SIG_prepare(GSource* source, gint* timeoutms)
+{
+ GSIGSource* sig_src = (GSIGSource*)source;
+
+ g_assert(IS_SIGSOURCE(sig_src));
+
+ /* Don't let a timing window keep us in poll() forever
+ *
+ * The timing window in question looks like this:
+ * No signal has occurred up to the point of prepare being called.
+ * Signal comes in _after_ prepare was called, but _before_ poll.
+ * signal_detected gets set, but no one checks it before going into poll
+ * We wait in poll forever... It's not a pretty sight :-(.
+ */
+ *timeoutms = 1000; /* Sigh... */
+
+ if (sig_src->signal_triggered) {
+ clock_t now;
+ clock_t diff;
+
+ /* detecttime is reset in the dispatch function */
+ if (cmp_longclock(lc_fetch(sig_src->detecttime), zero_longclock) != 0) {
+ cl_log(LOG_ERR, "%s: detecttime already set?", __FUNCTION__);
+ return TRUE;
+ }
+ /* Otherwise, this is when it was first detected */
+ now = cl_times();
+ diff = now - sig_src->sh_detecttime; /* How long since signal occurred? */
+ lc_store(
+ sig_src->detecttime,
+ sub_longclock(time_longclock(), (longclock_t)diff)
+ );
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * Did we notice any I/O events?
+ */
+
+static gboolean
+G_SIG_check(GSource* source)
+{
+
+ GSIGSource* sig_src = (GSIGSource*)source;
+
+ g_assert(IS_SIGSOURCE(sig_src));
+
+ if (sig_src->signal_triggered) {
+ clock_t now;
+ clock_t diff;
+ if (cmp_longclock(lc_fetch(sig_src->detecttime), zero_longclock) != 0){
+ return TRUE;
+ }
+ /* Otherwise, this is when it was first detected */
+ now = cl_times();
+ diff = now - sig_src->sh_detecttime;
+ lc_store(
+ sig_src->detecttime,
+ sub_longclock(time_longclock(), (longclock_t)diff)
+ );
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * Some kind of event occurred - notify the user.
+ */
+static gboolean
+G_SIG_dispatch(GSource * source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ GSIGSource* sig_src = (GSIGSource*)source;
+ longclock_t dispstart;
+
+ g_assert(IS_SIGSOURCE(sig_src));
+ CHECK_DISPATCH_DELAY(sig_src);
+
+ sig_src->sh_detecttime = 0UL;
+ sig_src->signal_triggered = FALSE;
+
+ if(sig_src->dispatch) {
+ if(!(sig_src->dispatch(sig_src->signal, sig_src->udata))){
+ G_main_del_SignalHandler(sig_src);
+ CHECK_DISPATCH_TIME(sig_src);
+ return FALSE;
+ }
+ }
+ CHECK_DISPATCH_TIME(sig_src);
+
+ return TRUE;
+}
+
+/*
+ * Free up our data, and notify the user process...
+ */
+static void
+G_SIG_destroy(GSource* source)
+{
+ GSIGSource* sig_src = (GSIGSource*)source;
+
+ g_assert(IS_SIGSOURCE(sig_src));
+ sig_src->gsourceid = 0;
+
+ if (sig_src->dnotify) {
+ sig_src->dnotify(sig_src->udata);
+ }
+}
+
+/* Find and set the correct mainloop input */
+
+static void
+G_main_signal_handler(int nsig)
+{
+ GSIGSource* sig_src = NULL;
+
+ if(G_main_signal_list == NULL) {
+ g_assert(G_main_signal_list != NULL);
+ return;
+ }
+ if(_NSIG <= nsig) {
+ g_assert(_NSIG > nsig);
+ return;
+ }
+
+ sig_src = G_main_signal_list[nsig];
+
+ if(sig_src == NULL) {
+ /* cl_log(LOG_CRIT, "No handler for signal -%d", nsig); */
+ return;
+ }
+
+ g_assert(IS_SIGSOURCE(sig_src));
+ /* Time from first occurance of signal */
+ if (!sig_src->signal_triggered) {
+ /* Avoid calling longclock_time() on a signal */
+ sig_src->sh_detecttime=cl_times();
+ }
+ sig_src->signal_triggered = TRUE;
+}
+
+/*
+ * Functions to handle child process
+ */
+
+#define WAITALARM 5000L /* milliseconds */
+
+static int alarm_count = 0;
+static void
+G_main_alarm_helper(int nsig)
+{
+ ++alarm_count;
+}
+
+static gboolean
+child_death_dispatch(int sig, gpointer notused)
+{
+ int status;
+ pid_t pid;
+ const int waitflags = WNOHANG;
+ struct sigaction saveaction;
+ int childcount = 0;
+
+ /*
+ * wait3(WNOHANG) isn't _supposed_ to hang
+ * Unfortunately, it seems to do just that on some OSes.
+ *
+ * The workaround is to set an alarm. I don't think for this purpose
+ * that it matters if siginterrupt(SIGALRM) is set TRUE or FALSE since
+ * the tiniest little excuse seems to cause the wait3() to finish.
+ */
+
+ memset(&saveaction, 0, sizeof(saveaction));
+ cl_signal_set_simple_handler(SIGALRM, G_main_alarm_helper, &saveaction);
+
+ alarm_count = 0;
+ cl_signal_set_interrupt(SIGALRM, TRUE);
+ setmsrepeattimer(WAITALARM); /* Might as well be persistent ;-) */
+ while((pid=wait3(&status, waitflags, NULL)) > 0
+ || (pid < 0 && errno == EINTR)) {
+ cancelmstimer();
+ if (pid > 0) {
+ ++childcount;
+ ReportProcHasDied(pid, status);
+ }
+ setmsrepeattimer(WAITALARM); /* Let's be persistent ;-) */
+ }
+ cancelmstimer();
+ cl_signal_set_simple_handler(SIGALRM, saveaction.sa_handler, &saveaction);
+
+ if (pid < 0 && errno != ECHILD) {
+ cl_perror("%s: wait3() failed"
+ , __FUNCTION__);
+ }
+#if defined(DEBUG)
+ if (childcount < 1) {
+ /*
+ * This happens when we receive a SIGCHLD after we clear
+ * 'sig_src->signal_triggered' in G_SIG_dispatch() but
+ * before the last wait3() call returns no child above.
+ */
+ cl_log(LOG_DEBUG, "NOTE: %s called without children to wait on"
+ , __FUNCTION__);
+ }
+#endif
+ if (alarm_count) {
+ cl_log(LOG_ERR
+ , "%s: wait3() call hung %d times. childcount = %d"
+ , __FUNCTION__, alarm_count, childcount);
+ alarm_count = 0;
+ }
+ return TRUE;
+}
+
+void
+set_sigchld_proctrack(int priority, unsigned long maxdisptime)
+{
+ GSIGSource* src = G_main_add_SignalHandler(priority, SIGCHLD
+ , child_death_dispatch, NULL, NULL);
+
+ G_main_setmaxdispatchdelay((GSource*) src, 100);
+ G_main_setmaxdispatchtime((GSource*) src, maxdisptime);
+ G_main_setdescription((GSource*)src, "SIGCHLD");
+ return;
+}
+
+
+/************************************************************
+ * Functions for Trigger inputs
+ ***********************************************************/
+static gboolean G_TRIG_prepare(GSource* source,
+ gint* timeout);
+static gboolean G_TRIG_check(GSource* source);
+
+static gboolean G_TRIG_dispatch(GSource* source,
+ GSourceFunc callback,
+ gpointer user_data);
+static void G_TRIG_destroy(GSource* source);
+
+static GSourceFuncs G_TRIG_SourceFuncs = {
+ G_TRIG_prepare,
+ G_TRIG_check,
+ G_TRIG_dispatch,
+ G_TRIG_destroy
+};
+
+void
+set_TriggerHandler_dnotify(GTRIGSource* trig_src, GDestroyNotify notify)
+{
+ trig_src->dnotify = notify;
+}
+
+/*
+ * Add an Trigger to the gmainloop world...
+ */
+GTRIGSource*
+G_main_add_TriggerHandler(int priority,
+ gboolean (*dispatch)(gpointer user_data),
+ gpointer userdata, GDestroyNotify notify)
+{
+ GTRIGSource* trig_src = NULL;
+ GSource * source = g_source_new(&G_TRIG_SourceFuncs, sizeof(GTRIGSource));
+ gboolean failed = FALSE;
+
+ trig_src = (GTRIGSource*)source;
+
+ trig_src->magno = MAG_GTRIGSOURCE;
+ trig_src->maxdispatchdelayms = DEFAULT_MAXDELAY;
+ trig_src->maxdispatchms = DEFAULT_MAXDISPATCH;
+ trig_src->dispatch = dispatch;
+ trig_src->udata = userdata;
+ trig_src->dnotify = notify;
+ lc_store((trig_src->detecttime), zero_longclock);
+
+ trig_src->manual_trigger = FALSE;
+
+ g_source_set_priority(source, priority);
+ g_source_set_can_recurse(source, FALSE);
+
+ if(!failed) {
+ trig_src->gsourceid = g_source_attach(source, NULL);
+ trig_src->description = "trigger";
+ if (trig_src->gsourceid < 1) {
+ cl_log(LOG_ERR, "G_main_add_TriggerHandler: Could not attach new source (%d)",
+ trig_src->gsourceid);
+ failed = TRUE;
+ }
+ }
+
+ if(failed) {
+ cl_log(LOG_ERR, "G_main_add_TriggerHandler: Trigger handler NOT added");
+ g_source_remove(trig_src->gsourceid);
+ g_source_unref(source);
+ source = NULL;
+ trig_src = NULL;
+ } else {
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG, "G_main_add_TriggerHandler: Added signal manual handler");
+ }
+ }
+
+ return trig_src;
+}
+
+void
+G_main_set_trigger(GTRIGSource* source)
+{
+ GTRIGSource* trig_src = (GTRIGSource*)source;
+
+ g_assert(IS_TRIGSOURCE(trig_src));
+
+ trig_src->manual_trigger = TRUE;
+ lc_store((trig_src->detecttime), time_longclock());
+}
+
+
+/*
+ * Delete a Trigger from the gmainloop world...
+ */
+gboolean
+G_main_del_TriggerHandler(GTRIGSource* trig_src)
+{
+ GSource* source = (GSource*) trig_src;
+
+ if (trig_src->gsourceid <= 0) {
+ return FALSE;
+ }
+ trig_src->gsourceid = 0;
+ trig_src->manual_trigger = FALSE;
+ g_source_remove(trig_src->gsourceid);
+ g_source_unref(source);
+
+ return TRUE;
+}
+
+static gboolean
+G_TRIG_prepare(GSource* source, gint* timeout)
+{
+ GTRIGSource* trig_src = (GTRIGSource*)source;
+
+ g_assert(IS_TRIGSOURCE(trig_src));
+
+
+ if (trig_src->manual_trigger
+ && cmp_longclock(lc_fetch(trig_src->detecttime), zero_longclock) == 0) {
+ lc_store((trig_src->detecttime), time_longclock());
+ }
+ return trig_src->manual_trigger;
+}
+
+/*
+ * Did we notice any I/O events?
+ */
+
+static gboolean
+G_TRIG_check(GSource* source)
+{
+
+ GTRIGSource* trig_src = (GTRIGSource*)source;
+
+ g_assert(IS_TRIGSOURCE(trig_src));
+ if (trig_src->manual_trigger
+ && cmp_longclock(lc_fetch(trig_src->detecttime), zero_longclock) == 0) {
+ lc_store((trig_src->detecttime), time_longclock());
+ }
+ return trig_src->manual_trigger;
+}
+
+/*
+ * Some kind of event occurred - notify the user.
+ */
+static gboolean
+G_TRIG_dispatch(GSource * source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ GTRIGSource* trig_src = (GTRIGSource*)source;
+ longclock_t dispstart;
+
+ g_assert(IS_TRIGSOURCE(trig_src));
+ CHECK_DISPATCH_DELAY(trig_src);
+
+ trig_src->manual_trigger = FALSE;
+
+ if(trig_src->dispatch) {
+ if(!(trig_src->dispatch(trig_src->udata))){
+ G_main_del_TriggerHandler(trig_src);
+ CHECK_DISPATCH_TIME(trig_src);
+ return FALSE;
+ }
+ CHECK_DISPATCH_TIME(trig_src);
+ }
+ lc_store((trig_src->detecttime), zero_longclock);
+
+ return TRUE;
+}
+
+/*
+ * Free up our data, and notify the user process...
+ */
+static void
+G_TRIG_destroy(GSource* source)
+{
+ GTRIGSource* trig_src = (GTRIGSource*)source;
+
+ g_assert(IS_TRIGSOURCE(trig_src));
+ trig_src->gsourceid = 0;
+
+ if (trig_src->dnotify) {
+ trig_src->dnotify(trig_src->udata);
+ }
+}
+/*
+ * Glib mainloop timeout handling code.
+ *
+ * These functions work correctly even if someone resets the
+ * time-of-day clock. The g_main_timeout_add() function does not have
+ * this property, since it relies on gettimeofday().
+ *
+ * Our functions have the same semantics - except they always work ;-)
+ *
+ * This is because we use longclock_t for our time values.
+ *
+ */
+
+
+static gboolean
+Gmain_timeout_prepare(GSource* src, gint* timeout);
+
+static gboolean
+Gmain_timeout_check(GSource* src);
+
+static gboolean
+Gmain_timeout_dispatch(GSource* src, GSourceFunc func, gpointer user_data);
+
+static GSourceFuncs Gmain_timeout_funcs = {
+ Gmain_timeout_prepare,
+ Gmain_timeout_check,
+ Gmain_timeout_dispatch,
+};
+
+
+struct GTimeoutAppend {
+ COMMON_STRUCTSTART;
+ longclock_t nexttime;
+ guint interval;
+};
+
+#define GTIMEOUT(GS) ((struct GTimeoutAppend*)((void*)(GS)))
+
+guint
+Gmain_timeout_add(guint interval
+, GSourceFunc function
+, gpointer data)
+{
+ return Gmain_timeout_add_full(G_PRIORITY_DEFAULT
+ , interval, function, data, NULL);
+}
+
+guint
+Gmain_timeout_add_full(gint priority
+, guint interval
+, GSourceFunc function
+, gpointer data
+, GDestroyNotify notify)
+{
+
+ struct GTimeoutAppend* append;
+
+ GSource* source = g_source_new( &Gmain_timeout_funcs,
+ sizeof(struct GTimeoutAppend));
+
+ append = GTIMEOUT(source);
+ append->magno = MAG_GTIMEOUTSRC;
+ append->maxdispatchms = DEFAULT_MAXDISPATCH;
+ append->maxdispatchdelayms = DEFAULT_MAXDELAY;
+ append->description = "(timeout)";
+ lc_store((append->detecttime), zero_longclock);
+ append->udata = NULL;
+
+ append->nexttime = add_longclock(time_longclock()
+ , msto_longclock(interval));
+ append->interval = interval;
+
+ g_source_set_priority(source, priority);
+
+ g_source_set_can_recurse(source, FALSE);
+
+ g_source_set_callback(source, function, data, notify);
+
+ append->gsourceid = g_source_attach(source, NULL);
+ g_source_unref(source);
+ return append->gsourceid;
+
+}
+
+void
+Gmain_timeout_remove(guint tag)
+{
+ GSource* source = g_main_context_find_source_by_id(NULL,tag);
+ struct GTimeoutAppend* append = GTIMEOUT(source);
+
+ if (source == NULL){
+ cl_log(LOG_ERR, "Attempt to remove timeout (%u)"
+ " with NULL source", tag);
+ }else{
+ g_assert(IS_TIMEOUTSRC(append));
+ g_source_remove(tag);
+ }
+
+ return;
+}
+
+/* g_main_loop-style prepare function */
+static gboolean
+Gmain_timeout_prepare(GSource* src, gint* timeout)
+{
+
+ struct GTimeoutAppend* append = GTIMEOUT(src);
+ longclock_t lnow = time_longclock();
+ longclock_t remain;
+
+ g_assert(IS_TIMEOUTSRC(append));
+ if (cmp_longclock(lnow, append->nexttime) >= 0) {
+ *timeout = 0L;
+ return TRUE;
+ }
+ /* This is safe - we will always have a positive result */
+ remain = sub_longclock(append->nexttime, lnow);
+ /* This is also safe - we started out in 'ms' */
+ *timeout = longclockto_ms(remain);
+ return ((*timeout) == 0);
+}
+
+/* g_main_loop-style check function */
+static gboolean
+Gmain_timeout_check (GSource* src)
+{
+ struct GTimeoutAppend* append = GTIMEOUT(src);
+ longclock_t lnow = time_longclock();
+
+ g_assert(IS_TIMEOUTSRC(append));
+ if (cmp_longclock(lnow, append->nexttime) >= 0) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* g_main_loop-style dispatch function */
+static gboolean
+Gmain_timeout_dispatch(GSource* src, GSourceFunc func, gpointer user_data)
+{
+ struct GTimeoutAppend* append = GTIMEOUT(src);
+ longclock_t dispstart;
+ gboolean ret;
+
+ g_assert(IS_TIMEOUTSRC(append));
+ lc_store(append->detecttime, append->nexttime);
+ CHECK_DISPATCH_DELAY(append);
+
+
+ /* Schedule our next dispatch */
+ append->nexttime = add_longclock(time_longclock()
+ , msto_longclock(append->interval));
+
+ /* Then call the user function */
+ ret = func(user_data);
+
+ CHECK_DISPATCH_TIME(append);
+ return ret;
+}
+void
+G_main_setmaxdispatchdelay(GSource* s, unsigned long delayms)
+{
+ GFDSource* fdp = (GFDSource*)s;
+ if (!IS_ONEOFOURS(fdp)) {
+ cl_log(LOG_ERR
+ , "Attempt to set max dispatch delay on wrong object");
+ return;
+ }
+ fdp->maxdispatchdelayms = delayms;
+}
+void
+G_main_setmaxdispatchtime(GSource* s, unsigned long dispatchms)
+{
+ GFDSource* fdp = (GFDSource*)s;
+ if (!IS_ONEOFOURS(fdp)) {
+ cl_log(LOG_ERR
+ , "Attempt to set max dispatch time on wrong object");
+ return;
+ }
+ fdp->maxdispatchms = dispatchms;
+}
+void
+G_main_setdescription(GSource* s, const char * description)
+{
+ GFDSource* fdp = (GFDSource*)s;
+ if (!IS_ONEOFOURS(fdp)) {
+ cl_log(LOG_ERR
+ , "Attempt to set max dispatch time on wrong object");
+ return;
+ }
+ fdp->description = description;
+}
+void
+G_main_setmaxdispatchdelay_id(guint id, unsigned long delayms)
+{
+ GSource* source = g_main_context_find_source_by_id(NULL,id);
+
+ if (source) {
+ G_main_setmaxdispatchdelay(source, delayms);
+ }
+}
+void
+G_main_setmaxdispatchtime_id(guint id, unsigned long dispatchms)
+{
+ GSource* source = g_main_context_find_source_by_id(NULL,id);
+
+ if (source) {
+ G_main_setmaxdispatchtime(source, dispatchms);
+ }
+}
+void
+G_main_setdescription_id(guint id, const char * description)
+{
+ GSource* source = g_main_context_find_source_by_id(NULL,id);
+
+ if (source) {
+ G_main_setdescription(source, description);
+ }
+}
+void
+G_main_setall_id(guint id, const char * description, unsigned long delay
+, unsigned long elapsed)
+{
+ G_main_setdescription_id(id, description);
+ G_main_setmaxdispatchdelay_id(id, delay);
+ G_main_setmaxdispatchtime_id(id, elapsed);
+}
+
+static void TempProcessRegistered(ProcTrack* p);
+static void TempProcessDied(ProcTrack* p, int status, int signo
+, int exitcode, int waslogged);
+static const char* TempProcessName(ProcTrack* p);
+
+/***********************************************************************
+ * Track our temporary child processes...
+ *
+ * We run no more than one of each type at once.
+ * If we need to run some and one is still running we run another one
+ * when this one exits.
+ *
+ * Requests to run a child process don't add up. So, 3 requests to run
+ * a child while one is running only cause it to be run once more, not
+ * three times.
+ *
+ * The only guarantee is that a new child process will run after a request
+ * was made.
+ *
+ * To create the possibility of running a particular type of child process
+ * call G_main_add_tempproc_trigger().
+ *
+ * To cause it to be run, call G_main_set_trigger().
+ *
+ ***********************************************************************/
+
+static ProcTrack_ops TempProcessTrackOps = {
+ TempProcessDied,
+ TempProcessRegistered,
+ TempProcessName
+};
+
+/*
+ * Information for tracking our generic temporary child processes.
+ */
+struct tempproc_track {
+ const char * procname; /* name of the process*/
+ GTRIGSource* trigger; /* Trigger for this event */
+ int (*fun)(gpointer userdata); /* Function to call
+ * in child process */
+ void (*prefork)(gpointer userdata);/* Call before fork */
+ void (*postfork)(gpointer userdata);/* Call after fork */
+ void (*complete)(gpointer userdata, int status, int signo, int exitcode);/* Call after complete */
+ gpointer userdata; /* Info to pass 'fun' */
+ gboolean isrunning; /* TRUE if child is running */
+ gboolean runagain; /* TRUE if we need to run
+ * again after child process
+ * finishes.
+ */
+};
+static void
+TempProcessRegistered(ProcTrack* p)
+{
+ return; /* Don't need to do much here... */
+}
+
+static void
+TempProcessDied(ProcTrack* p, int status, int signo, int exitcode
+, int waslogged)
+{
+ struct tempproc_track * pt = p->privatedata;
+
+ if (pt->complete) {
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG
+ , "%s: Calling 'complete' for temp process %s"
+ , __FUNCTION__, pt->procname);
+ }
+ pt->complete(pt->userdata, status, signo, exitcode);
+ }
+
+ pt->isrunning=FALSE;
+ if (pt->runagain) {
+ pt->runagain=FALSE;
+
+ /* Do it again, Sam! */
+ G_main_set_trigger(pt->trigger);
+
+ /* Note that we set the trigger for this, we don't
+ * fork or call the function now.
+ *
+ * This allows the mainloop scheduler to decide
+ * when the fork should happen according to the priority
+ * of this trigger event - NOT according to the priority
+ * of general SIGCHLD handling.
+ */
+ }
+ p->privatedata = NULL; /* Don't free until trigger is destroyed */
+ return;
+}
+
+static const char *
+TempProcessName(ProcTrack* p)
+{
+ struct tempproc_track * pt = p->privatedata;
+ return pt->procname;
+}
+/*
+ * Make sure only one copy is running at a time...
+ */
+static gboolean
+TempProcessTrigger(gpointer ginfo)
+{
+ struct tempproc_track* info = ginfo;
+ int pid;
+
+ /* Make sure only one copy is running at a time. */
+ /* This avoids concurrency problems. */
+ if (info->isrunning) {
+ info->runagain = TRUE;
+ return TRUE;
+ }
+ info->isrunning = TRUE;
+
+ if (info->prefork) {
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG
+ , "%s: Calling prefork for temp process %s"
+ , __FUNCTION__, info->procname);
+ }
+ info->prefork(info->userdata);
+ }
+ if (ANYDEBUG) {
+ cl_log(LOG_DEBUG, "Forking temp process %s", info->procname);
+ }
+ switch ((pid=fork())) {
+ int rc;
+ case -1: cl_perror("%s: Can't fork temporary child"
+ " process [%s]!", __FUNCTION__
+ , info->procname);
+ info->isrunning = FALSE;
+ break;
+
+ case 0: /* Child */
+ if ((rc=info->fun(info->userdata)) == HA_OK) {
+ exit(0);
+ }
+ cl_log(LOG_WARNING
+ , "%s: %s returns %d", __FUNCTION__
+ , info->procname, rc);
+ exit(1);
+ break;
+ default:
+ /* Fall out */;
+
+ }
+ if (pid > 0) {
+ NewTrackedProc(pid, 0, (ANYDEBUG? PT_LOGVERBOSE : PT_LOGNORMAL)
+ , ginfo, &TempProcessTrackOps);
+ if (info->postfork) {
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG
+ , "%s: Calling postfork for temp process %s"
+ , __FUNCTION__, info->procname);
+ }
+ info->postfork(info->userdata);
+ }
+ }
+ return TRUE;
+}
+
+static void
+tempproc_destroy_notify(gpointer userdata)
+{
+ if (userdata != NULL) {
+ free(userdata);
+ userdata = NULL;
+ }
+}
+
+GTRIGSource*
+G_main_add_tempproc_trigger(int priority
+, int (*triggerfun) (gpointer p)
+, const char * procname
+, gpointer userdata
+, void (*prefork)(gpointer p)
+, void (*postfork)(gpointer p)
+, void (*complete)(gpointer userdata, int status, int signo, int exitcode))
+{
+
+ struct tempproc_track* p;
+ GTRIGSource* ret;
+
+ p = (struct tempproc_track *) malloc(sizeof(struct tempproc_track));
+ if (p == NULL) {
+ return NULL;
+ }
+
+ memset(p, 0, sizeof(*p));
+ p->procname = procname;
+ p->fun = triggerfun;
+ p->userdata = userdata;
+ p->prefork = prefork;
+ p->postfork = postfork;
+ p->complete = complete;
+
+ ret = G_main_add_TriggerHandler(priority
+ , TempProcessTrigger, p, tempproc_destroy_notify);
+
+ if (ret == NULL) {
+ free(p);
+ p = NULL;
+ }else{
+ p->trigger = ret;
+ }
+ return ret;
+}
diff --git a/lib/clplumbing/Makefile.am b/lib/clplumbing/Makefile.am
new file mode 100644
index 0000000..1b504fc
--- /dev/null
+++ b/lib/clplumbing/Makefile.am
@@ -0,0 +1,99 @@
+#
+# plumbing: OCF general plumbing libraries
+#
+# Copyright (C) 2002 Alan Robertson, International Business Machines
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+
+halibdir = $(libdir)/@HB_PKG@
+
+INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \
+ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \
+ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl
+
+## libraries
+
+lib_LTLIBRARIES = libplumb.la libplumbgpl.la
+
+libplumb_la_SOURCES = \
+ base64.c \
+ cl_compress.c \
+ cl_log.c \
+ cl_misc.c \
+ cl_msg.c \
+ cl_msg_types.c \
+ cl_netstring.c \
+ cl_pidfile.c \
+ cl_poll.c \
+ cl_random.c \
+ cl_signal.c \
+ cl_syslog.c \
+ cl_uuid.c \
+ cl_plugin.c \
+ cl_reboot.c \
+ coredumps.c \
+ cpulimits.c \
+ GSource.c \
+ ipcsocket.c \
+ longclock.c \
+ md5.c \
+ mkstemp_mode.c \
+ ocf_ipc.c \
+ proctrack.c \
+ realtime.c \
+ replytrack.c \
+ timers.c \
+ uids.c
+
+libplumb_la_LIBADD = $(top_builddir)/replace/libreplace.la \
+ $(top_builddir)/lib/pils/libpils.la
+libplumb_la_LDFLAGS = -version-info 3:0:1
+
+libplumbgpl_la_SOURCES = setproctitle.c
+libplumbgpl_la_LIBADD = $(top_builddir)/replace/libreplace.la \
+ $(top_builddir)/lib/pils/libpils.la
+libplumbgpl_la_LDFLAGS = -version-info 2:0:0
+
+testdir = $(libdir)/@HB_PKG@
+test_PROGRAMS = ipctest ipctransientclient ipctransientserver base64_md5_test
+test_SCRIPTS = transient-test.sh
+
+ipctest_SOURCES = ipctest.c
+ipctest_LDADD = libplumb.la $(top_builddir)/replace/libreplace.la $(GLIBLIB) \
+ $(top_builddir)/lib/pils/libpils.la
+
+noinst_HEADERS = ipctransient.h
+
+#ipctransient_SOURCES = ipctransient.c
+#ipctransient_LDADD = libplumb.la $(top_builddir)/replace/libreplace.la $(top_builddir)/heartbeat/libhbclient.la $(GLIBLIB)
+
+ipctransientclient_SOURCES = ipctransientclient.c ipctransientlib.c
+ipctransientclient_LDADD = libplumb.la $(top_builddir)/replace/libreplace.la $(GLIBLIB) \
+ $(top_builddir)/lib/pils/libpils.la
+
+ipctransientserver_SOURCES = ipctransientserver.c ipctransientlib.c
+ipctransientserver_LDADD = libplumb.la $(top_builddir)/replace/libreplace.la $(GLIBLIB) \
+ $(top_builddir)/lib/pils/libpils.la
+
+#netstring_test_SOURCES = netstring_test.c
+#netstring_test_LDADD = libplumb.la $(top_builddir)/replace/libreplace.la libhbclient.la $(gliblib)
+
+base64_md5_test_SOURCES = base64_md5_test.c
+base64_md5_test_LDADD = libplumb.la $(top_builddir)/replace/libreplace.la $(GLIBLIB)
+
+EXTRA_DIST = $(test_SCRIPTS)
diff --git a/lib/clplumbing/base64.c b/lib/clplumbing/base64.c
new file mode 100644
index 0000000..c8ad325
--- /dev/null
+++ b/lib/clplumbing/base64.c
@@ -0,0 +1,422 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <lha_internal.h>
+#include <syslog.h>
+#include <string.h>
+#include <stdlib.h>
+#include "clplumbing/base64.h"
+
+/*
+ *
+ * Base64 conversion functions.
+ * They convert from a binary array into a single string
+ * in base 64. This is almost (but not quite) like section 5.2 of RFC 1341
+ * The only difference is that we don't care about line lengths.
+ * We do use their encoding algorithm.
+ *
+ */
+
+
+static char b64chars[]
+= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+#define EQUALS '='
+#define MASK6 (077)
+#define MASK24 (077777777)
+
+
+/* Convert from binary to a base64 string (~ according to RFC1341) */
+int
+binary_to_base64(const void * data, int nbytes, char * output, int outlen)
+{
+ int requiredlen = B64_stringlen(nbytes)+1; /* EOS */
+ char * outptr;
+ const unsigned char * inmax;
+ const unsigned char * inlast;
+ const unsigned char * inptr;
+ int bytesleft;
+
+ if (outlen < requiredlen) {
+ syslog(LOG_ERR, "binary_to_base64: output area too small.");
+ return -1;
+ }
+
+ inptr = data;
+ /* Location of last whole 3-byte chunk */
+ inmax = inptr + ((nbytes / B64inunit)*B64inunit);
+ inlast = inptr + nbytes;
+ outptr = output;
+
+
+ /* Convert whole 3-byte chunks */
+ for (;inptr < inmax; inptr += B64inunit) {
+ unsigned long chunk;
+ unsigned int sixbits;
+
+ chunk = ((*inptr) << 16
+ | ((*(inptr+1)) << 8)
+ | (*(inptr+2))) & MASK24;
+
+ sixbits = (chunk >> 18) & MASK6;
+ *outptr = b64chars[sixbits]; ++outptr;
+
+ sixbits = (chunk >> 12) & MASK6;
+ *outptr = b64chars[sixbits]; ++outptr;
+
+ sixbits = (chunk >> 6) & MASK6;
+ *outptr = b64chars[sixbits]; ++outptr;
+
+ sixbits = (chunk & MASK6);
+ *outptr = b64chars[sixbits]; ++outptr;
+ }
+
+ /* Do we have anything left over? */
+
+ bytesleft = inlast - inptr;
+ if (bytesleft > 0) {
+ /* bytesleft can only be 1 or 2 */
+
+ unsigned long chunk;
+ unsigned int sixbits;
+
+
+ /* Grab first byte */
+ chunk = (*inptr) << 16;
+
+ if (bytesleft == 2) {
+ /* Grab second byte */
+ chunk |= ((*(inptr+1)) << 8);
+ }
+ chunk &= MASK24;
+
+ /* OK, now we have our chunk... */
+ sixbits = (chunk >> 18) & MASK6;
+ *outptr = b64chars[sixbits]; ++outptr;
+ sixbits = (chunk >> 12) & MASK6;
+ *outptr = b64chars[sixbits]; ++outptr;
+
+ if (bytesleft == 2) {
+ sixbits = (chunk >> 6) & MASK6;
+ *outptr = b64chars[sixbits];
+ }else{
+ *outptr = EQUALS;
+ }
+ ++outptr;
+
+ *outptr = EQUALS; ++outptr;
+ }
+ *outptr = EOS; /* Don't increment */
+ return (outptr - output);
+}
+
+
+/* This macro is only usable by the base64_to_binary() function.
+ *
+ * There are faster ways of doing this, but I didn't spend the time
+ * to implement one of them. If we have an array of six bit values,
+ * sized by 256 or so, then we could look it up.
+ * FIXME: This is how it really ought to be done...
+ */
+
+static unsigned char b64values [256];
+#define BADVALUE 0xff
+
+static void
+init_b64_values(void)
+{
+ int j;
+ memset(b64values, BADVALUE, sizeof(b64values));
+
+ for (j=0; b64chars[j] != EOS; ++j) {
+ b64values[(int)b64chars[j]] = (unsigned char)j;
+ }
+}
+
+
+#define Char2SixBits(in, out) { \
+ unsigned char ch; \
+ ch = b64values[(unsigned int)in]; \
+ if (ch == BADVALUE) { \
+ syslog(LOG_ERR \
+ , "base64_to_binary: invalid input [%c]!" \
+ , in); \
+ return -1; \
+ } \
+ out = ch; \
+ } \
+
+
+/* Convert from a base64 string (~ according to RFC1341) to binary */
+int
+base64_to_binary(const char * in, int inlen, void * output, int outlen)
+{
+ int maxbinlen = B64_maxbytelen(inlen); /* Worst case size */
+ const char * input = in;
+ const char * lastinput = in + inlen - B64outunit;
+ int equalcount = 0;
+ unsigned char * startout;
+ unsigned char * out;
+ unsigned sixbits1;
+ unsigned sixbits2;
+ unsigned sixbits3;
+ unsigned sixbits4;
+ unsigned long chunk;
+ static int inityet = 0;
+
+ if (!inityet) {
+ inityet=1;
+ init_b64_values();
+ }
+
+ /* Make sure we have enough room */
+ if (outlen < maxbinlen) {
+ int residue = maxbinlen - outlen;
+
+ if (residue > 2
+ || input[inlen-1] != EQUALS
+ || (residue == 2 && input[inlen-2] != EQUALS)) {
+ syslog(LOG_ERR
+ , "base64_to_binary: output area too small.");
+ return -1;
+ }
+ }
+ if ((inlen % 4) != 0) {
+ syslog(LOG_ERR
+ , "base64_to_binary: input length invalid.");
+ return -1;
+ }
+
+ if (inlen == 0) {
+ return 0;
+ }
+
+ /* We have enough space. We are happy :-) */
+
+ startout = out = (unsigned char *)output;
+
+ while (input < lastinput) {
+ Char2SixBits(*input, sixbits1); ++input;
+ Char2SixBits(*input, sixbits2); ++input;
+ Char2SixBits(*input, sixbits3); ++input;
+ Char2SixBits(*input, sixbits4); ++input;
+
+ chunk = (sixbits1 << 18)
+ | (sixbits2 << 12) | (sixbits3 << 6) | sixbits4;
+
+ *out = ((chunk >> 16) & 0xff); ++out;
+ *out = ((chunk >> 8) & 0xff); ++out;
+ *out = (chunk & 0xff); ++out;
+ }
+
+ /* Process last 4 chars of input (1 to 3 bytes of output) */
+
+ /* The first two input chars must be normal chars */
+ Char2SixBits(*input, sixbits1); ++input;
+ Char2SixBits(*input, sixbits2); ++input;
+
+ /* We should find one of: (char,char) (char,=) or (=,=) */
+ /* We then output: (3 bytes) (2 bytes) (1 byte) */
+
+ if (*input == EQUALS) {
+ /* The (=,=): 1 byte case */
+ equalcount=2;
+ sixbits3 = 0;
+ sixbits4 = 0;
+ /* We assume the 2nd char is an = sign :-) */
+ }else{
+ /* We have either the (char,char) or (char,=) case */
+ Char2SixBits(*input, sixbits3); ++input;
+ if (*input == EQUALS) {
+ /* The (char, =): 2 bytes case */
+ equalcount=1;
+ sixbits4 = 0;
+ }else{
+ /* The (char, char): 3 bytes case */
+ Char2SixBits(*input, sixbits4); ++input;
+ equalcount=0;
+ }
+ }
+
+ chunk = (sixbits1 << 18)
+ | (sixbits2 << 12) | (sixbits3 << 6) | sixbits4;
+
+ /* We always have one more char to output... */
+ *out = ((chunk >> 16) & 0xff); ++out;
+
+ if (equalcount < 2) {
+ /* Zero or one equal signs: total of 2 or 3 bytes output */
+ *out = ((chunk >> 8) & 0xff); ++out;
+
+ if (equalcount < 1) {
+ /* No equal signs: total of 3 bytes output */
+ *out = (chunk & 0xff); ++out;
+ }
+ }
+
+ return out - startout;
+}
+
+#if 0
+#define RAND(upb) (rand()%(upb))
+
+void dumpbin(void * Bin, int length);
+void randbin(void * Bin, int length);
+
+void
+dumpbin(void * Bin, int length)
+{
+ unsigned char * bin = Bin;
+
+ int j;
+
+ for (j=0; j < length; ++j) {
+ fprintf(stderr, "%02x ", bin[j]);
+ if ((j % 32) == 31) {
+ fprintf(stderr, "\n");
+ }
+ }
+ fprintf(stderr, "\n");
+}
+
+void
+randbin(void * Bin, int length)
+{
+ unsigned char * bin = Bin;
+ int j;
+
+ for (j=0; j < length; ++j) {
+ bin[j] = (unsigned char)RAND(256);
+ }
+
+}
+
+#define MAXLEN 320
+#define MAXSTRING B64_stringlen(MAXLEN)+1
+#define MAXITER 300000
+int
+main(int argc, char ** argv)
+{
+ int errcount = 0;
+ char origbin[MAXLEN+1];
+ char sourcebin[MAXLEN+1];
+ char destbin[MAXLEN+1];
+ char deststr[MAXSTRING];
+ int maxiter = MAXITER;
+ int j;
+
+ for (j=0; j < maxiter; ++j) {
+ int iterlen = RAND(MAXLEN+1);
+ int slen;
+ int blen;
+
+ if ((j%100) == 99) {
+ fprintf(stderr, "+");
+ }
+
+ memset(origbin, 0, MAXLEN+1);
+ memset(sourcebin, 0, MAXLEN+1);
+ memset(destbin, 0, MAXLEN+1);
+ randbin(origbin, iterlen);
+ origbin[iterlen] = 0x1;
+ memcpy(sourcebin, origbin, iterlen);
+ sourcebin[iterlen] = 0x2;
+ slen = binary_to_base64(sourcebin, iterlen, deststr, MAXSTRING);
+ if (slen < 0) {
+ fprintf(stderr
+ , "binary_to_base64 failure: length %d\n"
+ , iterlen);
+ ++errcount;
+ continue;
+ }
+ if (strlen(deststr) != slen) {
+ fprintf(stderr
+ , "binary_to_base64 failure: length was %d (strlen) vs %d (ret value)\n"
+ , strlen(deststr), slen);
+ fprintf(stderr, "binlen: %d, deststr: [%s]\n"
+ , iterlen, deststr);
+ continue;
+ ++errcount;
+ }
+ destbin[iterlen] = 0x3;
+ blen = base64_to_binary(deststr, slen, destbin, iterlen);
+
+ if (blen != iterlen) {
+ fprintf(stderr
+ , "base64_to_binary failure: length was %d vs %d\n"
+ , blen, iterlen);
+ dumpbin(origbin, iterlen);
+ fprintf(stderr
+ , "Base64 intermediate: [%s]\n", deststr);
+ ++errcount;
+ continue;
+ }
+ if (memcmp(destbin, origbin, iterlen) != 0) {
+ fprintf(stderr
+ , "base64_to_binary mismatch. Orig:\n");
+ dumpbin(origbin, iterlen);
+ fprintf(stderr, "Dest:\n");
+ dumpbin(destbin, iterlen);
+ fprintf(stderr
+ , "Base64 intermediate: [%s]\n", deststr);
+ ++errcount;
+ }
+ if (destbin[iterlen] != 0x3) {
+ fprintf(stderr
+ , "base64_to_binary corruption. dest byte: 0x%02x\n"
+ , destbin[iterlen]);
+ ++errcount;
+ }
+
+ if (sourcebin[iterlen] != 0x2) {
+ fprintf(stderr
+ , "base64_to_binary corruption. source byte: 0x%02x\n"
+ , sourcebin[iterlen]);
+ ++errcount;
+ }
+ sourcebin[iterlen] = 0x0;
+ origbin[iterlen] = 0x0;
+ if (memcmp(sourcebin, origbin, MAXLEN+1) != 0) {
+ fprintf(stderr
+ , "base64_to_binary corruption. origbin:\n");
+ dumpbin(origbin, MAXLEN+1);
+ fprintf(stderr, "sourcebin:\n");
+ dumpbin(sourcebin, MAXLEN+1);
+ ++errcount;
+ }
+
+ }
+
+ fprintf(stderr, "\n%d iterations, %d errors.\n"
+ , maxiter, errcount);
+
+ return errcount;
+}
+/* HA-logging function */
+void
+ha_log(int priority, const char * fmt, ...)
+{
+ va_list ap;
+ char buf[MAXLINE];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, MAXLINE, fmt, ap);
+ va_end(ap);
+
+ fprintf(stderr, "%s\n", buf);
+
+}
+#endif
diff --git a/lib/clplumbing/base64_md5_test.c b/lib/clplumbing/base64_md5_test.c
new file mode 100644
index 0000000..d536776
--- /dev/null
+++ b/lib/clplumbing/base64_md5_test.c
@@ -0,0 +1,113 @@
+/* File: base64_md5_test.c
+ * Description: base64 and md5 algorithm tests
+ *
+ * Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+ * Copyright (c) 2005 International Business Machines
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <lha_internal.h>
+#include <stdio.h>
+#include <string.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/base64.h>
+#include <clplumbing/md5.h>
+
+#define MD5LEN 16 /* md5 buffer */
+#define BASE64_BUF_LEN 32
+
+/* gcc -o base64_md5_test base64_md5_test.c -lplumb */
+int main(void)
+{
+ int error_count = 0;
+
+ const char base64_encode[] = "YWJjZGVmZ2g=";
+ const char raw_data[] = "abcdefgh";
+
+ /* A test case from
+ * RFC 1321 - The MD5 Message-Digest Algorithm
+ */
+ const char * data1 = "message digest";
+ const char digest_rfc1321[(MD5LEN+1)*2+1]
+ = "f96b697d7cb7938d525a2f31aaf161d0";
+
+ /* A test case from
+ * RFC 2104 - HMAC: Keyed-Hashing for Message Authentication
+ */
+ const char *key = "Jefe";
+ const char *data2 = "what do ya want for nothing?";
+ const char digest_rfc2104[(MD5LEN+1)*2+1]
+ = "750c783e6ab0b503eaa86e310a5db738";
+
+ char buffer_tmp[BASE64_BUF_LEN];
+
+ char md[(MD5LEN+1)*2+1];
+ unsigned char digest[MD5LEN];
+ char * md_tmp;
+ int rc,i;
+
+ /* base64 encode test */
+ binary_to_base64(raw_data, strlen(raw_data), buffer_tmp
+ , BASE64_BUF_LEN);
+ /* printf("base64_encode = %s\n", buffer_tmp); */
+ if (0 != strncmp(buffer_tmp, base64_encode, strlen(buffer_tmp)) ) {
+ cl_log(LOG_ERR, "binary_to_base64 works bad.");
+ error_count++;
+ }
+
+ /* base64 decode test */
+ memset(buffer_tmp, 0, BASE64_BUF_LEN);
+ base64_to_binary(base64_encode, strlen(base64_encode)
+ , buffer_tmp, BASE64_BUF_LEN);
+ /* printf("decoded data= %s\n", buffer_tmp); */
+ if (0 != strncmp(buffer_tmp, raw_data, strlen(buffer_tmp)) ) {
+ cl_log(LOG_ERR, "base64_to_binary works bad.");
+ error_count++;
+ }
+
+ rc = MD5((const unsigned char *)data1, strlen(data1), digest);
+
+ md_tmp = md;
+ for (i = 0; i < MD5LEN; i++) {
+ snprintf(md_tmp, sizeof(md), "%02x", digest[i]);
+ md_tmp += 2;
+ }
+ *md_tmp = '\0';
+ /* printf("rc=%d MD5=%s\n", rc, md); */
+
+ if (0 != strncmp(md, digest_rfc1321, MD5LEN*2) ) {
+ cl_log(LOG_ERR, "The md5-rfc1321 algorithm works bad.");
+ error_count++;
+ }
+
+ rc = HMAC((const unsigned char *)key, strlen(key)
+ , (const unsigned char *)data2, strlen(data2), digest);
+ md_tmp = md;
+ for (i = 0; i < MD5LEN; i++) {
+ sprintf(md_tmp,"%02x", digest[i]);
+ md_tmp += 2;
+ }
+ *md_tmp = '\0';
+ /* printf("rc=%d HMAC=%s\n", rc, md); */
+
+ if (0 != strncmp(md, digest_rfc2104, MD5LEN*2) ) {
+ cl_log(LOG_ERR, "The md5-rfc2104 algorithm works bad.");
+ error_count++;
+ }
+
+ (void) rc; /* Suppress -Werror=unused-but-set-variable */
+ return error_count;
+}
diff --git a/lib/clplumbing/cl_compress.c b/lib/clplumbing/cl_compress.c
new file mode 100644
index 0000000..6b56ad6
--- /dev/null
+++ b/lib/clplumbing/cl_compress.c
@@ -0,0 +1,500 @@
+
+/*
+ * compress.c: Compression functions for Linux-HA
+ *
+ * Copyright (C) 2005 Guochun Shi <gshi@ncsa.uiuc.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Compression is designed to handle big messages, right now with 4 nodes
+ * cib message can go up to 64 KB or more. I expect much larger messages
+ * when the number of node increase. This makes message compression necessary.
+ *
+ *
+ * Compression is handled in field level. One can add a struct field using
+ * ha_msg_addstruct() -- the field will not get compressed, or using
+ * ha_msg_addstruct_compress(), and the field will get compressed when
+ * the message is converted to wire format, i.e. when msg2wirefmt() is called.
+ * The compressed field will stay compressed until it reached the desination.
+ * It will finally decompressed when the user start to get the field value.
+ * It is designed this way so that the compression/decompression only happens
+ * in end users so that heartbeat itself can save cpu cycle and memory.
+ * (more info about compression can be found in cl_msg_types.c about FT_COMPRESS
+ * FT_UNCOMPRESS types)
+ *
+ * compression has another legacy mode, which is there so it can be compatible
+ * to old ways of compression. In the old way, no field is compressed individually
+ * and the messages is compressed before it is sent out, and it will be decompressed
+ * in the receiver side immediately. So in each IPC channel, the message is compressed
+ * and decompressed once. This way will cost a lot of cpu time and memory and it is
+ * discouraged.
+ *
+ * If use_traditional_compression is true, then it is using the legacy mode, otherwise
+ * it is using the new compression. For back compatibility, the default is legacy mode.
+ *
+ * The real compression work is done by compression plugins. There are two plugins right
+ * now: zlib and bz2, they are in lib/plugins/compress
+ *
+ */
+
+#include <lha_internal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <assert.h>
+#include <glib.h>
+#include <compress.h>
+#include <ha_msg.h>
+#include <clplumbing/netstring.h>
+#include <pils/plugin.h>
+#include <pils/generic.h>
+#include <stonith/stonith.h>
+#include <stonith/stonith_plugin.h>
+
+#define COMPRESSED_FIELD "_compressed_payload"
+#define COMPRESS_NAME "_compression_algorithm"
+#define HACOMPRESSNAME "HA_COMPRESSION"
+#define DFLT_COMPRESS_PLUGIN "bz2"
+
+static struct hb_compress_fns* msg_compress_fns = NULL;
+static char* compress_name = NULL;
+GHashTable* CompressFuncs = NULL;
+
+static PILGenericIfMgmtRqst Reqs[] =
+ {
+ {"compress", &CompressFuncs, NULL, NULL, NULL},
+ {NULL, NULL, NULL, NULL, NULL}
+ };
+
+static PILPluginUniv* CompressPIsys = NULL;
+
+static int
+init_pluginsys(void){
+
+ if (CompressPIsys) {
+ return TRUE;
+ }
+
+ CompressPIsys = NewPILPluginUniv(HA_PLUGIN_DIR);
+
+ if (CompressPIsys) {
+ if (PILLoadPlugin(CompressPIsys, PI_IFMANAGER, "generic", Reqs)
+ != PIL_OK){
+ cl_log(LOG_ERR, "generic plugin load failed\n");
+ DelPILPluginUniv(CompressPIsys);
+ CompressPIsys = NULL;
+ }
+ }else{
+ cl_log(LOG_ERR, "pi univ creation failed\n");
+ }
+ return CompressPIsys != NULL;
+
+}
+
+int
+cl_compress_remove_plugin(const char* pluginname)
+{
+ return HA_OK;
+}
+
+int
+cl_compress_load_plugin(const char* pluginname)
+{
+ struct hb_compress_fns* funcs = NULL;
+
+ if (!init_pluginsys()){
+ return HA_FAIL;
+ }
+
+ if ((funcs = g_hash_table_lookup(CompressFuncs, pluginname))
+ == NULL){
+ if (PILPluginExists(CompressPIsys, HB_COMPRESS_TYPE_S,
+ pluginname) == PIL_OK){
+ PIL_rc rc;
+ if ((rc = PILLoadPlugin(CompressPIsys,
+ HB_COMPRESS_TYPE_S,
+ pluginname,
+ NULL))!= PIL_OK){
+ cl_log(LOG_ERR,
+ "Cannot load compress plugin %s[%s]",
+ pluginname,
+ PIL_strerror(rc));
+ return HA_FAIL;
+ }
+ funcs = g_hash_table_lookup(CompressFuncs,
+ pluginname);
+ }
+
+ }
+ if (funcs == NULL){
+ cl_log(LOG_ERR, "Compression module(%s) not found", pluginname);
+ return HA_FAIL;
+ }
+
+ /* set the environment variable so that later programs can
+ * load the appropriate plugin
+ */
+ setenv(HACOMPRESSNAME,pluginname,1);
+ msg_compress_fns = funcs;
+
+ return HA_OK;
+}
+
+int
+cl_set_compress_fns(const char* pluginname)
+{
+ /* this function was unnecessary duplication of the
+ * code in cl_compress_load_plugin
+ */
+ return cl_compress_load_plugin(pluginname);
+}
+
+struct hb_compress_fns*
+cl_get_compress_fns(void)
+{
+ static int try_dflt = 1;
+
+ if (try_dflt && !msg_compress_fns) {
+ try_dflt = 0;
+ cl_log(LOG_INFO, "%s: user didn't set compression type, "
+ "loading %s plugin",
+ __FUNCTION__, DFLT_COMPRESS_PLUGIN);
+ cl_compress_load_plugin(DFLT_COMPRESS_PLUGIN);
+ }
+ return msg_compress_fns;
+}
+
+static struct hb_compress_fns*
+get_compress_fns(const char* pluginname)
+{
+ struct hb_compress_fns* funcs = NULL;
+
+ if (cl_compress_load_plugin(pluginname) != HA_OK){
+ cl_log(LOG_ERR, "%s: loading compression module"
+ "(%s) failed",
+ __FUNCTION__, pluginname);
+ return NULL;
+ }
+
+ funcs = g_hash_table_lookup(CompressFuncs, pluginname);
+ return funcs;
+}
+
+void cl_realtime_malloc_check(void);
+
+char*
+cl_compressmsg(struct ha_msg* m, size_t* len)
+{
+ char* src;
+ char* dest;
+ size_t destlen;
+ int rc;
+ char* ret = NULL;
+ struct ha_msg* tmpmsg;
+ size_t datalen;
+
+ destlen = MAXMSG;
+
+ dest = malloc(destlen);
+ if (!dest) {
+ cl_log(LOG_ERR, "%s: failed to allocate destination buffer",
+ __FUNCTION__);
+ return NULL;
+ }
+
+ if (msg_compress_fns == NULL){
+ cl_log(LOG_ERR, "%s: msg_compress_fns is NULL!",
+ __FUNCTION__);
+ goto out;
+ }
+ if ( get_netstringlen(m) > MAXUNCOMPRESSED
+ || get_stringlen(m) > MAXUNCOMPRESSED){
+ cl_log(LOG_ERR, "%s: msg too big(stringlen=%d,"
+ "netstringlen=%d)",
+ __FUNCTION__,
+ get_stringlen(m),
+ get_netstringlen(m));
+ goto out;
+ }
+
+
+ if ((src = msg2wirefmt_noac(m, &datalen)) == NULL){
+ cl_log(LOG_ERR,"%s: converting msg"
+ " to wirefmt failed", __FUNCTION__);
+ goto out;
+ }
+
+ rc = msg_compress_fns->compress(dest, &destlen,
+ src, datalen);
+ if (rc != HA_OK){
+ cl_log(LOG_ERR, "%s: compression failed",
+ __FUNCTION__);
+ goto out;
+ }
+
+ free(src);
+
+ tmpmsg =ha_msg_new(0);
+ rc = ha_msg_addbin(tmpmsg, COMPRESSED_FIELD, dest, destlen)/*discouraged function*/;
+
+ if (rc != HA_OK){
+ cl_log(LOG_ERR, "%s: adding binary to msg failed",
+ __FUNCTION__);
+ goto out;
+ }
+
+ rc = ha_msg_add(tmpmsg, COMPRESS_NAME,
+ msg_compress_fns->getname());
+
+ if (rc != HA_OK){
+ cl_log(LOG_ERR, "%s: adding compress name to msg failed",
+ __FUNCTION__);
+ goto out;
+ }
+
+
+ ret = msg2netstring(tmpmsg, len);
+ ha_msg_del(tmpmsg);
+
+#if 0
+ cl_log(LOG_INFO, "------original stringlen=%d, netstringlen=%d,"
+ "compressed_datalen=%d,current len=%d",
+ get_stringlen(m), get_netstringlen(m),(int)destlen, (int)*len);
+
+#endif
+
+out:
+ if (dest) {
+ free(dest);
+ }
+
+ return ret;
+}
+
+
+gboolean
+is_compressed_msg(struct ha_msg* m)
+{
+ if( cl_get_binary(m, COMPRESSED_FIELD, NULL) /*discouraged function*/
+ != NULL){
+ return TRUE;
+ }
+
+ return FALSE;
+
+}
+
+/* the decompressmsg function is not exactly the reverse
+ * operation of compressmsg, it starts when the prorgram
+ * detects there is compressed_field in a msg
+ */
+
+struct ha_msg*
+cl_decompressmsg(struct ha_msg* m)
+{
+ const char* src;
+ size_t srclen;
+ char *dest = NULL;
+ size_t destlen = MAXUNCOMPRESSED;
+ int rc;
+ struct ha_msg* ret = NULL;
+ const char* decompress_name;
+ struct hb_compress_fns* funcs = NULL;
+
+ dest = malloc(destlen);
+
+ if (!dest) {
+ cl_log(LOG_ERR, "%s: Failed to allocate buffer.", __FUNCTION__);
+ return NULL;
+ }
+
+ if (m == NULL){
+ cl_log(LOG_ERR, "%s: NULL message", __FUNCTION__);
+ goto out;
+ }
+ src = cl_get_binary(m, COMPRESSED_FIELD, &srclen)/*discouraged function*/;
+ if (src == NULL){
+ cl_log(LOG_ERR, "%s: compressed-field is NULL",
+ __FUNCTION__);
+ goto out;
+ }
+
+ if (srclen > MAXMSG){
+ cl_log(LOG_ERR, "%s: field too long(%d)",
+ __FUNCTION__, (int)srclen);
+ goto out;
+ }
+
+ decompress_name = ha_msg_value(m, COMPRESS_NAME);
+ if (decompress_name == NULL){
+ cl_log(LOG_ERR, "compress name not found");
+ goto out;
+ }
+
+
+ funcs = get_compress_fns(decompress_name);
+
+ if (funcs == NULL){
+ cl_log(LOG_ERR, "%s: compress method(%s) is not"
+ " supported in this machine",
+ __FUNCTION__, decompress_name);
+ goto out;
+ }
+
+ rc = funcs->decompress(dest, &destlen, src, srclen);
+
+ if (rc != HA_OK){
+ cl_log(LOG_ERR, "%s: decompression failed",
+ __FUNCTION__);
+ goto out;
+ }
+
+ ret = wirefmt2msg(dest, destlen, 0);
+
+#if 0
+ cl_log(LOG_INFO, "%s: srclen =%d, destlen=%d",
+ __FUNCTION__,
+ srclen, destlen);
+#endif
+
+out:
+ if (dest) {
+ free(dest);
+ }
+
+ return ret;
+}
+
+
+int
+cl_decompress_field(struct ha_msg* msg, int index, char* buf, size_t* buflen)
+{
+ char* value;
+ int vallen;
+ int rc;
+ const char* decompress_name;
+ struct hb_compress_fns* funcs;
+
+ if ( msg == NULL|| index >= msg->nfields){
+ cl_log(LOG_ERR, "%s: wrong argument",
+ __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ value = msg->values[index];
+ vallen = msg->vlens[index];
+
+ decompress_name = ha_msg_value(msg, COMPRESS_NAME);
+ if (decompress_name == NULL){
+ cl_log(LOG_ERR, "compress name not found");
+ return HA_FAIL;
+ }
+
+
+ funcs = get_compress_fns(decompress_name);
+
+ if (funcs == NULL){
+ cl_log(LOG_ERR, "%s: compress method(%s) is not"
+ " supported in this machine",
+ __FUNCTION__, decompress_name);
+ return HA_FAIL;
+ }
+
+ rc = funcs->decompress(buf, buflen, value, vallen);
+ if (rc != HA_OK){
+ cl_log(LOG_ERR, "%s: decompression failed",
+ __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ return HA_OK;
+}
+
+
+int
+cl_compress_field(struct ha_msg* msg, int index, char* buf, size_t* buflen)
+{
+ char* src;
+ size_t srclen;
+ int rc;
+
+ if ( msg == NULL|| index >= msg->nfields
+ || msg->types[index] != FT_UNCOMPRESS){
+ cl_log(LOG_ERR, "%s: wrong argument",
+ __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ if (msg_compress_fns == NULL){
+ if (compress_name == NULL){
+ compress_name = getenv(HACOMPRESSNAME);
+ }
+
+ if (compress_name == NULL){
+ cl_log(LOG_ERR, "%s: no compression module name found",
+ __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ if(cl_set_compress_fns(compress_name) != HA_OK){
+ cl_log(LOG_ERR, "%s: loading compression module failed",
+ __FUNCTION__);
+ return HA_FAIL;
+ }
+ }
+
+ if (msg_compress_fns == NULL){
+ cl_log(LOG_ERR, "%s: msg_compress_fns is NULL!",
+ __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ src = msg2wirefmt_noac(msg->values[index], &srclen);
+ if (src == NULL){
+ cl_log(LOG_ERR,"%s: converting msg"
+ " to wirefmt failed", __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ rc = msg_compress_fns->compress(buf, buflen,
+ src, srclen);
+ if (rc != HA_OK){
+ cl_log(LOG_ERR, "%s: compression failed",
+ __FUNCTION__);
+ return HA_FAIL;
+ }
+
+
+ rc = ha_msg_mod(msg, COMPRESS_NAME,
+ msg_compress_fns->getname());
+
+ if (rc != HA_OK){
+ cl_log(LOG_ERR, "%s: adding compress name to msg failed",
+ __FUNCTION__);
+ return HA_FAIL;;
+ }
+
+ free(src);
+ src = NULL;
+
+ return HA_OK;
+
+}
diff --git a/lib/clplumbing/cl_log.c b/lib/clplumbing/cl_log.c
new file mode 100644
index 0000000..213e760
--- /dev/null
+++ b/lib/clplumbing/cl_log.c
@@ -0,0 +1,1261 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <lha_internal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <syslog.h>
+#include <time.h>
+#include <sys/utsname.h>
+#include <clplumbing/ipc.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/loggingdaemon.h>
+#include <clplumbing/longclock.h>
+#include <clplumbing/uids.h>
+#include <glib.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <clplumbing/GSource.h>
+#include <clplumbing/cl_misc.h>
+#include <clplumbing/cl_syslog.h>
+#include <ha_msg.h>
+
+#ifndef MAXLINE
+# define MAXLINE 512
+#endif
+/*
+ * <syslog.h> might not contain LOG_PRI...
+ * So, we define it ourselves, or error out if we can't...
+ */
+
+#ifndef LOG_PRI
+# ifdef LOG_PRIMASK
+ /* David Lee <T.D.Lee@durham.ac.uk> reports this works on Solaris */
+# define LOG_PRI(p) ((p) & LOG_PRIMASK)
+# else
+# error "Syslog.h does not define either LOG_PRI or LOG_PRIMASK."
+# endif
+#endif
+
+#define DFLT_ENTITY "cluster"
+#define DFLT_PREFIX ""
+#define NULLTIME 0
+#define QUEUE_SATURATION_FUZZ 10
+
+static IPC_Channel* logging_daemon_chan = NULL;
+static gboolean syslogformatfile = TRUE;
+/*
+ * If true, then output messages more or less like this...
+ * Jul 14 21:45:18 beam logd: [1056]: info: setting log file to /dev/null
+ */
+
+int LogToDaemon(int priority, const char * buf, int bstrlen, gboolean use_pri_str);
+
+static int LogToLoggingDaemon(int priority, const char * buf, int bstrlen, gboolean use_pri_str);
+static IPC_Message* ChildLogIPCMessage(int priority, const char *buf, int bstrlen,
+ gboolean use_priority_str, IPC_Channel* ch);
+static void FreeChildLogIPCMessage(IPC_Message* msg);
+static gboolean send_dropped_message(gboolean use_pri_str, IPC_Channel *chan);
+static int cl_set_logging_wqueue_maxlen(int qlen);
+
+static int use_logging_daemon = FALSE;
+static int conn_logd_time = 0;
+static char cl_log_entity[MAXENTITY]= DFLT_ENTITY;
+static char cl_log_syslogprefix[MAXENTITY] = DFLT_PREFIX;
+static char common_log_entity[MAXENTITY]= DFLT_ENTITY;
+static int cl_log_facility = LOG_USER;
+static int use_buffered_io = 0;
+
+static void cl_opensyslog(void);
+static int syslog_enabled = 0;
+static int stderr_enabled = 0;
+static int stdout_enabled = 0;
+static const char* logfile_name = NULL;
+static const char* debugfile_name = NULL;
+static int cl_process_pid = -1;
+int debug_level = 0;
+static GDestroyNotify destroy_logging_channel_callback;
+static void (*create_logging_channel_callback)(IPC_Channel* chan);
+static gboolean logging_chan_in_main_loop = FALSE;
+
+/***********************
+ *debug use only, do not use this function in your program
+ */
+IPC_Channel * get_log_chan(void);
+
+IPC_Channel* get_log_chan(void){
+ return logging_daemon_chan;
+}
+/*************************/
+
+/**************************
+ * check if the fd is in use for logging
+ **************************/
+int
+cl_log_is_logd_fd(int fd)
+{
+ return logging_daemon_chan && (
+ fd == logging_daemon_chan->ops->get_send_select_fd(logging_daemon_chan)
+ ||
+ fd == logging_daemon_chan->ops->get_recv_select_fd(logging_daemon_chan)
+ );
+}
+
+void
+cl_log_enable_stderr(int truefalse)
+{
+ stderr_enabled = truefalse;
+}
+
+void
+cl_log_enable_stdout(int truefalse)
+{
+ stdout_enabled = truefalse;
+}
+
+void
+cl_log_set_uselogd(int truefalse)
+{
+ use_logging_daemon = truefalse;
+}
+void
+cl_log_enable_syslog_filefmt(int truefalse)
+{
+ syslogformatfile = (gboolean)truefalse;
+}
+
+gboolean
+cl_log_get_uselogd(void)
+{
+ return use_logging_daemon;
+}
+
+
+int
+cl_log_get_logdtime(void)
+{
+ return conn_logd_time;
+
+}
+
+void
+cl_log_set_logdtime(int logdtime)
+{
+ conn_logd_time = logdtime;
+ return;
+}
+
+void
+cl_log_use_buffered_io(int truefalse)
+{
+ use_buffered_io = truefalse;
+ cl_log_close_log_files();
+}
+
+#define ENVPRE "HA_"
+
+#define ENV_HADEBUGVAL "HA_debug"
+#define ENV_LOGFENV "HA_logfile" /* well-formed log file :-) */
+#define ENV_DEBUGFENV "HA_debugfile" /* Debug log file */
+#define ENV_LOGFACILITY "HA_logfacility"/* Facility to use for logger */
+#define ENV_SYSLOGFMT "HA_syslogmsgfmt"/* TRUE if we should use syslog message formatting */
+#define ENV_LOGDAEMON "HA_use_logd"
+#define ENV_CONNINTVAL "HA_conn_logd_time"
+#define TRADITIONAL_COMPRESSION "HA_traditional_compression"
+#define COMPRESSION "HA_compression"
+
+static void
+inherit_compress(void)
+{
+ char* inherit_env = NULL;
+
+ inherit_env = getenv(TRADITIONAL_COMPRESSION);
+ if (inherit_env != NULL && *inherit_env != EOS) {
+ gboolean value;
+
+ if (cl_str_to_boolean(inherit_env, &value)!= HA_OK){
+ cl_log(LOG_ERR, "inherit traditional_compression failed");
+ }else{
+ cl_set_traditional_compression(value);
+ }
+ }
+
+}
+
+void
+cl_inherit_logging_environment(int logqueuemax)
+{
+ char * inherit_env = NULL;
+
+ /* Donnot need to free the return pointer from getenv */
+ inherit_env = getenv(ENV_HADEBUGVAL);
+ if (inherit_env != NULL && atoi(inherit_env) != 0 ) {
+ debug_level = atoi(inherit_env);
+ inherit_env = NULL;
+ }
+
+ inherit_env = getenv(ENV_LOGFENV);
+ if (inherit_env != NULL && *inherit_env != EOS) {
+ cl_log_set_logfile(inherit_env);
+ inherit_env = NULL;
+ }
+
+ inherit_env = getenv(ENV_DEBUGFENV);
+ if (inherit_env != NULL && *inherit_env != EOS) {
+ cl_log_set_debugfile(inherit_env);
+ inherit_env = NULL;
+ }
+
+ inherit_env = getenv(ENV_LOGFACILITY);
+ if (inherit_env != NULL && *inherit_env != EOS) {
+ int facility = -1;
+ facility = cl_syslogfac_str2int(inherit_env);
+ if ( facility >= 0 ) {
+ cl_log_set_facility(facility);
+ }
+ inherit_env = NULL;
+ }
+
+ inherit_env = getenv(ENV_SYSLOGFMT);
+ if (inherit_env != NULL && *inherit_env != EOS) {
+ gboolean truefalse;
+ if (cl_str_to_boolean(inherit_env, &truefalse) == HA_OK) {
+ cl_log_enable_syslog_filefmt(truefalse);
+ }
+ }
+
+ inherit_env = getenv(ENV_LOGDAEMON);
+ if (inherit_env != NULL && *inherit_env != EOS) {
+ gboolean uselogd;
+ cl_str_to_boolean(inherit_env, &uselogd);
+ cl_log_set_uselogd(uselogd);
+ if (uselogd) {
+ if (logqueuemax > 0) {
+ cl_set_logging_wqueue_maxlen(logqueuemax);
+ }
+ }
+ }
+
+ inherit_env = getenv(ENV_CONNINTVAL);
+ if (inherit_env != NULL && *inherit_env != EOS) {
+ int logdtime;
+ logdtime = cl_get_msec(inherit_env);
+ cl_log_set_logdtime(logdtime);
+ }
+
+ inherit_compress();
+ return;
+}
+
+
+static void
+add_logging_channel_mainloop(IPC_Channel* chan)
+{
+ GCHSource* chp=
+ G_main_add_IPC_Channel( G_PRIORITY_DEFAULT,
+ chan,
+ FALSE,
+ NULL,
+ NULL,
+ destroy_logging_channel_callback);
+
+ if (chp == NULL){
+ cl_log(LOG_INFO, "adding logging channel to mainloop failed");
+ }
+
+ logging_chan_in_main_loop = TRUE;
+
+
+ return;
+}
+
+static void
+remove_logging_channel_mainloop(gpointer userdata)
+{
+ logging_chan_in_main_loop = FALSE;
+
+ return;
+}
+
+
+static IPC_Channel*
+create_logging_channel(void)
+{
+ GHashTable* attrs;
+ char path[] = IPC_PATH_ATTR;
+ char sockpath[] = HA_LOGDAEMON_IPC;
+ IPC_Channel* chan;
+ static gboolean complained_yet = FALSE;
+
+ attrs = g_hash_table_new(g_str_hash, g_str_equal);
+ g_hash_table_insert(attrs, path, sockpath);
+
+ chan =ipc_channel_constructor(IPC_ANYTYPE, attrs);
+
+ g_hash_table_destroy(attrs);
+
+ if (chan == NULL) {
+ cl_log(LOG_ERR, "create_logging_channel:"
+ "contructing ipc channel failed");
+ return NULL;
+ }
+
+ if (chan->ops->initiate_connection(chan) != IPC_OK) {
+ if (!complained_yet) {
+ complained_yet = TRUE;
+ cl_log(LOG_WARNING, "Initializing connection"
+ " to logging daemon failed."
+ " Logging daemon may not be running");
+ }
+ if (!logging_chan_in_main_loop){
+ chan->ops->destroy(chan);
+ }
+
+ return NULL;
+ }
+ complained_yet = FALSE;
+
+ if (create_logging_channel_callback){
+ create_logging_channel_callback(chan);
+ }
+
+
+ return chan;
+
+}
+
+gboolean
+cl_log_test_logd(void)
+{
+ IPC_Channel* chan = logging_daemon_chan;
+
+ if (chan && chan->ops->get_chan_status(chan) == IPC_CONNECT){
+ return TRUE;
+ }
+ if (chan ){
+ if (!logging_chan_in_main_loop){
+ chan->ops->destroy(chan);
+ }
+ logging_daemon_chan = chan = NULL;
+ }
+
+ logging_daemon_chan = chan = create_logging_channel();
+
+ if (chan == NULL){
+ return FALSE;
+ }
+
+ if(chan->ops->get_chan_status(chan) != IPC_CONNECT){
+ if (!logging_chan_in_main_loop){
+ chan->ops->destroy(chan);
+ }
+ logging_daemon_chan = chan = NULL;
+ return FALSE;
+ }
+
+ return TRUE;
+
+}
+
+/* FIXME: This is way too ugly to bear */
+
+void
+cl_log_set_facility(int facility)
+{
+ if (syslog_enabled && facility == cl_log_facility) {
+ return;
+ }
+ cl_log_facility = facility;
+ closelog();
+ syslog_enabled = 0;
+ if (facility > 0) {
+ cl_opensyslog();
+ }
+}
+
+void
+cl_log_set_entity(const char * entity)
+{
+ if (entity == NULL) {
+ entity = DFLT_ENTITY;
+ }
+ strncpy(cl_log_entity, entity, MAXENTITY);
+ cl_log_entity[MAXENTITY-1] = '\0';
+ if (syslog_enabled) {
+ syslog_enabled = 0;
+ cl_opensyslog();
+ }
+}
+
+void
+cl_log_set_syslogprefix(const char *prefix)
+{
+ if (prefix == NULL) {
+ prefix = DFLT_PREFIX;
+ }
+ strncpy(cl_log_syslogprefix, prefix, MAXENTITY);
+ cl_log_syslogprefix[MAXENTITY-1] = '\0';
+ if (syslog_enabled) {
+ syslog_enabled = 0;
+ cl_opensyslog();
+ }
+}
+
+void
+cl_log_set_logfile(const char * path)
+{
+ if(path != NULL && strcasecmp("/dev/null", path) == 0) {
+ path = NULL;
+ }
+ logfile_name = path;
+ cl_log_close_log_files();
+}
+void
+cl_log_set_debugfile(const char * path)
+{
+ if(path != NULL && strcasecmp("/dev/null", path) == 0) {
+ path = NULL;
+ }
+ debugfile_name = path;
+ cl_log_close_log_files();
+}
+
+
+/*
+ * This function sets two callback functions.
+ * One for creating a channel and
+ * the other for destroying a channel*
+ */
+int
+cl_log_set_logd_channel_source( void (*create_callback)(IPC_Channel* chan),
+ GDestroyNotify destroy_callback)
+{
+ IPC_Channel* chan = logging_daemon_chan ;
+
+ if (destroy_callback == NULL){
+ destroy_logging_channel_callback = remove_logging_channel_mainloop;
+ }else{
+ destroy_logging_channel_callback = destroy_callback;
+ }
+
+ if (create_callback == NULL){
+ create_logging_channel_callback = add_logging_channel_mainloop;
+ }else{
+ create_logging_channel_callback = create_callback;
+ }
+
+ if (chan != NULL
+ && chan->ops->get_chan_status(chan) == IPC_CONNECT){
+ add_logging_channel_mainloop(chan);
+ }
+
+ return 0;
+}
+
+const char *
+prio2str(int priority)
+{
+ static const char *log_prio[8] = {
+ "EMERG",
+ "ALERT",
+ "CRIT",
+ "ERROR",
+ "WARN",
+ "notice",
+ "info",
+ "debug"
+ };
+ int logpri;
+
+ logpri = LOG_PRI(priority);
+
+ return (logpri < 0 || logpri >= DIMOF(log_prio)) ?
+ "(undef)" : log_prio[logpri];
+}
+
+/* print log line to a FILE *f */
+#define print_logline(fp,entity,entity_pid,ts,pristr,buf) { \
+ fprintf(fp, "%s[%d]: %s ",entity,entity_pid,ha_timestamp(ts)); \
+ if (pristr) \
+ fprintf(fp,"%s: %s\n",pristr,buf); \
+ else \
+ fprintf(fp,"%s\n",buf); \
+ }
+
+static char * syslog_timestamp(TIME_T t);
+static void cl_limit_log_update(struct msg_ctrl *ml, time_t ts);
+
+static void
+append_log(FILE * fp, const char * entity, int entity_pid
+, TIME_T timestamp, const char * pristr, const char * msg)
+{
+ static int got_uname = FALSE;
+ static struct utsname un;
+
+ if (!syslogformatfile) {
+ print_logline(fp, entity, entity_pid, timestamp, pristr, msg);
+ return;
+ }
+ if (!got_uname) {
+ uname(&un);
+ }
+ /*
+ * Jul 14 21:45:18 beam logd: [1056]: info: setting log file to /dev/null
+ */
+ fprintf(fp, "%s %s %s: [%d]: %s%s%s\n"
+ , syslog_timestamp(timestamp)
+ , un.nodename, entity, entity_pid
+ , (pristr ? pristr : "")
+ , (pristr ? ": " : "")
+ , msg);
+}
+
+/* As performance optimization we try to keep the file descriptor
+ * open all the time, but as logrotation needs to work, the calling
+ * program actually needs a signal handler.
+ *
+ * To be able to keep files open even without signal handler,
+ * we remember the stat info, and close/reopen if the inode changed.
+ * We keep the number of stat() calls to one per file per minute.
+ * logrotate should be configured for delayed compression, if any.
+ */
+
+struct log_file_context {
+ FILE *fp;
+ struct stat stat_buf;
+};
+
+static struct log_file_context log_file, debug_file;
+
+static void close_log_file(struct log_file_context *lfc)
+{
+ /* ignore errors, we cannot do anything about them anyways */
+ fflush(lfc->fp);
+ fsync(fileno(lfc->fp));
+ fclose(lfc->fp);
+ lfc->fp = NULL;
+}
+
+void cl_log_close_log_files(void)
+{
+ if (log_file.fp)
+ close_log_file(&log_file);
+ if (debug_file.fp)
+ close_log_file(&debug_file);
+}
+
+static void maybe_close_log_file(const char *fname, struct log_file_context *lfc)
+{
+ struct stat buf;
+ if (!lfc->fp)
+ return;
+ if (stat(fname, &buf) || buf.st_ino != lfc->stat_buf.st_ino) {
+ close_log_file(lfc);
+ cl_log(LOG_INFO, "log-rotate detected on logfile %s", fname);
+ }
+}
+
+/* Default to unbuffered IO. logd or others can use cl_log_use_buffered_io(1)
+ * to enable fully buffered mode, and then use fflush appropriately.
+ */
+static void open_log_file(const char *fname, struct log_file_context *lfc)
+{
+ lfc->fp = fopen(fname ,"a");
+ if (!lfc->fp) {
+ syslog(LOG_ERR, "Failed to open log file %s: %s\n" ,
+ fname, strerror(errno));
+ } else {
+ setvbuf(lfc->fp, NULL,
+ use_buffered_io ? _IOFBF : _IONBF,
+ BUFSIZ);
+ fstat(fileno(lfc->fp), &lfc->stat_buf);
+ }
+}
+
+static void maybe_reopen_log_files(const char *log_fname, const char *debug_fname)
+{
+ static TIME_T last_stat_time;
+
+ if (log_file.fp || debug_file.fp) {
+ TIME_T now = time(NULL);
+ if (now - last_stat_time > 59) {
+ /* Don't use an exact minute, have it jitter around a
+ * bit against cron or others. Note that, if there
+ * is no new log message, it can take much longer
+ * than this to notice logrotation and actually close
+ * our file handle on the possibly already rotated,
+ * or even deleted.
+ *
+ * As long as at least one minute pases between
+ * renaming the log file, and further processing,
+ * no message will be lost, so this should do fine:
+ * (mv ha-log ha-log.1; sleep 60; gzip ha-log.1)
+ */
+ maybe_close_log_file(log_fname, &log_file);
+ maybe_close_log_file(debug_fname, &debug_file);
+ last_stat_time = now;
+ }
+ }
+
+ if (log_fname && !log_file.fp)
+ open_log_file(log_fname, &log_file);
+
+ if (debug_fname && !debug_file.fp)
+ open_log_file(debug_fname, &debug_file);
+}
+
+/*
+ * This function can cost us realtime unless use_logging_daemon
+ * is enabled. Then we log everything through a child process using
+ * non-blocking IPC.
+ */
+
+/* Cluster logging function */
+void
+cl_direct_log(int priority, const char* buf, gboolean use_priority_str,
+ const char* entity, int entity_pid, TIME_T ts)
+{
+ const char * pristr;
+ int needprivs = !cl_have_full_privs();
+
+ pristr = use_priority_str ? prio2str(priority) : NULL;
+
+ if (!entity)
+ entity = *cl_log_entity ? cl_log_entity : DFLT_ENTITY;
+
+ if (needprivs) {
+ return_to_orig_privs();
+ }
+
+ if (syslog_enabled) {
+ snprintf(common_log_entity, MAXENTITY, "%s",
+ *cl_log_syslogprefix ? cl_log_syslogprefix : entity);
+
+ /* The extra trailing '\0' is supposed to work around some
+ * "known syslog bug that ends up concatinating entries".
+ * Knowledge about which syslog package, version, platform and
+ * what exactly the bug was has been lost, but leaving it in
+ * won't do any harm either. */
+ syslog(priority, "%s[%d]: %s%s%s%c",
+ *cl_log_syslogprefix ? entity : "",
+ entity_pid,
+ pristr ?: "", pristr ? ": " : "",
+ buf, 0);
+ }
+
+ maybe_reopen_log_files(logfile_name, debugfile_name);
+
+ if (debug_file.fp)
+ append_log(debug_file.fp, entity, entity_pid, ts, pristr, buf);
+
+ if (priority != LOG_DEBUG && log_file.fp)
+ append_log(log_file.fp, entity, entity_pid, ts, pristr, buf);
+
+ if (needprivs) {
+ return_to_dropped_privs();
+ }
+ return;
+}
+
+void cl_log_do_fflush(int do_fsync)
+{
+ if (log_file.fp) {
+ fflush(log_file.fp);
+ if (do_fsync)
+ fsync(fileno(log_file.fp));
+ }
+ if (debug_file.fp) {
+ fflush(debug_file.fp);
+ if (do_fsync)
+ fsync(fileno(debug_file.fp));
+ }
+}
+
+/*
+ * This function can cost us realtime unless use_logging_daemon
+ * is enabled. Then we log everything through a child process using
+ * non-blocking IPC.
+ */
+
+static int cl_log_depth = 0;
+
+/* Cluster logging function */
+void
+cl_log(int priority, const char * fmt, ...)
+{
+ va_list ap;
+ char buf[MAXLINE];
+ ssize_t nbytes;
+
+ cl_process_pid = (int)getpid();
+
+ cl_log_depth++;
+
+ buf[MAXLINE-1] = EOS;
+ va_start(ap, fmt);
+ nbytes=vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ if (nbytes >= (ssize_t)sizeof(buf)){
+ nbytes = sizeof(buf) -1 ;
+ }
+
+ if (stderr_enabled) {
+ append_log(stderr, cl_log_entity,cl_process_pid,
+ NULLTIME, prio2str(priority), buf);
+ }
+
+ if (stdout_enabled) {
+ append_log(stdout, cl_log_entity,cl_process_pid,
+ NULLTIME, prio2str(priority), buf);
+ }
+
+ if (use_logging_daemon && cl_log_depth <= 1) {
+ LogToLoggingDaemon(priority, buf, nbytes, TRUE);
+ }else{
+ /* this may cause blocking... maybe should make it optional? */
+ cl_direct_log(priority, buf, TRUE, NULL, cl_process_pid, NULLTIME);
+ }
+
+ cl_log_depth--;
+ return;
+}
+
+/*
+ * Log a message only if there were not too many messages of this
+ * kind recently. This is too prevent log spamming in case a
+ * condition persists over a long period of time. The maximum
+ * number of messages for the timeframe and other details are
+ * provided in struct logspam (see cl_log.h).
+ *
+ * Implementation details:
+ * - max number of time_t slots is allocated; slots keep time
+ * stamps of previous max number of messages
+ * - we check if the difference between now (i.e. new message just
+ * arrived) and the oldest message is _less_ than the window
+ * timeframe
+ * - it's up to the user to do cl_limit_log_new and afterwards
+ * cl_limit_log_destroy, though the latter is usually not
+ * necessary; the memory allocated with cl_limit_log_new stays
+ * constant during the lifetime of the process
+ *
+ * NB on Thu Aug 4 15:26:49 CEST 2011:
+ * This interface is very new, use with caution and report bugs.
+ */
+
+struct msg_ctrl *
+cl_limit_log_new(struct logspam *lspam)
+{
+ struct msg_ctrl *ml;
+
+ ml = (struct msg_ctrl *)malloc(sizeof(struct msg_ctrl));
+ if (!ml) {
+ cl_log(LOG_ERR, "%s:%d: out of memory"
+ , __FUNCTION__, __LINE__);
+ return NULL;
+ }
+ ml->msg_slots = (time_t *)calloc(lspam->max, sizeof(time_t));
+ if (!ml->msg_slots) {
+ cl_log(LOG_ERR, "%s:%d: out of memory"
+ , __FUNCTION__, __LINE__);
+ return NULL;
+ }
+ ml->lspam = lspam;
+ cl_limit_log_reset(ml);
+ return ml; /* to be passed later to cl_limit_log() */
+}
+
+void
+cl_limit_log_destroy(struct msg_ctrl *ml)
+{
+ if (!ml)
+ return;
+ g_free(ml->msg_slots);
+ g_free(ml);
+}
+
+void
+cl_limit_log_reset(struct msg_ctrl *ml)
+{
+ ml->last = -1;
+ ml->cnt = 0;
+ ml->suppress_t = (time_t)0;
+ memset(ml->msg_slots, 0, ml->lspam->max * sizeof(time_t));
+}
+
+static void
+cl_limit_log_update(struct msg_ctrl *ml, time_t ts)
+{
+ ml->last = (ml->last + 1) % ml->lspam->max;
+ *(ml->msg_slots + ml->last) = ts;
+ if (ml->cnt < ml->lspam->max)
+ ml->cnt++;
+}
+
+void
+cl_limit_log(struct msg_ctrl *ml, int priority, const char * fmt, ...)
+{
+ va_list ap;
+ char buf[MAXLINE];
+ time_t last_ts, now = time(NULL);
+
+ if (!ml)
+ goto log_msg;
+ if (ml->suppress_t) {
+ if ((now - ml->suppress_t) < ml->lspam->reset_time)
+ return;
+ /* message blocking expired */
+ cl_limit_log_reset(ml);
+ }
+ last_ts = ml->last != -1 ? *(ml->msg_slots + ml->last) : (time_t)0;
+ if (
+ ml->cnt < ml->lspam->max || /* not so many messages logged */
+ (now - last_ts) > ml->lspam->window /* messages far apart */
+ ) {
+ cl_limit_log_update(ml, now);
+ goto log_msg;
+ } else {
+ cl_log(LOG_INFO
+ , "'%s' messages logged too often, "
+ "suppressing messages of this kind for %ld seconds"
+ , ml->lspam->id, ml->lspam->reset_time);
+ cl_log(priority, "%s", ml->lspam->advice);
+ ml->suppress_t = now;
+ return;
+ }
+
+log_msg:
+ va_start(ap, fmt);
+ vsnprintf(buf, MAXLINE, fmt, ap);
+ va_end(ap);
+ cl_log(priority, "%s", buf);
+}
+
+void
+cl_perror(const char * fmt, ...)
+{
+ const char * err;
+
+ va_list ap;
+ char buf[MAXLINE];
+
+ err = strerror(errno);
+ va_start(ap, fmt);
+ vsnprintf(buf, MAXLINE, fmt, ap);
+ va_end(ap);
+
+ cl_log(LOG_ERR, "%s: %s", buf, err);
+
+}
+void
+cl_glib_msg_handler(const gchar *log_domain, GLogLevelFlags log_level
+, const gchar *message, gpointer user_data)
+{
+ GLogLevelFlags level = (log_level & G_LOG_LEVEL_MASK);
+ int ha_level;
+
+ switch(level) {
+ case G_LOG_LEVEL_ERROR: ha_level = LOG_ERR; break;
+ case G_LOG_LEVEL_CRITICAL: ha_level = LOG_ERR; break;
+ case G_LOG_LEVEL_WARNING: ha_level = LOG_WARNING; break;
+ case G_LOG_LEVEL_MESSAGE: ha_level = LOG_NOTICE; break;
+ case G_LOG_LEVEL_INFO: ha_level = LOG_INFO; break;
+ case G_LOG_LEVEL_DEBUG: ha_level = LOG_DEBUG; break;
+
+ default: ha_level = LOG_WARNING; break;
+ }
+
+
+ cl_log(ha_level, "glib: %s", message);
+}
+static char *
+syslog_timestamp(TIME_T t)
+{
+ static char ts[64];
+ struct tm* ttm;
+ TIME_T now;
+ time_t nowtt;
+ static const char* monthstr [12] = {
+ "Jan", "Feb", "Mar",
+ "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep",
+ "Oct", "Nov", "Dec"
+ };
+
+ /* Work around various weridnesses in different OSes and time_t definitions */
+ if (t == 0){
+ now = time(NULL);
+ }else{
+ now = t;
+ }
+
+ nowtt = (time_t)now;
+ ttm = localtime(&nowtt);
+
+ snprintf(ts, sizeof(ts), "%3s %02d %02d:%02d:%02d"
+ , monthstr[ttm->tm_mon], ttm->tm_mday
+ , ttm->tm_hour, ttm->tm_min, ttm->tm_sec);
+ return(ts);
+}
+
+
+
+char *
+ha_timestamp(TIME_T t)
+{
+ static char ts[64];
+ struct tm* ttm;
+ TIME_T now;
+ time_t nowtt;
+
+ /* Work around various weridnesses in different OSes and time_t definitions */
+ if (t == 0){
+ now = time(NULL);
+ }else{
+ now = t;
+ }
+
+ nowtt = (time_t)now;
+ ttm = localtime(&nowtt);
+
+ snprintf(ts, sizeof(ts), "%04d/%02d/%02d_%02d:%02d:%02d"
+ , ttm->tm_year+1900, ttm->tm_mon+1, ttm->tm_mday
+ , ttm->tm_hour, ttm->tm_min, ttm->tm_sec);
+ return(ts);
+}
+
+
+static int
+cl_set_logging_wqueue_maxlen(int qlen)
+{
+ int sendrc;
+ IPC_Channel* chan = logging_daemon_chan;
+
+ if (chan == NULL){
+ chan = logging_daemon_chan = create_logging_channel();
+ }
+
+ if (chan == NULL){
+ return HA_FAIL;
+ }
+
+ if (chan->ch_status != IPC_CONNECT){
+ cl_log(LOG_ERR, "cl_set_logging_wqueue_maxle:"
+ "channel is not connected");
+ if (!logging_chan_in_main_loop){
+ chan->ops->destroy(chan);
+ }
+ logging_daemon_chan = NULL;
+ return HA_FAIL;
+ }
+
+ sendrc = chan->ops->set_send_qlen(logging_daemon_chan, qlen);
+
+ if (sendrc == IPC_OK) {
+ return HA_OK;
+ }else {
+ return HA_FAIL;
+ }
+}
+
+
+
+int
+LogToDaemon(int priority, const char * buf,
+ int bufstrlen, gboolean use_pri_str)
+{
+ int rc;
+
+ cl_log_depth++;
+
+ rc= LogToLoggingDaemon(priority, buf, bufstrlen, use_pri_str);
+
+ cl_log_depth--;
+
+ return rc;
+}
+
+static int drop_msg_num = 0;
+
+void
+cl_flush_logs(void)
+{
+ if(logging_daemon_chan == NULL) {
+ return;
+ }
+ logging_daemon_chan->ops->waitout(logging_daemon_chan);
+}
+
+static int
+LogToLoggingDaemon(int priority, const char * buf,
+ int bufstrlen, gboolean use_pri_str)
+{
+ IPC_Channel* chan = logging_daemon_chan;
+ static longclock_t nexttime = 0;
+ IPC_Message* msg;
+ int sendrc = IPC_FAIL;
+ int intval = conn_logd_time;
+
+ /* make sure we don't hold file descriptors open
+ * we don't intend to use again */
+ cl_log_close_log_files();
+
+ if (chan == NULL) {
+ longclock_t lnow = time_longclock();
+
+ if (cmp_longclock(lnow, nexttime) >= 0){
+ nexttime = add_longclock(
+ lnow, msto_longclock(intval));
+
+ logging_daemon_chan = chan = create_logging_channel();
+ }
+ }
+
+ if (chan == NULL){
+ cl_direct_log(
+ priority, buf, TRUE, NULL, cl_process_pid, NULLTIME);
+ return HA_FAIL;
+ }
+
+ msg = ChildLogIPCMessage(priority, buf, bufstrlen, use_pri_str, chan);
+ if (msg == NULL) {
+ drop_msg_num++;
+ return HA_FAIL;
+ }
+
+ if (chan->ch_status == IPC_CONNECT){
+
+ if (chan->ops->is_sending_blocked(chan)) {
+ chan->ops->resume_io(chan);
+ }
+ /* Make sure there is room for the drop message _and_ the
+ * one we wish to log. Otherwise there is no point.
+ *
+ * Try to avoid bouncing on the limit by additionally
+ * waiting until there is room for QUEUE_SATURATION_FUZZ
+ * messages.
+ */
+ if (drop_msg_num > 0
+ && chan->send_queue->current_qlen
+ < (chan->send_queue->max_qlen -1 -QUEUE_SATURATION_FUZZ))
+ {
+ /* have to send it this way so the order is correct */
+ send_dropped_message(use_pri_str, chan);
+ }
+
+ /* Don't log a debug message if we're
+ * approaching the queue limit and already
+ * dropped a message
+ */
+ if (drop_msg_num == 0
+ || chan->send_queue->current_qlen <
+ (chan->send_queue->max_qlen -1 -QUEUE_SATURATION_FUZZ)
+ || priority != LOG_DEBUG )
+ {
+ sendrc = chan->ops->send(chan, msg);
+ }
+ }
+
+ if (sendrc == IPC_OK) {
+ return HA_OK;
+
+ } else {
+
+ if (chan->ops->get_chan_status(chan) != IPC_CONNECT) {
+ if (!logging_chan_in_main_loop){
+ chan->ops->destroy(chan);
+ }
+ logging_daemon_chan = NULL;
+ cl_direct_log(priority, buf, TRUE, NULL, cl_process_pid, NULLTIME);
+
+ if (drop_msg_num > 0){
+ /* Direct logging here is ok since we're
+ * switching to that for everything
+ * "for a while"
+ */
+ cl_log(LOG_ERR,
+ "cl_log: %d messages were dropped"
+ " : channel destroyed", drop_msg_num);
+ }
+
+ drop_msg_num=0;
+ FreeChildLogIPCMessage(msg);
+ return HA_FAIL;
+ }
+
+ drop_msg_num++;
+
+ }
+
+ FreeChildLogIPCMessage(msg);
+ return HA_FAIL;
+}
+
+
+static gboolean
+send_dropped_message(gboolean use_pri_str, IPC_Channel *chan)
+{
+ int sendrc;
+ char buf[64];
+ int buf_len = 0;
+ IPC_Message *drop_msg = NULL;
+
+ memset(buf, 0, 64);
+ snprintf(buf, 64, "cl_log: %d messages were dropped", drop_msg_num);
+ buf_len = strlen(buf)+1;
+ drop_msg = ChildLogIPCMessage(LOG_ERR, buf, buf_len, use_pri_str, chan);
+
+ if(drop_msg == NULL || drop_msg->msg_len == 0) {
+ return FALSE;
+ }
+
+ sendrc = chan->ops->send(chan, drop_msg);
+
+ if(sendrc == IPC_OK) {
+ drop_msg_num = 0;
+ }else{
+ FreeChildLogIPCMessage(drop_msg);
+ }
+ return sendrc == IPC_OK;
+}
+
+
+static IPC_Message*
+ChildLogIPCMessage(int priority, const char *buf, int bufstrlen,
+ gboolean use_prio_str, IPC_Channel* ch)
+{
+ IPC_Message* ret;
+ LogDaemonMsgHdr logbuf;
+ int msglen;
+ char* bodybuf;
+
+
+ if (ch->msgpad > MAX_MSGPAD){
+ cl_log(LOG_ERR, "ChildLogIPCMessage: invalid msgpad(%d)",
+ ch->msgpad);
+ return NULL;
+ }
+
+
+ ret = (IPC_Message*)malloc(sizeof(IPC_Message));
+
+ if (ret == NULL) {
+ return ret;
+ }
+
+ memset(ret, 0, sizeof(IPC_Message));
+
+ /* Compute msg len: including room for the EOS byte */
+ msglen = sizeof(LogDaemonMsgHdr)+bufstrlen + 1;
+ bodybuf = malloc(msglen + ch->msgpad);
+ if (bodybuf == NULL) {
+ free(ret);
+ return NULL;
+ }
+
+ memset(bodybuf, 0, msglen + ch->msgpad);
+ memset(&logbuf, 0, sizeof(logbuf));
+ logbuf.msgtype = LD_LOGIT;
+ logbuf.facility = cl_log_facility;
+ logbuf.priority = priority;
+ logbuf.use_pri_str = use_prio_str;
+ logbuf.entity_pid = getpid();
+ logbuf.timestamp = time(NULL);
+ if (*cl_log_entity){
+ strncpy(logbuf.entity,cl_log_entity,MAXENTITY);
+ }else {
+ strncpy(logbuf.entity,DFLT_ENTITY,MAXENTITY);
+ }
+
+ logbuf.msglen = bufstrlen + 1;
+ memcpy(bodybuf + ch->msgpad, &logbuf, sizeof(logbuf));
+ memcpy(bodybuf + ch->msgpad + sizeof(logbuf),
+ buf,
+ bufstrlen);
+
+ ret->msg_len = msglen;
+ ret->msg_buf = bodybuf;
+ ret->msg_body = bodybuf + ch->msgpad;
+ ret->msg_done = FreeChildLogIPCMessage;
+ ret->msg_ch = ch;
+
+ return ret;
+}
+
+
+static void
+FreeChildLogIPCMessage(IPC_Message* msg)
+{
+ if (msg == NULL) {
+ return;
+ }
+ memset(msg->msg_body, 0, msg->msg_len);
+ free(msg->msg_buf);
+
+ memset(msg, 0, sizeof (*msg));
+ free(msg);
+
+ return;
+
+}
+
+
+
+static void
+cl_opensyslog(void)
+{
+ if (*cl_log_entity == '\0' || cl_log_facility < 0) {
+ return;
+ }
+ syslog_enabled = 1;
+ strncpy(common_log_entity, cl_log_entity, MAXENTITY);
+ openlog(common_log_entity, LOG_CONS, cl_log_facility);
+}
+
+
+void
+cl_log_args(int argc, char **argv)
+{
+ int lpc = 0;
+ int len = 0;
+ int existing_len = 0;
+ char *arg_string = NULL;
+
+ if(argc == 0 || argv == NULL) {
+ return;
+ }
+
+ for(;lpc < argc; lpc++) {
+ if(argv[lpc] == NULL) {
+ break;
+ }
+
+ len = 2 + strlen(argv[lpc]); /* +1 space, +1 EOS */
+ if(arg_string) {
+ existing_len = strlen(arg_string);
+ }
+
+ arg_string = realloc(arg_string, len + existing_len);
+ sprintf(arg_string + existing_len, "%s ", argv[lpc]);
+ }
+ cl_log(LOG_INFO, "Invoked: %s", arg_string);
+ free(arg_string);
+}
diff --git a/lib/clplumbing/cl_malloc.c b/lib/clplumbing/cl_malloc.c
new file mode 100644
index 0000000..ca6dc0b
--- /dev/null
+++ b/lib/clplumbing/cl_malloc.c
@@ -0,0 +1,1044 @@
+/*
+ * Copyright (C) 2000 Alan Robertson <alanr@unix.sh>
+ *
+ * This software licensed under the GNU LGPL.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define HA_MALLOC_ORIGINAL
+#include <lha_internal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif /* HAVE_STDINT_H */
+#include <string.h>
+#include <errno.h>
+#ifndef BSD
+#ifdef HAVE_MALLOC_H
+# include <malloc.h>
+#endif
+#endif
+#include <clplumbing/cl_malloc.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/longclock.h>
+
+#include <ltdl.h>
+
+#ifndef _CLPLUMBING_CLMALLOC_NATIVE_H
+static cl_mem_stats_t default_memstats;
+static volatile cl_mem_stats_t * memstats = &default_memstats;
+
+/*
+ * Compile time malloc debugging switches:
+ *
+ * MARK_PRISTINE - puts known byte pattern in freed memory
+ * Good at finding "use after free" cases
+ * Cheap in memory, but expensive in CPU
+ *
+ * MAKE_GUARD - puts a known pattern *after* allocated memory
+ * Good at finding overrun problems after the fact
+ * Cheap in CPU, adds a few bytes to each malloc item
+ *
+ */
+
+#define MARK_PRISTINE 1 /* Expensive in CPU time */
+#undef MARK_PRISTINE
+#define MAKE_GUARD 1 /* Adds 'n' bytes memory - cheap in CPU*/
+#define USE_ASSERTS 1
+#define DUMPONERR 1
+#define RETURN_TO_MALLOC 1
+#undef RETURN_TO_MALLOC
+
+#ifndef DUMPONERR
+# define DUMPIFASKED() /* nothing */
+#else
+# define DUMPIFASKED() {abort();}
+#endif
+
+
+/*
+ *
+ * Malloc wrapper functions
+ *
+ * I wrote these so we can better track memory leaks, etc. and verify
+ * that the system is stable in terms of memory usage.
+ *
+ * For our purposes, these functions are a somewhat faster than using
+ * malloc directly (although they use a bit more memory)
+ *
+ * The general strategy is loosely related to the buddy system,
+ * except very simple, well-suited to our continuous running
+ * nature, and the constancy of the requests and messages.
+ *
+ * We keep an array of linked lists, each for a different size
+ * buffer. If we need a buffer larger than the largest one provided
+ * by the list, we go directly to malloc.
+ *
+ * Otherwise, we keep return them to the appropriate linked list
+ * when we're done with them, and reuse them from the list.
+ *
+ * We never coalesce buffers on our lists, and we never free them.
+ *
+ * It's very simple. We get usage stats. It makes me happy.
+ */
+
+#define HA_MALLOC_MAGIC 0xFEEDBEEFUL
+#define HA_FREE_MAGIC 0xDEADBEEFUL
+
+
+/*
+ * We put a struct cl_mhdr in front of every malloc item.
+ * This means each malloc item is at least 12 bytes bigger than it theoretically
+ * needs to be. But, it allows this code to be fast and recognize
+ * multiple free attempts, and memory corruption *before* the object
+ *
+ * It's probably possible to combine these fields a bit,
+ * since bucket and reqsize are only needed for allocated items,
+ * both are bounded in value, and fairly strong integrity checks apply
+ * to them. But then we wouldn't be able to tell *quite* as reliably
+ * if someone gave us an item to free that we didn't allocate...
+ *
+ * Could even make the bucket and reqsize objects into 16-bit ints...
+ *
+ * The idea of getting it all down into 32-bits of overhead is
+ * an interesting thought...
+ *
+ * But some architectures have alignment constraints. For instance, sparc
+ * requires that double-word accesses be aligned on double-word boundaries.
+ * Thus if the requested space is bigger than a double-word, then cl_mhdr
+ * should, for safety, be a double-word multiple (minimum 8bytes, 64bits).
+
+*/
+
+#ifdef HA_MALLOC_TRACK
+# define HA_MALLOC_OWNER 64
+struct cl_bucket;
+#endif
+
+struct cl_mhdr {
+# ifdef HA_MALLOC_MAGIC
+ unsigned long magic; /* Must match HA_*_MAGIC */
+#endif
+# ifdef HA_MALLOC_TRACK
+ char owner[HA_MALLOC_OWNER];
+ struct cl_bucket * left;
+ struct cl_bucket * right;
+ int dumped;
+ longclock_t mtime;
+#endif
+ size_t reqsize;
+ int bucket;
+};
+
+struct cl_bucket {
+ struct cl_mhdr hdr;
+ struct cl_bucket * next;
+};
+
+#define NUMBUCKS 12
+#define NOBUCKET (NUMBUCKS)
+
+static struct cl_bucket* cl_malloc_buckets[NUMBUCKS];
+static size_t cl_bucket_sizes[NUMBUCKS];
+static size_t buckminpow2 = 0L;
+
+static int cl_malloc_inityet = 0;
+static size_t cl_malloc_hdr_offset = sizeof(struct cl_mhdr);
+
+static void* cl_new_mem(size_t size, int numbuck);
+static void cl_malloc_init(void);
+static void cl_dump_item(const struct cl_bucket*b);
+
+#ifdef MARK_PRISTINE
+# define PRISTVALUE 0xff
+ static int cl_check_is_pristine(const void* v, unsigned size);
+ static void cl_mark_pristine(void* v, unsigned size);
+ static int pristoff;
+#endif
+
+#define BHDR(p) ((struct cl_bucket*)(void*)(((char*)p)-cl_malloc_hdr_offset))
+#define CBHDR(p) ((const struct cl_bucket*)(const void*)(((const char*)p)-cl_malloc_hdr_offset))
+#define MEMORYSIZE(p)(CBHDR(p)->hdr.reqsize)
+
+#define MALLOCSIZE(allocsize) ((allocsize) + cl_malloc_hdr_offset + GUARDSIZE)
+#define MAXMALLOC (SIZE_MAX-(MALLOCSIZE(0)+1))
+
+#ifdef MAKE_GUARD
+# define GUARDLEN 4
+ static const unsigned char cl_malloc_guard[] =
+#if GUARDLEN == 1
+ {0xA5};
+#endif
+#if GUARDLEN == 2
+ {0x5A, 0xA5};
+#endif
+#if GUARDLEN == 4
+ {0x5A, 0xA5, 0x5A, 0xA5};
+#endif
+# define GUARDSIZE sizeof(cl_malloc_guard)
+# define ADD_GUARD(cp) (memcpy((((char*)cp)+MEMORYSIZE(cp)), cl_malloc_guard, sizeof(cl_malloc_guard)))
+# define GUARD_IS_OK(cp) (memcmp((((const char*)cp)+MEMORYSIZE(cp)), \
+ cl_malloc_guard, sizeof(cl_malloc_guard)) == 0)
+# define CHECK_GUARD_BYTES(cp, msg) { \
+ if (!GUARD_IS_OK(cp)) { \
+ cl_log(LOG_ERR, "%s: guard corrupted at 0x%lx", msg \
+ , (unsigned long)cp); \
+ cl_dump_item(CBHDR(cp)); \
+ DUMPIFASKED(); \
+ } \
+ }
+#else
+# define GUARDSIZE 0
+# define ADD_GUARD(cp) /* */
+# define GUARD_IS_OK(cp) (1)
+# define CHECK_GUARD_BYTES(cp, msg) /* */
+#endif
+
+#define MALLOCROUND 4096 /* Round big mallocs up to a multiple of this size */
+
+
+#ifdef HA_MALLOC_TRACK
+
+static struct cl_bucket * cl_malloc_track_root = NULL;
+
+static void
+cl_ptr_tag(void *ptr, const char *file, const char *function, const int line)
+{
+ struct cl_bucket* bhdr = BHDR(ptr);
+ snprintf(bhdr->hdr.owner, HA_MALLOC_OWNER, "%s:%s:%d",
+ file, function, line);
+}
+
+static void
+cl_ptr_track(void *ptr)
+{
+ struct cl_bucket* bhdr = BHDR(ptr);
+
+#if defined(USE_ASSERTS)
+ g_assert(bhdr->hdr.left == NULL);
+ g_assert(bhdr->hdr.right == NULL);
+ g_assert((cl_malloc_track_root == NULL) || (cl_malloc_track_root->hdr.left == NULL));
+#endif
+
+ bhdr->hdr.dumped = 0;
+ bhdr->hdr.mtime = time_longclock();
+
+ if (cl_malloc_track_root == NULL) {
+ cl_malloc_track_root = bhdr;
+ } else {
+ bhdr->hdr.right = cl_malloc_track_root;
+ cl_malloc_track_root->hdr.left = bhdr;
+ cl_malloc_track_root = bhdr;
+ }
+}
+
+static void
+cl_ptr_release(void *ptr)
+{
+ struct cl_bucket* bhdr = BHDR(ptr);
+
+/* cl_log(LOG_DEBUG, "cl_free: Freeing memory belonging to %s"
+ , bhdr->hdr.owner); */
+
+#if defined(USE_ASSERTS)
+ g_assert(cl_malloc_track_root != NULL);
+ g_assert(cl_malloc_track_root->hdr.left == NULL);
+#endif
+
+ if (bhdr->hdr.left != NULL) {
+ bhdr->hdr.left->hdr.right=bhdr->hdr.right;
+ }
+
+ if (bhdr->hdr.right != NULL) {
+ bhdr->hdr.right->hdr.left=bhdr->hdr.left;
+ }
+
+ if (cl_malloc_track_root == bhdr) {
+ cl_malloc_track_root=bhdr->hdr.right;
+ }
+
+ bhdr->hdr.left = NULL;
+ bhdr->hdr.right = NULL;
+}
+
+static void
+cl_ptr_init(void)
+{
+ cl_malloc_track_root = NULL;
+}
+
+int
+cl_malloc_dump_allocated(int log_level, gboolean filter)
+{
+ int lpc = 0;
+ struct cl_bucket* cursor = cl_malloc_track_root;
+ longclock_t time_diff;
+
+ cl_log(LOG_INFO, "Dumping allocated memory buffers:");
+
+ while (cursor != NULL) {
+ if(filter && cursor->hdr.dumped) {
+
+ } else if(log_level > LOG_DEBUG) {
+ } else if(filter) {
+ lpc++;
+ cl_log(log_level, "cl_malloc_dump: %p owner %s, size %d"
+ , cursor+cl_malloc_hdr_offset
+ , cursor->hdr.owner
+ , (int)cursor->hdr.reqsize);
+ } else {
+ lpc++;
+ time_diff = sub_longclock(time_longclock(), cursor->hdr.mtime);
+ cl_log(log_level, "cl_malloc_dump: %p owner %s, size %d, dumped %d, age %lu ms"
+ , cursor+cl_malloc_hdr_offset
+ , cursor->hdr.owner
+ , (int)cursor->hdr.reqsize
+ , cursor->hdr.dumped
+ , longclockto_long(time_diff));
+ }
+ cursor->hdr.dumped = 1;
+ cursor = cursor->hdr.right;
+ }
+
+ cl_log(LOG_INFO, "End dump.");
+ return lpc;
+}
+#endif
+static const int LogTable256[] =
+{
+ 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+};
+#define POW2BYTE(b) (LogTable256[b])
+#define BYTE3(i) (((i)&0xFF000000)>>24)
+#define BYTE2(i) (((i)&0x00FF0000)>>16)
+#define BYTE1(i) (((i)&0x0000FF00)>>8)
+#define BYTE0(i) ((i)&0x000000FF)
+
+/* Works for malloc bucket sizes up to 2^8 */
+#define POW21BYTE(i) (POW2BYTE(BYTE0(i)))
+
+/* Works for malloc bucket sizes up to 2^16 */
+#define POW22BYTE(i) ((BYTE1(i) != 0x00)? (POW2BYTE(BYTE1(i))+8) \
+ : (POW21BYTE(i)))
+
+/* Works for malloc bucket sizes up to 2^24 */
+#define POW23BYTE(i) ((BYTE2(i) != 0x00)? (POW2BYTE(BYTE2(i))+16) \
+ : POW22BYTE(i))
+
+/* Works for malloc bucket sizes up to 2^32 */
+#define POW24BYTE(i) ((BYTE3(i) != 0x00)? (POW2BYTE(BYTE3(i))+24) \
+ : POW23BYTE(i))
+
+/* #define INT2POW2(i) POW24BYTE(i) / * This would allow 2G in our largest malloc chain */
+ /* which I don't think we need */
+#define INT2POW2(i) POW23BYTE(i) /* This allows up to about 16 Mbytes in our largest malloc chain */
+ /* and it's a little faster than the one above */
+
+
+/*
+ * cl_malloc: malloc clone
+ */
+
+void *
+cl_malloc(size_t size)
+{
+#if 0
+ int j;
+#endif
+ int numbuck = NOBUCKET;
+ struct cl_bucket* buckptr = NULL;
+ void* ret;
+
+ if(!size) {
+ cl_log(LOG_ERR
+ , "%s: refusing to allocate zero sized block"
+ , __FUNCTION__
+ );
+ return NULL;
+ }
+ if (size > MAXMALLOC) {
+ return NULL;
+ }
+ if (!cl_malloc_inityet) {
+ cl_malloc_init();
+ }
+
+#if 1
+ /*
+ * NOTE: This restricts bucket sizes to be powers of two
+ * - which is OK with me - and how the code has always worked :-D
+ */
+ numbuck = INT2POW2(size-1)-buckminpow2;
+ numbuck = MAX(0, numbuck);
+ if (numbuck < NUMBUCKS) {
+ if (size <= cl_bucket_sizes[numbuck]
+ || (numbuck > 0 && size <= (cl_bucket_sizes[numbuck]/2))) {
+ buckptr = cl_malloc_buckets[numbuck];
+ }else{
+ cl_log(LOG_ERR
+ , "%s: bucket size bug: %lu bytes in %lu byte bucket #%d"
+ , __FUNCTION__
+ , (unsigned long)size
+ , (unsigned long)cl_bucket_sizes[numbuck]
+ , numbuck);
+
+ }
+ }
+#else
+ /*
+ * Find which bucket would have buffers of the requested size
+ */
+ for (j=0; j < NUMBUCKS; ++j) {
+ if (size <= cl_bucket_sizes[j]) {
+ numbuck = j;
+ buckptr = cl_malloc_buckets[numbuck];
+ break;
+ }
+ }
+#endif
+
+ /*
+ * Pull it out of the linked list of free buffers if we can...
+ */
+
+ if (buckptr == NULL) {
+ ret = cl_new_mem(size, numbuck);
+ }else{
+ cl_malloc_buckets[numbuck] = buckptr->next;
+ buckptr->hdr.reqsize = size;
+ ret = (((char*)buckptr)+cl_malloc_hdr_offset);
+
+#ifdef MARK_PRISTINE
+ {
+ int bucksize = cl_bucket_sizes[numbuck];
+ if (!cl_check_is_pristine(ret, bucksize)) {
+ cl_log(LOG_ERR
+ , "attempt to allocate memory"
+ " which is not pristine.");
+ cl_dump_item(buckptr);
+ DUMPIFASKED();
+ }
+ }
+#endif
+
+#ifdef HA_MALLOC_MAGIC
+ switch (buckptr->hdr.magic) {
+
+ case HA_FREE_MAGIC:
+ break;
+
+ case HA_MALLOC_MAGIC:
+ cl_log(LOG_ERR
+ , "attempt to allocate memory"
+ " already allocated at 0x%lx"
+ , (unsigned long)ret);
+ cl_dump_item(buckptr);
+ DUMPIFASKED();
+ ret=NULL;
+ break;
+
+ default:
+ cl_log(LOG_ERR
+ , "corrupt malloc buffer at 0x%lx"
+ , (unsigned long)ret);
+ cl_dump_item(buckptr);
+ DUMPIFASKED();
+ ret=NULL;
+ break;
+ }
+ buckptr->hdr.magic = HA_MALLOC_MAGIC;
+#endif /* HA_MALLOC_MAGIC */
+ if (memstats) {
+ memstats->nbytes_req += size;
+ memstats->nbytes_alloc
+ += MALLOCSIZE(cl_bucket_sizes[numbuck]);
+ }
+
+ }
+
+ if (ret && memstats) {
+#if 0 && defined(HAVE_MALLINFO)
+ /* mallinfo is too expensive to use :-( */
+ struct mallinfo i = mallinfo();
+ memstats->arena = i.arena;
+#endif
+ memstats->numalloc++;
+ }
+ if (ret) {
+#ifdef HA_MALLOC_TRACK
+ /* If we were _always_ called via the wrapper functions,
+ * this wouldn't be necessary, but we aren't, some use
+ * function pointers directly to cl_malloc() */
+ cl_ptr_track(ret);
+ cl_ptr_tag(ret, "cl_malloc.c", "cl_malloc", 0);
+#endif
+ ADD_GUARD(ret);
+ }
+ return(ret);
+}
+
+int
+cl_is_allocated(const void *ptr)
+{
+#ifdef HA_MALLOC_MAGIC
+ if (NULL == ptr || CBHDR(ptr)->hdr.magic != HA_MALLOC_MAGIC) {
+ return FALSE;
+ }else if (GUARD_IS_OK(ptr)) {
+ return TRUE;
+ }
+ cl_log(LOG_ERR
+ , "cl_is_allocated: supplied storage is guard-corrupted at 0x%lx"
+ , (unsigned long)ptr);
+ cl_dump_item(CBHDR(ptr));
+ DUMPIFASKED();
+ return FALSE;
+#else
+ return (ptr != NULL);
+#endif
+}
+
+/*
+ * cl_free: "free" clone
+ */
+
+void
+cl_free(void *ptr)
+{
+ int bucket;
+ struct cl_bucket* bhdr;
+
+ if (!cl_malloc_inityet) {
+ cl_malloc_init();
+ }
+
+ if (ptr == NULL) {
+ cl_log(LOG_ERR, "attempt to free NULL pointer in cl_free()");
+ DUMPIFASKED();
+ return;
+ }
+
+ /* Find the beginning of our "hidden" structure */
+
+ bhdr = BHDR(ptr);
+
+#ifdef HA_MALLOC_MAGIC
+ switch (bhdr->hdr.magic) {
+ case HA_MALLOC_MAGIC:
+ break;
+
+ case HA_FREE_MAGIC:
+ cl_log(LOG_ERR
+ , "cl_free: attempt to free already-freed"
+ " object at 0x%lx"
+ , (unsigned long)ptr);
+ cl_dump_item(bhdr);
+ DUMPIFASKED();
+ return;
+ break;
+ default:
+ cl_log(LOG_ERR, "cl_free: Bad magic number"
+ " in object at 0x%lx"
+ , (unsigned long)ptr);
+ cl_dump_item(bhdr);
+ DUMPIFASKED();
+ return;
+ break;
+ }
+#endif
+ if (!GUARD_IS_OK(ptr)) {
+ cl_log(LOG_ERR
+ , "cl_free: attempt to free guard-corrupted"
+ " object at 0x%lx", (unsigned long)ptr);
+ cl_dump_item(bhdr);
+ DUMPIFASKED();
+ return;
+ }
+#ifdef HA_MALLOC_TRACK
+ cl_ptr_release(ptr);
+#endif
+ bucket = bhdr->hdr.bucket;
+#ifdef HA_MALLOC_MAGIC
+ bhdr->hdr.magic = HA_FREE_MAGIC;
+#endif
+
+ /*
+ * Return it to the appropriate bucket (linked list), or just free
+ * it if it didn't come from one of our lists...
+ */
+
+#ifndef RETURN_TO_MALLOC
+ if (bucket >= NUMBUCKS) {
+#endif
+#ifdef MARK_PRISTINE
+ /* Is this size right? */
+ cl_mark_pristine(ptr, bhdr->hdr.reqsize);
+#endif
+ if (memstats) {
+ memstats->nbytes_req -= bhdr->hdr.reqsize;
+ memstats->nbytes_alloc -= MALLOCSIZE(bhdr->hdr.reqsize);
+ memstats->mallocbytes -= MALLOCSIZE(bhdr->hdr.reqsize);
+ }
+ free(bhdr);
+#ifndef RETURN_TO_MALLOC
+ }else{
+ int bucksize = cl_bucket_sizes[bucket];
+#if defined(USE_ASSERTS)
+ g_assert(bhdr->hdr.reqsize <= cl_bucket_sizes[bucket]);
+# endif
+ if (memstats) {
+ memstats->nbytes_req -= bhdr->hdr.reqsize;
+ memstats->nbytes_alloc -= MALLOCSIZE(bucksize);
+ }
+ bhdr->next = cl_malloc_buckets[bucket];
+ cl_malloc_buckets[bucket] = bhdr;
+#ifdef MARK_PRISTINE
+ cl_mark_pristine(ptr, bucksize);
+# endif
+ }
+#endif /* RETURN_TO_MALLOC */
+ if (memstats) {
+ memstats->numfree++;
+ }
+}
+
+void*
+cl_realloc(void *ptr, size_t newsize)
+{
+ struct cl_bucket* bhdr;
+ int bucket;
+ size_t bucksize;
+
+ if (!cl_malloc_inityet) {
+ cl_malloc_init();
+ }
+
+ if (memstats) {
+ memstats->numrealloc++;
+ }
+ if (ptr == NULL) {
+ /* NULL is a legal 'ptr' value for realloc... */
+ return cl_malloc(newsize);
+ }
+ if (newsize == 0) {
+ /* realloc() is the most redundant interface ever */
+ cl_free(ptr);
+ return NULL;
+ }
+
+ /* Find the beginning of our "hidden" structure */
+
+ bhdr = BHDR(ptr);
+
+#ifdef HA_MALLOC_MAGIC
+ switch (bhdr->hdr.magic) {
+ case HA_MALLOC_MAGIC:
+ break;
+
+ case HA_FREE_MAGIC:
+ cl_log(LOG_ERR
+ , "cl_realloc: attempt to realloc already-freed"
+ " object at 0x%lx"
+ , (unsigned long)ptr);
+ cl_dump_item(bhdr);
+ DUMPIFASKED();
+ return NULL;
+ break;
+ default:
+ cl_log(LOG_ERR, "cl_realloc: Bad magic number"
+ " in object at 0x%lx"
+ , (unsigned long)ptr);
+ cl_dump_item(bhdr);
+ DUMPIFASKED();
+ return NULL;
+ break;
+ }
+#endif
+ CHECK_GUARD_BYTES(ptr, "cl_realloc");
+
+ bucket = bhdr->hdr.bucket;
+
+ /*
+ * Figure out which bucket it came from... If any...
+ */
+
+ if (bucket >= NUMBUCKS) {
+ /* Not from our bucket-area... Call realloc... */
+ if (memstats) {
+ memstats->nbytes_req -= bhdr->hdr.reqsize;
+ memstats->nbytes_alloc -= MALLOCSIZE(bhdr->hdr.reqsize);
+ memstats->mallocbytes -= MALLOCSIZE(bhdr->hdr.reqsize);
+ memstats->nbytes_req += newsize;
+ memstats->nbytes_alloc += MALLOCSIZE(newsize);
+ memstats->mallocbytes += MALLOCSIZE(newsize);
+ }
+#ifdef HA_MALLOC_TRACK
+ cl_ptr_release(ptr);
+#endif
+ bhdr = realloc(bhdr, newsize + cl_malloc_hdr_offset + GUARDSIZE);
+ if (!bhdr) {
+ return NULL;
+ }
+#ifdef HA_MALLOC_TRACK
+ cl_ptr_track(ptr);
+ cl_ptr_tag(ptr, "cl_malloc.c", "realloc", 0);
+#endif
+ bhdr->hdr.reqsize = newsize;
+ ptr = (((char*)bhdr)+cl_malloc_hdr_offset);
+ ADD_GUARD(ptr);
+ CHECK_GUARD_BYTES(ptr, "cl_realloc - real realloc return value");
+ /* Not really a memory leak... BEAM thinks so though... */
+ return ptr; /*memory leak*/
+ }
+ bucksize = cl_bucket_sizes[bucket];
+#if defined(USE_ASSERTS)
+ g_assert(bhdr->hdr.reqsize <= bucksize);
+#endif
+ if (newsize > bucksize) {
+ /* Need to allocate new space for it */
+ void* newret = cl_malloc(newsize);
+ if (newret != NULL) {
+ memcpy(newret, ptr, bhdr->hdr.reqsize);
+ CHECK_GUARD_BYTES(newret, "cl_realloc - cl_malloc case");
+ }
+ cl_free(ptr);
+ return newret;
+ }
+
+ /* Amazing! It fits into the space previously allocated for it! */
+ bhdr->hdr.reqsize = newsize;
+ if (memstats) {
+ memstats->nbytes_req -= bhdr->hdr.reqsize;
+ memstats->nbytes_req += newsize;
+ }
+ ADD_GUARD(ptr);
+ CHECK_GUARD_BYTES(ptr, "cl_realloc - fits in existing space");
+ return ptr;
+}
+
+/*
+ * cl_new_mem: use the real malloc to allocate some new memory
+ */
+
+static void*
+cl_new_mem(size_t size, int numbuck)
+{
+ struct cl_bucket* hdrret;
+ size_t allocsize;
+ size_t mallocsize;
+
+ if (numbuck < NUMBUCKS) {
+ allocsize = cl_bucket_sizes[numbuck];
+ }else{
+ allocsize = size;
+ }
+
+ mallocsize = MALLOCSIZE(allocsize);
+ if (numbuck == NOBUCKET) {
+ mallocsize = (((mallocsize + (MALLOCROUND-1))/MALLOCROUND)*MALLOCROUND);
+ }
+
+ if ((hdrret = malloc(mallocsize)) == NULL) {
+ return NULL;
+ }
+
+ hdrret->hdr.reqsize = size;
+ hdrret->hdr.bucket = numbuck;
+#ifdef HA_MALLOC_MAGIC
+ hdrret->hdr.magic = HA_MALLOC_MAGIC;
+#endif
+#ifdef HA_MALLOC_TRACK
+ hdrret->hdr.left = NULL;
+ hdrret->hdr.right = NULL;
+ hdrret->hdr.owner[0] = '\0';
+ hdrret->hdr.dumped = 0;
+#endif
+
+ if (memstats) {
+ memstats->nbytes_alloc += mallocsize;
+ memstats->nbytes_req += size;
+ memstats->mallocbytes += mallocsize;
+ }
+ /* BEAM BUG -- this is NOT a leak */
+ return(((char*)hdrret)+cl_malloc_hdr_offset); /*memory leak*/
+}
+
+
+/*
+ * cl_calloc: calloc clone
+ */
+
+void *
+cl_calloc(size_t nmemb, size_t size)
+{
+ void * ret = cl_malloc(nmemb*size);
+
+ if (ret != NULL) {
+ memset(ret, 0, nmemb*size);
+#ifdef HA_MALLOC_TRACK
+ cl_ptr_tag(ret, "cl_malloc.c", "cl_calloc", 0);
+#endif
+ }
+
+ return(ret);
+}
+
+#ifdef HA_MALLOC_TRACK
+void *
+cl_calloc_track(size_t nmemb, size_t size,
+ const char *file, const char *function, const int line)
+{
+ void* ret;
+
+ ret = cl_calloc(nmemb, size);
+
+ if (ret) {
+ cl_ptr_tag(ret, file, function, line);
+ }
+
+ return ret;
+}
+
+void*
+cl_realloc_track(void *ptr, size_t newsize,
+ const char *file, const char *function, const int line)
+{
+ void* ret;
+
+ ret = cl_realloc(ptr, newsize);
+
+ if (ret) {
+ cl_ptr_tag(ret, file, function, line);
+ }
+
+ return ret;
+}
+
+void *
+cl_malloc_track(size_t size,
+ const char *file, const char *function, const int line)
+{
+ void* ret;
+
+ ret = cl_malloc(size);
+ if (ret) {
+ /* Retag with the proper owner. */
+ cl_ptr_tag(ret, file, function, line);
+ }
+
+ return ret;
+}
+
+#endif
+
+/*
+ * cl_strdup: strdup clone
+ */
+
+char *
+cl_strdup(const char *s)
+{
+ void * ret;
+
+ if (!s) {
+ cl_log(LOG_ERR, "cl_strdup(NULL)");
+ return(NULL);
+ }
+ ret = cl_malloc((strlen(s) + 1) * sizeof(char));
+
+ if (ret) {
+ strcpy(ret, s);
+ }
+
+ return(ret);
+}
+
+
+/*
+ * cl_malloc_init(): initialize our malloc wrapper things
+ */
+
+static void
+cl_malloc_init()
+{
+ int j;
+ size_t cursize = 32;
+ int llcount = 1;
+
+ cl_malloc_inityet = 1;
+
+ /* cl_malloc_hdr_offset should be a double-word multiple */
+ while (cl_malloc_hdr_offset > (llcount * sizeof(long long))) {
+ llcount++;
+ }
+ cl_malloc_hdr_offset = llcount * sizeof(long long);
+
+
+ for (j=0; j < NUMBUCKS; ++j) {
+ cl_malloc_buckets[j] = NULL;
+
+ cl_bucket_sizes[j] = cursize;
+ cursize <<= 1;
+ }
+ buckminpow2 = INT2POW2(cl_bucket_sizes[0]-1);
+#ifdef MARK_PRISTINE
+ {
+ struct cl_bucket b;
+ pristoff = (unsigned char*)&(b.next)-(unsigned char*)&b;
+ pristoff += sizeof(b.next);
+ }
+#endif
+#ifdef HA_MALLOC_TRACK
+ cl_ptr_init();
+#endif
+}
+
+void
+cl_malloc_setstats(volatile cl_mem_stats_t *stats)
+{
+ if (memstats && stats) {
+ *stats = *memstats;
+ }
+ memstats = stats;
+}
+
+volatile cl_mem_stats_t *
+cl_malloc_getstats(void)
+{
+ return memstats;
+}
+
+static void
+cl_dump_item(const struct cl_bucket*b)
+{
+ const unsigned char * cbeg;
+ const unsigned char * cend;
+ const unsigned char * cp;
+ cl_log(LOG_INFO, "Dumping cl_malloc item @ 0x%lx, bucket address: 0x%lx"
+ , ((unsigned long)b)+cl_malloc_hdr_offset, (unsigned long)b);
+#ifdef HA_MALLOC_TRACK
+ cl_log(LOG_INFO, "Owner: %s"
+ , b->hdr.owner);
+#endif
+#ifdef HA_MALLOC_MAGIC
+ cl_log(LOG_INFO, "Magic number: 0x%lx reqsize=%ld"
+ ", bucket=%d, bucksize=%ld"
+ , b->hdr.magic
+ , (long)b->hdr.reqsize, b->hdr.bucket
+ , (long)(b->hdr.bucket >= NUMBUCKS ? 0
+ : cl_bucket_sizes[b->hdr.bucket]));
+#else
+ cl_log(LOG_INFO, "reqsize=%ld"
+ ", bucket=%d, bucksize=%ld"
+ , (long)b->hdr.reqsize, b->hdr.bucket
+ , (long)(b->hdr.bucket >= NUMBUCKS ? 0
+ : cl_bucket_sizes[b->hdr.bucket]));
+#endif
+ cbeg = ((const unsigned char *)b)+cl_malloc_hdr_offset;
+ cend = cbeg+b->hdr.reqsize+GUARDSIZE;
+
+ for (cp=cbeg; cp < cend; cp+= sizeof(unsigned)) {
+ cl_log(LOG_INFO, "%02x %02x %02x %02x \"%c%c%c%c\""
+ , (unsigned)cp[0], (unsigned)cp[1]
+ , (unsigned)cp[2], (unsigned)cp[3]
+ , cp[0], cp[1], cp[2], cp[3]);
+ }
+}
+
+/* The only reason these functions exist is because glib uses non-standard
+ * types (gsize)in place of size_t. Since size_t is 64-bits on some
+ * machines where gsize (unsigned int) is 32-bits, this is annoying.
+ */
+
+static gpointer
+cl_malloc_glib(gsize n_bytes)
+{
+ return (gpointer)cl_malloc((size_t)n_bytes);
+}
+
+static void
+cl_free_glib(gpointer mem)
+{
+ cl_free((void*)mem);
+}
+
+static void *
+cl_realloc_glib(gpointer mem, gsize n_bytes)
+{
+ return cl_realloc((void*)mem, (size_t)n_bytes);
+}
+
+
+/* Call before using any glib functions(!) */
+/* See also: g_mem_set_vtable() */
+void
+cl_malloc_forced_for_glib(void)
+{
+ static GMemVTable vt = {
+ cl_malloc_glib,
+ cl_realloc_glib,
+ cl_free_glib,
+ NULL,
+ NULL,
+ NULL,
+ };
+ if (!cl_malloc_inityet) {
+ cl_malloc_init();
+ }
+ g_mem_set_vtable(&vt);
+}
+
+#ifdef MARK_PRISTINE
+static int
+cl_check_is_pristine(const void* v, unsigned size)
+{
+ const unsigned char * cp;
+ const unsigned char * last;
+ cp = v;
+ last = cp + size;
+ cp += pristoff;
+
+ for (;cp < last; ++cp) {
+ if (*cp != PRISTVALUE) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+static void
+cl_mark_pristine(void* v, unsigned size)
+{
+ unsigned char * cp = v;
+ memset(cp+pristoff, PRISTVALUE, size-pristoff);
+}
+#endif
+
+#endif /* _CLPLUMBING_CLMALLOC_NATIVE_H */
diff --git a/lib/clplumbing/cl_misc.c b/lib/clplumbing/cl_misc.c
new file mode 100644
index 0000000..be6441d
--- /dev/null
+++ b/lib/clplumbing/cl_misc.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2005 Guochun Shi <gshi@ncsa.uiuc.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+#include <lha_internal.h>
+
+#include <strings.h>
+#include <clplumbing/cl_misc.h>
+#include <clplumbing/cl_log.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#include <sys/time.h>
+
+int
+cl_str_to_boolean(const char * s, int * ret)
+{
+ if(s == NULL) {
+ return HA_FAIL;
+ }
+
+ if ( strcasecmp(s, "true") == 0
+ || strcasecmp(s, "on") == 0
+ || strcasecmp(s, "yes") == 0
+ || strcasecmp(s, "y") == 0
+ || strcasecmp(s, "1") == 0){
+ *ret = TRUE;
+ return HA_OK;
+ }
+ if ( strcasecmp(s, "false") == 0
+ || strcasecmp(s, "off") == 0
+ || strcasecmp(s, "no") == 0
+ || strcasecmp(s, "n") == 0
+ || strcasecmp(s, "0") == 0){
+ *ret = FALSE;
+ return HA_OK;
+ }
+ return HA_FAIL;
+}
+
+int
+cl_file_exists(const char* filename)
+{
+ struct stat st;
+
+ if (filename == NULL){
+ cl_log(LOG_ERR, "%s: NULL filename",
+ __FUNCTION__);
+ return FALSE;
+ }
+
+ if (lstat(filename, &st) == 0){
+ return S_ISREG(st.st_mode);
+ }
+
+ return FALSE;
+}
+
+char*
+cl_get_env(const char* env_name)
+{
+ if (env_name == NULL){
+ cl_log(LOG_ERR, "%s: null name",
+ __FUNCTION__);
+ return NULL;
+ }
+
+ return getenv(env_name);
+}
+
+
+int
+cl_binary_to_int(const char* data, int len)
+{
+ const char *p = data;
+ const char *pmax = p + len;
+ guint h = *p;
+
+ if (h){
+ for (p += 1; p < pmax; p++){
+ h = (h << 5) - h + *p;
+ }
+ }
+
+ return h;
+}
+
+/*
+ * Convert a string into a positive, rounded number of milliseconds.
+ *
+ * Returns -1 on error.
+ *
+ * Permissible forms:
+ * [0-9]+ units are seconds
+ * [0-9]*.[0-9]+ units are seconds
+ * [0-9]+ *[Mm][Ss] units are milliseconds
+ * [0-9]*.[0-9]+ *[Mm][Ss] units are milliseconds
+ * [0-9]+ *[Uu][Ss] units are microseconds
+ * [0-9]*.[0-9]+ *[Uu][Ss] units are microseconds
+ *
+ * Examples:
+ *
+ * 1 = 1000 milliseconds
+ * 1000ms = 1000 milliseconds
+ * 1000000us = 1000 milliseconds
+ * 0.1 = 100 milliseconds
+ * 100ms = 100 milliseconds
+ * 100000us = 100 milliseconds
+ * 0.001 = 1 millisecond
+ * 1ms = 1 millisecond
+ * 1000us = 1 millisecond
+ * 499us = 0 milliseconds
+ * 501us = 1 millisecond
+ */
+
+#define NUMCHARS "0123456789."
+#define WHITESPACE " \t\n\r\f"
+#define EOS '\0'
+
+long
+cl_get_msec(const char * input)
+{
+ const char * cp = input;
+ const char * units;
+ long multiplier = 1000;
+ long divisor = 1;
+ long ret = -1;
+ double dret;
+
+ cp += strspn(cp, WHITESPACE);
+ units = cp + strspn(cp, NUMCHARS);
+ units += strspn(units, WHITESPACE);
+
+ if (strchr(NUMCHARS, *cp) == NULL) {
+ return ret;
+ }
+
+ if (strncasecmp(units, "ms", 2) == 0
+ || strncasecmp(units, "cl_get_msec", 4) == 0) {
+ multiplier = 1;
+ divisor = 1;
+ }else if (strncasecmp(units, "us", 2) == 0
+ || strncasecmp(units, "usec", 4) == 0) {
+ multiplier = 1;
+ divisor = 1000;
+ }else if (*units != EOS && *units != '\n'
+ && *units != '\r') {
+ return ret;
+ }
+ dret = atof(cp);
+ dret *= (double)multiplier;
+ dret /= (double)divisor;
+ dret += 0.5;
+ ret = (long)dret;
+ return(ret);
+}
diff --git a/lib/clplumbing/cl_msg.c b/lib/clplumbing/cl_msg.c
new file mode 100644
index 0000000..22f00e3
--- /dev/null
+++ b/lib/clplumbing/cl_msg.c
@@ -0,0 +1,2537 @@
+/*
+ * Heartbeat messaging object.
+ *
+ * Copyright (C) 2000 Alan Robertson <alanr@unix.sh>
+ *
+ * This software licensed under the GNU LGPL.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <lha_internal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/utsname.h>
+#include <ha_msg.h>
+#include <unistd.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/ipc.h>
+#include <clplumbing/base64.h>
+#include <clplumbing/netstring.h>
+#include <glib.h>
+#include <clplumbing/cl_uuid.h>
+#include <compress.h>
+#include <clplumbing/timers.h>
+#include <clplumbing/cl_signal.h>
+
+#define MAXMSGLINE 512
+#define MINFIELDS 30
+#define NEWLINE "\n"
+
+
+#define NEEDAUTH 1
+#define NOAUTH 0
+#define MAX_INT_LEN 64
+#define MAX_NAME_LEN 255
+#define UUID_SLEN 64
+#define MAXCHILDMSGLEN 512
+
+static int compression_threshold = (128*1024);
+
+static enum cl_msgfmt msgfmt = MSGFMT_NVPAIR;
+static gboolean use_traditional_compression = FALSE;
+
+const char*
+FT_strings[]={
+ "0",
+ "1",
+ "2",
+ "3",
+ "4",
+ "5",
+ "6",
+ "7",
+ "8",
+ "9"
+};
+
+#undef DOAUDITS
+#define DOAUDITS
+
+#undef DOPARANOIDAUDITS
+/* #define DOPARANOIDAUDITS */
+
+#ifdef DOAUDITS
+void ha_msg_audit(const struct ha_msg* msg);
+# define AUDITMSG(msg) ha_msg_audit(msg)
+# ifdef DOPARANOIDAUDITS
+# define PARANOIDAUDITMSG(msg) ha_msg_audit(msg)
+# else
+# define PARANOIDAUDITMSG(msg) /*nothing*/
+# endif
+#else
+# define AUDITMSG(msg) /*nothing*/
+# define PARANOIDAUDITMSG(msg) /*nothing*/
+#endif
+
+
+static volatile hb_msg_stats_t* msgstats = NULL;
+
+gboolean cl_msg_quiet_fmterr = FALSE;
+
+extern int netstring_format;
+
+static struct ha_msg* wirefmt2msg_ll(const char* s, size_t length, int need_auth);
+
+struct ha_msg* string2msg_ll(const char * s, size_t length, int need_auth, int depth);
+
+extern int struct_stringlen(size_t namlen, size_t vallen, const void* value);
+extern int struct_netstringlen(size_t namlen, size_t vallen, const void* value);
+extern int process_netstring_nvpair(struct ha_msg* m, const char* nvpair, int nvlen);
+static char* msg2wirefmt_ll(struct ha_msg*m, size_t* len, gboolean need_compress);
+extern GHashTable* CompressFuncs;
+
+
+void
+cl_set_traditional_compression(gboolean value)
+{
+ use_traditional_compression = value;
+ if (use_traditional_compression && CompressFuncs) {
+ cl_log(LOG_WARNING
+ , "Traditional compression selected"
+ ". Realtime behavior will likely be impacted(!)");
+ cl_log(LOG_INFO
+ , "See %s for more information."
+ , HAURL("Ha.cf#traditional_compression_-_controls_compression_mode"));
+ }
+}
+
+void
+cl_set_compression_threshold(size_t threadhold)
+{
+ compression_threshold = threadhold;
+
+}
+
+void
+cl_msg_setstats(volatile hb_msg_stats_t* stats)
+{
+ msgstats = stats;
+}
+
+static int msg_stats_fd = -1;
+
+static int
+cl_msg_stats_open(const char* filename)
+{
+ if (filename == NULL){
+ cl_log(LOG_ERR, "%s: filename is NULL", __FUNCTION__);
+ return -1;
+ }
+
+ return open(filename, O_WRONLY|O_CREAT|O_APPEND, 0644);
+
+}
+
+static int
+cl_msg_stats_close(void)
+{
+ if (msg_stats_fd > 0){
+ close(msg_stats_fd);
+ }
+
+ msg_stats_fd = -1;
+
+ return HA_OK;
+}
+
+#define STATSFILE "/var/log/ha_msg_stats"
+int
+cl_msg_stats_add(longclock_t time, int size)
+{
+ char buf[MAXLINE];
+ int len;
+
+ if (msg_stats_fd < 0){
+ msg_stats_fd = cl_msg_stats_open(STATSFILE);
+ if (msg_stats_fd < 0){
+ cl_log(LOG_ERR, "%s:opening file failed",
+ __FUNCTION__);
+ return HA_FAIL;
+ }
+ }
+
+
+ sprintf(buf, "%lld %d\n", (long long)time, size);
+ len = strnlen(buf, MAXLINE);
+ if (write(msg_stats_fd, buf, len) == len){
+ cl_msg_stats_close();
+ return HA_OK;
+ }
+
+ cl_msg_stats_close();
+
+ return HA_FAIL;;
+
+}
+
+
+/* Set default messaging format */
+void
+cl_set_msg_format(enum cl_msgfmt mfmt)
+{
+ msgfmt = mfmt;
+}
+
+void
+cl_dump_msgstats(void)
+{
+ if (msgstats){
+ cl_log(LOG_INFO, "dumping msg stats: "
+ "allocmsgs=%lu",
+ msgstats->allocmsgs);
+ }
+ return;
+}
+void
+list_cleanup(GList* list)
+{
+ size_t i;
+ for (i = 0; i < g_list_length(list); i++){
+ char* element = g_list_nth_data(list, i);
+ if (element == NULL){
+ cl_log(LOG_WARNING, "list_cleanup:"
+ "element is NULL");
+ continue;
+ }
+ free(element);
+ }
+ g_list_free(list);
+}
+
+
+
+/* Create a new (empty) message */
+struct ha_msg *
+ha_msg_new(int nfields)
+{
+ struct ha_msg * ret;
+ int nalloc;
+
+ ret = MALLOCT(struct ha_msg);
+ if (ret) {
+ ret->nfields = 0;
+
+ if (nfields > MINFIELDS) {
+ nalloc = nfields;
+ } else {
+ nalloc = MINFIELDS;
+ }
+
+ ret->nalloc = nalloc;
+ ret->names = (char **)calloc(sizeof(char *), nalloc);
+ ret->nlens = (size_t *)calloc(sizeof(size_t), nalloc);
+ ret->values = (void **)calloc(sizeof(void *), nalloc);
+ ret->vlens = (size_t *)calloc(sizeof(size_t), nalloc);
+ ret->types = (int*)calloc(sizeof(int), nalloc);
+
+ if (ret->names == NULL || ret->values == NULL
+ || ret->nlens == NULL || ret->vlens == NULL
+ || ret->types == NULL) {
+
+ cl_log(LOG_ERR, "%s"
+ , "ha_msg_new: out of memory for ha_msg");
+ /* It is safe to give this to ha_msg_del() */
+ /* at this point. It's well-enough-formed */
+ ha_msg_del(ret); /*violated property*/
+ ret = NULL;
+ }else if (msgstats) {
+ msgstats->allocmsgs++;
+ msgstats->totalmsgs++;
+ msgstats->lastmsg = time_longclock();
+ }
+ }
+ return(ret);
+}
+
+/* Delete (destroy) a message */
+void
+ha_msg_del(struct ha_msg *msg)
+{
+ if (msg) {
+ int j;
+ PARANOIDAUDITMSG(msg);
+ if (msgstats) {
+ msgstats->allocmsgs--;
+ }
+ if (msg->names) {
+ for (j=0; j < msg->nfields; ++j) {
+ if (msg->names[j]) {
+ free(msg->names[j]);
+ msg->names[j] = NULL;
+ }
+ }
+ free(msg->names);
+ msg->names = NULL;
+ }
+ if (msg->values) {
+ for (j=0; j < msg->nfields; ++j) {
+
+ if (msg->values[j] == NULL){
+ continue;
+ }
+
+ if(msg->types[j] < DIMOF(fieldtypefuncs)){
+ fieldtypefuncs[msg->types[j]].memfree(msg->values[j]);
+ }
+ }
+ free(msg->values);
+ msg->values = NULL;
+ }
+ if (msg->nlens) {
+ free(msg->nlens);
+ msg->nlens = NULL;
+ }
+ if (msg->vlens) {
+ free(msg->vlens);
+ msg->vlens = NULL;
+ }
+ if (msg->types){
+ free(msg->types);
+ msg->types = NULL;
+ }
+ msg->nfields = -1;
+ msg->nalloc = -1;
+ free(msg);
+ }
+}
+struct ha_msg*
+ha_msg_copy(const struct ha_msg *msg)
+{
+ struct ha_msg* ret;
+ int j;
+
+
+ PARANOIDAUDITMSG(msg);
+ if (msg == NULL || (ret = ha_msg_new(msg->nalloc)) == NULL) {
+ return NULL;
+ }
+
+ ret->nfields = msg->nfields;
+
+ memcpy(ret->nlens, msg->nlens, sizeof(msg->nlens[0])*msg->nfields);
+ memcpy(ret->vlens, msg->vlens, sizeof(msg->vlens[0])*msg->nfields);
+ memcpy(ret->types, msg->types, sizeof(msg->types[0])*msg->nfields);
+
+ for (j=0; j < msg->nfields; ++j) {
+
+ if ((ret->names[j] = malloc(msg->nlens[j]+1)) == NULL) {
+ goto freeandleave;
+ }
+ memcpy(ret->names[j], msg->names[j], msg->nlens[j]+1);
+
+
+ if(msg->types[j] < DIMOF(fieldtypefuncs)){
+ ret->values[j] = fieldtypefuncs[msg->types[j]].dup(msg->values[j],
+ msg->vlens[j]);
+ if (!ret->values[j]){
+ cl_log(LOG_ERR,"duplicating the message field failed");
+ goto freeandleave;
+ }
+ }
+ }
+ return ret;
+
+freeandleave:
+ /*
+ * ha_msg_del nicely handles partially constructed ha_msgs
+ * so, there's not really a memory leak here at all, but BEAM
+ * thinks there is.
+ */
+ ha_msg_del(ret);/* memory leak */ ret=NULL;
+ return ret;
+}
+
+#ifdef DOAUDITS
+void
+ha_msg_audit(const struct ha_msg* msg)
+{
+ int doabort = FALSE;
+ int j;
+
+ if (!msg) {
+ return;
+ }
+ if (!msg) {
+ cl_log(LOG_CRIT, "Message @ %p is not allocated"
+ , msg);
+ abort();
+ }
+ if (msg->nfields < 0) {
+ cl_log(LOG_CRIT, "Message @ %p has negative fields (%d)"
+ , msg, msg->nfields);
+ doabort = TRUE;
+ }
+ if (msg->nalloc < 0) {
+ cl_log(LOG_CRIT, "Message @ %p has negative nalloc (%d)"
+ , msg, msg->nalloc);
+ doabort = TRUE;
+ }
+
+ if (!msg->names) {
+ cl_log(LOG_CRIT
+ , "Message names @ %p is not allocated"
+ , msg->names);
+ doabort = TRUE;
+ }
+ if (!msg->values) {
+ cl_log(LOG_CRIT
+ , "Message values @ %p is not allocated"
+ , msg->values);
+ doabort = TRUE;
+ }
+ if (!msg->nlens) {
+ cl_log(LOG_CRIT
+ , "Message nlens @ %p is not allocated"
+ , msg->nlens);
+ doabort = TRUE;
+ }
+ if (!msg->vlens) {
+ cl_log(LOG_CRIT
+ , "Message vlens @ %p is not allocated"
+ , msg->vlens);
+ doabort = TRUE;
+ }
+ if (doabort) {
+ cl_log_message(LOG_INFO,msg);
+ abort();
+ }
+ for (j=0; j < msg->nfields; ++j) {
+
+ if (msg->nlens[j] == 0){
+ cl_log(LOG_ERR, "zero namelen found in msg");
+ abort();
+ }
+
+ if (msg->types[j] == FT_STRING){
+ if (msg->vlens[j] != strlen(msg->values[j])){
+ cl_log(LOG_ERR, "stringlen does not match");
+ cl_log_message(LOG_INFO,msg);
+ abort();
+ }
+ }
+
+ if (!msg->names[j]) {
+ cl_log(LOG_CRIT, "Message name[%d] @ 0x%p"
+ " is not allocated." ,
+ j, msg->names[j]);
+ abort();
+ }
+ if (msg->types[j] != FT_LIST && !msg->values[j]) {
+ cl_log(LOG_CRIT, "Message value [%d] @ 0x%p"
+ " is not allocated.", j, msg->values[j]);
+ cl_log_message(LOG_INFO, msg);
+ abort();
+ }
+ }
+}
+#endif
+
+
+
+int
+ha_msg_expand(struct ha_msg* msg )
+{
+ char ** names ;
+ size_t *nlens ;
+ void ** values ;
+ size_t* vlens ;
+ int * types ;
+ int nalloc;
+
+ if(!msg){
+ cl_log(LOG_ERR, "ha_msg_expand:"
+ "input msg is null");
+ return HA_FAIL;
+ }
+
+ names = msg->names;
+ nlens = msg->nlens;
+ values = msg->values;
+ vlens = msg->vlens;
+ types = msg->types;
+
+ nalloc = msg->nalloc + MINFIELDS;
+ msg->names = (char **)calloc(sizeof(char *), nalloc);
+ msg->nlens = (size_t *)calloc(sizeof(size_t), nalloc);
+ msg->values = (void **)calloc(sizeof(void *), nalloc);
+ msg->vlens = (size_t *)calloc(sizeof(size_t), nalloc);
+ msg->types= (int*)calloc(sizeof(int), nalloc);
+
+ if (msg->names == NULL || msg->values == NULL
+ || msg->nlens == NULL || msg->vlens == NULL
+ || msg->types == NULL) {
+
+ cl_log(LOG_ERR, "%s"
+ , " out of memory for ha_msg");
+ return(HA_FAIL);
+ }
+
+ memcpy(msg->names, names, msg->nalloc*sizeof(char *));
+ memcpy(msg->nlens, nlens, msg->nalloc*sizeof(size_t));
+ memcpy(msg->values, values, msg->nalloc*sizeof(void *));
+ memcpy(msg->vlens, vlens, msg->nalloc*sizeof(size_t));
+ memcpy(msg->types, types, msg->nalloc*sizeof(int));
+
+ free(names);
+ free(nlens);
+ free(values);
+ free(vlens);
+ free(types);
+
+ msg->nalloc = nalloc;
+
+ return HA_OK;
+}
+
+int
+cl_msg_remove_value(struct ha_msg* msg, const void* value)
+{
+ int j;
+
+ if (msg == NULL || value == NULL){
+ cl_log(LOG_ERR, "cl_msg_remove: invalid argument");
+ return HA_FAIL;
+ }
+
+ for (j = 0; j < msg->nfields; ++j){
+ if (value == msg->values[j]){
+ break;
+ }
+ }
+ if (j == msg->nfields){
+ cl_log(LOG_ERR, "cl_msg_remove: field %p not found", value);
+ return HA_FAIL;
+ }
+ return cl_msg_remove_offset(msg, j);
+
+}
+
+
+int
+cl_msg_remove(struct ha_msg* msg, const char* name)
+{
+ int j;
+
+ if (msg == NULL || name == NULL){
+ cl_log(LOG_ERR, "cl_msg_remove: invalid argument");
+ return HA_FAIL;
+ }
+
+ for (j = 0; j < msg->nfields; ++j){
+ if (strcmp(name, msg->names[j]) == 0){
+ break;
+ }
+ }
+
+ if (j == msg->nfields){
+ cl_log(LOG_ERR, "cl_msg_remove: field %s not found", name);
+ return HA_FAIL;
+ }
+ return cl_msg_remove_offset(msg, j);
+}
+
+int
+cl_msg_remove_offset(struct ha_msg* msg, int offset)
+{
+ int j = offset;
+ int i;
+
+ if (j == msg->nfields){
+ cl_log(LOG_ERR, "cl_msg_remove: field %d not found", j);
+ return HA_FAIL;
+ }
+
+ free(msg->names[j]);
+ fieldtypefuncs[msg->types[j]].memfree(msg->values[j]);
+
+ for (i= j + 1; i < msg->nfields ; i++){
+ msg->names[i -1] = msg->names[i];
+ msg->nlens[i -1] = msg->nlens[i];
+ msg->values[i -1] = msg->values[i];
+ msg->vlens[i-1] = msg->vlens[i];
+ msg->types[i-1] = msg->types[i];
+ }
+ msg->nfields--;
+
+
+ return HA_OK;
+}
+
+
+
+/* low level implementation for ha_msg_add
+ the caller is responsible to allocate/free memories
+ for @name and @value.
+
+*/
+
+static int
+ha_msg_addraw_ll(struct ha_msg * msg, char * name, size_t namelen,
+ void * value, size_t vallen, int type, int depth)
+{
+
+ size_t startlen = sizeof(MSG_START)-1;
+
+
+ int (*addfield) (struct ha_msg* msg, char* name, size_t namelen,
+ void* value, size_t vallen, int depth);
+
+ if (!msg || msg->names == NULL || (msg->values == NULL) ) {
+ cl_log(LOG_ERR, "ha_msg_addraw_ll: cannot add field to ha_msg");
+ return(HA_FAIL);
+ }
+
+ if (msg->nfields >= msg->nalloc) {
+ if( ha_msg_expand(msg) != HA_OK){
+ cl_log(LOG_ERR, "message expanding failed");
+ return(HA_FAIL);
+ }
+
+ }
+
+ if (namelen >= startlen
+ && name[0] == '>'
+ && strncmp(name, MSG_START, startlen) == 0) {
+ if(!cl_msg_quiet_fmterr) {
+ cl_log(LOG_ERR, "ha_msg_addraw_ll: illegal field");
+ }
+ return(HA_FAIL);
+ }
+
+ if (name == NULL || (value == NULL)
+ || namelen <= 0 || vallen < 0) {
+ cl_log(LOG_ERR, "ha_msg_addraw_ll: "
+ "cannot add name/value to ha_msg");
+ return(HA_FAIL);
+ }
+
+ HA_MSG_ASSERT(type < DIMOF(fieldtypefuncs));
+
+ addfield = fieldtypefuncs[type].addfield;
+ if (!addfield ||
+ addfield(msg, name, namelen, value, vallen,depth) != HA_OK){
+ cl_log(LOG_ERR, "ha_msg_addraw_ll: addfield failed");
+ return(HA_FAIL);
+ }
+
+ PARANOIDAUDITMSG(msg);
+
+ return(HA_OK);
+
+
+}
+
+static int
+ha_msg_addraw(struct ha_msg * msg, const char * name, size_t namelen,
+ const void * value, size_t vallen, int type, int depth)
+{
+
+ char *cpvalue = NULL;
+ char *cpname = NULL;
+ int ret;
+
+
+ if (namelen == 0){
+ cl_log(LOG_ERR, "%s: Adding a field with 0 name length", __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ if ((cpname = malloc(namelen+1)) == NULL) {
+ cl_log(LOG_ERR, "ha_msg_addraw: no memory for string (name)");
+ return(HA_FAIL);
+ }
+ strncpy(cpname, name, namelen);
+ cpname[namelen] = EOS;
+
+ HA_MSG_ASSERT(type < DIMOF(fieldtypefuncs));
+
+ if (fieldtypefuncs[type].dup){
+ cpvalue = fieldtypefuncs[type].dup(value, vallen);
+ }
+ if (cpvalue == NULL){
+ cl_log(LOG_ERR, "ha_msg_addraw: copying message failed");
+ free(cpname);
+ return(HA_FAIL);
+ }
+
+ ret = ha_msg_addraw_ll(msg, cpname, namelen, cpvalue, vallen
+ , type, depth);
+
+ if (ret != HA_OK){
+ cl_log(LOG_ERR, "ha_msg_addraw(): ha_msg_addraw_ll failed");
+ free(cpname);
+ fieldtypefuncs[type].memfree(cpvalue);
+ }
+
+ return(ret);
+
+}
+
+/*Add a null-terminated name and binary value to a message*/
+int
+ha_msg_addbin(struct ha_msg * msg, const char * name,
+ const void * value, size_t vallen)
+{
+
+ return(ha_msg_addraw(msg, name, strlen(name),
+ value, vallen, FT_BINARY, 0));
+
+}
+
+int
+ha_msg_adduuid(struct ha_msg* msg, const char *name, const cl_uuid_t* u)
+{
+ return(ha_msg_addraw(msg, name, strlen(name),
+ u, sizeof(cl_uuid_t), FT_BINARY, 0));
+}
+
+/*Add a null-terminated name and struct value to a message*/
+int
+ha_msg_addstruct(struct ha_msg * msg, const char * name, const void * value)
+{
+ const struct ha_msg* childmsg = (const struct ha_msg*) value;
+
+ if (get_netstringlen(childmsg) > MAXCHILDMSGLEN
+ || get_stringlen(childmsg) > MAXCHILDMSGLEN) {
+ /*cl_log(LOG_WARNING,
+ "%s: childmsg too big (name=%s, nslen=%d, len=%d)."
+ " Use ha_msg_addstruct_compress() instead.",
+ __FUNCTION__, name, get_netstringlen(childmsg),
+ get_stringlen(childmsg));
+ */
+ }
+
+ return ha_msg_addraw(msg, name, strlen(name), value,
+ sizeof(struct ha_msg), FT_STRUCT, 0);
+}
+
+int
+ha_msg_addstruct_compress(struct ha_msg * msg, const char * name, const void * value)
+{
+
+ if (use_traditional_compression){
+ return ha_msg_addraw(msg, name, strlen(name), value,
+ sizeof(struct ha_msg), FT_STRUCT, 0);
+ }else{
+ return ha_msg_addraw(msg, name, strlen(name), value,
+ sizeof(struct ha_msg), FT_UNCOMPRESS, 0);
+ }
+}
+
+int
+ha_msg_add_int(struct ha_msg * msg, const char * name, int value)
+{
+ char buf[MAX_INT_LEN];
+ snprintf(buf, MAX_INT_LEN, "%d", value);
+ return (ha_msg_add(msg, name, buf));
+}
+
+int
+ha_msg_mod_int(struct ha_msg * msg, const char * name, int value)
+{
+ char buf[MAX_INT_LEN];
+ snprintf(buf, MAX_INT_LEN, "%d", value);
+ return (cl_msg_modstring(msg, name, buf));
+}
+
+int
+ha_msg_value_int(const struct ha_msg * msg, const char * name, int* value)
+{
+ const char* svalue = ha_msg_value(msg, name);
+ if(NULL == svalue) {
+ return HA_FAIL;
+ }
+ *value = atoi(svalue);
+ return HA_OK;
+}
+
+int
+ha_msg_add_ul(struct ha_msg * msg, const char * name, unsigned long value)
+{
+ char buf[MAX_INT_LEN];
+ snprintf(buf, MAX_INT_LEN, "%lu", value);
+ return (ha_msg_add(msg, name, buf));
+}
+
+int
+ha_msg_mod_ul(struct ha_msg * msg, const char * name, unsigned long value)
+{
+ char buf[MAX_INT_LEN];
+ snprintf(buf, MAX_INT_LEN, "%lu", value);
+ return (cl_msg_modstring(msg, name, buf));
+}
+
+int
+ha_msg_value_ul(const struct ha_msg * msg, const char * name, unsigned long* value)
+{
+ const char* svalue = ha_msg_value(msg, name);
+ if(NULL == svalue) {
+ return HA_FAIL;
+ }
+ *value = strtoul(svalue, NULL, 10);
+ return HA_OK;
+}
+
+/*
+ * ha_msg_value_str_list()/ha_msg_add_str_list():
+ * transform a string list suitable for putting into an ha_msg is by a convention
+ * of naming the fields into the following format:
+ * listname1=foo
+ * listname2=bar
+ * listname3=stuff
+ * etc.
+ */
+
+GList*
+ha_msg_value_str_list(struct ha_msg * msg, const char * name)
+{
+
+ int i = 1;
+ int len = 0;
+ const char* value;
+ char* element;
+ GList* list = NULL;
+
+
+ if( NULL==msg||NULL==name||strnlen(name, MAX_NAME_LEN)>=MAX_NAME_LEN ){
+ return NULL;
+ }
+ len = cl_msg_list_length(msg,name);
+ for(i=0; i<len; i++) {
+ value = cl_msg_list_nth_data(msg,name,i);
+ if (NULL == value) {
+ break;
+ }
+ element = g_strdup(value);
+ list = g_list_append(list, element);
+ }
+ return list;
+}
+
+
+
+static void
+pair_to_msg(gpointer key, gpointer value, gpointer user_data)
+{
+ struct ha_msg* msg = (struct ha_msg*)user_data;
+ if( HA_OK != ha_msg_add(msg, key, value)) {
+ cl_log(LOG_ERR, "ha_msg_add in pair_to_msg failed");
+ }
+}
+
+
+static struct ha_msg*
+str_table_to_msg(GHashTable* hash_table)
+{
+ struct ha_msg* hash_msg;
+
+ if ( NULL == hash_table) {
+ return NULL;
+ }
+
+ hash_msg = ha_msg_new(5);
+ g_hash_table_foreach(hash_table, pair_to_msg, hash_msg);
+ return hash_msg;
+}
+
+
+static GHashTable*
+msg_to_str_table(struct ha_msg * msg)
+{
+ int i;
+ GHashTable* hash_table;
+
+ if ( NULL == msg) {
+ return NULL;
+ }
+
+ hash_table = g_hash_table_new(g_str_hash, g_str_equal);
+
+ for (i = 0; i < msg->nfields; i++) {
+ if( FT_STRING != msg->types[i] ) {
+ continue;
+ }
+ g_hash_table_insert(hash_table,
+ g_strndup(msg->names[i],msg->nlens[i]),
+ g_strndup(msg->values[i],msg->vlens[i]));
+ }
+ return hash_table;
+}
+
+GHashTable*
+ha_msg_value_str_table(struct ha_msg * msg, const char * name)
+{
+ struct ha_msg* hash_msg;
+ GHashTable * hash_table = NULL;
+
+ if (NULL == msg || NULL == name) {
+ return NULL;
+ }
+
+ hash_msg = cl_get_struct(msg, name);
+ if (NULL == hash_msg) {
+ return NULL;
+ }
+ hash_table = msg_to_str_table(hash_msg);
+ return hash_table;
+}
+
+int
+ha_msg_add_str_table(struct ha_msg * msg, const char * name,
+ GHashTable* hash_table)
+{
+ struct ha_msg* hash_msg;
+ if (NULL == msg || NULL == name || NULL == hash_table) {
+ return HA_FAIL;
+ }
+
+ hash_msg = str_table_to_msg(hash_table);
+ if( HA_OK != ha_msg_addstruct(msg, name, hash_msg)) {
+ ha_msg_del(hash_msg);
+ cl_log(LOG_ERR
+ , "ha_msg_addstruct in ha_msg_add_str_table failed");
+ return HA_FAIL;
+ }
+ ha_msg_del(hash_msg);
+ return HA_OK;
+}
+
+int
+ha_msg_mod_str_table(struct ha_msg * msg, const char * name,
+ GHashTable* hash_table)
+{
+ struct ha_msg* hash_msg;
+ if (NULL == msg || NULL == name || NULL == hash_table) {
+ return HA_FAIL;
+ }
+
+ hash_msg = str_table_to_msg(hash_table);
+ if( HA_OK != cl_msg_modstruct(msg, name, hash_msg)) {
+ ha_msg_del(hash_msg);
+ cl_log(LOG_ERR
+ , "ha_msg_modstruct in ha_msg_mod_str_table failed");
+ return HA_FAIL;
+ }
+ ha_msg_del(hash_msg);
+ return HA_OK;
+}
+
+int
+cl_msg_list_add_string(struct ha_msg* msg, const char* name, const char* value)
+{
+ GList* list = NULL;
+ int ret;
+
+ if(!msg || !name || !value){
+ cl_log(LOG_ERR, "cl_msg_list_add_string: input invalid");
+ return HA_FAIL;
+ }
+
+
+ list = g_list_append(list, UNCONST_CAST_POINTER(gpointer, value));
+ if (!list){
+ cl_log(LOG_ERR, "cl_msg_list_add_string: append element to"
+ "a glist failed");
+ return HA_FAIL;
+ }
+
+ ret = ha_msg_addraw(msg, name, strlen(name), list,
+ string_list_pack_length(list),
+ FT_LIST, 0);
+
+ g_list_free(list);
+
+ return ret;
+
+}
+
+/* Add a null-terminated name and value to a message */
+int
+ha_msg_add(struct ha_msg * msg, const char * name, const char * value)
+{
+ if(name == NULL || value == NULL) {
+ return HA_FAIL;
+ }
+ return(ha_msg_nadd(msg, name, strlen(name), value, strlen(value)));
+}
+
+/* Add a name/value pair to a message (with sizes for name and value) */
+int
+ha_msg_nadd(struct ha_msg * msg, const char * name, int namelen
+ , const char * value, int vallen)
+{
+ return(ha_msg_addraw(msg, name, namelen, value, vallen, FT_STRING, 0));
+
+}
+
+/* Add a name/value/type to a message (with sizes for name and value) */
+int
+ha_msg_nadd_type(struct ha_msg * msg, const char * name, int namelen
+ , const char * value, int vallen, int type)
+{
+ return(ha_msg_addraw(msg, name, namelen, value, vallen, type, 0));
+
+}
+
+
+
+/* Add a "name=value" line to the name, value pairs in a message */
+static int
+ha_msg_add_nv_depth(struct ha_msg* msg, const char * nvline,
+ const char * bufmax, int depth)
+{
+ int namelen;
+ const char * valp;
+ int vallen;
+
+ if (!nvline) {
+ cl_log(LOG_ERR, "ha_msg_add_nv: NULL nvline");
+ return(HA_FAIL);
+ }
+ /* How many characters before the '='? */
+ if ((namelen = strcspn(nvline, EQUAL)) <= 0
+ || nvline[namelen] != '=') {
+ if (!cl_msg_quiet_fmterr) {
+ cl_log(LOG_WARNING
+ , "ha_msg_add_nv_depth: line doesn't contain '='");
+ cl_log(LOG_INFO, "%s", nvline);
+ }
+ return(HA_FAIL);
+ }
+ valp = nvline + namelen +1; /* Point just *past* the '=' */
+ if (valp >= bufmax){
+ return HA_FAIL;
+ }
+ vallen = strcspn(valp, NEWLINE);
+ if ((valp + vallen) >= bufmax){
+ return HA_FAIL;
+ }
+
+ if (vallen == 0){
+ valp = NULL;
+ }
+ /* Call ha_msg_nadd to actually add the name/value pair */
+ return(ha_msg_addraw(msg, nvline, namelen, valp, vallen
+ , FT_STRING, depth));
+
+}
+
+int
+ha_msg_add_nv(struct ha_msg* msg, const char * nvline,
+ const char * bufmax)
+{
+
+ return(ha_msg_add_nv_depth(msg, nvline, bufmax, 0));
+
+}
+
+
+static void *
+cl_get_value(const struct ha_msg * msg, const char * name,
+ size_t * vallen, int *type)
+{
+
+ int j;
+ if (!msg || !msg->names || !msg->values) {
+ cl_log(LOG_ERR, "%s: wrong argument (%s)",
+ __FUNCTION__, name);
+ return(NULL);
+ }
+
+ PARANOIDAUDITMSG(msg);
+ for (j=0; j < msg->nfields; ++j) {
+ const char *local_name = msg->names[j];
+ if (name[0] == local_name[0]
+ && strcmp(name, local_name) == 0) {
+ if (vallen){
+ *vallen = msg->vlens[j];
+ }
+ if (type){
+ *type = msg->types[j];
+ }
+ return(msg->values[j]);
+ }
+ }
+ return(NULL);
+}
+
+static void *
+cl_get_value_mutate(struct ha_msg * msg, const char * name,
+ size_t * vallen, int *type)
+{
+
+ int j;
+ if (!msg || !msg->names || !msg->values) {
+ cl_log(LOG_ERR, "%s: wrong argument",
+ __FUNCTION__);
+ return(NULL);
+ }
+
+ AUDITMSG(msg);
+ for (j=0; j < msg->nfields; ++j) {
+ if (strcmp(name, msg->names[j]) == 0) {
+ int tp = msg->types[j];
+ if (fieldtypefuncs[tp].pregetaction){
+ fieldtypefuncs[tp].pregetaction(msg, j);
+ }
+
+ if (vallen){
+ *vallen = msg->vlens[j];
+ }
+ if (type){
+ *type = msg->types[j];
+ }
+ return(msg->values[j]);
+ }
+ }
+ return(NULL);
+}
+
+
+const void *
+cl_get_binary(const struct ha_msg *msg,
+ const char * name, size_t * vallen)
+{
+
+ const void *ret;
+ int type;
+
+ ret = cl_get_value( msg, name, vallen, &type);
+
+ if (ret == NULL){
+ /*
+ cl_log(LOG_WARNING, "field %s not found", name);
+ cl_log_message(msg);
+ */
+ return(NULL);
+ }
+ if ( type != FT_BINARY){
+ cl_log(LOG_WARNING, "field %s is not binary", name);
+ cl_log_message(LOG_WARNING, msg);
+ return(NULL);
+ }
+
+ return(ret);
+}
+
+/* UUIDs are stored with a machine-independent byte ordering (even though it's binary) */
+int
+cl_get_uuid(const struct ha_msg *msg, const char * name, cl_uuid_t* retval)
+{
+ const void * vret;
+ size_t vretsize;
+
+ cl_uuid_clear(retval);
+
+ if ((vret = cl_get_binary(msg, name, &vretsize)/*discouraged function*/) == NULL) {
+ /* But perfectly portable in this case */
+ return HA_FAIL;
+ }
+ if (vretsize != sizeof(cl_uuid_t)) {
+ cl_log(LOG_WARNING, "Binary field %s is not a uuid.", name);
+ cl_log(LOG_INFO, "expecting %d bytes, got %d bytes",
+ (int)sizeof(cl_uuid_t), (int)vretsize);
+ cl_log_message(LOG_INFO, msg);
+ return HA_FAIL;
+ }
+ memcpy(retval, vret, sizeof(cl_uuid_t));
+ return HA_OK;
+}
+
+const char *
+cl_get_string(const struct ha_msg *msg, const char *name)
+{
+
+ const void *ret;
+ int type;
+ ret = cl_get_value( msg, name, NULL, &type);
+
+ if (ret == NULL || type != FT_STRING){
+ return(NULL);
+ }
+
+ return(ret);
+
+}
+
+int
+cl_get_type(const struct ha_msg *msg, const char *name)
+{
+
+ const void *ret;
+ int type;
+
+ ret = cl_get_value( msg, name, NULL, &type);
+
+ if (ret == NULL) {
+ return -1;
+ }
+ if (type < 0){
+ cl_log(LOG_WARNING, "field %s not a valid type"
+ , name);
+ return(-1);
+ }
+
+ return(type);
+
+}
+
+/*
+struct ha_msg *
+cl_get_struct(const struct ha_msg *msg, const char* name)
+{
+ struct ha_msg* ret;
+ int type;
+ size_t vallen;
+
+ ret = cl_get_value(msg, name, &vallen, &type);
+
+ if (ret == NULL ){
+ return(NULL);
+ }
+
+ switch(type){
+
+ case FT_STRUCT:
+ break;
+
+ default:
+ cl_log(LOG_ERR, "%s: field %s is not a struct (%d)",
+ __FUNCTION__, name, type);
+ return NULL;
+ }
+
+ return ret;
+}
+*/
+
+
+struct ha_msg *
+cl_get_struct(struct ha_msg *msg, const char* name)
+{
+ struct ha_msg* ret;
+ int type = -1;
+ size_t vallen;
+
+ ret = cl_get_value_mutate(msg, name, &vallen, &type);
+
+ if (ret == NULL ){
+ return(NULL);
+ }
+
+ switch(type){
+
+ case FT_UNCOMPRESS:
+ case FT_STRUCT:
+ break;
+
+ default:
+ cl_log(LOG_ERR, "%s: field %s is not a struct (%d)",
+ __FUNCTION__, name, type);
+ return NULL;
+ }
+
+ return ret;
+}
+
+
+int
+cl_msg_list_length(struct ha_msg* msg, const char* name)
+{
+ GList* ret;
+ int type;
+
+ ret = cl_get_value( msg, name, NULL, &type);
+
+ if ( ret == NULL || type != FT_LIST){
+ return -1;
+ }
+
+ return g_list_length(ret);
+
+}
+
+
+void*
+cl_msg_list_nth_data(struct ha_msg* msg, const char* name, int n)
+{
+ GList* ret;
+ int type;
+
+ ret = cl_get_value( msg, name, NULL, &type);
+
+ if ( ret == NULL || type != FT_LIST){
+ cl_log(LOG_WARNING, "field %s not found "
+ " or type mismatch", name);
+ return NULL;
+ }
+
+ return g_list_nth_data(ret, n);
+
+}
+
+int
+cl_msg_add_list(struct ha_msg* msg, const char* name, GList* list)
+{
+ int ret;
+
+ if(msg == NULL|| name ==NULL || list == NULL){
+ cl_log(LOG_ERR, "cl_msg_add_list:"
+ "invalid arguments");
+ return HA_FAIL;
+ }
+
+ ret = ha_msg_addraw(msg, name, strlen(name), list,
+ string_list_pack_length(list),
+ FT_LIST, 0);
+
+ return ret;
+}
+
+GList*
+cl_msg_get_list(struct ha_msg* msg, const char* name)
+{
+ GList* ret;
+ int type;
+
+ ret = cl_get_value( msg, name, NULL, &type);
+
+ if ( ret == NULL || type != FT_LIST){
+ cl_log(LOG_WARNING, "field %s not found "
+ " or type mismatch", name);
+ return NULL;
+ }
+
+ return ret;
+}
+
+
+int
+cl_msg_add_list_str(struct ha_msg* msg, const char* name,
+ char** buf, size_t n)
+{
+ GList* list = NULL;
+ int i;
+ int ret = HA_FAIL;
+
+ if (n <= 0 || buf == NULL|| name ==NULL ||msg == NULL){
+ cl_log(LOG_ERR, "%s:"
+ "invalid parameter(%s)",
+ !n <= 0?"n is negative or zero":
+ !buf?"buf is NULL":
+ !name?"name is NULL":
+ "msg is NULL",__FUNCTION__);
+ return HA_FAIL;
+ }
+
+ for ( i = 0; i < n; i++){
+ if (buf[i] == NULL){
+ cl_log(LOG_ERR, "%s: %dth element in buf is null",
+ __FUNCTION__, i);
+ goto free_and_out;
+ }
+ list = g_list_append(list, buf[i]);
+ if (list == NULL){
+ cl_log(LOG_ERR, "%s:adding integer to list failed",
+ __FUNCTION__);
+ goto free_and_out;
+ }
+ }
+
+ ret = ha_msg_addraw(msg, name, strlen(name), list,
+ string_list_pack_length(list),
+ FT_LIST, 0);
+
+ free_and_out:
+ if (list){
+ g_list_free(list);
+ list = NULL;
+ }
+ return ret;
+}
+
+static void
+list_element_free(gpointer data, gpointer userdata)
+{
+ if (data){
+ g_free(data);
+ }
+}
+
+int
+cl_msg_add_list_int(struct ha_msg* msg, const char* name,
+ int* buf, size_t n)
+{
+
+ GList* list = NULL;
+ size_t i;
+ int ret = HA_FAIL;
+
+ if (n <= 0 || buf == NULL|| name ==NULL ||msg == NULL){
+ cl_log(LOG_ERR, "cl_msg_add_list_int:"
+ "invalid parameter(%s)",
+ !n <= 0?"n is negative or zero":
+ !buf?"buf is NULL":
+ !name?"name is NULL":
+ "msg is NULL");
+ goto free_and_out;
+ }
+
+ for ( i = 0; i < n; i++){
+ char intstr[MAX_INT_LEN];
+ sprintf(intstr,"%d", buf[i]);
+ list = g_list_append(list, g_strdup(intstr));
+ if (list == NULL){
+ cl_log(LOG_ERR, "cl_msg_add_list_int:"
+ "adding integer to list failed");
+ goto free_and_out;
+ }
+ }
+
+ ret = ha_msg_addraw(msg, name, strlen(name), list,
+ string_list_pack_length(list),
+ FT_LIST, 0);
+ free_and_out:
+ if (list){
+ g_list_foreach(list,list_element_free , NULL);
+ g_list_free(list);
+ list = NULL;
+ }
+
+ return ret;
+}
+int
+cl_msg_get_list_int(struct ha_msg* msg, const char* name,
+ int* buf, size_t* n)
+{
+ GList* list;
+ size_t len;
+ int i;
+ GList* list_element;
+
+
+ if (n == NULL || buf == NULL|| name ==NULL ||msg == NULL){
+ cl_log(LOG_ERR, "cl_msg_get_list_int:"
+ "invalid parameter(%s)",
+ !n?"n is NULL":
+ !buf?"buf is NULL":
+ !name?"name is NULL":
+ "msg is NULL");
+ return HA_FAIL;
+ }
+
+ list = cl_msg_get_list(msg, name);
+ if (list == NULL){
+ cl_log(LOG_ERR, "cl_msg_get_list_int:"
+ "list of integers %s not found", name);
+ return HA_FAIL;
+ }
+
+ len = g_list_length(list);
+ if (len > *n){
+ cl_log(LOG_ERR, "cl_msg_get_list_int:"
+ "buffer too small: *n=%ld, required len=%ld",
+ (long)*n, (long)len);
+ *n = len;
+ return HA_FAIL;
+ }
+
+ *n = len;
+ i = 0;
+ list_element = g_list_first(list);
+ while( list_element != NULL){
+ char* intstr = list_element->data;
+ if (intstr == NULL){
+ cl_log(LOG_ERR, "cl_msg_get_list_int:"
+ "element data is NULL");
+ return HA_FAIL;
+ }
+
+ if (sscanf(intstr,"%d", &buf[i]) != 1){
+ cl_log(LOG_ERR, "cl_msg_get_list_int:"
+ "element data is NULL");
+ return HA_FAIL;
+ }
+
+ i++;
+ list_element = g_list_next(list_element);
+ }
+
+ return HA_OK;
+}
+
+int
+cl_msg_replace_value(struct ha_msg* msg, const void *old_value,
+ const void* value, size_t vlen, int type)
+{
+ int j;
+
+ if (msg == NULL || old_value == NULL) {
+ cl_log(LOG_ERR, "cl_msg_replace: invalid argument");
+ return HA_FAIL;
+ }
+
+ for (j = 0; j < msg->nfields; ++j){
+ if (old_value == msg->values[j]){
+ break;
+ }
+ }
+ if (j == msg->nfields){
+ cl_log(LOG_ERR, "cl_msg_replace: field %p not found", old_value);
+ return HA_FAIL;
+ }
+ return cl_msg_replace(msg, j, value, vlen, type);
+}
+
+/*this function is for internal use only*/
+int
+cl_msg_replace(struct ha_msg* msg, int index,
+ const void* value, size_t vlen, int type)
+{
+ void * newv ;
+ int newlen = vlen;
+ int oldtype;
+
+ PARANOIDAUDITMSG(msg);
+ if (msg == NULL || value == NULL) {
+ cl_log(LOG_ERR, "%s: NULL input.", __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ if(type >= DIMOF(fieldtypefuncs)){
+ cl_log(LOG_ERR, "%s:"
+ "invalid type(%d)",__FUNCTION__, type);
+ return HA_FAIL;
+ }
+
+ if (index >= msg->nfields){
+ cl_log(LOG_ERR, "%s: index(%d) out of range(%d)",
+ __FUNCTION__,index, msg->nfields);
+ return HA_FAIL;
+ }
+
+ oldtype = msg->types[index];
+
+ newv = fieldtypefuncs[type].dup(value,vlen);
+ if (!newv){
+ cl_log(LOG_ERR, "%s: duplicating message fields failed"
+ "value=%p, vlen=%d, msg->names[i]=%s",
+ __FUNCTION__,value, (int)vlen, msg->names[index]);
+ return HA_FAIL;
+ }
+
+ fieldtypefuncs[oldtype].memfree(msg->values[index]);
+
+ msg->values[index] = newv;
+ msg->vlens[index] = newlen;
+ msg->types[index] = type;
+ PARANOIDAUDITMSG(msg);
+ return(HA_OK);
+
+}
+
+
+static int
+cl_msg_mod(struct ha_msg * msg, const char * name,
+ const void* value, size_t vlen, int type)
+{
+ int j;
+ int rc;
+
+ PARANOIDAUDITMSG(msg);
+ if (msg == NULL || name == NULL || value == NULL) {
+ cl_log(LOG_ERR, "cl_msg_mod: NULL input.");
+ return HA_FAIL;
+ }
+
+ if(type >= DIMOF(fieldtypefuncs)){
+ cl_log(LOG_ERR, "cl_msg_mod:"
+ "invalid type(%d)", type);
+ return HA_FAIL;
+ }
+
+ for (j=0; j < msg->nfields; ++j) {
+ if (strcmp(name, msg->names[j]) == 0) {
+
+ char * newv ;
+ int newlen = vlen;
+
+ if (type != msg->types[j]){
+ cl_log(LOG_ERR, "%s: type mismatch(%d %d)",
+ __FUNCTION__, type, msg->types[j]);
+ return HA_FAIL;
+ }
+
+ newv = fieldtypefuncs[type].dup(value,vlen);
+ if (!newv){
+ cl_log(LOG_ERR, "duplicating message fields failed"
+ "value=%p, vlen=%d, msg->names[j]=%s",
+ value, (int)vlen, msg->names[j]);
+ return HA_FAIL;
+ }
+
+ fieldtypefuncs[type].memfree(msg->values[j]);
+ msg->values[j] = newv;
+ msg->vlens[j] = newlen;
+ PARANOIDAUDITMSG(msg);
+ return(HA_OK);
+ }
+ }
+
+ rc = ha_msg_nadd_type(msg, name,strlen(name), value, vlen, type);
+
+ PARANOIDAUDITMSG(msg);
+ return rc;
+}
+
+int
+cl_msg_modstruct(struct ha_msg * msg, const char* name,
+ const struct ha_msg* value)
+{
+ return cl_msg_mod(msg, name, value, 0, FT_STRUCT);
+}
+
+int
+cl_msg_modbin(struct ha_msg * msg, const char* name,
+ const void* value, size_t vlen)
+{
+ return cl_msg_mod(msg, name, value, vlen, FT_BINARY);
+
+}
+int
+cl_msg_moduuid(struct ha_msg * msg, const char* name,
+ const cl_uuid_t* uuid)
+{
+ return cl_msg_mod(msg, name, uuid, sizeof(cl_uuid_t), FT_BINARY);
+}
+
+
+
+/* Modify the value associated with a particular name */
+int
+cl_msg_modstring(struct ha_msg * msg, const char * name, const char * value)
+{
+ return cl_msg_mod(msg, name, value, strlen(value), FT_STRING);
+}
+
+
+
+/* Return the next message found in the stream */
+struct ha_msg *
+msgfromstream(FILE * f)
+{
+ char buf[MAXMSGLINE];
+ char * getsret;
+ clearerr(f);
+ /* Skip until we find a MSG_START (hopefully we skip nothing) */
+ while(1) {
+ getsret = fgets(buf, sizeof(buf), f);
+ if (!getsret) {
+ break;
+ }
+ if (strcmp(buf, MSG_START) == 0) {
+ return msgfromstream_string(f);
+
+ }
+ if (strcmp(buf, MSG_START_NETSTRING) == 0){
+ return msgfromstream_netstring(f);
+ }
+
+ }
+
+ return NULL;
+}
+
+/* Return the next message found in the stream with string format */
+struct ha_msg *
+msgfromstream_string(FILE * f)
+{
+ char buf[MAXMSGLINE];
+ const char * bufmax = buf + sizeof(buf);
+ struct ha_msg* ret;
+ char * getsret;
+
+
+ if ((ret = ha_msg_new(0)) == NULL) {
+ /* Getting an error with EINTR is pretty normal */
+ /* (so is EOF) */
+ if ( (!ferror(f) || (errno != EINTR && errno != EAGAIN))
+ && !feof(f)) {
+ cl_log(LOG_ERR, "msgfromstream: cannot get message");
+ }
+ return(NULL);
+ }
+
+ /* Add Name=value pairs until we reach MSG_END or EOF */
+ while(1) {
+ getsret = fgets(buf, MAXMSGLINE, f);
+ if (!getsret) {
+ break;
+ }
+
+ if (strnlen(buf, MAXMSGLINE) > MAXMSGLINE - 2) {
+ cl_log(LOG_DEBUG
+ , "msgfromstream: field too long [%s]"
+ , buf);
+ }
+
+ if (!strcmp(buf, MSG_END)) {
+ break;
+ }
+
+
+ /* Add the "name=value" string on this line to the message */
+ if (ha_msg_add_nv(ret, buf, bufmax) != HA_OK) {
+ cl_log(LOG_ERR, "NV failure (msgfromsteam): [%s]"
+ , buf);
+ ha_msg_del(ret); ret=NULL;
+ return(NULL);
+ }
+ }
+ return(ret);
+}
+
+
+/* Return the next message found in the stream with netstring format*/
+
+struct ha_msg *
+msgfromstream_netstring(FILE * f)
+{
+ struct ha_msg * ret;
+
+ if ((ret = ha_msg_new(0)) == NULL) {
+ /* Getting an error with EINTR is pretty normal */
+ /* (so is EOF) */
+ if ( (!ferror(f) || (errno != EINTR && errno != EAGAIN))
+ && !feof(f)) {
+ cl_log(LOG_ERR
+ , "msgfromstream_netstring(): cannot get message");
+ }
+ return(NULL);
+ }
+
+ while(1) {
+ char* nvpair;
+ int nvlen;
+ int n;
+
+ if (fscanf(f, "%d:", &nvlen) <= 0 || nvlen <= 0){
+ return(ret);
+ }
+
+ nvpair = malloc(nvlen + 2);
+
+ if ((n =fread(nvpair, 1, nvlen + 1, f)) != nvlen + 1){
+ cl_log(LOG_WARNING, "msgfromstream_netstring()"
+ ": Can't get enough nvpair,"
+ "expecting %d bytes long, got %d bytes",
+ nvlen + 1, n);
+ ha_msg_del(ret);
+ return(NULL);
+ }
+
+ process_netstring_nvpair(ret, nvpair, nvlen);
+
+ }
+
+}
+
+static gboolean ipc_timer_expired = FALSE;
+
+static void cl_sigalarm_handler(int signum)
+{
+ if (signum == SIGALRM) {
+ ipc_timer_expired = TRUE;
+ }
+}
+
+int
+cl_ipc_wait_timeout(
+ IPC_Channel *chan, int (*waitfun)(IPC_Channel *chan), unsigned int timeout)
+{
+ int rc = IPC_FAIL;
+ struct sigaction old_action;
+
+ memset(&old_action, 0, sizeof(old_action));
+ cl_signal_set_simple_handler(SIGALRM, cl_sigalarm_handler, &old_action);
+
+ ipc_timer_expired = FALSE;
+
+ alarm(timeout);
+ rc = waitfun(chan);
+ if (rc == IPC_INTR && ipc_timer_expired) {
+ rc = IPC_TIMEOUT;
+ }
+
+ alarm(0); /* ensure it expires */
+ cl_signal_set_simple_handler(SIGALRM, old_action.sa_handler, &old_action);
+
+
+ return rc;
+}
+
+/* Return the next message found in the IPC channel */
+static struct ha_msg*
+msgfromIPC_ll(IPC_Channel * ch, int flag, unsigned int timeout, int *rc_out)
+{
+ int rc;
+ IPC_Message* ipcmsg;
+ struct ha_msg* hmsg;
+ int need_auth = flag & MSG_NEEDAUTH;
+ int allow_intr = flag & MSG_ALLOWINTR;
+
+ startwait:
+ if(timeout > 0) {
+ rc = cl_ipc_wait_timeout(ch, ch->ops->waitin, timeout);
+ } else {
+ rc = ch->ops->waitin(ch);
+ }
+
+ if(rc_out) {
+ *rc_out = rc;
+ }
+
+ switch(rc) {
+ default:
+ case IPC_FAIL:
+ cl_perror("msgfromIPC: waitin failure");
+ return NULL;
+
+ case IPC_TIMEOUT:
+ return NULL;
+
+ case IPC_BROKEN:
+ sleep(1);
+ return NULL;
+
+ case IPC_INTR:
+ if ( allow_intr){
+ goto startwait;
+ }else{
+ return NULL;
+ }
+
+ case IPC_OK:
+ break;
+ }
+
+
+ ipcmsg = NULL;
+ rc = ch->ops->recv(ch, &ipcmsg);
+#if 0
+ if (DEBUGPKTCONT) {
+ cl_log(LOG_DEBUG, "msgfromIPC: recv returns %d ipcmsg = 0x%lx"
+ , rc, (unsigned long)ipcmsg);
+ }
+#endif
+ if(rc_out) {
+ *rc_out = rc;
+ }
+
+ if (rc != IPC_OK) {
+ return NULL;
+ }
+
+ hmsg = wirefmt2msg_ll((char *)ipcmsg->msg_body, ipcmsg->msg_len, need_auth);
+ if (ipcmsg->msg_done) {
+ ipcmsg->msg_done(ipcmsg);
+ }
+
+ AUDITMSG(hmsg);
+ return hmsg;
+}
+
+/* Return the next message found in the IPC channel */
+struct ha_msg*
+msgfromIPC_timeout(IPC_Channel *ch, int flag, unsigned int timeout, int *rc_out)
+{
+ return msgfromIPC_ll(ch, flag, timeout, rc_out);
+}
+
+struct ha_msg*
+msgfromIPC(IPC_Channel * ch, int flag)
+{
+ return msgfromIPC_ll(ch, flag, 0, NULL);
+}
+
+
+struct ha_msg*
+msgfromIPC_noauth(IPC_Channel * ch)
+{
+ int flag = 0;
+
+ flag |= MSG_ALLOWINTR;
+ return msgfromIPC_ll(ch, flag, 0, NULL);
+}
+
+/* Return the next message found in the IPC channel */
+IPC_Message *
+ipcmsgfromIPC(IPC_Channel * ch)
+{
+ int rc;
+ IPC_Message* ipcmsg;
+
+ rc = ch->ops->waitin(ch);
+
+ switch(rc) {
+ default:
+ case IPC_FAIL:
+ cl_perror("msgfromIPC: waitin failure");
+ return NULL;
+
+ case IPC_BROKEN:
+ sleep(1);
+ return NULL;
+
+ case IPC_INTR:
+ return NULL;
+
+ case IPC_OK:
+ break;
+ }
+
+
+ ipcmsg = NULL;
+ rc = ch->ops->recv(ch, &ipcmsg);
+#if 0
+ if (DEBUGPKTCONT) {
+ cl_log(LOG_DEBUG, "msgfromIPC: recv returns %d ipcmsg = 0x%lx"
+ , rc, (unsigned long)ipcmsg);
+ }
+#endif
+ if (rc != IPC_OK) {
+ return NULL;
+ }
+
+
+ return(ipcmsg);
+}
+
+
+/* Writes a message into a stream - used for serial lines */
+int
+msg2stream(struct ha_msg* m, FILE * f)
+{
+ size_t len;
+ char * s = msg2wirefmt(m, &len);
+
+ if (s != NULL) {
+ int rc = HA_OK;
+ if (fputs(s, f) == EOF) {
+ rc = HA_FAIL;
+ cl_perror("msg2stream: fputs failure");
+ }
+ if (fflush(f) == EOF) {
+ cl_perror("msg2stream: fflush failure");
+ rc = HA_FAIL;
+ }
+ free(s);
+ return(rc);
+ }else{
+ return(HA_FAIL);
+ }
+}
+static void ipcmsg_done(IPC_Message* m);
+
+static int clmsg_ipcmsg_allocated = 0;
+static int clmsg_ipcmsg_freed = 0;
+
+void dump_clmsg_ipcmsg_stats(void);
+void
+dump_clmsg_ipcmsg_stats(void)
+{
+ cl_log(LOG_INFO, "clmsg ipcmsg allocated=%d, freed=%d, diff=%d",
+ clmsg_ipcmsg_allocated,
+ clmsg_ipcmsg_freed,
+ clmsg_ipcmsg_allocated - clmsg_ipcmsg_freed);
+
+ return;
+}
+
+static void
+ipcmsg_done(IPC_Message* m)
+{
+ if (!m) {
+ return;
+ }
+ if (m->msg_buf) {
+ free(m->msg_buf);
+ }
+ free(m);
+ m = NULL;
+ clmsg_ipcmsg_freed ++;
+}
+
+
+
+/*
+ * create an ipcmsg and copy the data
+ */
+
+IPC_Message*
+wirefmt2ipcmsg(void* p, size_t len, IPC_Channel* ch)
+{
+ IPC_Message* ret = NULL;
+
+ if (p == NULL){
+ return(NULL);
+ }
+
+ ret = MALLOCT(IPC_Message);
+ if (!ret) {
+ return(NULL);
+ }
+
+ memset(ret, 0, sizeof(IPC_Message));
+
+ if (NULL == (ret->msg_buf = malloc(len + ch->msgpad))) {
+ free(ret);
+ return NULL;
+ }
+ ret->msg_body = (char*)ret->msg_buf + ch->msgpad;
+ memcpy(ret->msg_body, p, len);
+
+ ret->msg_done = ipcmsg_done;
+ ret->msg_private = NULL;
+ ret->msg_ch = ch;
+ ret->msg_len = len;
+
+ clmsg_ipcmsg_allocated ++;
+
+ return ret;
+
+}
+
+IPC_Message*
+hamsg2ipcmsg(struct ha_msg* m, IPC_Channel* ch)
+{
+ size_t len;
+ char * s = msg2wirefmt_ll(m, &len, MSG_NEEDCOMPRESS);
+ IPC_Message* ret = NULL;
+
+ if (s == NULL) {
+ return ret;
+ }
+ ret = MALLOCT(IPC_Message);
+ if (!ret) {
+ free(s);
+ return ret;
+ }
+
+ memset(ret, 0, sizeof(IPC_Message));
+
+ if (NULL == (ret->msg_buf = malloc(len + ch->msgpad))) {
+ free(s);
+ free(ret);
+ return NULL;
+ }
+ ret->msg_body = (char*)ret->msg_buf + ch->msgpad;
+ memcpy(ret->msg_body, s, len);
+ free(s);
+
+ ret->msg_done = ipcmsg_done;
+ ret->msg_private = NULL;
+ ret->msg_ch = ch;
+ ret->msg_len = len;
+
+ clmsg_ipcmsg_allocated ++;
+
+ return ret;
+}
+
+struct ha_msg*
+ipcmsg2hamsg(IPC_Message*m)
+{
+ struct ha_msg* ret = NULL;
+
+
+ ret = wirefmt2msg(m->msg_body, m->msg_len,MSG_NEEDAUTH);
+ return ret;
+}
+
+int
+msg2ipcchan(struct ha_msg*m, IPC_Channel*ch)
+{
+ IPC_Message* imsg;
+
+ if (m == NULL || ch == NULL) {
+ cl_log(LOG_ERR, "Invalid msg2ipcchan argument");
+ errno = EINVAL;
+ return HA_FAIL;
+ }
+
+ if ((imsg = hamsg2ipcmsg(m, ch)) == NULL) {
+ cl_log(LOG_ERR, "hamsg2ipcmsg() failure");
+ return HA_FAIL;
+ }
+
+ if (ch->ops->send(ch, imsg) != IPC_OK) {
+ if (ch->ch_status == IPC_CONNECT) {
+ snprintf(ch->failreason,MAXFAILREASON,
+ "send failed,farside_pid=%d, sendq length=%ld(max is %ld)",
+ ch->farside_pid, (long)ch->send_queue->current_qlen,
+ (long)ch->send_queue->max_qlen);
+ }
+ imsg->msg_done(imsg);
+ return HA_FAIL;
+ }
+ return HA_OK;
+}
+
+static gboolean (*msg_authentication_method)(const struct ha_msg* ret) = NULL;
+
+
+void
+cl_set_oldmsgauthfunc(gboolean (*authfunc)(const struct ha_msg*))
+{
+ msg_authentication_method = authfunc;
+}
+
+
+
+/* Converts a string (perhaps received via UDP) into a message */
+struct ha_msg *
+string2msg_ll(const char * s, size_t length, int depth, int need_auth)
+{
+ struct ha_msg* ret;
+ int startlen;
+ int endlen;
+ const char * sp = s;
+ const char * smax = s + length;
+
+
+ if ((ret = ha_msg_new(0)) == NULL) {
+ cl_log(LOG_ERR, "%s: creating new msg failed", __FUNCTION__);
+ return(NULL);
+ }
+
+ startlen = sizeof(MSG_START)-1;
+ if (strncmp(sp, MSG_START, startlen) != 0) {
+ /* This can happen if the sender gets killed */
+ /* at just the wrong time... */
+ if (!cl_msg_quiet_fmterr) {
+ cl_log(LOG_WARNING, "string2msg_ll: no MSG_START");
+ cl_log(LOG_WARNING, "%s: s=%s", __FUNCTION__, s);
+ cl_log(LOG_WARNING, "depth=%d", depth);
+ }
+ ha_msg_del(ret);
+ return(NULL);
+ }else{
+ sp += startlen;
+ }
+
+ endlen = sizeof(MSG_END)-1;
+
+ /* Add Name=value pairs until we reach MSG_END or end of string */
+
+ while (*sp != EOS && strncmp(sp, MSG_END, endlen) != 0) {
+
+ if (sp >= smax) {
+ cl_log(LOG_ERR, "%s: buffer overflow(sp=%p, smax=%p)",
+ __FUNCTION__, sp, smax);
+ return(NULL);
+ }
+ /* Skip over initial CR/NL things */
+ sp += strspn(sp, NEWLINE);
+ if (sp >= smax) {
+ cl_log(LOG_ERR, "%s: buffer overflow after NEWLINE(sp=%p, smax=%p)",
+ __FUNCTION__, sp, smax);
+ return(NULL);
+ }
+ /* End of message marker? */
+ if (strncmp(sp, MSG_END, endlen) == 0) {
+ break;
+ }
+ /* Add the "name=value" string on this line to the message */
+ if (ha_msg_add_nv_depth(ret, sp, smax, depth) != HA_OK) {
+ if (!cl_msg_quiet_fmterr) {
+ cl_log(LOG_ERR, "NV failure (string2msg_ll):");
+ cl_log(LOG_ERR, "Input string: [%s]", s);
+ cl_log(LOG_ERR, "sp=%s", sp);
+ cl_log(LOG_ERR, "depth=%d", depth);
+ cl_log_message(LOG_ERR,ret);
+ }
+ ha_msg_del(ret);
+ return(NULL);
+ }
+ if (sp >= smax) {
+ cl_log(LOG_ERR, "%s: buffer overflow after adding field(sp=%p, smax=%p)",
+ __FUNCTION__, sp, smax);
+ return(NULL);
+ }
+ sp += strcspn(sp, NEWLINE);
+ }
+
+ if (need_auth && msg_authentication_method
+ && !msg_authentication_method(ret)) {
+ const char* from = ha_msg_value(ret, F_ORIG);
+ if (!cl_msg_quiet_fmterr) {
+ cl_log(LOG_WARNING,
+ "string2msg_ll: node [%s]"
+ " failed authentication", from ? from : "?");
+ }
+ ha_msg_del(ret);
+ ret = NULL;
+ }
+ return(ret);
+}
+
+
+
+struct ha_msg *
+string2msg(const char * s, size_t length)
+{
+ return(string2msg_ll(s, length, 0, MSG_NEEDAUTH));
+}
+
+
+
+
+
+
+/* Converts a message into a string (for sending out UDP interface)
+
+ used in two places:
+
+ 1.called by msg2string as a implementation for computing string for a
+ message provided the buffer
+
+ 2.called by is_authentic. In this case, there are no start/end string
+ and the "auth" field is not included in the string
+
+*/
+
+#define NOROOM { \
+ cl_log(LOG_ERR, "%s:%d: out of memory bound" \
+ ", bp=%p, buf + len=%p, len=%ld" \
+ , __FUNCTION__, __LINE__ \
+ , bp, buf + len, (long)len); \
+ cl_log_message(LOG_ERR, m); \
+ return(HA_FAIL); \
+ }
+
+#define CHECKROOM_CONST(c) CHECKROOM_INT(STRLEN_CONST(c))
+#define CHECKROOM_STRING(s) CHECKROOM_INT(strnlen(s, len))
+#define CHECKROOM_STRING_INT(s,i) CHECKROOM_INT(strnlen(s, len)+(i))
+#define CHECKROOM_INT(i) { \
+ if ((bp + (i)) > maxp) { \
+ NOROOM; \
+ } \
+ }
+
+
+int
+msg2string_buf(const struct ha_msg *m, char* buf, size_t len
+, int depth,int needhead)
+{
+
+ char * bp = NULL;
+ int j;
+ char* maxp = buf + len;
+
+ buf[0]=0;
+ bp = buf;
+
+ if (needhead){
+ CHECKROOM_CONST(MSG_START);
+ strcpy(bp, MSG_START);
+ bp += STRLEN_CONST(MSG_START);
+ }
+
+ for (j=0; j < m->nfields; ++j) {
+
+ int truelen;
+ int (*tostring)(char*, char*, void*, size_t, int);
+
+ if (needhead == NOHEAD && strcmp(m->names[j], F_AUTH) == 0) {
+ continue;
+ }
+
+ if (m->types[j] != FT_STRING){
+ CHECKROOM_STRING_INT(FT_strings[m->types[j]],2);
+ strcat(bp, "(");
+ bp++;
+ strcat(bp, FT_strings[m->types[j]]);
+ bp++;
+ strcat(bp,")");
+ bp++;
+ }
+
+ CHECKROOM_STRING_INT(m->names[j],1);
+ strcat(bp, m->names[j]);
+ bp += m->nlens[j];
+ strcat(bp, "=");
+ bp++;
+
+ if(m->types[j] < DIMOF(fieldtypefuncs)){
+ tostring = fieldtypefuncs[m->types[j]].tostring;
+ } else {
+ cl_log(LOG_ERR, "type(%d) unrecognized", m->types[j]);
+ return HA_FAIL;
+ }
+ if (!tostring ||
+ (truelen = tostring(bp, maxp, m->values[j], m->vlens[j], depth))
+ < 0){
+ cl_log(LOG_ERR, "tostring failed for field %d", j);
+ return HA_FAIL;
+ }
+
+ CHECKROOM_INT(truelen+1);
+ bp +=truelen;
+
+ strcat(bp,"\n");
+ bp++;
+ }
+ if (needhead){
+ CHECKROOM_CONST(MSG_END);
+ strcat(bp, MSG_END);
+ bp += strlen(MSG_END);
+ }
+
+ CHECKROOM_INT(1);
+ bp[0] = EOS;
+
+ return(HA_OK);
+}
+
+
+char *
+msg2string(const struct ha_msg *m)
+{
+ void *buf;
+ int len;
+
+ AUDITMSG(m);
+ if (m->nfields <= 0) {
+ cl_log(LOG_ERR, "msg2string: Message with zero fields");
+ return(NULL);
+ }
+
+ len = get_stringlen(m);
+
+ buf = malloc(len);
+
+ if (buf == NULL) {
+ cl_log(LOG_ERR, "msg2string: no memory for string");
+ return(NULL);
+ }
+
+ if (msg2string_buf(m, buf, len ,0, NEEDHEAD) != HA_OK){
+ cl_log(LOG_ERR, "msg2string: msg2string_buf failed");
+ free(buf);
+ return(NULL);
+ }
+
+ return(buf);
+}
+
+gboolean
+must_use_netstring(const struct ha_msg* msg)
+{
+ int i;
+
+ for ( i = 0; i < msg->nfields; i++){
+ if (msg->types[i] == FT_COMPRESS
+ || msg->types[i] == FT_UNCOMPRESS
+ || msg->types[i] == FT_STRUCT){
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+
+}
+
+#define use_netstring(m) (msgfmt == MSGFMT_NETSTRING || must_use_netstring(m))
+
+static char*
+msg2wirefmt_ll(struct ha_msg*m, size_t* len, int flag)
+{
+
+ int wirefmtlen;
+ int i;
+ int netstg = use_netstring(m);
+
+ wirefmtlen = netstg ? get_netstringlen(m) : get_stringlen(m);
+ if (use_traditional_compression
+ &&(flag & MSG_NEEDCOMPRESS)
+ && (wirefmtlen> compression_threshold)
+ && cl_get_compress_fns() != NULL){
+ return cl_compressmsg(m, len);
+ }
+
+ if (flag & MSG_NEEDCOMPRESS){
+ for (i=0 ;i < m->nfields; i++){
+ int type = m->types[i];
+ if (fieldtypefuncs[type].prepackaction){
+ fieldtypefuncs[type].prepackaction(m,i);
+ }
+ }
+ }
+
+ wirefmtlen = netstg ? get_netstringlen(m) : get_stringlen(m);
+ if (wirefmtlen >= MAXMSG){
+ if (flag&MSG_NEEDCOMPRESS) {
+ if (cl_get_compress_fns() != NULL)
+ return cl_compressmsg(m, len);
+ }
+ cl_log(LOG_ERR, "%s: msg too big(%d)",
+ __FUNCTION__, wirefmtlen);
+ return NULL;
+ }
+ if (flag & MSG_NEEDAUTH) {
+ return msg2netstring(m, len);
+ }
+ return msg2wirefmt_noac(m, len);
+}
+
+char*
+msg2wirefmt(struct ha_msg*m, size_t* len){
+ return msg2wirefmt_ll(m, len, MSG_NEEDAUTH|MSG_NEEDCOMPRESS);
+}
+
+char*
+msg2wirefmt_noac(struct ha_msg*m, size_t* len)
+{
+ if (use_netstring(m)) {
+ return msg2netstring_noauth(m, len);
+ } else {
+ char *tmp;
+
+ tmp = msg2string(m);
+ if(tmp == NULL){
+ *len = 0;
+ return NULL;
+ }
+ *len = strlen(tmp) + 1;
+ return tmp;
+ }
+}
+
+static struct ha_msg*
+wirefmt2msg_ll(const char* s, size_t length, int need_auth)
+{
+
+ size_t startlen;
+ struct ha_msg* msg = NULL;
+
+
+ startlen = sizeof(MSG_START)-1;
+
+ if (startlen > length){
+ return NULL;
+ }
+
+ if (strncmp( s, MSG_START, startlen) == 0) {
+ msg = string2msg_ll(s, length, 0, need_auth);
+ goto out;
+ }
+
+ startlen = sizeof(MSG_START_NETSTRING) - 1;
+
+ if (startlen > length){
+ return NULL;
+ }
+
+ if (strncmp(s, MSG_START_NETSTRING, startlen) == 0) {
+ msg = netstring2msg(s, length, need_auth);
+ goto out;
+ }
+
+out:
+ if (msg && is_compressed_msg(msg)){
+ struct ha_msg* ret;
+ if ((ret = cl_decompressmsg(msg))==NULL){
+ cl_log(LOG_ERR, "decompress msg failed");
+ ha_msg_del(msg);
+ return NULL;
+ }
+ ha_msg_del(msg);
+ return ret;
+ }
+ return msg;
+
+}
+
+
+
+
+struct ha_msg*
+wirefmt2msg(const char* s, size_t length, int flag)
+{
+ return wirefmt2msg_ll(s, length, flag& MSG_NEEDAUTH);
+
+}
+
+
+void
+cl_log_message (int log_level, const struct ha_msg *m)
+{
+ int j;
+
+ if(m == NULL) {
+ cl_log(log_level, "MSG: No message to dump");
+ return;
+ }
+
+ cl_log(log_level, "MSG: Dumping message with %d fields", m->nfields);
+
+ for (j=0; j < m->nfields; ++j) {
+
+ if(m->types[j] < DIMOF(fieldtypefuncs)){
+ fieldtypefuncs[m->types[j]].display(log_level, j,
+ m->names[j],
+ m->values[j],
+ m->vlens[j]);
+ }
+ }
+}
+
+
+#ifdef TESTMAIN_MSGS
+int
+main(int argc, char ** argv)
+{
+ struct ha_msg* m;
+ while (!feof(stdin)) {
+ if ((m=controlfifo2msg(stdin)) != NULL) {
+ fprintf(stderr, "Got message!\n");
+ if (msg2stream(m, stdout) == HA_OK) {
+ fprintf(stderr, "Message output OK!\n");
+ }else{
+ fprintf(stderr, "Could not output Message!\n");
+ }
+ }else{
+ fprintf(stderr, "Could not get message!\n");
+ }
+ }
+ return(0);
+}
+#endif
diff --git a/lib/clplumbing/cl_msg_types.c b/lib/clplumbing/cl_msg_types.c
new file mode 100644
index 0000000..56cf56a
--- /dev/null
+++ b/lib/clplumbing/cl_msg_types.c
@@ -0,0 +1,1736 @@
+/*
+ * Heartbeat message type functions
+ *
+ * Copyright (C) 2004 Guochun Shi <gshi@ncsa.uiuc.edu>
+ *
+ * This software licensed under the GNU LGPL.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+
+#include <lha_internal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/utsname.h>
+#include <ha_msg.h>
+#include <unistd.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/ipc.h>
+#include <clplumbing/base64.h>
+#include <clplumbing/netstring.h>
+#include <glib.h>
+
+#ifndef MAX
+# define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+
+extern const char* FT_strings[];
+
+
+
+#define NL_TO_SYM 0
+#define SYM_TO_NL 1
+
+static const int SPECIAL_SYMS[MAXDEPTH] = {
+ 20,
+ 21,
+ 22,
+ 23,
+ 24,
+ 25,
+ 26,
+ 27,
+ 28,
+ 29,
+ 30,
+ 31,
+ 15,
+ 16,
+ 17,
+ 18,
+};
+
+#define SPECIAL_SYM 19
+
+struct ha_msg* string2msg_ll(const char*, size_t, int, int);
+int compose_netstring(char*, const char*, const char*, size_t, size_t*);
+int msg2netstring_buf(const struct ha_msg*, char*, size_t, size_t*);
+int struct_display_print_spaces(char *buffer, int depth);
+int struct_display_as_xml(int log_level, int depth, struct ha_msg *data,
+ const char *prefix, gboolean formatted);
+int struct_stringlen(size_t namlen, size_t vallen, const void* value);
+int struct_netstringlen(size_t namlen, size_t vallen, const void* value);
+int convert_nl_sym(char* s, int len, char sym, int direction);
+int bytes_for_int(int x);
+
+int
+bytes_for_int(int x)
+{
+ int len = 0;
+ if(x < 0) {
+ x = 0-x;
+ len=1;
+ }
+ while(x > 9) {
+ x /= 10;
+ len++;
+ }
+ return len+1;
+}
+
+int
+netstring_extra(int x)
+{
+ return (bytes_for_int(x) + x + 2);
+}
+
+int
+get_netstringlen(const struct ha_msg *m)
+{
+ int i;
+ int total_len =0 ;
+
+ if (m == NULL){
+ cl_log(LOG_ERR, "get_netstringlen:"
+ "asking netstringlen of a NULL message");
+ return 0;
+ }
+
+ total_len = sizeof(MSG_START_NETSTRING)
+ + sizeof(MSG_END_NETSTRING) -2 ;
+
+
+ for (i = 0; i < m->nfields; i++){
+ int len;
+ len = fieldtypefuncs[m->types[i]].netstringlen(m->nlens[i],
+ m->vlens[i],
+ m->values[i]);
+ total_len += netstring_extra(len);
+ }
+
+
+ return total_len;
+
+
+}
+
+
+
+int
+get_stringlen(const struct ha_msg *m)
+{
+ int i;
+ int total_len =0 ;
+
+ if (m == NULL){
+ cl_log(LOG_ERR, "get_stringlen:"
+ "asking stringlen of a NULL message");
+ return 0;
+ }
+
+ total_len = sizeof(MSG_START)+sizeof(MSG_END)-1;
+
+ for (i = 0; i < m->nfields; i++){
+ total_len += fieldtypefuncs[m->types[i]].stringlen(m->nlens[i],
+ m->vlens[i],
+ m->values[i]);
+ }
+
+ return total_len;
+}
+
+
+
+/*
+ compute the total size of the resulted string
+ if the string list is to be converted
+
+*/
+size_t
+string_list_pack_length(const GList* _list)
+{
+ size_t i;
+ GList* list = UNCONST_CAST_POINTER(GList *, _list);
+ size_t total_length = 0;
+
+ if (list == NULL){
+ cl_log(LOG_WARNING, "string_list_pack_length():"
+ "list is NULL");
+
+ return 0;
+ }
+ for (i = 0; i < g_list_length(list) ; i++){
+
+ int len = 0;
+ char * element = g_list_nth_data(list, i);
+ if (element == NULL){
+ cl_log(LOG_ERR, "string_list_pack_length: "
+ "%luth element of the string list is NULL"
+ , (unsigned long)i);
+ return 0;
+ }
+ len = strlen(element);
+ total_length += bytes_for_int(len) + len + 2;
+ /* 2 is for ":" and "," */
+ }
+ return total_length ;
+}
+
+
+
+/*
+ convert a string list into a single string
+ the format to convert is similar to netstring:
+ <length> ":" <the actual string> ","
+
+ for example, a list containing two strings "abc" "defg"
+ will be converted into
+ 3:abc,4:defg,
+ @list: the list to be converted
+ @buf: the converted string should be put in the @buf
+ @maxp: max pointer
+*/
+
+
+int
+string_list_pack(GList* list, char* buf, char* maxp)
+{
+ size_t i;
+ char* p = buf;
+
+ for (i = 0; i < g_list_length(list) ; i++){
+ char * element = g_list_nth_data(list, i);
+ int element_len;
+
+ if (element == NULL){
+ cl_log(LOG_ERR, "string_list_pack: "
+ "%luth element of the string list is NULL"
+ , (unsigned long)i);
+ return 0;
+ }
+ element_len = strlen(element);
+ if (p + 2 + element_len + bytes_for_int(element_len)> maxp){
+ cl_log(LOG_ERR, "%s: memory out of boundary",
+ __FUNCTION__);
+ return 0;
+ }
+ p += sprintf(p, "%d:%s,", element_len,element);
+
+ if (p > maxp){
+ cl_log(LOG_ERR, "string_list_pack: "
+ "buffer overflowed ");
+ return 0;
+ }
+ }
+
+
+ return (p - buf);
+}
+
+
+
+/*
+ this is reverse process of pack_string_list
+*/
+GList*
+string_list_unpack(const char* packed_str_list, size_t length)
+{
+ GList* list = NULL;
+ const char* psl = packed_str_list;
+ const char * maxp= packed_str_list + length;
+ int len = 0;
+
+
+ while(TRUE){
+ char* buf;
+
+ if (*psl == '\0' || psl >= maxp){
+ break;
+ }
+
+ if (sscanf( psl, "%d:", &len) <= 0 ){
+ break;
+ }
+
+ if (len <=0){
+ cl_log(LOG_ERR, "unpack_string_list:"
+ "reading len of string error");
+ if (list){
+ list_cleanup(list);
+ }
+ return NULL;
+ }
+
+ while (*psl != ':' && *psl != '\0' ){
+ psl++;
+ }
+
+ if (*psl == '\0'){
+ break;
+ }
+
+ psl++;
+
+ buf = malloc(len + 1);
+ if (buf == NULL){
+ cl_log(LOG_ERR, "unpack_string_list:"
+ "unable to allocate buf");
+ if(list){
+ list_cleanup(list);
+ }
+ return NULL;
+
+ }
+ memcpy(buf, psl, len);
+ buf[len] = '\0';
+ list = g_list_append(list, buf);
+ psl +=len;
+
+ if (*psl != ','){
+ cl_log(LOG_ERR, "unpack_string_list:"
+ "wrong format, s=%s",packed_str_list);
+ }
+ psl++;
+ }
+
+ return list;
+
+}
+
+
+static void
+string_memfree(void* value)
+{
+ if (value){
+ free(value);
+ }else {
+ cl_log(LOG_ERR, "string_memfree: "
+ "value is NULL");
+ }
+
+
+ return;
+}
+
+static void
+binary_memfree(void* value)
+{
+ string_memfree(value);
+}
+
+
+static void
+struct_memfree( void* value)
+{
+ struct ha_msg* msg;
+
+ if (!value){
+ cl_log(LOG_ERR,
+ "value is NULL");
+ return ;
+ }
+
+ msg = (struct ha_msg*) value;
+ ha_msg_del(msg);
+ return ;
+}
+
+static void
+list_memfree(void* value)
+{
+
+ if (!value){
+ cl_log(LOG_ERR,
+ "value is NULL");
+ return ;
+ }
+
+ list_cleanup(value);
+
+}
+
+
+static void*
+binary_dup(const void* value, size_t len)
+{
+
+ char* dupvalue;
+
+ /* 0 byte binary field is allowed*/
+
+ if (value == NULL && len > 0){
+ cl_log(LOG_ERR, "binary_dup:"
+ "NULL value with non-zero len=%d",
+ (int)len);
+ return NULL;
+ }
+
+ dupvalue = malloc(len + 1);
+ if (dupvalue == NULL){
+ cl_log(LOG_ERR, "binary_dup:"
+ "malloc failed");
+ return NULL;
+ }
+
+ if (value != NULL) {
+ memcpy(dupvalue, value, len);
+ }
+
+ dupvalue[len] =0;
+
+ return dupvalue;
+}
+
+static void*
+string_dup(const void* value, size_t len)
+{
+ return binary_dup(value, len);
+}
+
+
+static void*
+struct_dup(const void* value, size_t len)
+{
+ char* dupvalue;
+
+ (void)len;
+
+ if (!value){
+ cl_log(LOG_ERR,"struct_dup:"
+ "value is NULL");
+ return NULL ;
+ }
+
+
+ dupvalue = (void*)ha_msg_copy((const struct ha_msg*)value);
+ if (dupvalue == NULL){
+ cl_log(LOG_ERR, "struct_dup: "
+ "ha_msg_copy failed");
+ return NULL;
+ }
+
+ return dupvalue;
+}
+
+static GList*
+list_copy(const GList* _list)
+{
+ size_t i;
+ GList* newlist = NULL;
+ GList* list = UNCONST_CAST_POINTER(GList *, _list);
+
+ for (i = 0; i < g_list_length(list); i++){
+ char* dup_element = NULL;
+ char* element = g_list_nth_data(list, i);
+ int len;
+ if (element == NULL){
+ cl_log(LOG_WARNING, "list_copy:"
+ "element is NULL");
+ continue;
+ }
+
+ len = strlen(element);
+ dup_element= malloc(len + 1);
+ if ( dup_element == NULL){
+ cl_log(LOG_ERR, "duplicate element failed");
+ continue;
+ }
+ memcpy(dup_element, element,len);
+ dup_element[len] = 0;
+
+ newlist = g_list_append(newlist, dup_element);
+ }
+
+ return newlist;
+}
+
+static void*
+list_dup( const void* value, size_t len)
+{
+ char* dupvalue;
+
+ (void)len;
+ if (!value){
+ cl_log(LOG_ERR,"struct_dup:"
+ "value is NULL");
+ return NULL ;
+ }
+
+ dupvalue = (void*)list_copy((const GList*)value);
+
+ if (!dupvalue){
+ cl_log(LOG_ERR, "list_dup: "
+ "list_copy failed");
+ return NULL;
+ }
+
+ return dupvalue;
+}
+
+
+static void
+general_display(int log_level, int seq, char* name, void* value, int vlen, int type)
+{
+ int netslen;
+ int slen;
+ HA_MSG_ASSERT(value);
+ HA_MSG_ASSERT(name);
+
+ slen = fieldtypefuncs[type].stringlen(strlen(name), vlen, value);
+ netslen = fieldtypefuncs[type].netstringlen(strlen(name), vlen, value);
+ cl_log(log_level, "MSG[%d] : [(%s)%s=%p(%d %d)]",
+ seq, FT_strings[type],
+ name, value, slen, netslen);
+
+}
+static void
+string_display(int log_level, int seq, char* name, void* value, int vlen)
+{
+ HA_MSG_ASSERT(name);
+ HA_MSG_ASSERT(value);
+ cl_log(log_level, "MSG[%d] : [%s=%s]",
+ seq, name, (const char*)value);
+ return;
+}
+
+static void
+binary_display(int log_level, int seq, char* name, void* value, int vlen)
+{
+ general_display(log_level, seq, name, value, vlen, FT_BINARY);
+}
+
+static void
+compress_display(int log_level, int seq, char* name, void* value, int vlen){
+ general_display(log_level, seq, name, value, vlen, FT_COMPRESS);
+}
+
+
+static void
+general_struct_display(int log_level, int seq, char* name, void* value, int vlen, int type)
+{
+ int slen;
+ int netslen;
+
+ HA_MSG_ASSERT(name);
+ HA_MSG_ASSERT(value);
+
+ slen = fieldtypefuncs[type].stringlen(strlen(name), vlen, value);
+ netslen = fieldtypefuncs[type].netstringlen(strlen(name), vlen, value);
+
+ cl_log(log_level, "MSG[%d] : [(%s)%s=%p(%d %d)]",
+ seq, FT_strings[type],
+ name, value, slen, netslen);
+ if(cl_get_string((struct ha_msg*) value, F_XML_TAGNAME) == NULL) {
+ cl_log_message(log_level, (struct ha_msg*) value);
+ } else {
+ /* use a more friendly output format for nested messages */
+ struct_display_as_xml(log_level, 0, value, NULL, TRUE);
+ }
+}
+static void
+struct_display(int log_level, int seq, char* name, void* value, int vlen)
+{
+ general_struct_display(log_level, seq, name, value, vlen, FT_STRUCT);
+
+}
+static void
+uncompress_display(int log_level, int seq, char* name, void* value, int vlen)
+{
+ general_struct_display(log_level, seq, name, value, vlen, FT_UNCOMPRESS);
+}
+
+#define update_buffer_head(buffer, len) if(len < 0) { \
+ (*buffer) = EOS; return -1; \
+ } else { \
+ buffer += len; \
+ }
+
+int
+struct_display_print_spaces(char *buffer, int depth)
+{
+ int lpc = 0;
+ int spaces = 2*depth;
+ /* <= so that we always print 1 space - prevents problems with syslog */
+ for(lpc = 0; lpc <= spaces; lpc++) {
+ if(sprintf(buffer, "%c", ' ') < 1) {
+ return -1;
+ }
+ buffer += 1;
+ }
+ return lpc;
+}
+
+int
+struct_display_as_xml(
+ int log_level, int depth, struct ha_msg *data,
+ const char *prefix, gboolean formatted)
+{
+ int lpc = 0;
+ int printed = 0;
+ gboolean has_children = FALSE;
+ char print_buffer[1000];
+ char *buffer = print_buffer;
+ const char *name = cl_get_string(data, F_XML_TAGNAME);
+
+ if(data == NULL) {
+ return 0;
+
+ } else if(name == NULL) {
+ cl_log(LOG_WARNING, "Struct at depth %d had no name", depth);
+ cl_log_message(log_level, data);
+ return 0;
+ }
+
+ if(formatted) {
+ printed = struct_display_print_spaces(buffer, depth);
+ update_buffer_head(buffer, printed);
+ }
+
+ printed = sprintf(buffer, "<%s", name);
+ update_buffer_head(buffer, printed);
+
+ for (lpc = 0; lpc < data->nfields; lpc++) {
+ const char *prop_name = data->names[lpc];
+ const char *prop_value = data->values[lpc];
+ if(data->types[lpc] != FT_STRING) {
+ continue;
+ } else if(prop_name == NULL) {
+ continue;
+ } else if(prop_name[0] == '_' && prop_name[1] == '_') {
+ continue;
+ }
+ printed = sprintf(buffer, " %s=\"%s\"", prop_name, prop_value);
+ update_buffer_head(buffer, printed);
+ }
+
+ for (lpc = 0; lpc < data->nfields; lpc++) {
+ if(data->types[lpc] == FT_STRUCT) {
+ has_children = TRUE;
+ break;
+ }
+ }
+
+ printed = sprintf(buffer, "%s>", has_children==0?"/":"");
+ update_buffer_head(buffer, printed);
+ cl_log(log_level, "%s%s", prefix?prefix:"", print_buffer);
+ buffer = print_buffer;
+
+ if(has_children == FALSE) {
+ return 0;
+ }
+
+ for (lpc = 0; lpc < data->nfields; lpc++) {
+ if(data->types[lpc] != FT_STRUCT) {
+ continue;
+ } else if(0 > struct_display_as_xml(
+ log_level, depth+1, data->values[lpc],
+ prefix, formatted)) {
+ return -1;
+ }
+ }
+
+ if(formatted) {
+ printed = struct_display_print_spaces(buffer, depth);
+ update_buffer_head(buffer, printed);
+ }
+ cl_log(log_level, "%s%s</%s>", prefix?prefix:"", print_buffer, name);
+
+ return 0;
+}
+
+
+
+
+static int
+liststring(GList* list, char* buf, int maxlen)
+{
+ char* p = buf;
+ char* maxp = buf + maxlen;
+ size_t i;
+
+ for ( i = 0; i < g_list_length(list); i++){
+ char* element = g_list_nth_data(list, i);
+ if (element == NULL) {
+ cl_log(LOG_ERR, "%luth element is NULL "
+ , (unsigned long)i);
+ return HA_FAIL;
+ } else{
+ if (i == 0){
+ p += sprintf(p,"%s",element);
+ }else{
+ p += sprintf(p," %s",element);
+ }
+
+ }
+ if ( p > maxp){
+ cl_log(LOG_ERR, "buffer overflow");
+ return HA_FAIL;
+ }
+
+ }
+
+ return HA_OK;
+}
+
+static void
+list_display(int log_level, int seq, char* name, void* value, int vlen)
+{
+ GList* list;
+ char buf[MAXLENGTH];
+
+ HA_MSG_ASSERT(name);
+ HA_MSG_ASSERT(value);
+
+ list = value;
+
+ if (liststring(list, buf, MAXLENGTH) != HA_OK){
+ cl_log(LOG_ERR, "liststring error");
+ return;
+ }
+ cl_log(log_level, "MSG[%d] :[(%s)%s=%s]",
+ seq, FT_strings[FT_LIST],
+ name, buf);
+
+ return ;
+
+}
+
+
+/*
+ * This function changes each new line in the input string
+ * into a special symbol, or the other way around
+ */
+
+int
+convert_nl_sym(char* s, int len, char sym, int direction)
+{
+ int i;
+
+ if (direction != NL_TO_SYM && direction != SYM_TO_NL){
+ cl_log(LOG_ERR, "convert_nl_sym(): direction not defined!");
+ return(HA_FAIL);
+ }
+
+
+ for (i = 0; i < len && s[i] != EOS; i++){
+
+ switch(direction){
+ case NL_TO_SYM :
+ if (s[i] == '\n'){
+ s[i] = sym;
+ break;
+ }
+
+ if (s[i] == sym){
+ cl_log(LOG_ERR
+ , "convert_nl_sym(): special symbol \'0x%x\' (%c) found"
+ " in string at %d (len=%d)", s[i], s[i], i, len);
+ i -= 10;
+ if(i < 0) {
+ i = 0;
+ }
+ cl_log(LOG_ERR, "convert_nl_sym(): %s", s + i);
+ return(HA_FAIL);
+ }
+
+ break;
+
+ case SYM_TO_NL:
+
+ if (s[i] == sym){
+ s[i] = '\n';
+ break;
+ }
+ break;
+ default:
+ /* nothing, never executed*/;
+
+ }
+ }
+
+ return(HA_OK);
+}
+
+
+/*
+ * This function changes each new line in the input string
+ * into a special symbol, or the other way around
+ */
+
+static int
+convert(char* s, int len, int depth, int direction)
+{
+
+ if (direction != NL_TO_SYM && direction != SYM_TO_NL){
+ cl_log(LOG_ERR, "convert(): direction not defined!");
+ return(HA_FAIL);
+ }
+
+
+ if (depth >= MAXDEPTH ){
+ cl_log(LOG_ERR, "convert(): MAXDEPTH exceeded: %d", depth);
+ return(HA_FAIL);
+ }
+
+ return convert_nl_sym(s, len, SPECIAL_SYMS[depth], direction);
+}
+
+
+
+
+static int
+string_stringlen(size_t namlen, size_t vallen, const void* value)
+{
+
+ HA_MSG_ASSERT(value);
+/* HA_MSG_ASSERT( vallen == strlen(value)); */
+ return namlen + vallen + 2;
+}
+
+static int
+binary_netstringlen(size_t namlen, size_t vallen, const void* value)
+{
+ int length;
+
+ HA_MSG_ASSERT(value);
+
+ length = 3 + namlen + 1 + vallen;
+
+ return length;
+}
+
+
+static int
+string_netstringlen(size_t namlen, size_t vallen, const void* value)
+{
+ HA_MSG_ASSERT(value);
+ HA_MSG_ASSERT( vallen == strlen(value));
+
+ return binary_netstringlen(namlen, vallen, value);
+}
+
+
+static int
+binary_stringlen(size_t namlen, size_t vallen, const void* value)
+{
+ HA_MSG_ASSERT(value);
+
+ return namlen + B64_stringlen(vallen) + 2 + 3;
+ /*overhead 3 is for type*/
+}
+
+
+
+
+
+int
+struct_stringlen(size_t namlen, size_t vallen, const void* value)
+{
+ const struct ha_msg* childmsg;
+
+ HA_MSG_ASSERT(value);
+
+ (void)vallen;
+ childmsg = (const struct ha_msg*)value;
+
+ return namlen +2 + 3 + get_stringlen(childmsg);
+ /*overhead 3 is for type*/
+}
+
+int
+struct_netstringlen(size_t namlen, size_t vallen, const void* value)
+{
+
+ int ret;
+ const struct ha_msg* childmsg;
+ int len;
+
+ HA_MSG_ASSERT(value);
+
+ (void)vallen;
+ childmsg = (const struct ha_msg*)value;
+
+ len = get_netstringlen(childmsg);
+
+ ret = 3 + namlen + 1 + len;
+
+ return ret;
+
+}
+
+
+static int
+list_stringlen(size_t namlen, size_t vallen, const void* value)
+{
+ (void)value;
+ return namlen + vallen + 2 + 3;
+ /*overhead 3 is for type (FT_STRUCT)*/
+}
+
+static int
+list_netstringlen(size_t namlen, size_t vallen, const void* value)
+{
+ int ret;
+ const GList* list;
+
+ list = (const GList*)value;
+
+ ret = 3 + namlen + 1 + string_list_pack_length(list);
+
+ return ret;
+
+}
+
+static int
+add_binary_field(struct ha_msg* msg, char* name, size_t namelen,
+ void* value, size_t vallen, int depth)
+{
+
+ int next;
+
+ if ( !msg || !name || !value
+ || depth < 0){
+ cl_log(LOG_ERR, "add_binary_field:"
+ " invalid input argument");
+ return HA_FAIL;
+ }
+
+
+ next = msg->nfields;
+ msg->names[next] = name;
+ msg->nlens[next] = namelen;
+ msg->values[next] = value;
+ msg->vlens[next] = vallen;
+ msg->types[next] = FT_BINARY;
+ msg->nfields++;
+
+ return HA_OK;
+}
+
+
+static int
+add_struct_field(struct ha_msg* msg, char* name, size_t namelen,
+ void* value, size_t vallen, int depth)
+{
+ int next;
+
+ if ( !msg || !name || !value
+ || depth < 0){
+ cl_log(LOG_ERR, "add_struct_field:"
+ " invalid input argument");
+ return HA_FAIL;
+ }
+
+ next = msg->nfields;
+ msg->names[next] = name;
+ msg->nlens[next] = namelen;
+ msg->values[next] = value;
+ msg->vlens[next] = vallen;
+ msg->types[next] = FT_STRUCT;
+
+ msg->nfields++;
+
+ return HA_OK;
+}
+
+
+
+
+static int
+add_list_field(struct ha_msg* msg, char* name, size_t namelen,
+ void* value, size_t vallen, int depth)
+{
+ int next;
+ int j;
+ GList* list = NULL;
+
+ if ( !msg || !name || !value
+ || namelen <= 0
+ || vallen <= 0
+ || depth < 0){
+ cl_log(LOG_ERR, "add_list_field:"
+ " invalid input argument");
+ return HA_FAIL;
+ }
+
+
+ for (j=0; j < msg->nfields; ++j) {
+ if (strcmp(name, msg->names[j]) == 0) {
+ break;
+ }
+ }
+
+ if ( j >= msg->nfields){
+ list = (GList*)value;
+
+ next = msg->nfields;
+ msg->names[next] = name;
+ msg->nlens[next] = namelen;
+ msg->values[next] = value;
+ msg->vlens[next] = vallen;
+ msg->types[next] = FT_LIST;
+ msg->nfields++;
+
+ } else if( msg->types[j] == FT_LIST ){
+
+ GList* oldlist = (GList*) msg->values[j];
+ int listlen;
+ size_t i;
+
+ for ( i =0; i < g_list_length((GList*)value); i++){
+ list = g_list_append(oldlist, g_list_nth_data((GList*)value, i));
+ }
+ if (list == NULL){
+ cl_log(LOG_ERR, "add_list_field:"
+ " g_list_append() failed");
+ return HA_FAIL;
+ }
+
+ listlen = string_list_pack_length(list);
+
+ msg->values[j] = list;
+ msg->vlens[j] = listlen;
+ g_list_free((GList*)value); /*we don't free each element
+ because they are used in new list*/
+ free(name); /* this name is no longer necessary
+ because msg->names[j] is reused */
+
+ } else {
+ cl_log(LOG_ERR, "field already exists "
+ "with differnt type=%d", msg->types[j]);
+ return (HA_FAIL);
+ }
+
+ return HA_OK;
+}
+
+
+static int
+add_compress_field(struct ha_msg* msg, char* name, size_t namelen,
+ void* value, size_t vallen, int depth)
+{
+
+ int next;
+
+ if ( !msg || !name || !value
+ || depth < 0){
+ cl_log(LOG_ERR, "add_binary_field:"
+ " invalid input argument");
+ return HA_FAIL;
+ }
+
+
+ next = msg->nfields;
+ msg->names[next] = name;
+ msg->nlens[next] = namelen;
+ msg->values[next] = value;
+ msg->vlens[next] = vallen;
+ msg->types[next] = FT_COMPRESS;
+ msg->nfields++;
+
+ return HA_OK;
+}
+
+
+
+
+static int
+add_uncompress_field(struct ha_msg* msg, char* name, size_t namelen,
+ void* value, size_t vallen, int depth)
+{
+ int next;
+
+ if ( !msg || !name || !value
+ || depth < 0){
+ cl_log(LOG_ERR, "add_struct_field:"
+ " invalid input argument");
+ return HA_FAIL;
+ }
+
+ next = msg->nfields;
+ msg->names[next] = name;
+ msg->nlens[next] = namelen;
+ msg->values[next] = value;
+ msg->vlens[next] = vallen;
+ msg->types[next] = FT_UNCOMPRESS;
+
+ msg->nfields++;
+
+ return HA_OK;
+}
+
+
+
+/*print a string to a string,
+ pretty simple one :)
+*/
+static int
+str2string(char* buf, char* maxp, void* value, size_t len, int depth)
+{
+ char* s = value;
+ char* p = buf;
+ (void)maxp;
+ (void)depth;
+
+ if (buf + len > maxp){
+ cl_log(LOG_ERR, "%s: out of boundary",
+ __FUNCTION__);
+ return -1;
+ }
+
+ if ( strlen(s) != len){
+ cl_log(LOG_ERR, "str2string:"
+ "the input len != string length");
+ return -1;
+ }
+
+ strcat(buf, s);
+ while(*p != '\0'){
+ if (*p == '\n'){
+ *p = SPECIAL_SYM;
+ }
+ p++;
+ }
+
+ return len;
+
+}
+
+/*print a binary value to a string using base64
+ library
+*/
+
+static int
+binary2string(char* buf, char* maxp, void* value, size_t len, int depth)
+{
+ int baselen;
+ int truelen = 0;
+
+ (void)depth;
+ baselen = B64_stringlen(len) + 1;
+
+ if ( buf + baselen > maxp){
+ cl_log(LOG_ERR, "binary2string: out of bounary");
+ return -1;
+ }
+
+ truelen = binary_to_base64(value, len, buf, baselen);
+
+ return truelen;
+}
+
+/*print a struct(ha_msg) to a string
+ @depth denotes the number of recursion
+*/
+
+static int
+struct2string(char* buf, char* maxp, void* value, size_t len, int depth)
+{
+
+ struct ha_msg* msg = value;
+ int baselen = get_stringlen(msg);
+
+ (void)len;
+
+ if ( buf + baselen > maxp){
+ cl_log(LOG_ERR, "struct2string: not enough buffer"
+ "for the struct to generate a string");
+ return -1;
+ }
+
+ if (msg2string_buf(msg, buf ,baselen,depth + 1, NEEDHEAD)
+ != HA_OK){
+
+ cl_log(LOG_ERR
+ , "struct2string(): msg2string_buf for"
+ " child message failed");
+ return -1;
+
+ }
+
+ if (convert(buf, baselen, depth, NL_TO_SYM) != HA_OK){
+ cl_log(LOG_ERR , "struct2string(): convert failed");
+ return -1;
+ }
+
+ return strlen(buf);
+}
+
+
+
+
+/* print a list to a string
+ */
+
+static int
+list2string(char* buf, char* maxp, void* value, size_t len, int depth)
+{
+ int listlen;
+ GList* list = (GList*) value;
+
+ (void)len;
+ (void)depth;
+ listlen = string_list_pack(list , buf, maxp);
+ if (listlen == 0){
+ cl_log(LOG_ERR, "list2string():"
+ "string_list_pack() failed");
+ return -1;
+ }
+
+ return listlen;
+
+}
+
+
+static int
+string2str(void* value, size_t len, int depth, void** nv, size_t* nlen )
+{
+ if (!value || !nv || !nlen || depth < 0){
+ cl_log(LOG_ERR, "string2str:invalid input");
+ return HA_FAIL;
+ }
+
+ if (convert_nl_sym(value, len, SPECIAL_SYM, SYM_TO_NL) != HA_OK){
+ cl_log(LOG_ERR, "string2str:convert_nl_sym"
+ "from symbol to new line failed");
+ return HA_FAIL;
+ }
+ *nv = value;
+ *nlen = len;
+
+ return HA_OK;
+}
+
+static int
+string2binary(void* value, size_t len, int depth, void** nv, size_t* nlen)
+{
+ char tmpbuf[MAXLINE];
+ char* buf = NULL;
+ int buf_malloced = 0;
+ int ret = HA_FAIL;
+ if (len > MAXLINE){
+ buf = malloc(len);
+ if (buf == NULL){
+ cl_log(LOG_ERR, "%s: malloc failed",
+ __FUNCTION__);
+ goto out;
+ }
+ buf_malloced = 1;
+ }else {
+ buf = &tmpbuf[0];
+ }
+
+ if (value == NULL && len == 0){
+ *nv = NULL;
+ *nlen = 0;
+ ret = HA_OK;
+ goto out;
+ }
+
+ if ( !value || !nv || depth < 0){
+ cl_log(LOG_ERR, "string2binary:invalid input");
+ ret = HA_FAIL;
+ goto out;
+ }
+
+ memcpy(buf, value, len);
+ *nlen = base64_to_binary(buf, len, value, len);
+
+ *nv = value;
+ ret = HA_OK;
+ out:
+ if (buf_malloced && buf){
+ free(buf);
+ }
+ return ret;
+}
+
+static int
+string2struct(void* value, size_t vallen, int depth, void** nv, size_t* nlen)
+{
+
+ struct ha_msg *tmpmsg;
+
+ if (!value || !nv || depth < 0){
+ cl_log(LOG_ERR, "string2struct:invalid input");
+ return HA_FAIL;
+ }
+
+
+ if (convert(value, vallen, depth,SYM_TO_NL) != HA_OK){
+ cl_log(LOG_ERR
+ , "ha_msg_addraw_ll(): convert failed");
+ return(HA_FAIL);
+ }
+
+ tmpmsg = string2msg_ll(value, vallen,depth + 1, 0);
+ if (tmpmsg == NULL){
+ cl_log(LOG_ERR
+ , "string2struct()"
+ ": string2msg_ll failed");
+ return(HA_FAIL);
+ }
+ free(value);
+ *nv = tmpmsg;
+ *nlen = 0;
+
+ return HA_OK;
+
+}
+
+static int
+string2list(void* value, size_t vallen, int depth, void** nv, size_t* nlen)
+{
+ GList* list;
+
+ if (!value || !nv || !nlen || depth < 0){
+ cl_log(LOG_ERR, "string2struct:invalid input");
+ return HA_FAIL;
+ }
+
+ list = string_list_unpack(value, vallen);
+ if (list == NULL){
+ cl_log(LOG_ERR, "ha_msg_addraw_ll():"
+ "unpack_string_list failed: %s", (char*)value);
+ return(HA_FAIL);
+ }
+ free(value);
+
+ *nv = (void*)list;
+ *nlen = string_list_pack_length(list);
+
+ return HA_OK;
+
+}
+
+static int
+fields2netstring(char* sp, char* smax, char* name, size_t nlen,
+ void* value, size_t vallen, int type, size_t* comlen)
+{
+ size_t fieldlen;
+ size_t slen;
+ int ret = HA_OK;
+ char* sp_save = sp;
+ char* tmpsp;
+
+ fieldlen = fieldtypefuncs[type].netstringlen(nlen, vallen, value);
+ /* this check seems to be superfluous because of the next one
+ if (fieldlen > MAXMSG){
+ cl_log(LOG_INFO, "%s: field too big(%d)", __FUNCTION__, (int)fieldlen);
+ return HA_FAIL;
+ }
+ */
+ tmpsp = sp + netstring_extra(fieldlen);
+ if (tmpsp > smax){
+ cl_log(LOG_ERR, "%s: memory out of boundary, tmpsp=%p, smax=%p",
+ __FUNCTION__, tmpsp, smax);
+ return HA_FAIL;
+ }
+ sp += sprintf(sp , "%d:(%d)%s=", (int)fieldlen, type, name);
+ switch (type){
+
+ case FT_STRING:
+ case FT_BINARY:
+ case FT_COMPRESS:
+ memcpy(sp, value, vallen);
+ slen = vallen;
+ break;
+
+ case FT_UNCOMPRESS:
+ case FT_STRUCT:
+ {
+ struct ha_msg* msg = (struct ha_msg*) value;
+ /* infinite recursion? Must say that I got lost at
+ * this point
+ */
+ ret = msg2netstring_buf(msg, sp,get_netstringlen(msg),
+ &slen);
+ break;
+ }
+ case FT_LIST:
+ {
+
+ char buf[MAXLENGTH];
+ GList* list = NULL;
+ int tmplen;
+
+ list = (GList*) value;
+
+ tmplen = string_list_pack_length(list);
+ if (tmplen >= MAXLENGTH){
+ cl_log(LOG_ERR,
+ "string list length exceeds limit");
+ return(HA_FAIL);
+ }
+
+ if (string_list_pack(list, buf, buf + MAXLENGTH)
+ != tmplen ){
+ cl_log(LOG_ERR,
+ "packing string list return wrong length");
+ return(HA_FAIL);
+ }
+
+
+ memcpy(sp, buf, tmplen);
+ slen = tmplen;
+ ret = HA_OK;
+ break;
+ }
+
+ default:
+ ret = HA_FAIL;
+ cl_log(LOG_ERR, "%s: Wrong type (%d)", __FUNCTION__,type);
+ }
+
+ if (ret == HA_FAIL){
+ return ret;
+ }
+
+ sp +=slen;
+ *sp++ = ',';
+ *comlen = sp - sp_save;
+
+ return HA_OK;
+
+
+}
+
+
+static int
+netstring2string(const void* value, size_t vlen, void** retvalue, size_t* ret_vlen)
+{
+ char* dupvalue;
+
+ if (value == NULL && vlen == 0){
+ *retvalue = NULL;
+ *ret_vlen = 0;
+ return HA_OK;
+ }
+
+ if ( !value || !retvalue || !ret_vlen){
+ cl_log(LOG_ERR, " netstring2string:"
+ "invalid input arguments");
+ return HA_FAIL;
+ }
+
+ dupvalue = binary_dup(value, vlen);
+ if (!dupvalue){
+ cl_log(LOG_ERR, "netstring2string:"
+ "duplicating value failed");
+ return HA_FAIL;
+ }
+
+ *retvalue = dupvalue;
+ *ret_vlen = vlen;
+
+ return HA_OK;
+}
+
+static int
+netstring2binary(const void* value, size_t vlen, void** retvalue, size_t* ret_vlen)
+{
+ return netstring2string(value, vlen, retvalue, ret_vlen);
+
+}
+
+static int
+netstring2struct(const void* value, size_t vlen, void** retvalue, size_t* ret_vlen)
+{
+ struct ha_msg* msg;
+
+ if ( !value || !retvalue || !ret_vlen){
+ cl_log(LOG_ERR, " netstring2struct:"
+ "invalid input arguments");
+ return HA_FAIL;
+ }
+
+ msg = netstring2msg(value, vlen, 0);
+ if (!msg){
+ cl_log(LOG_ERR, "netstring2struct:"
+ "netstring2msg failed");
+ return HA_FAIL;
+ }
+
+ *retvalue =(void* ) msg;
+ *ret_vlen = 0;
+
+ return HA_OK;
+
+}
+
+static int
+netstring2list(const void* value, size_t vlen, void** retvalue, size_t* ret_vlen)
+{
+ GList* list;
+
+ if ( !value || !retvalue || !ret_vlen){
+ cl_log(LOG_ERR, " netstring2struct:"
+ "invalid input arguments");
+ return HA_FAIL;
+ }
+
+
+ list = string_list_unpack(value, vlen);
+ if (list == NULL){
+ cl_log(LOG_ERR, "netstring2list: unpacking string list failed");
+ cl_log(LOG_INFO, "thisbuf=%s", (const char*)value);
+ return HA_FAIL;
+ }
+ *retvalue = (void*)list;
+
+ *ret_vlen = string_list_pack_length(list);
+
+ return HA_OK;
+
+}
+
+
+
+
+
+static int
+add_string_field(struct ha_msg* msg, char* name, size_t namelen,
+ void* value, size_t vallen, int depth)
+{
+
+ size_t internal_type;
+ unsigned long tmptype;
+ char *cp_name = NULL;
+ size_t cp_namelen;
+ size_t cp_vallen;
+ void *cp_value = NULL;
+ int next;
+
+ if ( !msg || !name || !value
+ || namelen <= 0
+ || depth < 0){
+ cl_log(LOG_ERR, "add_string_field:"
+ " invalid input argument");
+ return HA_FAIL;
+ }
+
+
+
+ internal_type = FT_STRING;
+ if (name[0] == '('){
+
+ int nlo = 3; /*name length overhead */
+ if (name[2] != ')'){
+ if (!cl_msg_quiet_fmterr) {
+ cl_log(LOG_ERR
+ , "ha_msg_addraw_ll(): no closing parentheses");
+ }
+ return(HA_FAIL);
+ }
+ tmptype = name[1] - '0';
+ if (tmptype < 0 || tmptype > 9) {
+ cl_log(LOG_ERR
+ , "ha_msg_addraw_ll(): not a number.");
+ return(HA_FAIL);
+ }
+
+ internal_type = tmptype;
+
+ if (internal_type == FT_STRING){
+ cl_log(LOG_ERR
+ , "ha_msg_addraw_ll(): wrong type");
+ return(HA_FAIL);
+ }
+
+ cp_name = name;
+ cp_namelen = namelen - nlo ;
+ memmove(cp_name, name + nlo, namelen - nlo);
+ cp_name[namelen - nlo] = EOS;
+ }else {
+ cp_name = name;
+ cp_namelen = namelen;
+
+ }
+
+ if(internal_type < DIMOF(fieldtypefuncs)){
+ int (*stringtofield)(void*, size_t, int depth, void**, size_t* );
+ int (*fieldstringlen)( size_t, size_t, const void*);
+
+ stringtofield= fieldtypefuncs[internal_type].stringtofield;
+
+ if (!stringtofield || stringtofield(value, vallen, depth, &cp_value, &cp_vallen) != HA_OK){
+ cl_log(LOG_ERR, "add_string_field: stringtofield failed");
+ return HA_FAIL;
+ }
+
+ fieldstringlen = fieldtypefuncs[internal_type].stringlen;
+ if (!fieldstringlen ||
+ fieldstringlen(cp_namelen, cp_vallen, cp_value) <= 0 ){
+
+ cl_log(LOG_ERR, "add_string_field: stringlen failed");
+ return HA_FAIL;
+ }
+
+ } else {
+ cl_log(LOG_ERR, "add_string_field():"
+ " wrong type %lu", (unsigned long)internal_type);
+ return HA_FAIL;
+ }
+
+
+ next = msg->nfields;
+ msg->values[next] = cp_value;
+ msg->vlens[next] = cp_vallen;
+ msg->names[next] = cp_name;
+ msg->nlens[next] = cp_namelen;
+ msg->types[next] = internal_type;
+ msg->nfields++;
+
+ return HA_OK;
+
+}
+
+static int
+uncompress2compress(struct ha_msg* msg, int index)
+{
+ char* buf;
+ size_t buflen = MAXMSG;
+ int rc = HA_FAIL;
+
+ buf = malloc(buflen);
+ if (!buf) {
+ cl_log(LOG_ERR, "%s: failed to allocate buffer",
+ __FUNCTION__);
+ goto err;
+ }
+
+ if (msg->types[index] != FT_UNCOMPRESS){
+ cl_log(LOG_ERR, "%s: the %dth field is not FT_UNCOMPRESS type",
+ __FUNCTION__, index);
+ goto err;
+ }
+
+
+ if (cl_compress_field(msg, index, buf, &buflen) != HA_OK){
+ cl_log(LOG_ERR, "%s: compressing %dth field failed", __FUNCTION__, index);
+ goto err;
+ }
+
+ rc = cl_msg_replace(msg, index, buf, buflen, FT_COMPRESS);
+
+err:
+ if (buf) {
+ free(buf);
+ }
+
+ return rc;
+}
+
+static int
+compress2uncompress(struct ha_msg* msg, int index)
+{
+ char *buf = NULL;
+ size_t buflen = MAXUNCOMPRESSED;
+ struct ha_msg* msgfield;
+ int err = HA_FAIL;
+
+ buf = malloc(buflen);
+
+ if (!buf) {
+ cl_log(LOG_ERR, "%s: allocating buffer for uncompression failed",
+ __FUNCTION__);
+ goto out;
+ }
+
+ if (cl_decompress_field(msg, index, buf, &buflen) != HA_OK){
+ cl_log(LOG_ERR, "%s: compress field failed",
+ __FUNCTION__);
+ goto out;
+ }
+
+ msgfield = wirefmt2msg(buf, buflen, 0);
+ if (msgfield == NULL){
+ cl_log(LOG_ERR, "%s: wirefmt to msg failed",
+ __FUNCTION__);
+ goto out;
+ }
+
+ err = cl_msg_replace(msg, index, (char*)msgfield, 0, FT_UNCOMPRESS);
+
+ ha_msg_del(msgfield);
+
+out:
+ if (buf) {
+ free(buf);
+ }
+
+ return err;
+}
+
+/*
+ * string FT_STRING
+ * string is the basic type used in heartbeat, it is used for printable ascii value
+ *
+ * binary FT_BINARY
+ * binary means the value can be any binary value, including non-printable ascii value
+ *
+ * struct FT_STRUCT
+ * struct means the value is also an ha_msg (actually it is a pointer to an ha message)
+ *
+ * list FT_LIST
+ * LIST means the value is a GList. Right now we only suppport a Glist of strings
+ *
+ * compress FT_COMPRESS
+ * This field and the next one(FT_UNCOMPRESS) is designed to optimize compression in message
+ * (see cl_compression.c for more about compression). This field is similar to the binary field.
+ * It stores a compressed field, which will be an ha_msg if uncompressed. Most of time this field
+ * act like a binary field until compress2uncompress() is called. That function will be called
+ * when someone calls cl_get_struct() to get this field value. After that this field is converted
+ * to a new type FT_UNCOMPRESS
+ *
+ * uncompress FT_UNCOMPRESS
+ * As said above, this field is used to optimize compression. This field is similar to the struct
+ * field. It's value is a pointer to an ha_msg. This field will be converted to a new type FT_COMPRESS
+ * when msg2wirefmt() is called, where uncompress2compress is called to do the field compression
+ */
+
+struct fieldtypefuncs_s fieldtypefuncs[NUM_MSG_TYPES]=
+ { {string_memfree, string_dup, string_display, add_string_field,
+ string_stringlen,string_netstringlen, str2string,fields2netstring,
+ string2str, netstring2string, NULL, NULL},
+
+ {binary_memfree, binary_dup, binary_display, add_binary_field,
+ binary_stringlen,binary_netstringlen, binary2string,fields2netstring,
+ string2binary, netstring2binary, NULL, NULL},
+
+ {struct_memfree, struct_dup, struct_display, add_struct_field,
+ struct_stringlen, struct_netstringlen, struct2string, fields2netstring, \
+ string2struct, netstring2struct, NULL, NULL},
+
+ {list_memfree, list_dup, list_display, add_list_field,
+ list_stringlen, list_netstringlen, list2string, fields2netstring,
+ string2list, netstring2list, NULL, NULL},
+
+ {binary_memfree, binary_dup, compress_display, add_compress_field,
+ binary_stringlen,binary_netstringlen, binary2string ,fields2netstring,
+ string2binary , netstring2binary, NULL, compress2uncompress}, /*FT_COMPRESS*/
+
+ {struct_memfree, struct_dup, uncompress_display, add_uncompress_field,
+ struct_stringlen, struct_netstringlen, NULL , fields2netstring,
+ NULL , netstring2struct, uncompress2compress, NULL}, /*FT_UNCOMPRSS*/
+ };
+
+
diff --git a/lib/clplumbing/cl_netstring.c b/lib/clplumbing/cl_netstring.c
new file mode 100644
index 0000000..f4040e0
--- /dev/null
+++ b/lib/clplumbing/cl_netstring.c
@@ -0,0 +1,570 @@
+/*
+ * netstring implementation
+ *
+ * Copyright (c) 2003 Guochun Shi <gshi@ncsa.uiuc.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <lha_internal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <ha_msg.h>
+#include <unistd.h>
+#include <clplumbing/ipc.h>
+#include <clplumbing/netstring.h>
+#include <clplumbing/base64.h>
+#include <assert.h>
+#include <ctype.h>
+
+/*
+ * Avoid sprintf. Use snprintf instead, even if you count your bytes.
+ * It can detect calculation errors (if used properly)
+ * and will not make the security audit tools crazy.
+ */
+
+#define MAX_AUTH_BYTES 64
+
+
+int msg2netstring_buf(const struct ha_msg*, char*, size_t, size_t*);
+int compose_netstring(char*, const char*, const char*, size_t, size_t*);
+int is_auth_netstring(const char*, size_t, const char*, size_t);
+char* msg2netstring(const struct ha_msg*, size_t*);
+int process_netstring_nvpair(struct ha_msg* m, const char* nvpair, int nvlen);
+extern int bytes_for_int(int x);
+extern const char * FT_strings[];
+
+static int (*authmethod)(int whichauth
+, const void * data
+, size_t datalen
+, char * authstr
+, size_t authlen) = NULL;
+
+void
+cl_set_authentication_computation_method(int (*method)(int whichauth
+, const void * data
+, size_t datalen
+, char * authstr
+, size_t authlen))
+{
+ authmethod = method;
+}
+
+int cl_parse_int(const char *sp, const char *smax, int* len);
+
+int
+cl_parse_int(const char *sp, const char *smax, int* len)
+{
+ char ch = 0;
+ int offset = 0;
+ *len = 0;
+
+ errno = 0;
+ for( ; sp+offset < smax; offset++) {
+ ch = sp[offset] - '0';
+ if(ch > 9) { /* ch >= 0 is implied by the data type*/
+ break;
+ }
+ *len *= 10;
+ *len += ch;
+ }
+
+ if(offset == 0) {
+ cl_log(LOG_ERR,
+ "cl_parse_int: Couldn't parse an int from: %.5s", sp);
+ }
+ return offset;
+}
+
+int
+compose_netstring(char * s, const char * smax, const char* data,
+ size_t len, size_t* comlen)
+{
+
+ char * sp = s;
+
+ /* 2 == ":" + "," */
+ if (s + len + 2 + bytes_for_int(len) > smax) {
+ cl_log(LOG_ERR,
+ "netstring pointer out of boundary(compose_netstring)");
+ return(HA_FAIL);
+ }
+
+ sp += sprintf(sp, "%ld:", (long)len);
+
+ if(data){
+ memcpy(sp, data, len);
+ }
+ sp += len;
+ *sp++ = ',';
+
+ *comlen = sp - s;
+
+ return(HA_OK);
+}
+
+
+
+/* Converts a message into a netstring */
+
+int
+msg2netstring_buf(const struct ha_msg *m, char *s,
+ size_t buflen, size_t * slen)
+{
+ int i;
+ char * sp;
+ char * smax;
+ int ret = HA_OK;
+
+ sp = s;
+ smax = s + buflen;
+
+ strcpy(sp, MSG_START_NETSTRING);
+
+ sp += strlen(MSG_START_NETSTRING);
+
+ for (i=0; i < m->nfields; i++) {
+ size_t flen;
+ int tmplen;
+
+ /* some of these functions in its turn invoke us again */
+ ret = fieldtypefuncs[m->types[i]].tonetstring(sp,
+ smax,
+ m->names[i],
+ m->nlens[i],
+ m->values[i],
+ m->vlens[i],
+ m->types[i],
+ &flen);
+
+ if (ret != HA_OK){
+ cl_log(LOG_ERR, "encoding msg to netstring failed");
+ cl_log_message(LOG_ERR, m);
+ return ret;
+ }
+
+ tmplen = netstring_extra(fieldtypefuncs[m->types[i]].netstringlen(m->nlens[i],
+ m->vlens[i],
+ m->values[i]));
+
+ if (flen != tmplen ){
+ cl_log(LOG_ERR,"netstring len discrepency: actual usage is %d bytes"
+ "it should use %d", (int)flen, tmplen);
+ }
+ sp +=flen;
+
+ }
+
+ if (sp + strlen(MSG_END_NETSTRING) > smax){
+ cl_log(LOG_ERR, "%s: out of boundary for MSG_END_NETSTRING",
+ __FUNCTION__);
+ return HA_FAIL;
+ }
+ strcpy(sp, MSG_END_NETSTRING);
+ sp += sizeof(MSG_END_NETSTRING) -1;
+
+ if (sp > smax){
+ cl_log(LOG_ERR,
+ "msg2netstring: exceed memory boundary sp =%p smax=%p",
+ sp, smax);
+ return(HA_FAIL);
+ }
+
+ *slen = sp - s;
+ return(HA_OK);
+}
+
+
+int get_netstringlen_auth(const struct ha_msg* m);
+
+int get_netstringlen_auth(const struct ha_msg* m)
+{
+ int len = get_netstringlen(m) + MAX_AUTH_BYTES;
+ return len;
+}
+
+
+
+static char *
+msg2netstring_ll(const struct ha_msg *m, size_t * slen, int need_auth)
+{
+ int len;
+ char* s;
+ int authnum;
+ char authtoken[MAXLINE];
+ char authstring[MAXLINE];
+ char* sp;
+ size_t payload_len;
+ char* smax;
+
+ len= get_netstringlen_auth(m) + 1;
+
+ /* use MAXUNCOMPRESSED for the in memory size check */
+ if (len >= MAXUNCOMPRESSED){
+ cl_log(LOG_ERR, "%s: msg is too large; len=%d,"
+ " MAX msg allowed=%d", __FUNCTION__, len, MAXUNCOMPRESSED);
+ return NULL;
+ }
+
+ s = calloc(1, len);
+ if (!s){
+ cl_log(LOG_ERR, "%s: no memory for netstring", __FUNCTION__);
+ return(NULL);
+ }
+
+ smax = s + len;
+
+ if (msg2netstring_buf(m, s, len, &payload_len) != HA_OK){
+ cl_log(LOG_ERR, "%s: msg2netstring_buf() failed", __FUNCTION__);
+ free(s);
+ return(NULL);
+ }
+
+ sp = s + payload_len;
+
+ if ( need_auth && authmethod){
+ int auth_strlen;
+
+ authnum = authmethod(-1, s, payload_len, authtoken,sizeof(authtoken));
+ if (authnum < 0){
+ cl_log(LOG_WARNING
+ , "Cannot compute message authentication!");
+ free(s);
+ return(NULL);
+ }
+
+ sprintf(authstring, "%d %s", authnum, authtoken);
+ auth_strlen = strlen(authstring);
+ if (sp + 2 + auth_strlen + bytes_for_int(auth_strlen) >= smax){
+ cl_log(LOG_ERR, "%s: out of boundary for auth", __FUNCTION__);
+ free(s);
+ return NULL;
+ }
+ sp += sprintf(sp, "%ld:%s,", (long)strlen(authstring), authstring);
+
+ }
+ *slen = sp - s;
+
+ return(s);
+}
+
+char *
+msg2netstring(const struct ha_msg *m, size_t * slen)
+{
+ char* ret;
+ ret = msg2netstring_ll(m, slen, TRUE);
+
+ return ret;
+
+}
+char *
+msg2netstring_noauth(const struct ha_msg *m, size_t * slen)
+{
+ char * ret;
+
+ ret = msg2netstring_ll(m, slen, FALSE);
+
+ return ret;
+}
+
+
+/*
+ * Peel one string off in a netstring
+ */
+
+static int
+peel_netstring(const char * s, const char * smax, int* len,
+ const char ** data, int* parselen )
+{
+ int offset = 0;
+ const char * sp = s;
+
+ if (sp >= smax){
+ return(HA_FAIL);
+ }
+
+ offset = cl_parse_int(sp, smax, len);
+ if (*len < 0 || offset <= 0){
+ cl_log(LOG_ERR, "peel_netstring: Couldn't parse an int starting at: %.5s", sp);
+ return(HA_FAIL);
+ }
+
+ sp = sp+offset;
+ while (*sp != ':' && sp < smax) {
+ sp ++;
+ }
+
+ if (sp >= smax) {
+ return(HA_FAIL);
+ }
+
+ sp ++;
+
+ *data = sp;
+
+ sp += (*len);
+ if (sp >= smax) {
+ return(HA_FAIL);
+ }
+ if (*sp != ','){
+ return(HA_FAIL);
+ }
+ sp++;
+
+ *parselen = sp - s;
+
+ return(HA_OK);
+}
+
+
+int
+process_netstring_nvpair(struct ha_msg* m, const char* nvpair, int nvlen)
+{
+
+ const char *name;
+ int nlen;
+ const char *ns_value;
+ int ns_vlen;
+ void *value;
+ size_t vlen;
+ int type;
+ void (*memfree)(void*);
+ int ret = HA_OK;
+
+ assert(*nvpair == '(');
+ nvpair++;
+
+ type = nvpair[0] - '0';
+ nvpair++;
+
+ /* if this condition is no longer true, change the above to:
+ * nvpair += cl_parse_int(nvpair, nvpair+nvlen, &type)
+ */
+ assert(type >= 0 && type < 10);
+
+ assert(*nvpair == ')');
+ nvpair++;
+
+ if ((nlen = strcspn(nvpair, EQUAL)) <= 0
+ || nvpair[nlen] != '=') {
+ if (!cl_msg_quiet_fmterr) {
+ cl_log(LOG_WARNING
+ , "%s: line doesn't contain '='", __FUNCTION__);
+ cl_log(LOG_INFO, "%s", nvpair);
+ }
+ return(HA_FAIL);
+ }
+
+ name = nvpair;
+ ns_value = name +nlen + 1;
+ ns_vlen = nvpair + nvlen - ns_value -3 ;
+ if (fieldtypefuncs[type].netstringtofield(ns_value,ns_vlen, &value, &vlen) != HA_OK){
+ cl_log(LOG_ERR, "netstringtofield failed in %s", __FUNCTION__);
+ return HA_FAIL;
+
+ }
+
+ memfree = fieldtypefuncs[type].memfree;
+
+ if (ha_msg_nadd_type(m , name, nlen, value, vlen,type)
+ != HA_OK) {
+ cl_log(LOG_ERR, "ha_msg_nadd fails(netstring2msg_rec)");
+ ret = HA_FAIL;
+ }
+
+
+ if (memfree && value){
+ memfree(value);
+ } else{
+ cl_log(LOG_ERR, "netstring2msg_rec:"
+ "memfree or ret_value is NULL");
+ ret= HA_FAIL;
+ }
+
+ return ret;
+
+
+}
+
+
+/* Converts a netstring into a message*/
+static struct ha_msg *
+netstring2msg_rec(const char *s, size_t length, int* slen)
+{
+ struct ha_msg* ret = NULL;
+ const char * sp = s;
+ const char * smax = s + length;
+ int startlen;
+ int endlen;
+
+ if ((ret = ha_msg_new(0)) == NULL){
+ return(NULL);
+ }
+
+ startlen = sizeof(MSG_START_NETSTRING)-1;
+
+ if (strncmp(sp, MSG_START_NETSTRING, startlen) != 0) {
+ /* This can happen if the sender gets killed */
+ /* at just the wrong time... */
+ if (!cl_msg_quiet_fmterr) {
+ cl_log(LOG_WARNING, "netstring2msg_rec: no MSG_START");
+ ha_msg_del(ret);
+ }
+ return(NULL);
+ }else{
+ sp += startlen;
+ }
+
+ endlen = sizeof(MSG_END_NETSTRING) - 1;
+
+ while (sp < smax && strncmp(sp, MSG_END_NETSTRING, endlen) !=0 ){
+
+ const char *nvpair;
+ int nvlen;
+ int parselen;
+
+ if (peel_netstring(sp , smax, &nvlen, &nvpair,&parselen) != HA_OK){
+ cl_log(LOG_ERR
+ , "%s:peel_netstring fails for name/value pair", __FUNCTION__);
+ cl_log(LOG_ERR, "sp=%s", sp);
+ ha_msg_del(ret);
+ return(NULL);
+ }
+ sp += parselen;
+
+ if (process_netstring_nvpair(ret, nvpair, nvlen) != HA_OK){
+ cl_log(LOG_ERR, "%s: processing nvpair failed", __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ }
+
+
+ sp += sizeof(MSG_END_NETSTRING) -1;
+ *slen = sp - s;
+ return(ret);
+
+}
+
+
+struct ha_msg *
+netstring2msg(const char* s, size_t length, int needauth)
+{
+ const char *sp;
+ struct ha_msg *msg;
+ const char *smax = s + length;
+ int parselen;
+ int authlen;
+ const char *authstring;
+ /*actual string length used excluding auth string*/
+ int slen = 0; /* assign to keep compiler happy */
+
+ msg = netstring2msg_rec(s, length, &slen);
+
+ if (needauth == FALSE || !authmethod){
+ goto out;
+ }
+
+ sp = s + slen;
+
+ if (peel_netstring(sp , smax, &authlen, &authstring, &parselen) !=HA_OK){
+ cl_log(LOG_ERR,
+ "peel_netstring() error in getting auth string");
+ cl_log(LOG_ERR, "sp=%s", sp);
+ cl_log(LOG_ERR, "s=%s", s);
+ ha_msg_del(msg);
+ return(NULL);
+ }
+
+ if (sp + parselen > smax){
+ cl_log(LOG_ERR, " netstring2msg: smax passed");
+ ha_msg_del(msg);
+ return NULL;
+ }
+
+ if (!is_auth_netstring(s, slen, authstring,authlen) ){
+ if (!cl_msg_quiet_fmterr) {
+ cl_log(LOG_ERR
+ , "netstring authentication"
+ " failed, s=%s, autotoken=%s"
+ , s, authstring);
+ cl_log_message(LOG_ERR, msg);
+ }
+ ha_msg_del(msg);
+ return(NULL);
+ }
+
+ out:
+ return msg;
+}
+
+
+
+
+int
+is_auth_netstring(const char * datap, size_t datalen,
+ const char * authstring, size_t authlen)
+{
+
+ char authstr[MAXLINE]; /* A copy of authstring */
+ int authwhich;
+ char authtoken[MAXLINE];
+
+
+ /*
+ * If we don't have any authentication method - everything is authentic...
+ */
+ if (!authmethod) {
+ return TRUE;
+ }
+ strncpy(authstr, authstring, MAXLINE);
+ authstr[authlen] = 0;
+ if (sscanf(authstr, "%d %s", &authwhich, authtoken) != 2) {
+ if (!cl_msg_quiet_fmterr) {
+ cl_log(LOG_WARNING
+ , "Bad/invalid netstring auth string");
+ }
+ return(0);
+ }
+
+ memset(authstr, 0, MAXLINE);
+ if (authmethod(authwhich, datap, datalen, authstr, MAXLINE)
+ != authwhich) {
+
+ if (!cl_msg_quiet_fmterr) {
+ cl_log(LOG_WARNING
+ , "Invalid authentication [%d] in message!"
+ , authwhich);
+ }
+ return(FALSE);
+ }
+
+ if (strcmp(authtoken, authstr) == 0) {
+ return(TRUE);
+ }
+
+ if (!cl_msg_quiet_fmterr) {
+ cl_log(LOG_ERR
+ , "authtoken does not match, authtoken=%s, authstr=%s"
+ , authtoken, authstr);
+ }
+ return(FALSE);
+}
+
diff --git a/lib/clplumbing/cl_pidfile.c b/lib/clplumbing/cl_pidfile.c
new file mode 100644
index 0000000..b94573b
--- /dev/null
+++ b/lib/clplumbing/cl_pidfile.c
@@ -0,0 +1,294 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <lha_internal.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <string.h>
+#include <clplumbing/cl_signal.h>
+#include <clplumbing/cl_pidfile.h>
+#include <clplumbing/lsb_exitcodes.h>
+
+/*
+ * The following information is from the Filesystem Hierarchy Standard
+ * version 2.1 dated 12 April, 2000.
+ *
+ * 5.6 /var/lock : Lock files
+ * Lock files should be stored within the /var/lock directory structure.
+ * Device lock files, such as the serial device lock files that were originally
+ * found in either /usr/spool/locks or /usr/spool/uucp, must now be stored in
+ * /var/lock. The naming convention which must be used is LCK.. followed by
+ * the base name of the device file. For example, to lock /dev/cua0 the file
+ * LCK..cua0 would be created.
+ *
+ * The format used for device lock files must be the HDB UUCP lock file format.
+ * The HDB format is to store the process identifier (PID) as a ten byte
+ * ASCII decimal number, with a trailing newline. For example, if process 1230
+ * holds a lock file, it would contain the eleven characters: space, space,
+ * space, space, space, space, one, two, three, zero, and newline.
+ * Then, anything wishing to use /dev/cua0 can read the lock file and act
+ * accordingly (all locks in /var/lock should be world-readable).
+ *
+ *
+ * PERMISSIONS NOTE:
+ * Different linux distributions set the mode of the lock directory differently
+ * Any process which wants to create lock files must have write permissions
+ * on FILE_LOCK_D (probably /var/lock). For things like the heartbeat API
+ * code, this may mean allowing the uid of the processes that use this API
+ * to join group uucp, or making the binaries setgid to uucp.
+ */
+
+/* The code in this file originally written by Guenther Thomsen */
+/* Somewhat mangled by Alan Robertson */
+
+/*
+ * Lock a tty (using lock files, see linux `man 2 open` close to O_EXCL)
+ * serial_device has to be _the complete path_, i.e. including '/dev/' to the
+ * special file, which denotes the tty to lock -tho
+ * return 0 on success,
+ * -1 if device is locked (lockfile exists and isn't stale),
+ * -2 for temporarily failure, try again,
+ * other negative value, if something unexpected happend (failure anyway)
+ */
+
+
+/* This is what the FHS standard specifies for the size of our lock file */
+#define LOCKSTRLEN 11
+#include <clplumbing/cl_log.h>
+static int IsRunning(long pid)
+{
+ int rc = 0;
+ long mypid;
+ int running = 0;
+ char proc_path[PATH_MAX], exe_path[PATH_MAX], myexe_path[PATH_MAX];
+
+ /* check if pid is running */
+ if (CL_KILL(pid, 0) < 0 && errno == ESRCH) {
+ goto bail;
+ }
+
+#ifndef HAVE_PROC_PID
+ return 1;
+#endif
+
+ /* check to make sure pid hasn't been reused by another process */
+ snprintf(proc_path, sizeof(proc_path), "/proc/%lu/exe", pid);
+
+ rc = readlink(proc_path, exe_path, PATH_MAX-1);
+ if(rc < 0) {
+ cl_perror("Could not read from %s", proc_path);
+ goto bail;
+ }
+ exe_path[rc] = 0;
+
+ mypid = (unsigned long) getpid();
+
+ snprintf(proc_path, sizeof(proc_path), "/proc/%lu/exe", mypid);
+ rc = readlink(proc_path, myexe_path, PATH_MAX-1);
+ if(rc < 0) {
+ cl_perror("Could not read from %s", proc_path);
+ goto bail;
+ }
+ myexe_path[rc] = 0;
+
+ if(strcmp(exe_path, myexe_path) == 0) {
+ running = 1;
+ }
+
+ bail:
+ return running;
+}
+
+static int
+DoLock(const char *filename)
+{
+ char lf_name[256], tf_name[256], buf[LOCKSTRLEN+1];
+ int fd;
+ long pid, mypid;
+ int rc;
+ struct stat sbuf;
+
+ mypid = (unsigned long) getpid();
+
+ snprintf(lf_name, sizeof(lf_name), "%s",filename);
+
+ snprintf(tf_name, sizeof(tf_name), "%s.%lu",
+ filename, mypid);
+
+ if ((fd = open(lf_name, O_RDONLY)) >= 0) {
+ if (fstat(fd, &sbuf) >= 0 && sbuf.st_size < LOCKSTRLEN) {
+ sleep(1); /* if someone was about to create one,
+ * give'm a sec to do so
+ * Though if they follow our protocol,
+ * this won't happen. They should really
+ * put the pid in, then link, not the
+ * other way around.
+ */
+ }
+ if (read(fd, buf, sizeof(buf)) < 1) {
+ /* lockfile empty -> rm it and go on */;
+ } else {
+ if (sscanf(buf, "%lu", &pid) < 1) {
+ /* lockfile screwed up -> rm it and go on */
+ } else {
+ if (pid > 1 && (getpid() != pid)
+ && IsRunning(pid)) {
+ /* is locked by existing process
+ * -> give up */
+ close(fd);
+ return -1;
+ } else {
+ /* stale lockfile -> rm it and go on */
+ }
+ }
+ }
+ unlink(lf_name);
+ close(fd);
+ }
+ if ((fd = open(tf_name, O_CREAT | O_WRONLY | O_EXCL, 0644)) < 0) {
+ /* Hmmh, why did we fail? Anyway, nothing we can do about it */
+ return -3;
+ }
+
+ /* Slight overkill with the %*d format ;-) */
+ snprintf(buf, sizeof(buf), "%*lu\n", LOCKSTRLEN-1, mypid);
+
+ if (write(fd, buf, LOCKSTRLEN) != LOCKSTRLEN) {
+ /* Again, nothing we can do about this */
+ rc = -3;
+ close(fd);
+ goto out;
+ }
+ close(fd);
+
+ switch (link(tf_name, lf_name)) {
+ case 0:
+ if (stat(tf_name, &sbuf) < 0) {
+ /* something weird happened */
+ rc = -3;
+ break;
+ }
+ if (sbuf.st_nlink < 2) {
+ /* somehow, it didn't get through - NFS trouble? */
+ rc = -2;
+ break;
+ }
+ rc = 0;
+ break;
+ case EEXIST:
+ rc = -1;
+ break;
+ default:
+ rc = -3;
+ }
+ out:
+ unlink(tf_name);
+ return rc;
+}
+
+static int
+DoUnlock(const char * filename)
+{
+ char lf_name[256];
+
+ snprintf(lf_name, sizeof(lf_name), "%s", filename);
+
+ return unlink(lf_name);
+}
+
+
+int
+cl_read_pidfile(const char*filename)
+{
+ long pid = 0;
+
+ pid = cl_read_pidfile_no_checking(filename);
+
+ if (pid < 0){
+ return - LSB_STATUS_STOPPED;
+ }
+
+ if (IsRunning(pid)){
+ return pid;
+ }else{
+ return -LSB_STATUS_VAR_PID;
+ }
+}
+
+
+int
+cl_read_pidfile_no_checking(const char*filename)
+{
+ int fd;
+ long pid = 0;
+ char buf[LOCKSTRLEN+1];
+ if ((fd = open(filename, O_RDONLY)) < 0) {
+ return -1;
+ }
+
+ if (read(fd, buf, sizeof(buf)) < 1) {
+ close(fd);
+ return -1;
+ }
+
+ if (sscanf(buf, "%lu", &pid) <= 0) {
+ close(fd);
+ return -1;
+ }
+
+ if (pid <= 0){
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ return pid;
+}
+
+
+int
+cl_lock_pidfile(const char *filename)
+{
+ if (filename == NULL) {
+ errno = EFAULT;
+ return -3;
+ }
+ return DoLock(filename);
+}
+
+/*
+ * Unlock a file (remove its lockfile)
+ * do we need to check, if its (still) ours? No, IMHO, if someone else
+ * locked our line, it's his fault -tho
+ * returns 0 on success
+ * <0 if some failure occured
+ */
+
+int
+cl_unlock_pidfile(const char *filename)
+{
+ if (filename == NULL) {
+ errno = EFAULT;
+ return -3;
+ }
+
+ return(DoUnlock(filename));
+}
diff --git a/lib/clplumbing/cl_plugin.c b/lib/clplumbing/cl_plugin.c
new file mode 100644
index 0000000..c039a35
--- /dev/null
+++ b/lib/clplumbing/cl_plugin.c
@@ -0,0 +1,140 @@
+
+/*
+ * cl_plugin.c: This file handle plugin loading and deleting
+ *
+ * Copyright (C) 2005 Guochun Shi <gshi@ncsa.uiuc.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <lha_internal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <assert.h>
+#include <glib.h>
+#include <ha_msg.h>
+#include <clplumbing/netstring.h>
+#include <pils/plugin.h>
+#include <pils/generic.h>
+/* #include <stonith/stonith.h> */
+/* #include <stonith/stonith_plugin.h> */
+#include <clplumbing/cl_plugin.h>
+
+#define MAXTYPES 16
+#define MAXTYPELEN 64
+
+static GHashTable* funcstable[MAXTYPES];
+
+static PILPluginUniv* plugin_univ = NULL;
+
+static PILGenericIfMgmtRqst reqs[] =
+ {
+ {"compress", &funcstable[0], NULL, NULL, NULL},
+ {"HBcoms", &funcstable[1], NULL, NULL, NULL},
+ {"HBauth", &funcstable[2], NULL, NULL, NULL},
+ {"RAExec", &funcstable[3], NULL, NULL, NULL},
+ {"quorum", &funcstable[4], NULL, NULL, NULL},
+ {"tiebreaker", &funcstable[5], NULL, NULL, NULL},
+ {"quorumd", &funcstable[6], NULL, NULL, NULL},
+ {NULL, NULL, NULL, NULL, NULL}
+ };
+
+static int
+init_pluginsys(void){
+
+ if (plugin_univ) {
+ return TRUE;
+ }
+
+ plugin_univ = NewPILPluginUniv(HA_PLUGIN_DIR);
+
+ if (plugin_univ) {
+ if (PILLoadPlugin(plugin_univ, PI_IFMANAGER, "generic", reqs)
+ != PIL_OK){
+ cl_log(LOG_ERR, "generic plugin load failed\n");
+ DelPILPluginUniv(plugin_univ);
+ plugin_univ = NULL;
+ }
+ }else{
+ cl_log(LOG_ERR, "pi univ creation failed\n");
+ }
+ return plugin_univ != NULL;
+
+}
+
+int
+cl_remove_plugin(const char* type, const char* pluginname)
+{
+ return HA_OK;
+}
+
+void*
+cl_load_plugin(const char* type, const char* pluginname)
+{
+ void* funcs = NULL;
+ int i = 0;
+ GHashTable** table = NULL;
+
+ while (reqs[i].iftype != NULL){
+ if ( strcmp(reqs[i].iftype,type) != 0){
+ i++;
+ continue;
+ }
+
+ table = reqs[i].ifmap;
+ break;
+ }
+
+ if (table == NULL){
+ cl_log(LOG_ERR, "%s: function table not found",__FUNCTION__);
+ return NULL;
+ }
+
+ if (!init_pluginsys()){
+ cl_log(LOG_ERR, "%s: init plugin universe failed", __FUNCTION__);
+ return NULL;
+ }
+
+ if ((funcs = g_hash_table_lookup(*table, pluginname))
+ == NULL){
+ if (PILPluginExists(plugin_univ, type, pluginname) == PIL_OK){
+ PIL_rc rc;
+ rc = PILLoadPlugin(plugin_univ, type, pluginname, NULL);
+ if (rc != PIL_OK){
+ cl_log(LOG_ERR,
+ "Cannot load plugin %s[%s]",
+ pluginname,
+ PIL_strerror(rc));
+ return NULL;
+ }
+ funcs = g_hash_table_lookup(*table,
+ pluginname);
+ }
+
+ }
+ if (funcs == NULL){
+ cl_log(LOG_ERR, "%s: module(%s) not found",
+ __FUNCTION__, pluginname);
+ return NULL;
+ }
+
+ return funcs;
+
+}
+
diff --git a/lib/clplumbing/cl_poll.c b/lib/clplumbing/cl_poll.c
new file mode 100644
index 0000000..789eb1a
--- /dev/null
+++ b/lib/clplumbing/cl_poll.c
@@ -0,0 +1,809 @@
+#include <lha_internal.h>
+#include <stdlib.h>
+#include <unistd.h>
+/*
+ * Substitute poll(2) function using POSIX real time signals.
+ *
+ * The poll(2) system call often has significant latencies and realtime
+ * impacts (probably because of its variable length argument list).
+ *
+ * These functions let us use real time signals and sigtimedwait(2) instead
+ * of poll - for those files which work with real time signals.
+ * In the 2.4 series of Linux kernels, this does *not* include FIFOs.
+ *
+ * NOTE: We (have to) grab the SIGPOLL signal for our own purposes.
+ * Hope that's OK with you...
+ *
+ * Special caution: We can only incompletely simulate the difference between
+ * the level-triggered interface of poll(2) and the edge-triggered behavior
+ * of I/O signals. As a result you *must* read all previously-indicated
+ * incoming data before calling cl_poll() again. Otherwise you may miss
+ * some incoming data (and possibly hang).
+ *
+ *
+ * Copyright (C) 2003 IBM Corporation
+ *
+ * Author: <alanr@unix.sh>
+ *
+ * This software licensed under the GNU LGPL.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ **************************************************************************/
+
+
+#define __USE_GNU 1
+# include <fcntl.h>
+#undef __USE_GNU
+
+#include <errno.h>
+#include <string.h>
+#include <glib.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/cl_poll.h>
+#include <clplumbing/cl_signal.h>
+
+
+
+/* Turn on to log odd realtime behavior */
+
+#define TIME_CALLS 1
+#ifdef TIME_CALLS
+# include <clplumbing/longclock.h>
+# include <clplumbing/cl_log.h>
+#endif
+
+static int debug = 0;
+
+int /* Slightly sleazy... */
+cl_glibpoll(GPollFD* ufds, guint nfsd, gint timeout)
+{
+ (void)debug;
+ return cl_poll((struct pollfd*)ufds, nfsd, timeout);
+}
+
+#if defined (F_SETSIG) && defined(F_SETOWN) && defined (O_ASYNC)
+# define HAVE_FCNTL_F_SETSIG
+#endif
+
+#ifndef HAVE_FCNTL_F_SETSIG
+
+/*
+ * Dummy cl_poll() and cl_poll_ignore() functions for systems where
+ * we don't have all the support we need.
+ */
+
+int
+cl_poll(struct pollfd *fds, unsigned int nfds, int timeout)
+{
+ return poll(fds, (nfds_t)nfds, timeout);
+}
+
+int
+cl_poll_ignore(int fd)
+{
+ return 0;
+}
+
+#else /* HAVE_FCNTL_F_SETSIG */
+static void dump_fd_info(struct pollfd *fds, unsigned int nfds, int timeoutms);
+static void check_fd_info(struct pollfd *fds, unsigned int nfds);
+static void cl_real_poll_fd(int fd);
+static void cl_poll_sigpoll_overflow_sigaction(int nsig, siginfo_t* , void*);
+static void cl_poll_sigpoll_overflow(void);
+static int cl_poll_get_sigqlimit(void);
+typedef unsigned char poll_bool;
+
+/*
+ * Here's our strategy:
+ * We have a set of signals which we use for these file descriptors,
+ * and we use sigtimedwait(2) to wait on information from these various
+ * signals.
+ *
+ * If we are ever asked to wait for a particular signal, then we will
+ * enable signals for that file descriptor, and post the events in
+ * our own cache. The next time you include that signal in a call
+ * to cl_poll(), you will get the information delivered
+ * to you in your cl_poll() call.
+ *
+ * If you want to stop monitoring a particular file descriptor, use
+ * cl_poll_ignore() for that purpose. Doing this is a good idea, but
+ * not fatal if omitted...
+ */
+
+/* Information about a file descriptor we're monitoring */
+
+typedef struct poll_fd_info_s {
+ short nsig; /* Which signal goes with it? */
+ short pendevents; /* Pending events */
+}poll_info_t;
+
+static int max_allocated = 0;
+static poll_bool* is_monitored = NULL; /* Sized by max_allocated */
+static poll_info_t* monitorinfo = NULL; /* Sized by max_allocated */
+static int cl_nsig = 0;
+static gboolean SigQOverflow = FALSE;
+
+static int cl_init_poll_sig(struct pollfd *fds, unsigned int nfds);
+static short cl_poll_assignsig(int fd);
+static void cl_poll_sigaction(int nsig, siginfo_t* info, void* v);
+static int cl_poll_prepsig(int nsig);
+
+
+/*
+ * SignalSet is the set of all file descriptors we're monitoring.
+ *
+ * We monitor a file descriptor forever, unless you tell us not to
+ * by calling cl_poll_ignore(), or you (mistakenly) give it to
+ * us to look at in another poll call after you've closed it.
+ */
+
+static sigset_t SignalSet;
+
+/* Select the signal you want us to use (must be a RT signal) */
+int
+cl_poll_setsig(int nsig)
+{
+ if (nsig < SIGRTMIN || nsig >= SIGRTMAX) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (cl_poll_prepsig(nsig) < 0) {
+ return -1;
+ }
+ cl_nsig = nsig;
+ return 0;
+}
+
+/*
+ * It's harmless to call us multiple times on the same signal.
+ */
+static int
+cl_poll_prepsig(int nsig)
+{
+ static gboolean setinityet=FALSE;
+
+ if (!setinityet) {
+ CL_SIGEMPTYSET(&SignalSet);
+ cl_signal_set_simple_action(SIGPOLL
+ , cl_poll_sigpoll_overflow_sigaction
+ , NULL);
+ setinityet = TRUE;
+ }
+ if (CL_SIGINTERRUPT(nsig, FALSE) < 0) {
+ cl_perror("sig_interrupt(%d, FALSE)", nsig);
+ return -1;
+ }
+ if (CL_SIGADDSET(&SignalSet, nsig) < 0) {
+ cl_perror("sig_addset(&SignalSet, %d)", nsig);
+ return -1;
+ }
+ if (CL_SIGPROCMASK(SIG_BLOCK, &SignalSet, NULL) < 0) {
+ cl_perror("sig_sigprocmask(SIG_BLOCK, sig %d)", nsig);
+ return -1;
+ }
+ if (debug) {
+ cl_log(LOG_DEBUG
+ , "Signal %d belongs to us...", nsig);
+ cl_log(LOG_DEBUG, "cl_poll_prepsig(%d) succeeded.", nsig);
+ }
+
+ return 0;
+}
+
+#define FD_CHUNKSIZE 64
+
+/* Set of events everyone must monitor whether they want to or not ;-) */
+#define CONSTEVENTS (POLLHUP|POLLERR|POLLNVAL)
+
+#define RECORDFDEVENT(fd, flags) (monitorinfo[fd].pendevents |= (flags))
+
+/*
+ * Initialized our poll-simulation data structures.
+ * This means (among other things) registering any monitored
+ * file descriptors.
+ */
+static int
+cl_init_poll_sig(struct pollfd *fds, unsigned int nfds)
+{
+ unsigned j;
+ int maxmonfd = -1;
+ int nmatch = 0;
+
+
+ if (cl_nsig == 0) {
+ cl_nsig = ((SIGRTMIN+SIGRTMAX)/2);
+ if (cl_poll_setsig(cl_nsig) < 0) {
+ return -1;
+ }
+ }
+ for (j=0; j < nfds; ++j) {
+ const int fd = fds[j].fd;
+
+ if (fd > maxmonfd) {
+ maxmonfd = fd;
+ }
+ }
+
+ /* See if we need to malloc/realloc our data structures */
+
+ if (maxmonfd >= max_allocated) {
+ int newsize;
+ int growthamount;
+
+ newsize = ((maxmonfd + FD_CHUNKSIZE)/FD_CHUNKSIZE)
+ * FD_CHUNKSIZE;
+ growthamount = newsize - max_allocated;
+
+ /* This can't happen ;-) */
+ if (growthamount <= 0 || newsize <= maxmonfd) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Allocate (more) memory! */
+
+ if ((is_monitored = (poll_bool*)realloc(is_monitored
+ , newsize * sizeof(poll_bool))) == NULL
+ || (monitorinfo = (poll_info_t*) realloc(monitorinfo
+ , newsize * sizeof(poll_info_t))) == NULL) {
+
+ if (is_monitored) {
+ free(is_monitored);
+ is_monitored = NULL;
+ }
+ if (monitorinfo) {
+ free(monitorinfo);
+ monitorinfo = NULL;
+ }
+ max_allocated = 0;
+ errno = ENOMEM;
+ return -1;
+ }
+ memset(monitorinfo+max_allocated, 0
+ , growthamount * sizeof(monitorinfo[0]));
+ memset(is_monitored+max_allocated, FALSE
+ , growthamount*sizeof(is_monitored[0]));
+ max_allocated = newsize;
+ }
+
+ if (fds->events != 0 && debug) {
+ cl_log(LOG_DEBUG
+ , "Current event mask for fd [0] {%d} 0x%x"
+ , fds->fd, fds->events);
+ }
+ /*
+ * Examine each fd for the following things:
+ * Is it already monitored?
+ * if not, set it up for monitoring.
+ * Do we have events for it?
+ * if so, post events...
+ */
+
+ for (j=0; j < nfds; ++j) {
+ const int fd = fds[j].fd;
+ poll_info_t* moni = monitorinfo+fd;
+ short nsig;
+ int badfd = FALSE;
+
+ is_monitored[fd] = TRUE;
+
+ if (moni->nsig <= 0) {
+ nsig = cl_poll_assignsig(fd);
+ if (nsig < 0) {
+ RECORDFDEVENT(fd, POLLERR);
+ badfd = TRUE;
+ }else{
+ /* Use real poll(2) to get initial
+ * event status
+ */
+ moni->nsig = nsig;
+ cl_real_poll_fd(fd);
+ }
+ }else if (fcntl(fd, F_GETFD) < 0) {
+ cl_log(LOG_ERR, "bad fd(%d)", fd);
+ RECORDFDEVENT(fd, POLLNVAL);
+ badfd = TRUE;
+ }
+
+ /* Look for pending events... */
+
+ fds[j].revents = (moni->pendevents
+ & (fds[j].events|CONSTEVENTS));
+
+ if (fds[j].revents) {
+ ++nmatch;
+ moni->pendevents &= ~(fds[j].revents);
+ if (debug) {
+ cl_log(LOG_DEBUG
+ , "revents for fd %d: 0x%x"
+ , fds[j].fd, fds[j].revents);
+ cl_log(LOG_DEBUG
+ , "events for fd %d: 0x%x"
+ , fds[j].fd, fds[j].events);
+ }
+ }else if (fds[j].events && debug) {
+ cl_log(LOG_DEBUG
+ , "pendevents for fd %d: 0x%x"
+ , fds[j].fd, moni->pendevents);
+ }
+ if (badfd) {
+ cl_poll_ignore(fd);
+ }
+ }
+ if (nmatch != 0 && debug) {
+ cl_log(LOG_DEBUG, "Returning %d events from cl_init_poll_sig()"
+ , nmatch);
+ }
+ return nmatch;
+}
+
+/*
+ * Initialize our current state of the world with info from the
+ * real poll(2) call.
+ *
+ * We call this when we first see a particular fd, and after a signal
+ * queue overflow.
+ */
+static void
+cl_real_poll_fd(int fd)
+{
+ struct pollfd pfd[1];
+
+ if (fd >= max_allocated || !is_monitored[fd]) {
+ return;
+ }
+
+ if (debug) {
+ cl_log(LOG_DEBUG
+ , "Calling poll(2) on fd %d", fd);
+ }
+ /* Get the current state of affaris from poll(2) */
+ pfd[0].fd = fd;
+ pfd[0].revents = 0;
+ pfd[0].events = ~0;
+ if (poll(pfd, 1, 0) >= 0) {
+ RECORDFDEVENT(fd, pfd[0].revents);
+ if (pfd[0].revents & (POLLNVAL|POLLERR)) {
+ cl_log(LOG_INFO, "cl_poll_real_fd(%d): error in revents [%d]"
+ , fd, pfd[0].revents);
+ }
+ if (debug) {
+ cl_log(LOG_DEBUG
+ , "Old news from poll(2) for fd %d: 0x%x"
+ , fd, pfd[0].revents);
+ }
+ }else{
+ if (fcntl(fd, F_GETFL) < 0) {
+ cl_perror("cl_poll_real_fd(%d): F_GETFL failure"
+ , fd);
+ RECORDFDEVENT(fd, POLLNVAL);
+ }else{
+ RECORDFDEVENT(fd, POLLERR);
+ }
+ }
+}
+
+/*
+ * Assign a signal for monitoring the given file descriptor
+ */
+
+static short
+cl_poll_assignsig(int fd)
+{
+ int flags;
+
+
+ if (debug) {
+ cl_log(LOG_DEBUG
+ , "Signal %d monitors fd %d...", cl_nsig, fd);
+ }
+
+ /* Test to see if the file descriptor is good */
+ if ((flags = fcntl(fd, F_GETFL)) < 0) {
+ cl_perror("cl_poll_assignsig(%d) F_GETFL failure"
+ , fd);
+ return -1;
+ }
+
+ /* Associate the right signal with the fd */
+
+ if (fcntl(fd, F_SETSIG, cl_nsig) < 0) {
+ cl_perror("cl_poll_assignsig(%d) F_SETSIG failure"
+ , fd);
+ return -1;
+ }
+
+ /* Direct the signals to us */
+ if (fcntl(fd, F_SETOWN, getpid()) < 0) {
+ cl_perror("cl_poll_assignsig(%d) F_SETOWN failure", fd);
+ return -1;
+ }
+
+ /* OK... Go ahead and send us signals! */
+
+ if (fcntl(fd, F_SETFL, flags|O_ASYNC) < 0) {
+ cl_perror("cl_poll_assignsig(%d) F_SETFL(O_ASYNC) failure"
+ , fd);
+ return -1;
+ }
+
+ return cl_nsig;
+}
+
+
+/*
+ * This is a function we call as a (fake) signal handler.
+ *
+ * It records events to our "monitorinfo" structure.
+ *
+ * Except for the cl_log() call, it could be called in a signal
+ * context.
+ */
+
+static void
+cl_poll_sigaction(int nsig, siginfo_t* info, void* v)
+{
+ int fd;
+
+ /* What do you suppose all the various si_code values mean? */
+
+ fd = info->si_fd;
+ if (debug) {
+ cl_log(LOG_DEBUG
+ , "cl_poll_sigaction(nsig=%d fd=%d"
+ ", si_code=%d si_band=0x%lx)"
+ , nsig, fd, info->si_code
+ , (unsigned long)info->si_band);
+ }
+
+ if (fd <= 0) {
+ return;
+ }
+
+
+ if (fd >= max_allocated || !is_monitored[fd]) {
+ return;
+ }
+
+ /* We should not call logging functions in (real) signal handlers */
+ if (nsig != monitorinfo[fd].nsig) {
+ cl_log(LOG_ERR, "cl_poll_sigaction called with signal %d/%d"
+ , nsig, monitorinfo[fd].nsig);
+ }
+
+ /* Record everything as a pending event. */
+ RECORDFDEVENT(fd, info->si_band);
+}
+
+
+
+/*
+ * This is called whenever a file descriptor shouldn't be
+ * monitored any more.
+ */
+int
+cl_poll_ignore(int fd)
+{
+ int flags;
+
+ if (debug) {
+ cl_log(LOG_DEBUG
+ , "cl_poll_ignore(%d)", fd);
+ }
+ if (fd < 0 || fd >= max_allocated) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (!is_monitored[fd]) {
+ return 0;
+ }
+
+ is_monitored[fd] = FALSE;
+ memset(monitorinfo+fd, 0, sizeof(monitorinfo[0]));
+
+ if ((flags = fcntl(fd, F_GETFL)) >= 0) {
+ flags &= ~O_ASYNC;
+ if (fcntl(fd, F_SETFL, flags) < 0) {
+ return -1;
+ }
+ }else{
+ return flags;
+ }
+ return 0;
+}
+
+
+/*
+ * cl_poll: fake poll routine based on POSIX realtime signals.
+ *
+ * We want to emulate poll as exactly as possible, but poll has a couple
+ * of problems: scaleability, and it tends to sleep in the kernel
+ * because the first argument is an argument of arbitrary size, and
+ * generally requires allocating memory.
+ *
+ * The challenge is that poll is level-triggered, but the POSIX
+ * signals (and sigtimedwait(2)) are edge triggered. This is
+ * one of the reasons why we have the cl_real_poll_fd() function
+ * - to get the current "level" before we start.
+ * Once we have this level we can compute something like the current
+ * level
+ */
+
+int
+cl_poll(struct pollfd *fds, unsigned int nfds, int timeoutms)
+{
+ int nready;
+ struct timespec ts;
+ static const struct timespec zerotime = {0L, 0L};
+ const struct timespec* itertime = &ts;
+ siginfo_t info;
+ int eventcount = 0;
+ unsigned int j;
+ int savederrno = errno;
+ int stw_errno;
+ int rc;
+ longclock_t starttime;
+ longclock_t endtime;
+ const int msfudge
+ = 2* 1000/hz_longclock();
+ int mselapsed = 0;
+
+ /* Do we have any old news to report? */
+ if ((nready=cl_init_poll_sig(fds, nfds)) != 0) {
+ /* Return error or old news to report */
+ if (debug) {
+ cl_log(LOG_DEBUG, "cl_poll: early return(%d)", nready);
+ }
+ return nready;
+ }
+
+ /* Nothing to report yet... */
+
+ /* So, we'll do a sigtimedwait(2) to wait for signals
+ * and see if we can find something to report...
+ *
+ * cl_init_poll() prepared a set of file signals to watch...
+ */
+
+recalcandwaitagain:
+ if (timeoutms >= 0) {
+ ts.tv_sec = timeoutms / 1000;
+ ts.tv_nsec = (((unsigned long)timeoutms) % 1000UL)*1000000UL;
+ }else{
+ ts.tv_sec = G_MAXLONG;
+ ts.tv_nsec = 99999999UL;
+ }
+
+ /*
+ * Perform a timed wait for any of our signals...
+ *
+ * We shouldn't sleep for any call but (possibly) the first one.
+ * Subsequent calls should just pick up other events without
+ * sleeping.
+ */
+
+ starttime = time_longclock();
+ /*
+ * Wait up to the prescribed time for a signal.
+ * If we get a signal, then loop grabbing all other
+ * pending signals. Note that subsequent iterations will
+ * use &zerotime to get the minimum wait time.
+ */
+ if (debug) {
+ check_fd_info(fds, nfds);
+ dump_fd_info(fds, nfds, timeoutms);
+ }
+waitagain:
+ while (sigtimedwait(&SignalSet, &info, itertime) >= 0) {
+ int nsig = info.si_signo;
+
+ /* Call signal handler to simulate signal reception */
+
+ cl_poll_sigaction(nsig, &info, NULL);
+ itertime = &zerotime;
+ }
+ stw_errno=errno; /* Save errno for later use */
+ endtime = time_longclock();
+ mselapsed = longclockto_ms(sub_longclock(endtime, starttime));
+
+#ifdef TIME_CALLS
+ if (timeoutms >= 0 && mselapsed > timeoutms + msfudge) {
+ /* We slept too long... */
+ cl_log(LOG_WARNING
+ , "sigtimedwait() sequence for %d ms took %d ms"
+ , timeoutms, mselapsed);
+ }
+#endif
+
+ if (SigQOverflow) {
+ /* OOPS! Better recover from this! */
+ /* This will use poll(2) to correct our current status */
+ cl_poll_sigpoll_overflow();
+ }
+
+ /* Post observed events and count them... */
+
+ for (j=0; j < nfds; ++j) {
+ int fd = fds[j].fd;
+ poll_info_t* moni = monitorinfo+fd;
+ fds[j].revents = (moni->pendevents
+ & (fds[j].events|CONSTEVENTS));
+ if (fds[j].revents) {
+ ++eventcount;
+ moni->pendevents &= ~(fds[j].revents);
+ /* Make POLLHUP persistent */
+ if (fds[j].revents & POLLHUP) {
+ moni->pendevents |= POLLHUP;
+ /* Don't lose input events at EOF */
+ if (fds[j].events & POLLIN) {
+ cl_real_poll_fd(fds[j].fd);
+ }
+ }
+ }
+ }
+ if (eventcount == 0 && stw_errno == EAGAIN && timeoutms != 0) {
+ /* We probably saw an event the user didn't ask to see. */
+ /* Consquently, we may have more waiting to do */
+ if (timeoutms < 0) {
+ /* Restore our infinite wait time */
+ itertime = &ts;
+ goto waitagain;
+ }else if (timeoutms > 0) {
+ if (mselapsed < timeoutms) {
+ timeoutms -= mselapsed;
+ goto recalcandwaitagain;
+ }
+ }
+ }
+ rc = (eventcount > 0 ? eventcount : (stw_errno == EAGAIN ? 0 : -1));
+
+ if (rc >= 0) {
+ errno = savederrno;
+ }
+ return rc;
+}
+/*
+ * Debugging routine for printing current poll arguments, etc.
+ */
+static void
+dump_fd_info(struct pollfd *fds, unsigned int nfds, int timeoutms)
+{
+ unsigned j;
+
+ cl_log(LOG_DEBUG, "timeout: %d milliseconds", timeoutms);
+ for (j=0; j < nfds; ++j) {
+ int fd = fds[j].fd;
+ poll_info_t* moni = monitorinfo+fd;
+
+ cl_log(LOG_DEBUG, "fd %d flags: 0%o, signal: %d, events: 0x%x"
+ ", revents: 0x%x, pendevents: 0x%x"
+ , fd, fcntl(fd, F_GETFL), moni->nsig
+ , fds[j].events, fds[j].revents, moni->pendevents);
+ }
+ for (j=SIGRTMIN; j < (unsigned)SIGRTMAX; ++j) {
+ if (!sigismember(&SignalSet, j)) {
+ continue;
+ }
+ cl_log(LOG_DEBUG, "Currently monitoring RT signal %d", j);
+ }
+}
+
+/*
+ * Debugging routine for auditing our file descriptors, etc.
+ */
+static void
+check_fd_info(struct pollfd *fds, unsigned int nfds)
+{
+ unsigned j;
+
+ for (j=0; j < nfds; ++j) {
+ int fd = fds[j].fd;
+ poll_info_t* moni = monitorinfo+fd;
+
+ if (!sigismember(&SignalSet, moni->nsig)) {
+ cl_log(LOG_ERR, "SIGNAL %d not in monitored SignalSet"
+ , moni->nsig);
+ }
+ }
+ for (j=0; j < 10; ++j) {
+ int sig;
+ int flags;
+ int pid;
+ if ((flags = fcntl(j, F_GETFL)) < 0 || (flags & O_ASYNC) ==0){
+ continue;
+ }
+ sig = fcntl(j, F_GETSIG);
+ if (sig == 0) {
+ cl_log(LOG_ERR, "FD %d will get SIGIO", j);
+ }
+ if (!sigismember(&SignalSet, sig)) {
+ cl_log(LOG_ERR, "FD %d (signal %d) is not in SignalSet"
+ , j, sig);
+ }
+ if (sig < SIGRTMIN || sig >= SIGRTMAX) {
+ cl_log(LOG_ERR, "FD %d (signal %d) is not RealTime"
+ , j, sig);
+ }
+ pid = fcntl(j, F_GETOWN);
+ if (pid != getpid()) {
+ cl_log(LOG_ERR, "FD %d (signal %d) owner is pid %d"
+ , j, sig, pid);
+ }
+ }
+}
+
+/* Note that the kernel signalled an event queue overflow */
+static void
+cl_poll_sigpoll_overflow_sigaction(int nsig, siginfo_t* info, void* v)
+{
+ SigQOverflow = TRUE;
+}
+
+#define MAXQNAME "rtsig-max"
+/*
+ * Called when signal queue overflow is suspected.
+ * We then use poll(2) to get the current data. It's slow, but it
+ * should work quite nicely.
+ */
+static void
+cl_poll_sigpoll_overflow(void)
+{
+ int fd;
+ int limit;
+
+ if (!SigQOverflow) {
+ return;
+ }
+ cl_log(LOG_WARNING, "System signal queue overflow.");
+ limit = cl_poll_get_sigqlimit();
+ if (limit > 0) {
+ cl_log(LOG_WARNING, "Increase '%s'. Current limit is %d"
+ " (see sysctl(8)).", MAXQNAME, limit);
+ }
+
+ SigQOverflow = FALSE;
+
+ for (fd = 0; fd < max_allocated; ++fd) {
+ if (is_monitored[fd]) {
+ cl_real_poll_fd(fd);
+ }
+ }
+}
+
+#define PSK "/proc/sys/kernel/"
+
+/* Get current kernel signal queue limit */
+/* This only works on Linux - but that's not a big problem... */
+static int
+cl_poll_get_sigqlimit(void)
+{
+ int limit = -1;
+ int pfd;
+ char result[32];
+
+ pfd = open(PSK MAXQNAME, O_RDONLY);
+ if (pfd >= 0 && read(pfd, result, sizeof(result)) > 1) {
+ limit = atoi(result);
+ if (limit < 1) {
+ limit = -1;
+ }
+ }
+ if (pfd >= 0) {
+ close(pfd);
+ }
+ return limit;
+}
+#endif /* HAVE_FCNTL_F_SETSIG */
diff --git a/lib/clplumbing/cl_random.c b/lib/clplumbing/cl_random.c
new file mode 100644
index 0000000..4bafcfe
--- /dev/null
+++ b/lib/clplumbing/cl_random.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2005 Guochun Shi <gshi@ncsa.uiuc.edu>
+ * Copyright (C) 2005 International Business Machines Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+#include <lha_internal.h>
+#include <strings.h>
+#include <clplumbing/cl_misc.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/cl_misc.h>
+#include <clplumbing/Gmain_timeout.h>
+#include <clplumbing/cl_random.h>
+#include <clplumbing/longclock.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+#include <sys/time.h>
+#include <sys/times.h>
+
+/* Used to provide seed to the random number generator */
+unsigned int
+cl_randseed(void)
+{
+ char buf[16];
+ FILE* fs;
+ struct timeval tv;
+ const char * randdevname [] = {"/dev/urandom", "/dev/random"};
+ int idev;
+#if 0
+ long horrid;
+#endif
+
+ /*
+ * Notes, based on reading of man pages of Solaris, FreeBSD and Linux,
+ * and on proof-of-concept tests on Solaris and Linux (32- and 64-bit).
+ *
+ * Reminder of a subtlety: our intention is not to return a random
+ * number, but rather to return a random-enough seed for future
+ * random numbers. So don't bother trying (e.g.) "rand()" and
+ * "random()".
+ *
+ * /dev/random and dev/urandom seem to be a related pair. In the
+ * words of the song: "You can't have one without the other".
+ *
+ * /dev/random is probably the best. But it can block. The Solaris
+ * implementation can apparently honour "O_NONBLOCK" and "O_NDELAY".
+ * But can others? For this reason, I chose not to use it at present.
+ *
+ * /dev/urandom (with the "u") is also good. This doesn't block.
+ * But some OSes may lack it. It is tempting to detect its presence
+ * with autoconf and use the result in a "hash-if" here. BUT... in
+ * at least one OS, its presence can vary depending upon patch levels,
+ * so a binary/package built on an enabled machine might hit trouble
+ * when run on one where it is absent. (And vice versa: a build on a
+ * disabled machine would be unable to take advantage of it on an
+ * enabled machine.) Therefore always try for it at run time.
+ *
+ * "gettimeofday()" returns a random-ish number in its millisecond
+ * component.
+ *
+ * -- David Lee, Jan 2006
+ */
+
+ /*
+ * Each block below is logically of the form:
+ * if good-feature appears present {
+ * try feature
+ * if feature worked {
+ * return its result
+ * }
+ * }
+ * -- fall through to not-quite-so-good feature --
+ */
+
+ /*
+ * Does any of the random device names work?
+ */
+ for (idev=0; idev < DIMOF(randdevname); ++idev) {
+ fs = fopen(randdevname[idev], "r");
+ if (fs == NULL) {
+ cl_log(LOG_INFO, "%s: Opening file %s failed"
+ , __FUNCTION__, randdevname[idev]);
+ }else{
+ if (fread(buf, 1, sizeof(buf), fs)!= sizeof(buf)){
+ cl_log(LOG_INFO, "%s: reading file %s failed"
+ , __FUNCTION__, randdevname[idev]);
+ fclose(fs);
+ }else{
+ fclose(fs);
+ return (unsigned int)cl_binary_to_int(buf, sizeof(buf));
+ }
+ }
+ }
+
+ /*
+ * Try "gettimeofday()"; use its microsecond output.
+ * (Might it be prudent to let, say, the seconds further adjust this,
+ * in case the microseconds are too predictable?)
+ */
+ if (gettimeofday(&tv, NULL) != 0) {
+ cl_log(LOG_INFO, "%s: gettimeofday failed",
+ __FUNCTION__);
+ }else{
+ return (unsigned int) tv.tv_usec;
+ }
+ /*
+ * times(2) returns the number of clock ticks since
+ * boot. Fairly predictable, but not completely so...
+ */
+ return (unsigned int) cl_times();
+
+
+#if 0
+ /*
+ * If all else has failed, return (as a number) the address of
+ * something on the stack.
+ * Poor, but at least it has a chance of some sort of variability.
+ */
+ horrid = (long) &tv;
+ return (unsigned int) horrid; /* pointer to local variable exposed */
+#endif
+}
+
+static gboolean inityet = FALSE;
+
+static void
+cl_init_random(void)
+{
+ if (inityet)
+ return;
+
+ inityet=TRUE;
+ srand(cl_randseed());
+}
+
+int
+get_next_random(void)
+{
+ if (!inityet)
+ cl_init_random();
+
+ return rand();
+}
diff --git a/lib/clplumbing/cl_reboot.c b/lib/clplumbing/cl_reboot.c
new file mode 100644
index 0000000..c4c3ab0
--- /dev/null
+++ b/lib/clplumbing/cl_reboot.c
@@ -0,0 +1,59 @@
+#include <lha_internal.h>
+#include <clplumbing/cl_reboot.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_SYS_REBOOT_H
+# include <sys/reboot.h>
+#endif
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+#include <clplumbing/cl_log.h>
+#include <clplumbing/timers.h>
+
+enum rebootopt {
+ REBOOT_DEFAULT = 0,
+ REBOOT_NOCOREDUMP = 1,
+ REBOOT_COREDUMP = 2,
+};
+static enum rebootopt coredump = REBOOT_DEFAULT;
+
+void
+cl_enable_coredump_before_reboot(gboolean yesno)
+{
+ coredump = (yesno ? REBOOT_COREDUMP : REBOOT_NOCOREDUMP);
+}
+
+
+void cl_reboot(int msdelaybeforereboot, const char * reason)
+{
+ int rebootflag = 0;
+ int systemrc = 0;
+#ifdef RB_AUTOBOOT
+ rebootflag = RB_AUTOBOOT;
+#endif
+#ifdef RB_NOSYNC
+ rebootflag = RB_NOSYNC;
+#endif
+#ifdef RB_DUMP
+ if (coredump == REBOOT_COREDUMP) {
+ rebootflag = RB_DUMP;
+ }
+#endif
+ cl_log(LOG_EMERG, "Rebooting system. Reason: %s", reason);
+ sync();
+ mssleep(msdelaybeforereboot);
+#if REBOOT_ARGS == 1
+ reboot(rebootflag);
+#elif REBOOT_ARGS == 2
+ reboot(rebootflag, NULL);
+#else
+#error "reboot() call needs to take one or two args"
+#endif
+ /* Shouldn't ever get here, but just in case... */
+ systemrc=system(REBOOT " " REBOOT_OPTIONS);
+ cl_log(LOG_EMERG, "ALL REBOOT OPTIONS FAILED: %s returned %d"
+ , REBOOT " " REBOOT_OPTIONS, systemrc);
+ exit(1);
+}
diff --git a/lib/clplumbing/cl_signal.c b/lib/clplumbing/cl_signal.c
new file mode 100644
index 0000000..feedb3d
--- /dev/null
+++ b/lib/clplumbing/cl_signal.c
@@ -0,0 +1,209 @@
+/*
+ * cl_signal.c: signal handling routines to be used by Linux-HA programmes
+ *
+ * Copyright (C) 2002 Horms <horms@verge.net.au>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <lha_internal.h>
+#include <string.h>
+#include <errno.h>
+
+#include <clplumbing/cl_signal.h>
+#include <clplumbing/cl_log.h>
+
+
+int
+cl_signal_set_handler(int sig, void (*handler)(int), sigset_t *mask
+, int flags, struct sigaction *oldact)
+{
+ struct sigaction sa;
+
+ sa.sa_handler = handler;
+ sa.sa_mask = *mask;
+ sa.sa_flags = flags;
+
+ if (sigaction(sig, &sa, oldact) < 0) {
+ cl_perror("cl_signal_set_handler(): sigaction()");
+ return(-1);
+ }
+
+ return(0);
+}
+
+
+int
+cl_signal_set_simple_handler(int sig, void (*handler)(int)
+, struct sigaction *oldact)
+{
+ struct sigaction sa;
+ sigset_t mask;
+
+ if(sigemptyset(&mask) < 0) {
+ cl_perror("cl_signal_set_simple_handler(): "
+ "sigemptyset()");
+ return(-1);
+ }
+
+ sa.sa_handler = handler;
+ sa.sa_mask = mask;
+ sa.sa_flags = 0;
+
+ if(sigaction(sig, &sa, oldact) < 0) {
+ cl_perror("cl_signal_set_simple_handler()"
+ ": sigaction()");
+ return(-1);
+ }
+
+ return(0);
+}
+
+
+int
+cl_signal_set_action(int sig, void (*action)(int, siginfo_t *, void *)
+, sigset_t *mask, int flags, struct sigaction *oldact)
+{
+ struct sigaction sa;
+
+ sa.sa_sigaction = action;
+ sa.sa_mask = *mask;
+ sa.sa_flags = flags;
+
+ if(sigaction(sig, &sa, oldact) < 0) {
+ cl_perror("cl_signal_set_action(): sigaction()");
+ return(-1);
+ }
+
+ return(0);
+}
+
+
+int
+cl_signal_set_simple_action(int sig, void (*action)(int, siginfo_t *, void *)
+, struct sigaction *oldact)
+{
+ struct sigaction sa;
+ sigset_t mask;
+
+ if(sigemptyset(&mask) < 0) {
+ cl_perror("cl_signal_set_simple_action()"
+ ": sigemptyset()");
+ return(-1);
+ }
+
+ sa.sa_sigaction = action;
+ sa.sa_mask = mask;
+ sa.sa_flags = 0;
+
+ if(sigaction(sig, &sa, oldact) < 0) {
+ cl_perror("cl_signal_set_simple_action()"
+ ": sigaction()");
+ return(-1);
+ }
+
+ return(0);
+}
+
+
+int
+cl_signal_set_interrupt(int sig, int flag)
+{
+ if(siginterrupt(sig, flag) < 0) {
+ cl_perror("cl_signal_set_interrupt(): siginterrupt()");
+ return(-1);
+ }
+
+ return(0);
+}
+
+
+int
+cl_signal_block(int how, int signal, sigset_t *oldset)
+{
+ sigset_t set;
+
+ if(sigemptyset(&set) < 0) {
+ cl_perror("cl_signal_block(): sigemptyset()");
+ return(-1);
+ }
+
+ if(sigaddset(&set, signal) < 0) {
+ cl_perror("cl_signal_block(): sigaddset()");
+ return(-1);
+ }
+
+ if(sigprocmask(how, &set, oldset) < 0) {
+ cl_perror("cl_signal_block(): sigprocmask()");
+ return(-1);
+ }
+
+ return(0);
+}
+
+
+int
+cl_signal_block_set(int how, const sigset_t *set, sigset_t *oldset)
+{
+ if(sigprocmask(how, set, oldset) < 0) {
+ cl_perror("cl_signal_block_mask(): sigprocmask()");
+ return(-1);
+ }
+
+ return(0);
+}
+
+
+int
+cl_signal_set_handler_mode(const cl_signal_mode_t *mode, sigset_t *set)
+{
+ size_t i;
+ sigset_t our_set;
+ sigset_t *use_set;
+
+ use_set = (set) ? set : &our_set;
+
+ for (i=0; mode[i].sig; ++i) {
+ if(sigaddset(use_set, mode[i].sig) < 0) {
+ cl_perror("cl_signal_set_handler_mode(): "
+ "sigaddset() [signum=%d]", mode[i].sig);
+ return(-1);
+ }
+ }
+
+ if (sigprocmask(SIG_UNBLOCK, use_set, NULL) < 0) {
+ cl_perror("cl_signal_set_handler_mode()"
+ ": sigprocmask()");
+ return(-1);
+ }
+
+ for (i=0; mode[i].sig; ++i) {
+ if(cl_signal_set_handler(mode[i].sig, mode[i]. handler
+ , use_set, SA_NOCLDSTOP, NULL) < 0) {
+ cl_log(LOG_ERR, "cl_signal_set_handler_mode(): "
+ "ha_set_sig_handler()");
+ return(-1);
+ }
+ if(cl_signal_set_interrupt(mode[i].sig, mode[i].interrupt) < 0) {
+ cl_log(LOG_ERR, "cl_signal_set_handler_mode(): "
+ "hb_signal_interrupt()");
+ return(-1);
+ }
+ }
+
+ return(0);
+}
+
diff --git a/lib/clplumbing/cl_syslog.c b/lib/clplumbing/cl_syslog.c
new file mode 100644
index 0000000..6920bd5
--- /dev/null
+++ b/lib/clplumbing/cl_syslog.c
@@ -0,0 +1,149 @@
+/*
+ * Functions to support syslog.
+ * David Lee (c) 2005
+ */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <lha_internal.h>
+
+/*
+ * Some OSes already have tables to convert names into corresponding numbers.
+ * For instance Linux makes these available if SYSLOG_NAMES is defined.
+ */
+#define SYSLOG_NAMES
+#include <stdlib.h>
+#include <clplumbing/cl_syslog.h>
+
+#include <syslog.h>
+#include <string.h>
+
+struct _syslog_code {
+ const char *c_name;
+ int c_val;
+};
+
+#if defined(HAVE_SYSLOG_FACILITYNAMES)
+
+/*
+ * <cl_syslog.h> will have included a table called "facilitynames" structured
+ * as a "struct _syslog_code" but the tag "_syslog_code" may be something else.
+ */
+
+#else
+
+struct _syslog_code facilitynames[] =
+{
+#ifdef LOG_AUTH
+ { "auth", LOG_AUTH },
+ { "security", LOG_AUTH }, /* DEPRECATED */
+#endif
+#ifdef LOG_AUTHPRIV
+ { "authpriv", LOG_AUTHPRIV },
+#endif
+#ifdef LOG_CRON
+ { "cron", LOG_CRON },
+#endif
+#ifdef LOG_DAEMON
+ { "daemon", LOG_DAEMON },
+#endif
+#ifdef LOG_FTP
+ { "ftp", LOG_FTP },
+#endif
+#ifdef LOG_KERN
+ { "kern", LOG_KERN },
+#endif
+#ifdef LOG_LPR
+ { "lpr", LOG_LPR },
+#endif
+#ifdef LOG_MAIL
+ { "mail", LOG_MAIL },
+#endif
+
+/* { "mark", INTERNAL_MARK }, * INTERNAL */
+
+#ifdef LOG_NEWS
+ { "news", LOG_NEWS },
+#endif
+#ifdef LOG_SYSLOG
+ { "syslog", LOG_SYSLOG },
+#endif
+#ifdef LOG_USER
+ { "user", LOG_USER },
+#endif
+#ifdef LOG_UUCP
+ { "uucp", LOG_UUCP },
+#endif
+#ifdef LOG_LOCAL0
+ { "local0", LOG_LOCAL0 },
+#endif
+#ifdef LOG_LOCAL1
+ { "local1", LOG_LOCAL1 },
+#endif
+#ifdef LOG_LOCAL2
+ { "local2", LOG_LOCAL2 },
+#endif
+#ifdef LOG_LOCAL3
+ { "local3", LOG_LOCAL3 },
+#endif
+#ifdef LOG_LOCAL4
+ { "local4", LOG_LOCAL4 },
+#endif
+#ifdef LOG_LOCAL5
+ { "local5", LOG_LOCAL5 },
+#endif
+#ifdef LOG_LOCAL6
+ { "local6", LOG_LOCAL6 },
+#endif
+#ifdef LOG_LOCAL7
+ { "local7", LOG_LOCAL7 },
+#endif
+ { NULL, -1 }
+};
+
+#endif /* HAVE_SYSLOG_FACILITYNAMES */
+
+/* Convert string "auth" to equivalent number "LOG_AUTH" etc. */
+int
+cl_syslogfac_str2int(const char *fname)
+{
+ int i;
+
+ if(fname == NULL || strcmp("none", fname) == 0) {
+ return 0;
+ }
+
+ for (i = 0; facilitynames[i].c_name != NULL; i++) {
+ if (strcmp(fname, facilitynames[i].c_name) == 0) {
+ return facilitynames[i].c_val;
+ }
+ }
+ return -1;
+}
+
+/* Convert number "LOG_AUTH" to equivalent string "auth" etc. */
+const char *
+cl_syslogfac_int2str(int fnum)
+{
+ int i;
+
+ for (i = 0; facilitynames[i].c_name != NULL; i++) {
+ if (facilitynames[i].c_val == fnum) {
+ return facilitynames[i].c_name;
+ }
+ }
+ return NULL;
+}
diff --git a/lib/clplumbing/cl_uuid.c b/lib/clplumbing/cl_uuid.c
new file mode 100644
index 0000000..d0dfcb6
--- /dev/null
+++ b/lib/clplumbing/cl_uuid.c
@@ -0,0 +1,180 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <lha_internal.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+/*
+ * uuid: wrapper declarations.
+ *
+ * heartbeat originally used "uuid" functionality by calling directly,
+ * and only, onto the "e2fsprogs" implementation.
+ *
+ * The run-time usages in the code have since been abstracted, funnelled
+ * through a thin, common interface layer: a Good Thing.
+ *
+ * Similarly, the compile-time usages of "include <uuid/uuid.h>" are
+ * replaced, being funnelled through a reference to this header file.
+ *
+ * This header file interfaces onto the actual underlying implementation.
+ * In the case of the "e2fsprogs" implementation, it is simply a stepping
+ * stone onto "<uuid/uuid.h>". As other implementations are accommodated,
+ * so their header requirements can be accommodated here.
+ *
+ * Copyright (C) 2004 David Lee <t.d.lee@durham.ac.uk>
+ */
+
+#if defined (HAVE_UUID_UUID_H)
+/*
+ * Almost certainly the "e2fsprogs" implementation.
+ */
+# include <uuid/uuid.h>
+
+/* elif defined(HAVE...UUID_OTHER_1 e.g. OSSP ...) */
+
+/* elif defined(HAVE...UUID_OTHER_2...) */
+#else
+# include <replace_uuid.h>
+#endif
+
+#include <clplumbing/cl_uuid.h>
+#include <clplumbing/cl_log.h>
+#include <assert.h>
+
+void
+cl_uuid_copy(cl_uuid_t* dst, cl_uuid_t* src)
+{
+ if (dst == NULL || src == NULL){
+ cl_log(LOG_ERR, "cl_uuid_copy: "
+ "wrong argument %s is NULL",
+ dst == NULL?"dst":"src");
+ assert(0);
+ }
+
+ uuid_copy(dst->uuid, src->uuid);
+}
+
+void
+cl_uuid_clear(cl_uuid_t* uu)
+{
+ if (uu == NULL){
+ cl_log(LOG_ERR, "cl_uuid_clear: "
+ "wrong argument (uu is NULL)");
+ assert(0);
+ }
+
+ uuid_clear(uu->uuid);
+
+}
+
+int
+cl_uuid_compare(const cl_uuid_t* uu1, const cl_uuid_t* uu2)
+{
+ if (uu1 == NULL || uu2 == NULL){
+ cl_log(LOG_ERR, "cl_uuid_compare: "
+ " wrong argument (%s is NULL)",
+ uu1 == NULL?"uu1":"uu2");
+ assert(0);
+ }
+
+ return uuid_compare(uu1->uuid, uu2->uuid);
+
+}
+
+
+
+void
+cl_uuid_generate(cl_uuid_t* out)
+{
+ if (out == NULL){
+ cl_log(LOG_ERR, "cl_uuid_generate: "
+ " wrong argument (out is NULL)");
+ assert(0);
+ }
+
+ uuid_generate(out->uuid);
+
+}
+
+int
+cl_uuid_is_null(cl_uuid_t* uu)
+{
+ if (uu == NULL){
+ cl_log(LOG_ERR, "cl_uuid_is_null: "
+ "wrong argument (uu is NULL)");
+ assert(0);
+ }
+
+ return uuid_is_null(uu->uuid);
+
+}
+
+int
+cl_uuid_parse( char *in, cl_uuid_t* uu)
+{
+ if (in == NULL || uu == NULL){
+
+ cl_log(LOG_ERR, "cl_uuid_parse: "
+ "wrong argument (%s is NULL)",
+ in == NULL? "in":"uu");
+ assert(0);
+ }
+
+ return uuid_parse(in, uu->uuid);
+}
+
+
+void
+cl_uuid_unparse(const cl_uuid_t* uu, char *out){
+
+ if (uu == NULL || out == NULL){
+ cl_log(LOG_ERR, "cl_uuid_unparse: "
+ "wrong argument (%s is NULL)",
+ uu == NULL? "uu":"out");
+ assert(0);
+ }
+
+ uuid_unparse(uu->uuid, out);
+}
+
+
+guint
+cl_uuid_g_hash(gconstpointer uuid_ptr)
+{
+ guint ret = 0U;
+ guint32 value32;
+ int index;
+ const unsigned char * uuid_char = uuid_ptr;
+
+ /* It is probably not strictly necessary, but I'm trying to get the
+ * same hash result on all platforms. After all, the uuids are the
+ * same on every platform.
+ */
+
+ for (index = 0; index < sizeof(cl_uuid_t); index += sizeof(value32)) {
+ memcpy(&value32, uuid_char+index, sizeof (value32));
+ ret += g_ntohl(value32);
+ }
+ return ret;
+}
+gboolean
+cl_uuid_g_equal(gconstpointer uuid_ptr_a, gconstpointer uuid_ptr_b)
+{
+ return cl_uuid_compare(uuid_ptr_a, uuid_ptr_b) == 0;
+}
diff --git a/lib/clplumbing/coredumps.c b/lib/clplumbing/coredumps.c
new file mode 100644
index 0000000..79da737
--- /dev/null
+++ b/lib/clplumbing/coredumps.c
@@ -0,0 +1,309 @@
+/*
+ * Basic Core dump control functions.
+ *
+ * Author: Alan Robertson
+ *
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * This software licensed under the GNU LGPL.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <lha_internal.h>
+
+#include <unistd.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <string.h>
+#include <stdlib.h>
+#ifdef HAVE_SYS_PRCTL_H
+# include <sys/prctl.h>
+#endif
+#include <clplumbing/coredumps.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/uids.h>
+#include <clplumbing/cl_signal.h>
+
+static char * coreroot = NULL;
+
+/* Set the root directory of our core directory hierarchy */
+int
+cl_set_corerootdir(const char * dir)
+{
+ if (dir == NULL || *dir != '/') {
+ cl_perror("Invalid dir in cl_set_corerootdir() [%s]"
+ , dir ? dir : "<NULL>");
+ errno = EINVAL;
+ return -1;
+ }
+ if (coreroot != NULL) {
+ free(coreroot);
+ coreroot = NULL;
+ }
+ coreroot = strdup(dir);
+ if (coreroot == NULL) {
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Change directory to the directory our core file needs to go in
+ * Call after you establish the userid you're running under.
+ */
+int
+cl_cdtocoredir(void)
+{
+ const char * dir = coreroot;
+ int rc;
+ struct passwd* pwent;
+
+ if (dir == NULL) {
+ dir = HA_COREDIR;
+ }
+ if ((rc=chdir(dir)) < 0) {
+ int errsave = errno;
+ cl_perror("Cannot chdir to [%s]", dir);
+ errno = errsave;
+ return rc;
+ }
+ pwent = getpwuid(getuid());
+ if (pwent == NULL) {
+ int errsave = errno;
+ cl_perror("Cannot get name for uid [%d]", getuid());
+ errno = errsave;
+ return -1;
+ }
+ if ((rc=chdir(pwent->pw_name)) < 0) {
+ int errsave = errno;
+ cl_perror("Cannot chdir to [%s/%s]", dir, pwent->pw_name);
+ errno = errsave;
+ }
+ return rc;
+}
+
+#define CHECKED_KERNEL_CORE_ENV "_PROC_SYS_CORE_CHECKED_"
+#define PROC_SYS_KERNEL_CORE_PID "/proc/sys/kernel/core_uses_pid"
+#define PROC_SYS_KERNEL_CORE_PAT "/proc/sys/kernel/core_pattern"
+
+static void cl_coredump_signal_handler(int nsig);
+
+/*
+ * core_uses_pid():
+ *
+ * returns {-1, 0, 1}
+ * -1: not supported
+ * 0: supported and disabled
+ * 1: supported and enabled
+ */
+#define BUF_MAX 256
+static int
+core_uses_pid(void)
+{
+ const char * uses_pid_pathnames[] = {PROC_SYS_KERNEL_CORE_PID};
+ const char * corepats_pathnames[] = {PROC_SYS_KERNEL_CORE_PAT};
+ const char * goodpats [] = {"%t", "%p"};
+ int j;
+
+
+ for (j=0; j < DIMOF(corepats_pathnames); ++j) {
+ int fd;
+ char buf[BUF_MAX];
+ int rc;
+ int k;
+
+ if ((fd = open(corepats_pathnames[j], O_RDONLY)) < 0) {
+ continue;
+ }
+
+ memset(buf, 0, BUF_MAX);
+ rc = read(fd, buf, BUF_MAX - 1); /* Ensure it is always NULL terminated */
+ close(fd);
+
+ for (k=0; rc > 0 && k < DIMOF(goodpats); ++k) {
+ if (strstr(buf, goodpats[k]) != NULL) {
+ return 1;
+ }
+ }
+
+ break;
+ }
+ for (j=0; j < DIMOF(uses_pid_pathnames); ++j) {
+ int fd;
+ char buf[2];
+ int rc;
+ if ((fd = open(uses_pid_pathnames[j], O_RDONLY)) < 0) {
+ continue;
+ }
+ rc = read(fd, buf, sizeof(buf));
+ close(fd);
+ if (rc < 1) {
+ continue;
+ }
+ return (buf[0] == '1');
+ }
+ setenv(CHECKED_KERNEL_CORE_ENV, "1", TRUE);
+ return -1;
+}
+
+/* Enable/disable core dumps for ourselves and our child processes */
+int
+cl_enable_coredumps(int doenable)
+{
+ int rc;
+ struct rlimit rlim;
+
+ if ((rc = getrlimit(RLIMIT_CORE, &rlim)) < 0) {
+ int errsave = errno;
+ cl_perror("Cannot get current core limit value.");
+ errno = errsave;
+ return rc;
+ }
+ if (rlim.rlim_max == 0 && geteuid() == 0) {
+ rlim.rlim_max = RLIM_INFINITY;
+ }
+
+ rlim.rlim_cur = (doenable ? rlim.rlim_max : 0);
+
+ if (doenable && rlim.rlim_max == 0) {
+ cl_log(LOG_WARNING
+ , "Not possible to enable core dumps (rlim_max is 0)");
+ }
+
+ if ((rc = setrlimit(RLIMIT_CORE, &rlim)) < 0) {
+ int errsave = errno;
+ cl_perror("Unable to %s core dumps"
+ , doenable ? "enable" : "disable");
+ errno = errsave;
+ return rc;
+ }
+ if (getenv(CHECKED_KERNEL_CORE_ENV) == NULL
+ && core_uses_pid() == 0) {
+ cl_log(LOG_WARNING
+ , "Core dumps could be lost if multiple dumps occur.");
+ cl_log(LOG_WARNING
+ , "Consider setting non-default value in %s"
+ " (or equivalent) for maximum supportability", PROC_SYS_KERNEL_CORE_PAT);
+ cl_log(LOG_WARNING
+ , "Consider setting %s (or equivalent) to"
+ " 1 for maximum supportability", PROC_SYS_KERNEL_CORE_PID);
+ }
+ return 0;
+}
+
+/*
+ * SIGQUIT 3 Core Quit from keyboard
+ * SIGILL 4 Core Illegal Instruction
+ * SIGABRT 6 Core Abort signal from abort(3)
+ * SIGFPE 8 Core Floating point exception
+ * SIGSEGV 11 Core Invalid memory reference
+ * SIGBUS 10,7,10 Core Bus error (bad memory access)
+ * SIGSYS 2,-,12 Core Bad argument to routine (SVID)
+ * SIGTRAP 5 Core Trace/breakpoint trap
+ * SIGXCPU 24,24,30 Core CPU time limit exceeded (4.2 BSD)
+ * SIGXFSZ 25,25,31 Core File size limit exceeded (4.2 BSD)
+ */
+
+/*
+ * This function exists to allow security-sensitive programs
+ * to safely take core dumps. Such programs can't can't call
+ * cl_untaint_coredumps() alone - because it might cause a
+ * leak of confidential information - as information which should
+ * only be known by the "high-privilege" user id will be written
+ * into a core dump which is readable by the "low-privilege" user id.
+ * This is a bad thing.
+ *
+ * This function causes this program to call a special signal handler
+ * on receipt of any core dumping signal. This handler then does
+ * the following four things on receipt of a core dumping signal:
+ *
+ * 1) Set privileges to "maximum" on receipt of a signal
+ * 2) "untaint" themselves with regard to core dumping
+ * 3) set SIG_DFLT for the received signal
+ * 4) Kill themselves with the received core-dumping signal
+ *
+ * Any process *could* do this to get core dumps, but if your stack
+ * is screwed up, then the signal handler might not work.
+ * If you're core dumping because of a stack overflow, it certainly won't work.
+ *
+ * On the other hand, this function may work on some OSes that don't support
+ * prctl(2). This is an untested theory at this time...
+ */
+void
+cl_set_all_coredump_signal_handlers(void)
+{
+ static const int coresigs [] = {SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGSEGV
+#ifdef SIGBUS
+, SIGBUS
+#endif
+#ifdef SIGSYS
+, SIGSYS
+#endif
+#ifdef SIGTRAP
+, SIGTRAP
+#endif
+#ifdef SIGXCPU
+, SIGXCPU
+#endif
+#ifdef SIGXFSZ
+, SIGXFSZ
+#endif
+};
+ int j;
+
+ for (j=0; j < DIMOF(coresigs); ++j) {
+ cl_set_coredump_signal_handler(coresigs[j]);
+ }
+}
+
+/*
+ * See note above about why using this function directly is sometimes
+ * a bad idea, and you might need to use cl_set_all_coredump_signal_handlers()
+ * instead.
+ */
+void
+cl_untaint_coredumps(void)
+{
+#if defined(PR_SET_DUMPABLE)
+ prctl(PR_SET_DUMPABLE, (unsigned long)TRUE, 0UL, 0UL, 0UL);
+#endif
+}
+static void
+cl_coredump_signal_handler(int nsig)
+{
+ return_to_orig_privs();
+ if (geteuid() == 0) {
+ /* Put ALL privileges back to root... */
+ if (setuid(0) < 0) {
+ cl_perror("cl_coredump_signal_handler: unable to setuid(0)");
+ }
+ }
+ cl_untaint_coredumps(); /* Do the best we know how to do... */
+ CL_SIGNAL(nsig, SIG_DFL);
+ kill(getpid(), nsig);
+}
+
+void
+cl_set_coredump_signal_handler(int nsig)
+{
+ CL_SIGNAL(nsig, cl_coredump_signal_handler);
+}
diff --git a/lib/clplumbing/cpulimits.c b/lib/clplumbing/cpulimits.c
new file mode 100644
index 0000000..4c03f23
--- /dev/null
+++ b/lib/clplumbing/cpulimits.c
@@ -0,0 +1,219 @@
+/*
+ * Functions to put dynamic limits on CPU consumption.
+ *
+ * Copyright (C) 2003 IBM Corporation
+ *
+ * Author: <alanr@unix.sh>
+ *
+ * This software licensed under the GNU LGPL.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ **************************************************************************
+ *
+ * This allows us to better catch runaway realtime processes that
+ * might otherwise hang the whole system (if they're POSIX realtime
+ * processes).
+ *
+ * We do this by getting a "lease" on CPU time, and then extending
+ * the lease every so often as real time elapses. Since we only
+ * extend the lease by a bounded amount computed on the basis of an
+ * upper bound of how much CPU the code is "expected" to consume during
+ * the lease interval, this means that if we go into an infinite
+ * loop, it is highly probable that this will be detected and our
+ * process will be terminated by the operating system with a SIGXCPU.
+ *
+ * If you want to handle this signal, then fine... Do so...
+ *
+ * If not, the default is to terminate the process and produce a core
+ * dump. This is a great default for debugging...
+ *
+ *
+ * The process is basically this:
+ * - Set the CPU percentage limit with cl_cpu_limit_setpercent()
+ * according to what you expect the CPU percentage to top out at
+ * measured over an interval at >= 60 seconds
+ * - Call cl_cpu_limit_ms_interval() to figure out how often to update
+ * the CPU limit (it returns milliseconds)
+ * - At least as often as indicated above, call cl_cpu_limit_update()
+ * to update our current CPU limit.
+ *
+ * These limits are approximate, so be a little conservative.
+ * If you've gone into an infinite loop, it'll likely get caught ;-)
+ *
+ * As of this writing, this code will never set the soft CPU limit less
+ * than four seconds, or greater than 60 seconds.
+ *
+ */
+#include <lha_internal.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <clplumbing/longclock.h>
+#include <unistd.h>
+#include <clplumbing/cpulimits.h>
+#include <clplumbing/cl_log.h>
+
+static longclock_t nexttimetoupdate;
+
+/* How long between checking out CPU usage? */
+static int cpuinterval_ms = 0;
+
+/* How much cpu (in seconds) allowed at each check interval? */
+static int cpusecs;
+
+#define ROUND(foo) ((int)((foo)+0.5))
+
+
+/*
+ * Update our current CPU limit (via setrlimit) according to our
+ * current resource consumption, and our current cpu % limit
+ *
+ * We only set the soft CPU limit, and do not change the maximum
+ * (hard) CPU limit, but we respect it if it's already set.
+ *
+ * As a result, this code can be used by privileged and non-privileged
+ * processes.
+ */
+
+static int
+update_cpu_interval(void)
+{
+ struct rusage ru;
+ struct rlimit rlim;
+ unsigned long timesecs;
+ unsigned long microsec;
+
+ /* Compute how much CPU we've used so far... */
+
+ getrusage(RUSAGE_SELF, &ru);
+ timesecs = ru.ru_utime.tv_sec + ru.ru_stime.tv_sec;
+ microsec = ru.ru_utime.tv_usec + ru.ru_stime.tv_usec;
+
+ /* Round up to the next higher second */
+ if (microsec > 1000000) {
+ timesecs += 2;
+ }else{
+ timesecs += 1;
+ }
+
+ /* Compute our next CPU limit */
+ timesecs += cpusecs;
+
+ /* Figure out when we next need to update our CPU limit */
+ nexttimetoupdate = add_longclock(time_longclock()
+ , msto_longclock(cpuinterval_ms));
+
+ getrlimit(RLIMIT_CPU, &rlim);
+
+ /* Make sure we don't exceed the hard CPU limit (if set) */
+ if (rlim.rlim_max != RLIM_INFINITY && timesecs > rlim.rlim_max) {
+ timesecs = rlim.rlim_max;
+ }
+#if 0
+ cl_log(LOG_DEBUG
+ , "Setting max CPU limit to %ld seconds", timesecs);
+#endif
+
+ /* Update the OS-level soft CPU limit */
+ rlim.rlim_cur = timesecs;
+ return setrlimit(RLIMIT_CPU, &rlim);
+}
+
+#define MININTERVAL 60 /* seconds */
+
+int
+cl_cpu_limit_setpercent(int ipercent)
+{
+ float percent;
+ int interval;
+
+ if (ipercent > 99) {
+ ipercent = 99;
+ }
+ if (ipercent < 1) {
+ ipercent = 1;
+ }
+ percent = ipercent;
+ percent /= (float)100;
+
+ interval= MININTERVAL;
+
+ /*
+ * Compute how much CPU we will allow to be used
+ * for each check interval.
+ *
+ * Rules:
+ * - we won't require checking more often than
+ * every 60 seconds
+ * - we won't limit ourselves to less than
+ * 4 seconds of CPU per checking interval
+ */
+ for (;;) {
+ cpusecs = ROUND((float)interval*percent);
+ if (cpusecs >= 4) {
+ break;
+ }
+ interval *= 2;
+ }
+
+ /*
+ * Now compute how long to go between updates to our CPU limit
+ * from the perspective of the OS (via setrlimit(2)).
+ *
+ * We do the computation this way because the CPU limit
+ * can only be set to the nearest second, but timers can
+ * generally be set more accurately.
+ */
+ cpuinterval_ms = (int)(((float)cpusecs / percent)*1000.0);
+
+ cl_log(LOG_DEBUG
+ , "Limiting CPU: %d CPU seconds every %d milliseconds"
+ , cpusecs, cpuinterval_ms);
+
+ return update_cpu_interval();
+}
+
+int
+cl_cpu_limit_ms_interval(void)
+{
+ return cpuinterval_ms;
+}
+
+int
+cl_cpu_limit_update(void)
+{
+ longclock_t now = time_longclock();
+ long msleft;
+
+ if (cpuinterval_ms <= 0) {
+ return 0;
+ }
+ if (cmp_longclock(now, nexttimetoupdate) > 0) {
+ return update_cpu_interval();
+ }
+ msleft = longclockto_ms(sub_longclock(nexttimetoupdate, now));
+ if (msleft < 500) {
+ return update_cpu_interval();
+ }
+ return 0;
+}
+int
+cl_cpu_limit_disable(void)
+{
+ struct rlimit rlim;
+ getrlimit(RLIMIT_CPU, &rlim);
+ rlim.rlim_cur = rlim.rlim_max;
+ return setrlimit(RLIMIT_CPU, &rlim);
+}
diff --git a/lib/clplumbing/ipcsocket.c b/lib/clplumbing/ipcsocket.c
new file mode 100644
index 0000000..14c3504
--- /dev/null
+++ b/lib/clplumbing/ipcsocket.c
@@ -0,0 +1,2767 @@
+/*
+ * ipcsocket unix domain socket implementation of IPC abstraction.
+ *
+ * Copyright (c) 2002 Xiaoxiang Liu <xiliu@ncsa.uiuc.edu>
+ *
+ * Stream support (c) 2004,2006 David Lee <t.d.lee@durham.ac.uk>
+ * Note: many of the variable/function names "*socket*" should be
+ * interpreted as having a more generic "ipc-channel-type" meaning.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <lha_internal.h>
+
+#include <clplumbing/ipc.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/realtime.h>
+#include <clplumbing/cl_poll.h>
+
+#include <ha_msg.h>
+/* avoid including cib.h - used in gshi's "late message" code to avoid
+ * printing insanely large messages
+ */
+#define F_CIB_CALLDATA "cib_calldata"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <sched.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/uio.h>
+#ifdef HAVE_SYS_FILIO_H
+# include <sys/filio.h>
+#endif
+#ifdef HAVE_SYS_SYSLIMITS_H
+# include <sys/syslimits.h>
+#endif
+#ifdef HAVE_SYS_CRED_H
+# include <sys/cred.h>
+#endif
+#ifdef HAVE_SYS_UCRED_H
+# include <sys/ucred.h>
+#endif
+
+/* For 'getpeerucred()' (Solaris 10 upwards) */
+#ifdef HAVE_UCRED_H
+# include <ucred.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+
+/*
+ * Normally use "socket" code. But on some OSes alternatives may be
+ * preferred (or necessary).
+ */
+#define HB_IPC_SOCKET 1
+#define HB_IPC_STREAM 2
+/* #define HB_IPC_ANOTHER 3 */
+
+#ifndef HB_IPC_METHOD
+# if defined(SO_PEERCRED) || defined(HAVE_GETPEEREID) \
+ || defined(SCM_CREDS) || defined(HAVE_GETPEERUCRED)
+# define HB_IPC_METHOD HB_IPC_SOCKET
+# elif defined(HAVE_STROPTS_H)
+# define HB_IPC_METHOD HB_IPC_STREAM
+# else
+# error. Surely we have sockets or streams...
+# endif
+#endif
+
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+# include <sys/poll.h>
+# include <netinet/in.h>
+# include <sys/un.h>
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+# include <stropts.h>
+#else
+# error "IPC type invalid"
+#endif
+
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#ifndef UNIX_PATH_MAX
+# define UNIX_PATH_MAX 108
+#endif
+
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+
+# define MAX_LISTEN_NUM 128
+
+# ifndef MSG_NOSIGNAL
+# define MSG_NOSIGNAL 0
+# endif
+
+# ifndef AF_LOCAL
+# define AF_LOCAL AF_UNIX
+# endif
+
+#endif /* HB_IPC_METHOD */
+
+/***********************************************************************
+ *
+ * Determine the IPC authentication scheme... More machine dependent than
+ * we'd like, but don't know any better way...
+ *
+ ***********************************************************************/
+#ifdef SO_PEERCRED
+# define USE_SO_PEERCRED
+#elif HAVE_GETPEEREID
+# define USE_GETPEEREID
+#elif defined(SCM_CREDS)
+# define USE_SCM_CREDS
+#elif HAVE_GETPEERUCRED /* e.g. Solaris 10 upwards */
+# define USE_GETPEERUCRED
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+# define USE_STREAM_CREDS
+#else
+# define USE_DUMMY_CREDS
+/* This will make it compile, but attempts to authenticate
+ * will fail. This is a stopgap measure ;-)
+ */
+#endif
+
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+
+# ifdef USE_BINDSTAT_CREDS
+# ifndef SUN_LEN
+# define SUN_LEN(ptr) ((size_t) (offsetof (sockaddr_un, sun_path) + strlen ((ptr)->sun_path))
+# endif
+# endif
+
+#endif /* HB_IPC_METHOD */
+
+/* wait connection private data. */
+struct SOCKET_WAIT_CONN_PRIVATE{
+ /* the path name wich the connection will be built on. */
+ char path_name[UNIX_PATH_MAX];
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ /* the domain socket. */
+ int s;
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+ /* the streams pipe */
+ int pipefds[2];
+#endif
+};
+
+/* channel private data. */
+struct SOCKET_CH_PRIVATE{
+ /* the path name wich the connection will be built on. */
+ char path_name[UNIX_PATH_MAX];
+ /* the domain socket. */
+ int s;
+ /* the size of expecting data for below buffered message buf_msg */
+ int remaining_data;
+
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ /* The address of our peer - used by USE_BINDSTAT_CREDS version of
+ * socket_verify_auth()
+ */
+ struct sockaddr_un *peer_addr;
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+ uid_t farside_uid;
+ gid_t farside_gid;
+#endif
+
+ /* the buf used to save unfinished message */
+ struct IPC_MESSAGE *buf_msg;
+};
+
+struct IPC_Stats {
+ long nsent;
+ long noutqueued;
+ long send_count;
+ long nreceived;
+ long ninqueued;
+ long recv_count;
+ int last_recv_errno;
+ int last_recv_rc;
+ int last_send_errno;
+ int last_send_rc;
+};
+
+static struct IPC_Stats SocketIPCStats = {0, 0, 0, 0};
+extern int debug_level;
+
+/* unix domain socket implementations of IPC functions. */
+
+static int socket_resume_io(struct IPC_CHANNEL *ch);
+
+static struct IPC_MESSAGE* socket_message_new(struct IPC_CHANNEL*ch
+, int msg_len);
+
+struct IPC_WAIT_CONNECTION *socket_wait_conn_new(GHashTable* ch_attrs);
+
+/* *** FIXME: This is also declared in 'ocf_ipc.c'. */
+struct IPC_CHANNEL* socket_client_channel_new(GHashTable *attrs);
+
+static struct IPC_CHANNEL* socket_server_channel_new(int sockfd);
+
+static struct IPC_CHANNEL * channel_new(int sockfd, int conntype, const char *pathname);
+static int client_channel_new_auth(int sockfd);
+static int verify_creds(struct IPC_AUTH *auth_info, uid_t uid, gid_t gid);
+
+typedef void (*DelProc)(IPC_Message*);
+
+static struct IPC_MESSAGE * ipcmsg_new(struct IPC_CHANNEL* ch,
+ const void* data, int len, void* private, DelProc d);
+
+static pid_t socket_get_farside_pid(int sockfd);
+
+extern int (*ipc_pollfunc_ptr)(struct pollfd *, nfds_t, int);
+
+static int socket_resume_io_read(struct IPC_CHANNEL *ch, int*, gboolean read1anyway);
+
+static struct IPC_OPS socket_ops;
+static gboolean ipc_time_debug_flag = TRUE;
+
+void
+set_ipc_time_debug_flag(gboolean flag)
+{
+ ipc_time_debug_flag = flag;
+}
+
+#ifdef IPC_TIME_DEBUG
+
+extern struct ha_msg* wirefmt2msg(const char* s, size_t length, int flag);
+void cl_log_message (int log_level, const struct ha_msg *m);
+int timediff(longclock_t t1, longclock_t t2);
+void ha_msg_del(struct ha_msg* msg);
+void ipc_time_debug(IPC_Channel* ch, IPC_Message* ipcmsg, int whichpos);
+
+#define SET_ENQUEUE_TIME(x,t) memcpy(&((struct SOCKET_MSG_HEAD*)x->msg_buf)->enqueue_time, &t, sizeof(longclock_t))
+#define SET_SEND_TIME(x,t) memcpy(&((struct SOCKET_MSG_HEAD*)x->msg_buf)->send_time, &t, sizeof(longclock_t))
+#define SET_RECV_TIME(x,t) memcpy(&((struct SOCKET_MSG_HEAD*)x->msg_buf)->recv_time, &t, sizeof(longclock_t))
+#define SET_DEQUEUE_TIME(x,t) memcpy(&((struct SOCKET_MSG_HEAD*)x->msg_buf)->dequeue_time, &t, sizeof(longclock_t))
+
+static
+longclock_t
+get_enqueue_time(IPC_Message *ipcmsg)
+{
+ longclock_t t;
+
+ memcpy(&t,
+ &(((struct SOCKET_MSG_HEAD *)ipcmsg->msg_buf)->enqueue_time),
+ sizeof(longclock_t));
+
+ return t;
+}
+
+int
+timediff(longclock_t t1, longclock_t t2)
+{
+ longclock_t remain;
+
+ remain = sub_longclock(t1, t2);
+
+ return longclockto_ms(remain);
+}
+
+void
+ipc_time_debug(IPC_Channel* ch, IPC_Message* ipcmsg, int whichpos)
+{
+ int msdiff = 0;
+ longclock_t lnow = time_longclock();
+ char positions[4][16]={
+ "enqueue",
+ "send",
+ "recv",
+ "dequeue"};
+
+ if (ipc_time_debug_flag == FALSE) {
+ return ;
+ }
+
+ if (ipcmsg->msg_body == NULL
+ || ipcmsg->msg_buf == NULL) {
+ cl_log(LOG_ERR, "msg_body =%p, msg_bu=%p",
+ ipcmsg->msg_body, ipcmsg->msg_buf);
+ abort();
+ return;
+ }
+
+ switch(whichpos) {
+ case MSGPOS_ENQUEUE:
+ SET_ENQUEUE_TIME(ipcmsg, lnow);
+ break;
+ case MSGPOS_SEND:
+ SET_SEND_TIME(ipcmsg, lnow);
+ goto checktime;
+ case MSGPOS_RECV:
+ SET_RECV_TIME(ipcmsg, lnow);
+ goto checktime;
+ case MSGPOS_DEQUEUE:
+ SET_DEQUEUE_TIME(ipcmsg, lnow);
+
+ checktime:
+ msdiff = timediff(lnow, get_enqueue_time(ipcmsg));
+ if (msdiff > MAXIPCTIME) {
+ struct ha_msg* hamsg = NULL;
+ cl_log(LOG_WARNING,
+ " message delayed from enqueue to %s %d ms "
+ "(enqueue-time=%lu, peer pid=%d) ",
+ positions[whichpos],
+ msdiff,
+ longclockto_ms(get_enqueue_time(ipcmsg)),
+ ch->farside_pid);
+
+ (void)hamsg;
+#if 0
+ hamsg = wirefmt2msg(ipcmsg->msg_body, ipcmsg->msg_len, 0);
+ if (hamsg != NULL) {
+ struct ha_msg *crm_data = NULL;
+ crm_data = cl_get_struct(
+ hamsg, F_CRM_DATA);
+
+ if(crm_data == NULL) {
+ crm_data = cl_get_struct(
+ hamsg, F_CIB_CALLDATA);
+ }
+ if(crm_data != NULL) {
+ cl_msg_remove_value(
+ hamsg, crm_data);
+ }
+
+ cl_log_message(LOG_DEBUG, hamsg);
+ ha_msg_del(hamsg);
+ } else {
+ if (!ipcmsg) {
+ cl_log(LOG_ERR,
+ "IPC msg 0x%lx is unallocated"
+ , (gulong)ipcmsg);
+ return;
+ }
+ if (!ipcmsg->msg_body) {
+ cl_log(LOG_ERR,
+ "IPC msg body 0x%lx is unallocated"
+ , (gulong)ipcmsg->msg_body);
+ return;
+ }
+ }
+#endif
+
+ }
+ break;
+ default:
+ cl_log(LOG_ERR, "wrong position value in IPC:%d", whichpos);
+ return;
+ }
+}
+#endif
+
+void dump_ipc_info(const IPC_Channel* chan);
+
+#undef AUDIT_CHANNELS
+
+#ifndef AUDIT_CHANNELS
+# define CHANAUDIT(ch) /*NOTHING */
+#else
+# define CHANAUDIT(ch) socket_chan_audit(ch)
+# define MAXPID 65535
+
+static void
+socket_chan_audit(const struct IPC_CHANNEL* ch)
+{
+ int badch = FALSE;
+
+ struct SOCKET_CH_PRIVATE *chp;
+ struct stat b;
+
+ if ((chp = ch->ch_private) == NULL) {
+ cl_log(LOG_CRIT, "Bad ch_private");
+ badch = TRUE;
+ }
+ if (ch->ops != &socket_ops) {
+ cl_log(LOG_CRIT, "Bad socket_ops");
+ badch = TRUE;
+ }
+ if (ch->ch_status == IPC_DISCONNECT) {
+ return;
+ }
+ if (!IPC_ISRCONN(ch)) {
+ cl_log(LOG_CRIT, "Bad ch_status [%d]", ch->ch_status);
+ badch = TRUE;
+ }
+ if (ch->farside_pid < 0 || ch->farside_pid > MAXPID) {
+ cl_log(LOG_CRIT, "Bad farside_pid");
+ badch = TRUE;
+ }
+ if (fstat(chp->s, &b) < 0) {
+ badch = TRUE;
+ } else if ((b.st_mode & S_IFMT) != S_IFSOCK) {
+ cl_log(LOG_CRIT, "channel @ 0x%lx: not a socket"
+ , (unsigned long)ch);
+ badch = TRUE;
+ }
+ if (chp->remaining_data < 0) {
+ cl_log(LOG_CRIT, "Negative remaining_data");
+ badch = TRUE;
+ }
+ if (chp->remaining_data < 0 || chp->remaining_data > MAXMSG) {
+ cl_log(LOG_CRIT, "Excessive/bad remaining_data");
+ badch = TRUE;
+ }
+ if (chp->remaining_data && chp->buf_msg == NULL) {
+ cl_log(LOG_CRIT
+ , "inconsistent remaining_data [%ld]/buf_msg[0x%lx]"
+ , (long)chp->remaining_data, (unsigned long)chp->buf_msg);
+ badch = TRUE;
+ }
+ if (chp->remaining_data == 0 && chp->buf_msg != NULL) {
+ cl_log(LOG_CRIT
+ , "inconsistent remaining_data [%ld]/buf_msg[0x%lx] (2)"
+ , (long)chp->remaining_data, (unsigned long)chp->buf_msg);
+ badch = TRUE;
+ }
+ if (ch->send_queue == NULL || ch->recv_queue == NULL) {
+ cl_log(LOG_CRIT, "bad send/recv queue");
+ badch = TRUE;
+ }
+ if (ch->recv_queue->current_qlen < 0
+ || ch->recv_queue->current_qlen > ch->recv_queue->max_qlen) {
+ cl_log(LOG_CRIT, "bad recv queue");
+ badch = TRUE;
+ }
+ if (ch->send_queue->current_qlen < 0
+ || ch->send_queue->current_qlen > ch->send_queue->max_qlen) {
+ cl_log(LOG_CRIT, "bad send_queue");
+ badch = TRUE;
+ }
+ if (badch) {
+ cl_log(LOG_CRIT, "Bad channel @ 0x%lx", (unsigned long)ch);
+ dump_ipc_info(ch);
+ abort();
+ }
+}
+#endif
+
+#ifdef CHEAT_CHECKS
+long SeqNums[32];
+
+static long
+cheat_get_sequence(IPC_Message* msg)
+{
+ const char header [] = "String-";
+ size_t header_len = sizeof(header)-1;
+ char * body;
+
+ if (msg == NULL || msg->msg_len < sizeof(header)
+ || msg->msg_len > sizeof(header) + 10
+ || strncmp(msg->msg_body, header, header_len) != 0) {
+ return -1L;
+ }
+ body = msg->msg_body;
+ return atol(body+header_len);
+}
+static char SavedReadBody[32];
+static char SavedReceivedBody[32];
+static char SavedQueuedBody[32];
+static char SavedSentBody[32];
+#ifndef MIN
+# define MIN(a,b) (a < b ? a : b)
+#endif
+
+static void
+save_body(struct IPC_MESSAGE *msg, char * savearea, size_t length)
+{
+ int mlen = strnlen(msg->msg_body, MIN(length, msg->msg_len));
+ memcpy(savearea, msg->msg_body, mlen);
+ savearea[mlen] = EOS;
+}
+
+static void
+audit_readmsgq_msg(gpointer msg, gpointer user_data)
+{
+ long cheatseq = cheat_get_sequence(msg);
+
+ if (cheatseq < SeqNums[1] || cheatseq > SeqNums[2]) {
+ cl_log(LOG_ERR
+ , "Read Q Message %ld not in range [%ld:%ld]"
+ , cheatseq, SeqNums[1], SeqNums[2]);
+ }
+}
+
+static void
+saveandcheck(struct IPC_CHANNEL * ch, struct IPC_MESSAGE* msg, char * savearea
+, size_t savesize, long* lastseq, const char * text)
+{
+ long cheatseq = cheat_get_sequence(msg);
+
+ save_body(msg, savearea, savesize);
+ if (*lastseq != 0 ) {
+ if (cheatseq != *lastseq +1) {
+ int j;
+ cl_log(LOG_ERR
+ , "%s packets out of sequence! %ld versus %ld [pid %d]"
+ , text, cheatseq, *lastseq, (int)getpid());
+ dump_ipc_info(ch);
+ for (j=0; j < 4; ++j) {
+ cl_log(LOG_DEBUG
+ , "SeqNums[%d] = %ld"
+ , j, SeqNums[j]);
+ }
+ cl_log(LOG_ERR
+ , "SocketIPCStats.nsent = %ld"
+ , SocketIPCStats.nsent);
+ cl_log(LOG_ERR
+ , "SocketIPCStats.noutqueued = %ld"
+ , SocketIPCStats.noutqueued);
+ cl_log(LOG_ERR
+ , "SocketIPCStats.nreceived = %ld"
+ , SocketIPCStats.nreceived);
+ cl_log(LOG_ERR
+ , "SocketIPCStats.ninqueued = %ld"
+ , SocketIPCStats.ninqueued);
+ }
+
+ }
+ g_list_foreach(ch->recv_queue->queue, audit_readmsgq_msg, NULL);
+ if (cheatseq > 0) {
+ *lastseq = cheatseq;
+ }
+}
+
+# define CHECKFOO(which, ch, msg, area, text) { \
+ saveandcheck(ch,msg,area,sizeof(area),SeqNums+which,text); \
+ }
+#else
+# define CHECKFOO(which, ch, msg, area, text) /* Nothing */
+#endif
+
+static void
+dump_msg(struct IPC_MESSAGE *msg, const char * label)
+{
+#ifdef CHEAT_CHECKS
+ cl_log(LOG_DEBUG, "%s packet (length %d) [%s] %ld pid %d"
+ , label, (int)msg->msg_len, (char*)msg->msg_body
+ , cheat_get_sequence(msg), (int)getpid());
+#else
+ cl_log(LOG_DEBUG, "%s length %d [%s] pid %d"
+ , label, (int)msg->msg_len, (char*)msg->msg_body
+ , (int)getpid());
+#endif
+}
+
+static void
+dump_msgq_msg(gpointer data, gpointer user_data)
+{
+ dump_msg(data, user_data);
+}
+
+void
+dump_ipc_info(const IPC_Channel* chan)
+{
+ char squeue[] = "Send queue";
+ char rqueue[] = "Receive queue";
+#ifdef CHEAT_CHECKS
+ cl_log(LOG_DEBUG, "Saved Last Body read[%s]", SavedReadBody);
+ cl_log(LOG_DEBUG, "Saved Last Body received[%s]", SavedReceivedBody);
+ cl_log(LOG_DEBUG, "Saved Last Body Queued[%s]", SavedQueuedBody);
+ cl_log(LOG_DEBUG, "Saved Last Body Sent[%s]", SavedSentBody);
+#endif
+ g_list_foreach(chan->send_queue->queue, dump_msgq_msg, squeue);
+ g_list_foreach(chan->recv_queue->queue, dump_msgq_msg, rqueue);
+ CHANAUDIT(chan);
+}
+
+/* destroy socket wait channel */
+static void
+socket_destroy_wait_conn(struct IPC_WAIT_CONNECTION * wait_conn)
+{
+ struct SOCKET_WAIT_CONN_PRIVATE * wc = wait_conn->ch_private;
+
+ if (wc != NULL) {
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ if (wc->s >= 0) {
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG
+ , "%s: closing socket %d"
+ , __FUNCTION__, wc->s);
+ }
+ close(wc->s);
+ cl_poll_ignore(wc->s);
+ unlink(wc->path_name);
+ wc->s = -1;
+ }
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+ cl_poll_ignore(wc->pipefds[0]);
+ if (wc->pipefds[0] >= 0) {
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG
+ , "%s: closing pipe[0] %d"
+ , __FUNCTION__, wc->pipefds[0]);
+ }
+ wc->pipefds[0] = -1;
+ }
+ if (wc->pipefds[1] >= 0) {
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG
+ , "%s: closing pipe[1] %d"
+ , __FUNCTION__, wc->pipefds[1]);
+ }
+ wc->pipefds[0] = -1;
+ }
+ unlink(wc->path_name);
+#endif
+ g_free(wc);
+ }
+ g_free((void*) wait_conn);
+}
+
+/* return a fd which can be listened on for new connections. */
+static int
+socket_wait_selectfd(struct IPC_WAIT_CONNECTION *wait_conn)
+{
+ struct SOCKET_WAIT_CONN_PRIVATE * wc = wait_conn->ch_private;
+
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ return (wc == NULL ? -1 : wc->s);
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+ return (wc == NULL ? -1 : wc->pipefds[0]);
+#endif
+}
+
+/* socket accept connection. */
+static struct IPC_CHANNEL*
+socket_accept_connection(struct IPC_WAIT_CONNECTION * wait_conn
+, struct IPC_AUTH *auth_info)
+{
+ struct IPC_CHANNEL * ch = NULL;
+ int s;
+ int new_sock;
+ struct SOCKET_WAIT_CONN_PRIVATE* conn_private;
+ struct SOCKET_CH_PRIVATE * ch_private ;
+ int auth_result = IPC_FAIL;
+ int saveerrno=errno;
+ gboolean was_error = FALSE;
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ /* make peer_addr a pointer so it can be used by the
+ * USE_BINDSTAT_CREDS implementation of socket_verify_auth()
+ */
+ struct sockaddr_un * peer_addr = NULL;
+ socklen_t sin_size;
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+ struct strrecvfd strrecvfd;
+#endif
+
+ /* get select fd */
+
+ s = wait_conn->ops->get_select_fd(wait_conn);
+ if (s < 0) {
+ cl_log(LOG_ERR, "get_select_fd: invalid fd");
+ return NULL;
+ }
+
+ /* Get client connection. */
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ peer_addr = g_new(struct sockaddr_un, 1);
+ *peer_addr->sun_path = '\0';
+ sin_size = sizeof(struct sockaddr_un);
+ new_sock = accept(s, (struct sockaddr *)peer_addr, &sin_size);
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+ if (ioctl(s, I_RECVFD, &strrecvfd) == -1) {
+ new_sock = -1;
+ }
+ else {
+ new_sock = strrecvfd.fd;
+ }
+#endif
+ saveerrno=errno;
+ if (new_sock == -1) {
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ cl_perror("socket_accept_connection: accept(sock=%d)"
+ , s);
+ }
+ was_error = TRUE;
+
+ } else {
+ if ((ch = socket_server_channel_new(new_sock)) == NULL) {
+ cl_log(LOG_ERR
+ , "socket_accept_connection:"
+ " Can't create new channel");
+ was_error = TRUE;
+ } else {
+ conn_private=(struct SOCKET_WAIT_CONN_PRIVATE*)
+ ( wait_conn->ch_private);
+ ch_private = (struct SOCKET_CH_PRIVATE *)(ch->ch_private);
+ strncpy(ch_private->path_name,conn_private->path_name
+ , sizeof(conn_private->path_name));
+
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ ch_private->peer_addr = peer_addr;
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+ ch_private->farside_uid = strrecvfd.uid;
+ ch_private->farside_gid = strrecvfd.gid;
+#endif
+ }
+ }
+
+ /* Verify the client authorization information. */
+ if(was_error == FALSE) {
+ auth_result = ch->ops->verify_auth(ch, auth_info);
+ if (auth_result == IPC_OK) {
+ ch->ch_status = IPC_CONNECT;
+ ch->farside_pid = socket_get_farside_pid(new_sock);
+ return ch;
+ }
+ saveerrno=errno;
+ }
+
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ g_free(peer_addr);
+ peer_addr = NULL;
+#endif
+ errno=saveerrno;
+ return NULL;
+}
+
+/*
+ * Called by socket_destroy(). Disconnect the connection
+ * and set ch_status to IPC_DISCONNECT.
+ *
+ * parameters :
+ * ch (IN) the pointer to the channel.
+ *
+ * return values :
+ * IPC_OK the connection is disconnected successfully.
+ * IPC_FAIL operation fails.
+*/
+
+static int
+socket_disconnect(struct IPC_CHANNEL* ch)
+{
+ struct SOCKET_CH_PRIVATE* conn_info;
+
+ conn_info = (struct SOCKET_CH_PRIVATE*) ch->ch_private;
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG
+ , "%s(sock=%d, ch=0x%lx){"
+ , __FUNCTION__
+ , conn_info->s, (unsigned long)ch);
+ }
+#if 0
+ if (ch->ch_status != IPC_DISCONNECT) {
+ cl_log(LOG_INFO, "forced disconnect for fd %d", conn_info->s);
+ }
+#endif
+ if (ch->ch_status == IPC_CONNECT) {
+ socket_resume_io(ch);
+ }
+
+ if (conn_info->s >= 0) {
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG
+ , "%s: closing socket %d"
+ , __FUNCTION__, conn_info->s);
+ }
+ close(conn_info->s);
+ cl_poll_ignore(conn_info->s);
+ conn_info->s = -1;
+ }
+ ch->ch_status = IPC_DISCONNECT;
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG, "}/*%s(sock=%d, ch=0x%lx)*/"
+ , __FUNCTION__, conn_info->s, (unsigned long)ch);
+ }
+ return IPC_OK;
+}
+
+/*
+ * destroy a ipc queue and clean all memory space assigned to this queue.
+ * parameters:
+ * q (IN) the pointer to the queue which should be destroied.
+ *
+ * FIXME: This function does not free up messages that might
+ * be in the queue.
+ */
+
+static void
+socket_destroy_queue(struct IPC_QUEUE * q)
+{
+ g_list_free(q->queue);
+
+ g_free((void *) q);
+}
+
+static void
+socket_destroy_channel(struct IPC_CHANNEL * ch)
+{
+ --ch->refcount;
+ if (ch->refcount > 0) {
+ return;
+ }
+ if (ch->ch_status == IPC_CONNECT) {
+ socket_resume_io(ch);
+ }
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG, "socket_destroy(ch=0x%lx){"
+ , (unsigned long)ch);
+ }
+ socket_disconnect(ch);
+ socket_destroy_queue(ch->send_queue);
+ socket_destroy_queue(ch->recv_queue);
+
+ if (ch->pool) {
+ ipc_bufpool_unref(ch->pool);
+ }
+
+ if (ch->ch_private != NULL) {
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ struct SOCKET_CH_PRIVATE *priv = (struct SOCKET_CH_PRIVATE *)
+ ch->ch_private;
+ if(priv->peer_addr != NULL) {
+ if (*priv->peer_addr->sun_path) {
+ unlink(priv->peer_addr->sun_path);
+ }
+ g_free((void*)(priv->peer_addr));
+ }
+#endif
+ g_free((void*)(ch->ch_private));
+ }
+ memset(ch, 0xff, sizeof(*ch));
+ g_free((void*)ch);
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG, "}/*socket_destroy(ch=0x%lx)*/"
+ , (unsigned long)ch);
+ }
+}
+
+static int
+socket_check_disc_pending(struct IPC_CHANNEL* ch)
+{
+ int rc;
+ struct pollfd sockpoll;
+
+ if (ch->ch_status == IPC_DISCONNECT) {
+ cl_log(LOG_ERR, "check_disc_pending() already disconnected");
+ return IPC_BROKEN;
+ }
+ if (ch->recv_queue->current_qlen > 0) {
+ return IPC_OK;
+ }
+ sockpoll.fd = ch->ops->get_recv_select_fd(ch);
+ sockpoll.events = POLLIN;
+
+ rc = ipc_pollfunc_ptr(&sockpoll, 1, 0);
+
+ if (rc < 0) {
+ cl_log(LOG_INFO
+ , "socket_check_disc_pending() bad poll call");
+ ch->ch_status = IPC_DISCONNECT;
+ return IPC_BROKEN;
+ }
+
+ if (sockpoll.revents & POLLHUP) {
+ if (sockpoll.revents & POLLIN) {
+ ch->ch_status = IPC_DISC_PENDING;
+ } else {
+#if 1
+ cl_log(LOG_INFO, "HUP without input");
+#endif
+ ch->ch_status = IPC_DISCONNECT;
+ return IPC_BROKEN;
+ }
+ }
+ if (sockpoll.revents & POLLIN) {
+ int dummy;
+ socket_resume_io_read(ch, &dummy, FALSE);
+ }
+ return IPC_OK;
+}
+
+static int
+socket_initiate_connection(struct IPC_CHANNEL * ch)
+{
+ struct SOCKET_CH_PRIVATE* conn_info;
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ struct sockaddr_un peer_addr; /* connector's address information */
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+#endif
+
+ conn_info = (struct SOCKET_CH_PRIVATE*) ch->ch_private;
+
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ /* Prepare the socket */
+ memset(&peer_addr, 0, sizeof(peer_addr));
+ peer_addr.sun_family = AF_LOCAL; /* host byte order */
+
+ if (strlen(conn_info->path_name) >= sizeof(peer_addr.sun_path)) {
+ return IPC_FAIL;
+ }
+ strncpy(peer_addr.sun_path, conn_info->path_name, sizeof(peer_addr.sun_path));
+
+ /* Send connection request */
+ if (connect(conn_info->s, (struct sockaddr *)&peer_addr
+ , sizeof(struct sockaddr_un)) == -1) {
+ return IPC_FAIL;
+ }
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+
+#endif
+
+ ch->ch_status = IPC_CONNECT;
+ ch->farside_pid = socket_get_farside_pid(conn_info->s);
+ return IPC_OK;
+}
+
+static void
+socket_set_high_flow_callback(IPC_Channel* ch,
+ flow_callback_t callback,
+ void* userdata) {
+ ch->high_flow_callback = callback;
+ ch->high_flow_userdata = userdata;
+}
+
+static void
+socket_set_low_flow_callback(IPC_Channel* ch,
+ flow_callback_t callback,
+ void* userdata) {
+ ch->low_flow_callback = callback;
+ ch->low_flow_userdata = userdata;
+}
+
+static void
+socket_check_flow_control(struct IPC_CHANNEL* ch,
+ int orig_qlen,
+ int curr_qlen)
+{
+ if (!IPC_ISRCONN(ch)) {
+ return;
+ }
+
+ if (curr_qlen >= ch->high_flow_mark
+ && ch->high_flow_callback) {
+ ch->high_flow_callback(ch, ch->high_flow_userdata);
+ }
+
+ if (curr_qlen <= ch->low_flow_mark
+ && orig_qlen > ch->low_flow_mark
+ && ch->low_flow_callback) {
+ ch->low_flow_callback(ch, ch->low_flow_userdata);
+ }
+}
+
+static int
+socket_send(struct IPC_CHANNEL * ch, struct IPC_MESSAGE* msg)
+{
+ int orig_qlen;
+ int diff;
+ struct IPC_MESSAGE* newmsg;
+
+ if (msg->msg_len > MAXMSG) {
+ cl_log(LOG_ERR, "%s: sorry, cannot send messages "
+ "bigger than %d (requested %lu)",
+ __FUNCTION__, MAXMSG, (unsigned long)msg->msg_len);
+ return IPC_FAIL;
+ }
+ if (msg->msg_len < 0) {
+ cl_log(LOG_ERR, "socket_send: "
+ "invalid message");
+ return IPC_FAIL;
+ }
+
+ if (ch->ch_status != IPC_CONNECT) {
+ return IPC_FAIL;
+ }
+
+ ch->ops->resume_io(ch);
+
+ if (ch->send_queue->maxqlen_cnt &&
+ time(NULL) - ch->send_queue->last_maxqlen_warn >= 60) {
+ cl_log(LOG_ERR, "%u messages dropped on a non-blocking channel (send queue maximum length %d)",
+ ch->send_queue->maxqlen_cnt, (int)ch->send_queue->max_qlen);
+ ch->send_queue->maxqlen_cnt = 0;
+ }
+ if ( !ch->should_send_block &&
+ ch->send_queue->current_qlen >= ch->send_queue->max_qlen) {
+ if (!ch->send_queue->maxqlen_cnt) {
+ ch->send_queue->last_maxqlen_warn = time(NULL);
+ }
+ ch->send_queue->maxqlen_cnt++;
+
+ if (ch->should_block_fail) {
+ return IPC_FAIL;
+ } else {
+ return IPC_OK;
+ }
+ }
+
+ while (ch->send_queue->current_qlen >= ch->send_queue->max_qlen) {
+ if (ch->ch_status != IPC_CONNECT) {
+ cl_log(LOG_WARNING, "socket_send:"
+ " message queue exceeded and IPC not connected");
+ return IPC_FAIL;
+ }
+ cl_shortsleep();
+ ch->ops->resume_io(ch);
+ }
+
+ /* add the message into the send queue */
+ CHECKFOO(0,ch, msg, SavedQueuedBody, "queued message");
+ SocketIPCStats.noutqueued++;
+
+ diff = 0;
+ if (msg->msg_buf ) {
+ diff = (char*)msg->msg_body - (char*)msg->msg_buf;
+ }
+ if ( diff < (int)sizeof(struct SOCKET_MSG_HEAD) ) {
+ /* either we don't have msg->msg_buf set
+ * or we don't have enough bytes for socket head
+ * we delete this message and creates
+ * a new one and delete the old one
+ */
+
+ newmsg= socket_message_new(ch, msg->msg_len);
+ if (newmsg == NULL) {
+ cl_log(LOG_ERR, "socket_resume_io_write: "
+ "allocating memory for new ipc msg failed");
+ return IPC_FAIL;
+ }
+
+ memcpy(newmsg->msg_body, msg->msg_body, msg->msg_len);
+
+ if(msg->msg_done) {
+ msg->msg_done(msg);
+ };
+ msg = newmsg;
+ }
+#ifdef IPC_TIME_DEBUG
+ ipc_time_debug(ch,msg, MSGPOS_ENQUEUE);
+#endif
+ ch->send_queue->queue = g_list_append(ch->send_queue->queue,
+ msg);
+ orig_qlen = ch->send_queue->current_qlen++;
+
+ socket_check_flow_control(ch, orig_qlen, orig_qlen +1 );
+
+ /* resume io */
+ ch->ops->resume_io(ch);
+ return IPC_OK;
+}
+
+static int
+socket_recv(struct IPC_CHANNEL * ch, struct IPC_MESSAGE** message)
+{
+ GList *element;
+
+ int nbytes;
+ int result;
+
+ socket_resume_io(ch);
+ result = socket_resume_io_read(ch, &nbytes, TRUE);
+
+ *message = NULL;
+
+ if (ch->recv_queue->current_qlen == 0) {
+ return result != IPC_OK ? result : IPC_FAIL;
+ /*return IPC_OK;*/
+ }
+ element = g_list_first(ch->recv_queue->queue);
+
+ if (element == NULL) {
+ /* Internal accounting error, but correctable */
+ cl_log(LOG_ERR
+ , "recv failure: qlen (%ld) > 0, but no message found."
+ , (long)ch->recv_queue->current_qlen);
+ ch->recv_queue->current_qlen = 0;
+ return IPC_FAIL;
+ }
+ *message = (struct IPC_MESSAGE *) (element->data);
+#ifdef IPC_TIME_DEBUG
+ ipc_time_debug(ch, *message, MSGPOS_DEQUEUE);
+#endif
+
+ CHECKFOO(1,ch, *message, SavedReadBody, "read message");
+ SocketIPCStats.nreceived++;
+ ch->recv_queue->queue = g_list_remove(ch->recv_queue->queue
+ , element->data);
+ ch->recv_queue->current_qlen--;
+ return IPC_OK;
+}
+
+static int
+socket_check_poll(struct IPC_CHANNEL * ch
+, struct pollfd * sockpoll)
+{
+ if (ch->ch_status == IPC_DISCONNECT) {
+ return IPC_OK;
+ }
+ if (sockpoll->revents & POLLHUP) {
+ /* If input present, or this is an output-only poll... */
+ if (sockpoll->revents & POLLIN
+ || (sockpoll-> events & POLLIN) == 0 ) {
+ ch->ch_status = IPC_DISC_PENDING;
+ return IPC_OK;
+ }
+#if 1
+ cl_log(LOG_INFO, "socket_check_poll(): HUP without input");
+#endif
+ ch->ch_status = IPC_DISCONNECT;
+ return IPC_BROKEN;
+
+ } else if (sockpoll->revents & (POLLNVAL|POLLERR)) {
+ /* Have we already closed the socket? */
+ if (fcntl(sockpoll->fd, F_GETFL) < 0) {
+ cl_perror("socket_check_poll(pid %d): bad fd [%d]"
+ , (int) getpid(), sockpoll->fd);
+ ch->ch_status = IPC_DISCONNECT;
+ return IPC_OK;
+ }
+ cl_log(LOG_ERR
+ , "revents failure: fd %d, flags 0x%x"
+ , sockpoll->fd, sockpoll->revents);
+ errno = EINVAL;
+ return IPC_FAIL;
+ }
+ return IPC_OK;
+}
+
+static int
+socket_waitfor(struct IPC_CHANNEL * ch
+, gboolean (*finished)(struct IPC_CHANNEL * ch))
+{
+ struct pollfd sockpoll;
+
+ CHANAUDIT(ch);
+ if (finished(ch)) {
+ return IPC_OK;
+ }
+
+ if (ch->ch_status == IPC_DISCONNECT) {
+ return IPC_BROKEN;
+ }
+ sockpoll.fd = ch->ops->get_recv_select_fd(ch);
+
+ while (!finished(ch) && IPC_ISRCONN(ch)) {
+ int rc;
+
+ sockpoll.events = POLLIN;
+
+ /* Cannot call resume_io after the call to finished()
+ * and before the call to poll because we might
+ * change the state of the thing finished() is
+ * waiting for.
+ * This means that the poll call below would be
+ * not only pointless, but might
+ * make us hang forever waiting for this
+ * event which has already happened
+ */
+ if (ch->send_queue->current_qlen > 0) {
+ sockpoll.events |= POLLOUT;
+ }
+
+ rc = ipc_pollfunc_ptr(&sockpoll, 1, -1);
+
+ if (rc < 0) {
+ return (errno == EINTR ? IPC_INTR : IPC_FAIL);
+ }
+
+ rc = socket_check_poll(ch, &sockpoll);
+ if (sockpoll.revents & POLLIN) {
+ socket_resume_io(ch);
+ }
+ if (rc != IPC_OK) {
+ CHANAUDIT(ch);
+ return rc;
+ }
+ }
+
+ CHANAUDIT(ch);
+ return IPC_OK;
+}
+
+static int
+socket_waitin(struct IPC_CHANNEL * ch)
+{
+ return socket_waitfor(ch, ch->ops->is_message_pending);
+}
+static gboolean
+socket_is_output_flushed(struct IPC_CHANNEL * ch)
+{
+ return ! ch->ops->is_sending_blocked(ch);
+}
+
+static int
+socket_waitout(struct IPC_CHANNEL * ch)
+{
+ int rc;
+ CHANAUDIT(ch);
+ rc = socket_waitfor(ch, socket_is_output_flushed);
+
+ if (rc != IPC_OK) {
+ cl_log(LOG_ERR
+ , "socket_waitout failure: rc = %d", rc);
+ } else if (ch->ops->is_sending_blocked(ch)) {
+ cl_log(LOG_ERR, "socket_waitout output still blocked");
+ }
+ CHANAUDIT(ch);
+ return rc;
+}
+
+static gboolean
+socket_is_message_pending(struct IPC_CHANNEL * ch)
+{
+ int nbytes;
+
+ socket_resume_io_read(ch, &nbytes, TRUE);
+ ch->ops->resume_io(ch);
+ if (ch->recv_queue->current_qlen > 0) {
+ return TRUE;
+ }
+
+ return !IPC_ISRCONN(ch);
+}
+
+static gboolean
+socket_is_output_pending(struct IPC_CHANNEL * ch)
+{
+ socket_resume_io(ch);
+ return ch->ch_status == IPC_CONNECT
+ && ch->send_queue->current_qlen > 0;
+}
+
+static gboolean
+socket_is_sendq_full(struct IPC_CHANNEL * ch)
+{
+ ch->ops->resume_io(ch);
+ return(ch->send_queue->current_qlen == ch->send_queue->max_qlen);
+}
+
+static gboolean
+socket_is_recvq_full(struct IPC_CHANNEL * ch)
+{
+ ch->ops->resume_io(ch);
+ return(ch->recv_queue->current_qlen == ch->recv_queue->max_qlen);
+}
+
+static int
+socket_get_conntype(struct IPC_CHANNEL* ch)
+{
+ return ch->conntype;
+}
+
+static int
+socket_assert_auth(struct IPC_CHANNEL *ch, GHashTable *auth)
+{
+ cl_log(LOG_ERR
+ , "the assert_auth function for domain socket is not implemented");
+ return IPC_FAIL;
+}
+
+static int
+socket_resume_io_read(struct IPC_CHANNEL *ch, int* nbytes, gboolean read1anyway)
+{
+ struct SOCKET_CH_PRIVATE* conn_info;
+ int retcode = IPC_OK;
+ struct pollfd sockpoll;
+ int debug_loopcount = 0;
+ int debug_bytecount = 0;
+ size_t maxqlen = ch->recv_queue->max_qlen;
+ struct ipc_bufpool* pool = ch->pool;
+ int nmsgs = 0;
+ int spaceneeded;
+ *nbytes = 0;
+
+ CHANAUDIT(ch);
+ conn_info = (struct SOCKET_CH_PRIVATE *) ch->ch_private;
+
+ if (ch->ch_status == IPC_DISCONNECT) {
+ return IPC_BROKEN;
+ }
+
+ if (pool == NULL) {
+ ch->pool = pool = ipc_bufpool_new(0);
+ if (pool == NULL) {
+ cl_log(LOG_ERR, "socket_resume_io_read: "
+ "memory allocation for ipc pool failed");
+ return IPC_FAIL;
+ }
+ }
+
+ if (ipc_bufpool_full(pool, ch, &spaceneeded)) {
+ struct ipc_bufpool* newpool;
+
+ newpool = ipc_bufpool_new(spaceneeded);
+ if (newpool == NULL) {
+ cl_log(LOG_ERR, "socket_resume_io_read: "
+ "memory allocation for a new ipc pool failed");
+ return IPC_FAIL;
+ }
+
+ ipc_bufpool_partial_copy(newpool, pool);
+ ipc_bufpool_unref(pool);
+ ch->pool = pool = newpool;
+ }
+ if (maxqlen <= 0 && read1anyway) {
+ maxqlen = 1;
+ }
+ if (ch->recv_queue->current_qlen < maxqlen && retcode == IPC_OK) {
+ void * msg_begin;
+ int msg_len;
+ int len;
+#if HB_IPC_METHOD == HB_IPC_STREAM
+ struct strbuf d;
+ int flags, rc;
+#endif
+
+ CHANAUDIT(ch);
+ ++debug_loopcount;
+
+ len = ipc_bufpool_spaceleft(pool);
+ msg_begin = pool->currpos;
+
+ CHANAUDIT(ch);
+
+ /* Now try to receive some data */
+
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ msg_len = recv(conn_info->s, msg_begin, len, MSG_DONTWAIT);
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+ d.maxlen = len;
+ d.len = 0;
+ d.buf = msg_begin;
+ flags = 0;
+ rc = getmsg(conn_info->s, NULL, &d, &flags);
+ msg_len = (rc < 0) ? rc : d.len;
+#endif
+ SocketIPCStats.last_recv_rc = msg_len;
+ SocketIPCStats.last_recv_errno = errno;
+ ++SocketIPCStats.recv_count;
+
+ /* Did we get an error? */
+ if (msg_len < 0) {
+ switch (errno) {
+ case EAGAIN:
+ if (ch->ch_status==IPC_DISC_PENDING) {
+ ch->ch_status =IPC_DISCONNECT;
+ retcode = IPC_BROKEN;
+ }
+ break;
+
+ case ECONNREFUSED:
+ case ECONNRESET:
+ ch->ch_status = IPC_DISC_PENDING;
+ retcode= socket_check_disc_pending(ch);
+ break;
+
+ default:
+ cl_perror("socket_resume_io_read"
+ ": unknown recv error, peerpid=%d",
+ ch->farside_pid);
+ ch->ch_status = IPC_DISCONNECT;
+ retcode = IPC_FAIL;
+ break;
+ }
+
+ } else if (msg_len == 0) {
+ ch->ch_status = IPC_DISC_PENDING;
+ if(ch->recv_queue->current_qlen <= 0) {
+ ch->ch_status = IPC_DISCONNECT;
+ retcode = IPC_FAIL;
+ }
+ } else {
+ /* We read something! */
+ /* Note that all previous cases break out of the loop */
+ debug_bytecount += msg_len;
+ *nbytes = msg_len;
+ nmsgs = ipc_bufpool_update(pool, ch, msg_len, ch->recv_queue) ;
+
+ if (nmsgs < 0) {
+ /* we didn't like the other side */
+ cl_log(LOG_ERR, "socket_resume_io_read: "
+ "disconnecting the other side");
+ ch->ch_status = IPC_DISCONNECT;
+ retcode = IPC_FAIL;
+ } else {
+ SocketIPCStats.ninqueued += nmsgs;
+ }
+ }
+ }
+
+ /* Check for errors uncaught by recv() */
+ /* NOTE: It doesn't seem right we have to do this every time */
+ /* FIXME?? */
+
+ memset(&sockpoll,0, sizeof(struct pollfd));
+ if ((retcode == IPC_OK)
+ && (sockpoll.fd = conn_info->s) >= 0) {
+ /* Just check for errors, not for data */
+ sockpoll.events = 0;
+ ipc_pollfunc_ptr(&sockpoll, 1, 0);
+ retcode = socket_check_poll(ch, &sockpoll);
+ }
+
+ CHANAUDIT(ch);
+ if (retcode != IPC_OK) {
+ return retcode;
+ }
+
+ return IPC_ISRCONN(ch) ? IPC_OK : IPC_BROKEN;
+}
+
+static int
+socket_resume_io_write(struct IPC_CHANNEL *ch, int* nmsg)
+{
+ int retcode = IPC_OK;
+ struct SOCKET_CH_PRIVATE* conn_info;
+
+ CHANAUDIT(ch);
+ *nmsg = 0;
+ conn_info = (struct SOCKET_CH_PRIVATE *) ch->ch_private;
+
+ while (ch->ch_status == IPC_CONNECT
+ && retcode == IPC_OK
+ && ch->send_queue->current_qlen > 0) {
+
+ GList * element;
+ struct IPC_MESSAGE * msg;
+ struct SOCKET_MSG_HEAD head;
+ struct IPC_MESSAGE* oldmsg = NULL;
+ int sendrc = 0;
+ struct IPC_MESSAGE* newmsg;
+ char* p;
+ unsigned int bytes_remaining;
+ int diff;
+
+ CHANAUDIT(ch);
+ element = g_list_first(ch->send_queue->queue);
+ if (element == NULL) {
+ /* OOPS! - correct consistency problem */
+ ch->send_queue->current_qlen = 0;
+ break;
+ }
+ msg = (struct IPC_MESSAGE *) (element->data);
+
+ diff = 0;
+ if (msg->msg_buf ) {
+ diff = (char*)msg->msg_body - (char*)msg->msg_buf;
+ }
+ if ( diff < (int)sizeof(struct SOCKET_MSG_HEAD) ) {
+ /* either we don't have msg->msg_buf set
+ * or we don't have enough bytes for socket head
+ * we delete this message and creates
+ * a new one and delete the old one
+ */
+
+ newmsg= socket_message_new(ch, msg->msg_len);
+ if (newmsg == NULL) {
+ cl_log(LOG_ERR, "socket_resume_io_write: "
+ "allocating memory for new ipc msg failed");
+ return IPC_FAIL;
+ }
+
+ memcpy(newmsg->msg_body, msg->msg_body, msg->msg_len);
+ oldmsg = msg;
+ msg = newmsg;
+ }
+
+ head.msg_len = msg->msg_len;
+ head.magic = HEADMAGIC;
+ memcpy(msg->msg_buf, &head, sizeof(struct SOCKET_MSG_HEAD));
+
+ if (ch->bytes_remaining == 0) {
+ /*we start to send a new message*/
+#ifdef IPC_TIME_DEBUG
+ ipc_time_debug(ch, msg, MSGPOS_SEND);
+#endif
+ bytes_remaining = msg->msg_len + ch->msgpad;
+ p = msg->msg_buf;
+ } else {
+ bytes_remaining = ch->bytes_remaining;
+ p = ((char*)msg->msg_buf) + msg->msg_len + ch->msgpad
+ - bytes_remaining;
+
+ }
+
+ sendrc = 0;
+
+ do {
+#if HB_IPC_METHOD == HB_IPC_STREAM
+ struct strbuf d;
+ int msglen, putmsgrc;
+#endif
+
+ CHANAUDIT(ch);
+
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ sendrc = send(conn_info->s, p
+ , bytes_remaining, (MSG_DONTWAIT|MSG_NOSIGNAL));
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+ d.maxlen = 0;
+ d.len = msglen = bytes_remaining;
+ d.buf = p;
+ putmsgrc = putmsg(conn_info->s, NULL, &d, 0);
+ sendrc = putmsgrc == 0 ? msglen : -1;
+#endif
+ SocketIPCStats.last_send_rc = sendrc;
+ SocketIPCStats.last_send_errno = errno;
+ ++SocketIPCStats.send_count;
+
+ if (sendrc <= 0) {
+ break;
+ } else {
+ p = p + sendrc;
+ bytes_remaining -= sendrc;
+ }
+
+ } while(bytes_remaining > 0 );
+
+ ch->bytes_remaining = bytes_remaining;
+
+ if (sendrc < 0) {
+ switch (errno) {
+ case EAGAIN:
+ retcode = IPC_OK;
+ break;
+ case EPIPE:
+ ch->ch_status = IPC_DISC_PENDING;
+ socket_check_disc_pending(ch);
+ retcode = IPC_BROKEN;
+ break;
+ default:
+ cl_perror("socket_resume_io_write"
+ ": send2 bad errno");
+ ch->ch_status = IPC_DISCONNECT;
+ retcode = IPC_FAIL;
+ break;
+ }
+ break;
+ } else {
+ int orig_qlen;
+
+ CHECKFOO(3,ch, msg, SavedSentBody, "sent message")
+
+ if (oldmsg) {
+ if (msg->msg_done != NULL) {
+ msg->msg_done(msg);
+ }
+ msg=oldmsg;
+ }
+
+ if(ch->bytes_remaining ==0) {
+ ch->send_queue->queue = g_list_remove(ch->send_queue->queue, msg);
+ if (msg->msg_done != NULL) {
+ msg->msg_done(msg);
+ }
+
+ SocketIPCStats.nsent++;
+ orig_qlen = ch->send_queue->current_qlen--;
+ socket_check_flow_control(ch, orig_qlen, orig_qlen -1 );
+ (*nmsg)++;
+ }
+ }
+ }
+ CHANAUDIT(ch);
+ if (retcode != IPC_OK) {
+ return retcode;
+ }
+ return IPC_ISRCONN(ch) ? IPC_OK : IPC_BROKEN;
+}
+
+static int
+socket_resume_io(struct IPC_CHANNEL *ch)
+{
+ int rc1 = IPC_OK;
+ int rc2 = IPC_OK;
+ int nwmsg = 1;
+ int nbytes_r = 1;
+ gboolean OKonce = FALSE;
+
+ CHANAUDIT(ch);
+ if (!IPC_ISRCONN(ch)) {
+ return IPC_BROKEN;
+ }
+
+ do {
+ if (nbytes_r > 0) {
+ rc1 = socket_resume_io_read(ch, &nbytes_r, FALSE);
+ }
+ if (nwmsg > 0) {
+ nwmsg = 0;
+ rc2 = socket_resume_io_write(ch, &nwmsg);
+ }
+ if (rc1 == IPC_OK || rc2 == IPC_OK) {
+ OKonce = TRUE;
+ }
+ } while ((nbytes_r > 0 || nwmsg > 0) && IPC_ISRCONN(ch));
+
+ if (IPC_ISRCONN(ch)) {
+ if (rc1 != IPC_OK) {
+ cl_log(LOG_ERR
+ , "socket_resume_io_read() failure");
+ }
+ if (rc2 != IPC_OK && IPC_CONNECT == ch->ch_status) {
+ cl_log(LOG_ERR
+ , "socket_resume_io_write() failure");
+ }
+ } else {
+ return (OKonce ? IPC_OK : IPC_BROKEN);
+ }
+
+ return (rc1 != IPC_OK ? rc1 : rc2);
+}
+
+static int
+socket_get_recv_fd(struct IPC_CHANNEL *ch)
+{
+ struct SOCKET_CH_PRIVATE* chp = ch ->ch_private;
+
+ return (chp == NULL ? -1 : chp->s);
+}
+
+static int
+socket_get_send_fd(struct IPC_CHANNEL *ch)
+{
+ return socket_get_recv_fd(ch);
+}
+
+static void
+socket_adjust_buf(struct IPC_CHANNEL *ch, int optname, unsigned q_len)
+{
+ const char *direction = optname == SO_SNDBUF ? "snd" : "rcv";
+ int fd = socket_get_send_fd(ch);
+ unsigned byte;
+
+ /* Arbitrary scaling.
+ * DEFAULT_MAX_QLEN is 64, default socket buf is often 64k to 128k,
+ * at least on those linux I checked.
+ * Keep that ratio, and allow for some overhead. */
+ if (q_len == 0)
+ /* client does not want anything,
+ * reduce system buffers as well */
+ byte = 4096;
+ else if (q_len < 512)
+ byte = (32 + q_len) * 1024;
+ else
+ byte = q_len * 1024;
+
+ if (0 == setsockopt(fd, SOL_SOCKET, optname, &byte, sizeof(byte))) {
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG, "adjusted %sbuf size to %u",
+ direction, byte);
+ }
+ } else {
+ /* If this fails, you may need to adjust net.core.rmem_max,
+ * ...wmem_max, or equivalent */
+ cl_log(LOG_WARNING, "adjust %sbuf size to %u failed: %s",
+ direction, byte, strerror(errno));
+ }
+}
+
+static int
+socket_set_send_qlen (struct IPC_CHANNEL* ch, int q_len)
+{
+ /* This seems more like an assertion failure than a normal error */
+ if (ch->send_queue == NULL) {
+ return IPC_FAIL;
+ }
+ socket_adjust_buf(ch, SO_SNDBUF, q_len);
+ ch->send_queue->max_qlen = q_len;
+ return IPC_OK;
+}
+
+static int
+socket_set_recv_qlen (struct IPC_CHANNEL* ch, int q_len)
+{
+ /* This seems more like an assertion failure than a normal error */
+ if (ch->recv_queue == NULL) {
+ return IPC_FAIL;
+ }
+ socket_adjust_buf(ch, SO_RCVBUF, q_len);
+ ch->recv_queue->max_qlen = q_len;
+ return IPC_OK;
+}
+
+static int ipcmsg_count_allocated = 0;
+static int ipcmsg_count_freed = 0;
+void socket_ipcmsg_dump_stats(void);
+void
+socket_ipcmsg_dump_stats(void) {
+ cl_log(LOG_INFO, "ipcsocket ipcmsg allocated=%d, freed=%d, diff=%d",
+ ipcmsg_count_allocated,
+ ipcmsg_count_freed,
+ ipcmsg_count_allocated - ipcmsg_count_freed);
+}
+
+static void
+socket_del_ipcmsg(IPC_Message* m)
+{
+ if (m == NULL) {
+ cl_log(LOG_ERR, "socket_del_ipcmsg:"
+ "msg is NULL");
+ return;
+ }
+
+ if (m->msg_body) {
+ memset(m->msg_body, 0, m->msg_len);
+ }
+ if (m->msg_buf) {
+ g_free(m->msg_buf);
+ }
+
+ memset(m, 0, sizeof(*m));
+ g_free(m);
+
+ ipcmsg_count_freed ++;
+}
+
+static IPC_Message*
+socket_new_ipcmsg(IPC_Channel* ch, const void* data, int len, void* private)
+{
+ IPC_Message* hdr;
+
+ if (ch == NULL || len < 0) {
+ cl_log(LOG_ERR, "socket_new_ipcmsg:"
+ " invalid parameter");
+ return NULL;
+ }
+
+ if (ch->msgpad > MAX_MSGPAD) {
+ cl_log(LOG_ERR, "socket_new_ipcmsg: too many pads "
+ "something is wrong");
+ return NULL;
+ }
+
+ hdr = ipcmsg_new(ch, data, len, private, socket_del_ipcmsg);
+
+ if (hdr) ipcmsg_count_allocated ++;
+
+ return hdr;
+}
+
+static
+struct IPC_MESSAGE *
+ipcmsg_new(struct IPC_CHANNEL * ch, const void* data, int len, void* private,
+ DelProc delproc)
+{
+ struct IPC_MESSAGE * hdr;
+ char* copy = NULL;
+ char* buf;
+ char* body;
+
+ if ((hdr = g_new(struct IPC_MESSAGE, 1)) == NULL) {
+ return NULL;
+ }
+ memset(hdr, 0, sizeof(*hdr));
+
+ if (len > 0) {
+ if ((copy = (char*)g_malloc(ch->msgpad + len)) == NULL) {
+ g_free(hdr);
+ return NULL;
+ }
+ if (data) {
+ memcpy(copy + ch->msgpad, data, len);
+ }
+ buf = copy;
+ body = copy + ch->msgpad;;
+ } else {
+ len = 0;
+ buf = body = NULL;
+ }
+ hdr->msg_len = len;
+ hdr->msg_buf = buf;
+ hdr->msg_body = body;
+ hdr->msg_ch = ch;
+ hdr->msg_done = delproc;
+ hdr->msg_private = private;
+
+ return hdr;
+}
+
+static int
+socket_get_chan_status(IPC_Channel* ch)
+{
+ socket_resume_io(ch);
+ return ch->ch_status;
+}
+
+/* socket object of the function table */
+static struct IPC_WAIT_OPS socket_wait_ops = {
+ socket_destroy_wait_conn,
+ socket_wait_selectfd,
+ socket_accept_connection,
+};
+
+/*
+ * create a new ipc queue whose length = 0 and inner queue = NULL.
+ * return the pointer to a new ipc queue or NULL is the queue can't be created.
+ */
+
+static struct IPC_QUEUE*
+socket_queue_new(void)
+{
+ struct IPC_QUEUE *temp_queue;
+
+ /* temp queue with length = 0 and inner queue = NULL. */
+ temp_queue = g_new(struct IPC_QUEUE, 1);
+ temp_queue->current_qlen = 0;
+ temp_queue->max_qlen = DEFAULT_MAX_QLEN;
+ temp_queue->queue = NULL;
+ temp_queue->last_maxqlen_warn = 0;
+ temp_queue->maxqlen_cnt = 0;
+ return temp_queue;
+}
+
+/*
+ * socket_wait_conn_new:
+ * Called by ipc_wait_conn_constructor to get a new socket
+ * waiting connection.
+ * (better explanation of this role might be nice)
+ *
+ * Parameters :
+ * ch_attrs (IN) the attributes used to create this connection.
+ *
+ * Return :
+ * the pointer to the new waiting connection or NULL if the connection
+ * can't be created.
+ *
+ * NOTE :
+ * for domain socket implementation, the only attribute needed is path name.
+ * so the user should
+ * create the hash table like this:
+ * GHashTable * attrs;
+ * attrs = g_hash_table_new(g_str_hash, g_str_equal);
+ * g_hash_table_insert(attrs, PATH_ATTR, path_name);
+ * here PATH_ATTR is defined as "path".
+ *
+ * NOTE :
+ * The streams implementation uses "Streams Programming Guide", Solaris 8,
+ * as its guide (sample code near end of "Configuration" chapter 11).
+ */
+struct IPC_WAIT_CONNECTION *
+socket_wait_conn_new(GHashTable *ch_attrs)
+{
+ struct IPC_WAIT_CONNECTION * temp_ch;
+ char *path_name;
+ char *mode_attr;
+ int s, flags;
+ struct SOCKET_WAIT_CONN_PRIVATE *wait_private;
+ mode_t s_mode;
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ struct sockaddr_un my_addr;
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+ int pipefds[2];
+#endif
+
+ path_name = (char *) g_hash_table_lookup(ch_attrs, IPC_PATH_ATTR);
+ mode_attr = (char *) g_hash_table_lookup(ch_attrs, IPC_MODE_ATTR);
+
+ if (mode_attr != NULL) {
+ s_mode = (mode_t)strtoul((const char *)mode_attr, NULL, 8);
+ } else {
+ s_mode = 0777;
+ }
+ if (path_name == NULL) {
+ return NULL;
+ }
+
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ /* prepare the unix domain socket */
+ if ((s = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) {
+ cl_perror("socket_wait_conn_new: socket() failure");
+ return NULL;
+ }
+
+ if (unlink(path_name) < 0 && errno != ENOENT) {
+ cl_perror("socket_wait_conn_new: unlink failure(%s)",
+ path_name);
+ }
+ memset(&my_addr, 0, sizeof(my_addr));
+ my_addr.sun_family = AF_LOCAL; /* host byte order */
+
+ if (strlen(path_name) >= sizeof(my_addr.sun_path)) {
+ close(s);
+ return NULL;
+ }
+
+ strncpy(my_addr.sun_path, path_name, sizeof(my_addr.sun_path));
+
+ if (bind(s, (struct sockaddr *)&my_addr, sizeof(my_addr)) == -1) {
+ cl_perror("socket_wait_conn_new: trying to create in %s bind:"
+ , path_name);
+ close(s);
+ return NULL;
+ }
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+ /* Set up the communication channel the clients will use to us (server) */
+ if (pipe(pipefds) == -1) {
+ cl_perror("pipe() failure");
+ return NULL;
+ }
+
+ /* Let clients have unique connections to us */
+ if (ioctl(pipefds[1], I_PUSH, "connld") == -1) {
+ cl_perror("ioctl(%d, I_PUSH, \"connld\") failure", pipefds[1]);
+ return NULL;
+ }
+
+ if (unlink(path_name) < 0 && errno != ENOENT) {
+ cl_perror("socket_wait_conn_new: unlink failure(%s)",
+ path_name);
+ }
+
+ if (mkfifo(path_name, s_mode) == -1) {
+ cl_perror("socket_wait_conn_new: mkfifo(%s, ...) failure", path_name);
+ return NULL;
+ }
+
+ if (fattach(pipefds[1], path_name) == -1) {
+ cl_perror("socket_wait_conn_new: fattach(..., %s) failure", path_name);
+ return NULL;
+ }
+
+ /* the pseudo-socket is the other part of the pipe */
+ s = pipefds[0];
+#endif
+
+ /* Change the permission of the socket */
+ if (chmod(path_name,s_mode) < 0) {
+ cl_perror("socket_wait_conn_new: failure trying to chmod %s"
+ , path_name);
+ close(s);
+ return NULL;
+ }
+
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ /* listen to the socket */
+ if (listen(s, MAX_LISTEN_NUM) == -1) {
+ cl_perror("socket_wait_conn_new: listen(MAX_LISTEN_NUM)");
+ close(s);
+ return NULL;
+ }
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+
+#endif
+
+ flags = fcntl(s, F_GETFL);
+ if (flags == -1) {
+ cl_perror("socket_wait_conn_new: cannot read file descriptor flags");
+ close(s);
+ return NULL;
+ }
+ flags |= O_NONBLOCK;
+ if (fcntl(s, F_SETFL, flags) < 0) {
+ cl_perror("socket_wait_conn_new: cannot set O_NONBLOCK");
+ close(s);
+ return NULL;
+ }
+
+ wait_private = g_new(struct SOCKET_WAIT_CONN_PRIVATE, 1);
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ wait_private->s = s;
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+ wait_private->pipefds[0] = pipefds[0];
+ wait_private->pipefds[1] = pipefds[1];
+#endif
+ strncpy(wait_private->path_name, path_name, sizeof(wait_private->path_name));
+ temp_ch = g_new(struct IPC_WAIT_CONNECTION, 1);
+ temp_ch->ch_private = (void *) wait_private;
+ temp_ch->ch_status = IPC_WAIT;
+ temp_ch->ops = (struct IPC_WAIT_OPS *)&socket_wait_ops;
+
+ return temp_ch;
+}
+
+/*
+ * will be called by ipc_channel_constructor to create a new socket channel.
+ * parameters :
+ * attrs (IN) the hash table of the attributes used to create this channel.
+ *
+ * return:
+ * the pointer to the new waiting channel or NULL if the channel can't be created.
+*/
+
+struct IPC_CHANNEL *
+socket_client_channel_new(GHashTable *ch_attrs) {
+ char *path_name;
+ int sockfd;
+
+ /*
+ * I don't really understand why the client and the server use different
+ * parameter names...
+ *
+ * It's a really bad idea to store both integers and strings
+ * in the same table.
+ *
+ * Maybe we need an internal function with a different set of parameters?
+ */
+
+ /*
+ * if we want to seperate them. I suggest
+ * <client side>
+ * user call ipc_channel_constructor(ch_type,attrs) to create a new channel.
+ * ipc_channel_constructor() call socket_channel_new(GHashTable*)to
+ * create a new socket channel.
+ * <server side>
+ * wait_conn->accept_connection() will call another function to create a
+ * new channel. This function will take socketfd as the parameter to
+ * create a socket channel.
+ */
+
+ path_name = (char *) g_hash_table_lookup(ch_attrs, IPC_PATH_ATTR);
+ if (path_name == NULL) {
+ return NULL;
+ }
+
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ /* prepare the socket */
+ if ((sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) {
+ cl_perror("socket_client_channel_new: socket");
+ return NULL;
+ }
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+ sockfd = open(path_name, O_RDWR|O_NONBLOCK);
+ if (sockfd == -1) {
+ cl_perror("socket_client_channel_new: open(%s, ...) failure", path_name);
+ return NULL;
+ }
+#endif
+
+ if (client_channel_new_auth(sockfd) < 0) {
+ close(sockfd);
+ return NULL;
+ }
+ return channel_new(sockfd, IPC_CLIENT, path_name);
+}
+
+static
+int client_channel_new_auth(int sockfd) {
+#ifdef USE_BINDSTAT_CREDS
+ char rand_id[16];
+ char uuid_str_tmp[40];
+ struct sockaddr_un sock_addr;
+
+ /* Prepare the socket */
+ memset(&sock_addr, 0, sizeof(sock_addr));
+ sock_addr.sun_family = AF_UNIX;
+
+ /* make sure socket paths never clash */
+ uuid_generate(rand_id);
+ uuid_unparse(rand_id, uuid_str_tmp);
+
+ snprintf(sock_addr.sun_path, sizeof(sock_addr.sun_path),
+ "%s/%s", HA_VARLIBHBDIR, uuid_str_tmp);
+
+ unlink(sock_addr.sun_path);
+ if(bind(sockfd, (struct sockaddr*)&sock_addr, SUN_LEN(&sock_addr)) < 0) {
+ perror("Client bind() failure");
+ return 0;
+ }
+#endif
+
+ return 0;
+}
+
+static
+struct IPC_CHANNEL *
+socket_server_channel_new(int sockfd) {
+ return channel_new(sockfd, IPC_SERVER, "?");
+}
+
+static
+struct IPC_CHANNEL *
+channel_new(int sockfd, int conntype, const char *path_name) {
+ struct IPC_CHANNEL * temp_ch;
+ struct SOCKET_CH_PRIVATE* conn_info;
+ int flags;
+
+ if (path_name == NULL || strlen(path_name) >= sizeof(conn_info->path_name)) {
+ return NULL;
+ }
+
+ temp_ch = g_new(struct IPC_CHANNEL, 1);
+ if (temp_ch == NULL) {
+ cl_log(LOG_ERR, "channel_new: allocating memory for channel failed");
+ return NULL;
+ }
+ memset(temp_ch, 0, sizeof(struct IPC_CHANNEL));
+
+ conn_info = g_new(struct SOCKET_CH_PRIVATE, 1);
+
+ flags = fcntl(sockfd, F_GETFL);
+ if (flags == -1) {
+ cl_perror("channel_new: cannot read file descriptor flags");
+ g_free(conn_info); conn_info = NULL;
+ g_free(temp_ch);
+ if (conntype == IPC_CLIENT) close(sockfd);
+ return NULL;
+ }
+ flags |= O_NONBLOCK;
+ if (fcntl(sockfd, F_SETFL, flags) < 0) {
+ cl_perror("channel_new: cannot set O_NONBLOCK");
+ g_free(conn_info); conn_info = NULL;
+ g_free(temp_ch);
+ if (conntype == IPC_CLIENT) close(sockfd);
+ return NULL;
+ }
+
+ conn_info->s = sockfd;
+ conn_info->remaining_data = 0;
+ conn_info->buf_msg = NULL;
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ conn_info->peer_addr = NULL;
+#endif
+ strncpy(conn_info->path_name, path_name, sizeof(conn_info->path_name));
+
+#ifdef DEBUG
+ cl_log(LOG_INFO, "Initializing socket %d to DISCONNECT", sockfd);
+#endif
+ temp_ch->ch_status = IPC_DISCONNECT;
+ temp_ch->ch_private = (void*) conn_info;
+ temp_ch->ops = (struct IPC_OPS *)&socket_ops;
+ temp_ch->msgpad = sizeof(struct SOCKET_MSG_HEAD);
+ temp_ch->bytes_remaining = 0;
+ temp_ch->should_send_block = FALSE;
+ temp_ch->should_block_fail = TRUE;
+ temp_ch->send_queue = socket_queue_new();
+ temp_ch->recv_queue = socket_queue_new();
+ temp_ch->pool = NULL;
+ temp_ch->high_flow_mark = temp_ch->send_queue->max_qlen;
+ temp_ch->low_flow_mark = -1;
+ temp_ch->conntype = conntype;
+ temp_ch->refcount = 0;
+ temp_ch->farside_uid = -1;
+ temp_ch->farside_gid = -1;
+
+ return temp_ch;
+}
+
+/*
+ * Create a new pair of pre-connected IPC channels similar to
+ * the result of pipe(2), or socketpair(2).
+ */
+
+int
+ipc_channel_pair(IPC_Channel* channels[2])
+{
+ int sockets[2];
+ int rc;
+ int j;
+ const char *pname;
+
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ pname = "[socketpair]";
+
+ if ((rc = socketpair(AF_LOCAL, SOCK_STREAM, 0, sockets)) < 0) {
+ return IPC_FAIL;
+ }
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+ pname = "[pipe]";
+
+ if ((rc = pipe(sockets)) < 0) {
+ return IPC_FAIL;
+ }
+ rc = 0;
+ for (j=0; j < 2; ++j) {
+ if (fcntl(sockets[j], F_SETFL, O_NONBLOCK) < 0) {
+ cl_perror("ipc_channel_pair: cannot set O_NONBLOCK");
+ rc = -1;
+ }
+ }
+ if (rc < 0) {
+ close(sockets[0]);
+ close(sockets[1]);
+ return IPC_FAIL;
+ }
+#endif
+
+ if ((channels[0] = socket_server_channel_new(sockets[0])) == NULL) {
+ close(sockets[0]);
+ close(sockets[1]);
+ return IPC_FAIL;
+ }
+ if ((channels[1] = socket_server_channel_new(sockets[1])) == NULL) {
+ close(sockets[0]);
+ close(sockets[1]);
+ channels[0]->ops->destroy(channels[0]);
+ return IPC_FAIL;
+ }
+ for (j=0; j < 2; ++j) {
+ struct SOCKET_CH_PRIVATE* p = channels[j]->ch_private;
+ channels[j]->ch_status = IPC_CONNECT;
+ channels[j]->conntype = IPC_PEER;
+ /* Valid, but not terribly meaningful */
+ channels[j]->farside_pid = getpid();
+ strncpy(p->path_name, pname, sizeof(p->path_name));
+ }
+
+ return IPC_OK;
+}
+
+/* brief free the memory space allocated to msg and destroy msg. */
+
+static void
+socket_free_message(struct IPC_MESSAGE * msg) {
+#if 0
+ memset(msg->msg_body, 0xff, msg->msg_len);
+#endif
+ if (msg->msg_buf) {
+ g_free(msg->msg_buf);
+ } else {
+ g_free(msg->msg_body);
+ }
+#if 0
+ memset(msg, 0xff, sizeof(*msg));
+#endif
+ g_free((void *)msg);
+}
+
+/*
+ * create a new ipc message whose msg_body's length is msg_len.
+ *
+ * parameters :
+ * msg_len (IN) the length of this message body in this message.
+ *
+ * return :
+ * the pointer to the new message or NULL if the message can't be created.
+ */
+
+static struct IPC_MESSAGE*
+socket_message_new(struct IPC_CHANNEL *ch, int msg_len)
+{
+ return ipcmsg_new(ch, NULL, msg_len, NULL, socket_free_message);
+}
+
+/***********************************************************************
+ *
+ * IPC authentication schemes... More machine dependent than
+ * we'd like, but don't know any better way...
+ *
+ ***********************************************************************/
+
+static int
+verify_creds(struct IPC_AUTH *auth_info, uid_t uid, gid_t gid)
+{
+ int ret = IPC_FAIL;
+
+ if (!auth_info || (!auth_info->uid && !auth_info->gid)) {
+ return IPC_OK;
+ }
+ if ( auth_info->uid
+ && (g_hash_table_lookup(auth_info->uid
+ , GUINT_TO_POINTER((guint)uid)) != NULL)) {
+ ret = IPC_OK;
+ } else if (auth_info->gid
+ && (g_hash_table_lookup(auth_info->gid
+ , GUINT_TO_POINTER((guint)gid)) != NULL)) {
+ ret = IPC_OK;
+ }
+ return ret;
+}
+
+/***********************************************************************
+ * SO_PEERCRED VERSION... (Linux)
+ ***********************************************************************/
+
+#ifdef USE_SO_PEERCRED
+/* verify the authentication information. */
+static int
+socket_verify_auth(struct IPC_CHANNEL* ch, struct IPC_AUTH * auth_info)
+{
+ struct SOCKET_CH_PRIVATE * conn_info;
+ int ret = IPC_FAIL;
+ struct ucred cred;
+ socklen_t n = sizeof(cred);
+
+ if (ch == NULL || ch->ch_private == NULL) {
+ return IPC_FAIL;
+ }
+ if (auth_info == NULL
+ || (auth_info->uid == NULL && auth_info->gid == NULL)) {
+ ret = IPC_OK; /* no restriction for authentication */
+ }
+
+ /* Get the credential information for our peer */
+ conn_info = (struct SOCKET_CH_PRIVATE *) ch->ch_private;
+ if (getsockopt(conn_info->s, SOL_SOCKET, SO_PEERCRED, &cred, &n) != 0
+ || (size_t)n != sizeof(cred)) {
+ return ret;
+ }
+
+ ch->farside_uid = cred.uid;
+ ch->farside_gid = cred.gid;
+ if (ret == IPC_OK) {
+ return ret;
+ }
+#if 0
+ cl_log(LOG_DEBUG, "SO_PEERCRED returned [%d, (%ld:%ld)]"
+ , cred.pid, (long)cred.uid, (long)cred.uid);
+ cl_log(LOG_DEBUG, "Verifying authentication: cred.uid=%d cred.gid=%d"
+ , cred.uid, cred.gid);
+ cl_log(LOG_DEBUG, "Verifying authentication: uidptr=0x%lx gidptr=0x%lx"
+ , (unsigned long)auth_info->uid
+ , (unsigned long)auth_info->gid);
+#endif
+ /* verify the credential information. */
+ return verify_creds(auth_info, cred.uid, cred.gid);
+}
+
+/* get farside pid for our peer process */
+
+static
+pid_t
+socket_get_farside_pid(int sockfd)
+{
+ socklen_t n;
+ struct ucred *cred;
+ pid_t f_pid;
+
+ /* Get the credential information from peer */
+ n = sizeof(struct ucred);
+ cred = g_new(struct ucred, 1);
+ if (getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, cred, &n) != 0) {
+ g_free(cred);
+ return -1;
+ }
+
+ f_pid = cred->pid;
+ g_free(cred);
+ return f_pid;
+}
+#endif /* SO_PEERCRED version */
+
+#ifdef USE_GETPEEREID
+/*
+ * This is implemented in OpenBSD and FreeBSD.
+ *
+ * It's not a half-bad interface...
+ *
+ * This should probably be our standard way of doing it, and put it
+ * as a replacement library. That would simplify things...
+ */
+
+static int
+socket_verify_auth(struct IPC_CHANNEL* ch, struct IPC_AUTH * auth_info)
+{
+ struct SOCKET_CH_PRIVATE *conn_info;
+ uid_t euid;
+ gid_t egid;
+ int ret = IPC_FAIL;
+
+ if (auth_info == NULL
+ || (auth_info->uid == NULL && auth_info->gid == NULL)) {
+ ret = IPC_OK; /* no restriction for authentication */
+ }
+ conn_info = (struct SOCKET_CH_PRIVATE *) ch->ch_private;
+
+ if (getpeereid(conn_info->s, &euid, &egid) < 0) {
+ cl_perror("getpeereid() failure");
+ return ret;
+ }
+
+ ch->farside_uid = euid;
+ ch->farside_gid = egid;
+
+ /* verify the credential information. */
+ return verify_creds(auth_info, euid, egid);
+}
+
+static
+pid_t
+socket_get_farside_pid(int sock)
+{
+ return -1;
+}
+#endif /* USE_GETPEEREID */
+
+/***********************************************************************
+ * SCM_CREDS VERSION... (*BSD systems)
+ ***********************************************************************/
+#ifdef USE_SCM_CREDS
+/* FIXME! Need to implement SCM_CREDS mechanism for BSD-based systems
+ * This isn't an emergency, but should be done in the future...
+ * Hint: * Postgresql does both types of authentication...
+ * see src/backend/libpq/auth.c
+ * Not clear its SO_PEERCRED implementation works though ;-)
+ */
+
+/* Done.... Haven't tested yet. */
+static int
+socket_verify_auth(struct IPC_CHANNEL* ch, struct IPC_AUTH * auth_info)
+{
+ struct msghdr msg;
+ /* Credentials structure */
+
+#define EXTRASPACE 0
+
+#ifdef HAVE_STRUCT_CMSGCRED
+ /* FreeBSD */
+ typedef struct cmsgcred Cred;
+# define crRuid cmcred_uid
+# define crEuid cmcred_euid
+# define crRgid cmcred_gid
+# define crEgid cmcred_groups[0] /* Best guess */
+# define crpid cmcred_pid
+# define crngrp cmcred_ngroups
+# define crgrps cmcred_groups
+
+#elif HAVE_STRUCT_FCRED
+ /* Stevens' book */
+ typedef struct fcred Cred;
+# define crRuid fc_uid
+# define crRgid fc_rgid
+# define crEgid fc_gid
+# define crngrp fc_ngroups
+# define crgrps fc_groups
+
+#elif HAVE_STRUCT_SOCKCRED
+ /* NetBSD */
+ typedef struct sockcred Cred;
+# define crRuid sc_uid
+# define crEuid sc_euid
+# define crRgid sc_gid
+# define crEgid sc_egid
+# define crngrp sc_ngroups
+# define crgrps sc_groups
+# undef EXTRASPACE
+# define EXTRASPACE SOCKCREDSIZE(ngroups)
+
+#elif HAVE_STRUCT_CRED
+ typedef struct cred Cred;
+#define cruid c_uid
+
+#elif HAVE_STRUCT_UCRED
+ typedef struct ucred Cred;
+
+ /* reuse this define for the moment */
+# if HAVE_STRUCT_UCRED_DARWIN
+# define crEuid cr_uid
+# define crEgid cr_groups[0] /* Best guess */
+# define crgrps cr_groups
+# define crngrp cr_ngroups
+# else
+# define crEuid c_uid
+# define crEgid c_gid
+# endif
+#else
+# error "No credential type found!"
+#endif
+
+ struct SOCKET_CH_PRIVATE *conn_info;
+ int ret = IPC_FAIL;
+ char buf;
+
+ /* Compute size without padding */
+ #define CMSGSIZE (sizeof(struct cmsghdr)+(sizeof(Cred))+EXTRASPACE)
+
+ union {
+ char mem[CMSGSIZE];
+ struct cmsghdr hdr;
+ Cred credu;
+ }cmsgmem;
+ Cred cred;
+
+ /* Point to start of first structure */
+ struct cmsghdr *cmsg = &cmsgmem.hdr;
+
+ if (auth_info == NULL
+ || (auth_info->uid == NULL && auth_info->gid == NULL)) {
+ ret = IPC_OK; /* no restriction for authentication */
+ }
+ conn_info = (struct SOCKET_CH_PRIVATE *) ch->ch_private;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = g_new(struct iovec, 1);
+ msg.msg_iovlen = 1;
+ msg.msg_control = (char *) cmsg;
+ msg.msg_controllen = CMSGSIZE;
+ memset(cmsg, 0, sizeof(cmsgmem));
+
+ /*
+ * The one character which is received here is not meaningful; its
+ * purpose is only to make sure that recvmsg() blocks long enough for
+ * the other side to send its credentials.
+ */
+ msg.msg_iov->iov_base = &buf;
+ msg.msg_iov->iov_len = 1;
+
+ if (recvmsg(conn_info->s, &msg, 0) < 0
+ || cmsg->cmsg_len < CMSGSIZE
+ || cmsg->cmsg_type != SCM_CREDS) {
+ cl_perror("can't get credential information from peer");
+ return ret;
+ }
+
+ /* Avoid alignment issues - just copy it! */
+ memcpy(&cred, CMSG_DATA(cmsg), sizeof(cred));
+
+ ch->farside_uid = cred.crEuid;
+ ch->farside_gid = cred.crEgid;
+ if (ret == IPC_OK) {
+ return ret;
+ }
+
+ /* verify the credential information. */
+ return verify_creds(auth_info, cred.crEuid, cred.crEgid);
+}
+
+/*
+ * FIXME! Need to implement SCM_CREDS mechanism for BSD-based systems
+ * this is similar to the SCM_CREDS mechanism for verify_auth() function.
+ * here we just want to get the pid of the other side from the credential
+ * information.
+ */
+
+static
+pid_t
+socket_get_farside_pid(int sock)
+{
+ /* FIXME! */
+ return -1;
+}
+#endif /* SCM_CREDS version */
+
+/***********************************************************************
+ * Bind/Stat VERSION... (Supported on OSX/Darwin and 4.3+BSD at least...)
+ *
+ * This is for use on systems such as OSX-Darwin where
+ * none of the other options is available.
+ *
+ * This implementation has been adapted from "Advanced Programming
+ * in the Unix Environment", Section 15.5.2, by W. Richard Stevens.
+ *
+ */
+#ifdef USE_BINDSTAT_CREDS
+
+static int
+socket_verify_auth(struct IPC_CHANNEL* ch, struct IPC_AUTH * auth_info)
+{
+ int len = 0;
+ int ret = IPC_FAIL;
+ struct stat stat_buf;
+ struct sockaddr_un *peer_addr = NULL;
+ struct SOCKET_CH_PRIVATE *ch_private = NULL;
+
+ if(ch != NULL) {
+ ch_private = (struct SOCKET_CH_PRIVATE *)(ch->ch_private);
+ if(ch_private != NULL) {
+ peer_addr = ch_private->peer_addr;
+ }
+ }
+
+ if(ch == NULL) {
+ cl_log(LOG_ERR, "No channel to authenticate");
+ return IPC_FAIL;
+
+ } else if (auth_info == NULL
+ || (auth_info->uid == NULL && auth_info->gid == NULL)) {
+ ret = IPC_OK; /* no restriction for authentication */
+
+ }
+
+ if(ch_private == NULL) {
+ cl_log(LOG_ERR, "No channel private data available");
+ return ret;
+
+ } else if(peer_addr == NULL) {
+ cl_log(LOG_ERR, "No peer information available");
+ return ret;
+ }
+
+ len = SUN_LEN(peer_addr);
+
+ if(len < 1) {
+ cl_log(LOG_ERR, "No peer information available");
+ return ret;
+ }
+ peer_addr->sun_path[len] = 0;
+ stat(peer_addr->sun_path, &stat_buf);
+
+ ch->farside_uid = stat_buf.st_uid;
+ ch->farside_gid = stat_buf.st_gid;
+ if (ret == IPC_OK) {
+ return ret;
+ }
+
+ if ((auth_info->uid == NULL || g_hash_table_size(auth_info->uid) == 0)
+ && auth_info->gid != NULL
+ && g_hash_table_size(auth_info->gid) != 0) {
+ cl_log(LOG_WARNING,
+ "GID-Only IPC security is not supported"
+ " on this platform.");
+ return IPC_BROKEN;
+ }
+
+ /* verify the credential information. */
+ return verify_creds(auth_info, stat_buf.st_uid, stat_buf.st_gid);
+}
+
+static pid_t
+socket_get_farside_pid(int sock)
+{
+ return -1;
+}
+#endif /* Bind/stat version */
+
+/***********************************************************************
+ * USE_STREAM_CREDS VERSION... (e.g. Solaris pre-10)
+ ***********************************************************************/
+#ifdef USE_STREAM_CREDS
+static int
+socket_verify_auth(struct IPC_CHANNEL* ch, struct IPC_AUTH * auth_info)
+{
+ struct SOCKET_CH_PRIVATE *conn_info;
+
+ if (ch == NULL || ch->ch_private == NULL) {
+ return IPC_FAIL;
+ }
+
+ conn_info = (struct SOCKET_CH_PRIVATE *) ch->ch_private;
+
+ ch->farside_uid = conn_info->farside_uid;
+ ch->farside_gid = conn_info->farside_gid;
+
+ /* verify the credential information. */
+ return verify_creds(auth_info,
+ conn_info->farside_uid, conn_info->farside_gid);
+}
+
+static
+pid_t
+socket_get_farside_pid(int sock)
+{
+ return -1;
+}
+#endif
+
+/***********************************************************************
+ * GETPEERUCRED VERSION... (e.g. Solaris 10 upwards)
+ ***********************************************************************/
+
+#ifdef USE_GETPEERUCRED
+/* verify the authentication information. */
+static int
+socket_verify_auth(struct IPC_CHANNEL* ch, struct IPC_AUTH * auth_info)
+{
+ struct SOCKET_CH_PRIVATE *conn_info;
+ ucred_t *ucred = NULL;
+ int rc = IPC_FAIL;
+
+ if (ch == NULL || ch->ch_private == NULL) {
+ return IPC_FAIL;
+ }
+
+ conn_info = (struct SOCKET_CH_PRIVATE *) ch->ch_private;
+
+ if (auth_info == NULL
+ || (auth_info->uid == NULL && auth_info->gid == NULL)) {
+ rc = IPC_OK; /* no restriction for authentication */
+ }
+
+ if (getpeerucred(conn_info->s, &ucred) < 0) {
+ cl_perror("getpeereid() failure");
+ return rc;
+ }
+
+ ch->farside_uid = ucred_geteuid(ucred);
+ ch->farside_gid = ucred_getegid(ucred);
+ if (rc == IPC_OK) {
+ return rc;
+ }
+
+ /* verify the credential information. */
+ rc = verify_creds(auth_info,
+ ucred_geteuid(ucred), ucred_getegid(ucred));
+ ucred_free(ucred);
+ return rc;
+}
+
+static
+pid_t
+socket_get_farside_pid(int sockfd)
+{
+ ucred_t *ucred = NULL;
+ pid_t pid;
+
+ if (getpeerucred(sockfd, &ucred) < 0) {
+ cl_perror("getpeereid() failure");
+ return IPC_FAIL;
+ }
+
+ pid = ucred_getpid(ucred);
+
+ ucred_free(ucred);
+
+ return pid;
+}
+#endif
+
+/***********************************************************************
+ * DUMMY VERSION... (other systems...)
+ *
+ * Other options that seem to be out there include
+ * SCM_CREDENTIALS and LOCAL_CREDS
+ * There are some kludgy things you can do with SCM_RIGHTS
+ * to pass an fd which could only be opened by the user id to
+ * validate the user id, but I don't know of a similar kludge which
+ * would work for group ids. And, even the uid one will fail
+ * if normal users are allowed to give away (chown) files.
+ *
+ * Unfortunately, this set of authentication routines have become
+ * very important to this API and its users (like heartbeat).
+ *
+ ***********************************************************************/
+
+#ifdef USE_DUMMY_CREDS
+static int
+socket_verify_auth(struct IPC_CHANNEL* ch, struct IPC_AUTH * auth_info)
+{
+ return IPC_FAIL;
+}
+
+static
+pid_t
+socket_get_farside_pid(int sock)
+{
+ return -1;
+}
+#endif /* Dummy version */
+
+/* socket object of the function table */
+static struct IPC_OPS socket_ops = {
+ socket_destroy_channel,
+ socket_initiate_connection,
+ socket_verify_auth,
+ socket_assert_auth,
+ socket_send,
+ socket_recv,
+ socket_waitin,
+ socket_waitout,
+ socket_is_message_pending,
+ socket_is_output_pending,
+ socket_resume_io,
+ socket_get_send_fd,
+ socket_get_recv_fd,
+ socket_set_send_qlen,
+ socket_set_recv_qlen,
+ socket_set_high_flow_callback,
+ socket_set_low_flow_callback,
+ socket_new_ipcmsg,
+ socket_get_chan_status,
+ socket_is_sendq_full,
+ socket_is_recvq_full,
+ socket_get_conntype,
+ socket_disconnect,
+};
diff --git a/lib/clplumbing/ipctest.c b/lib/clplumbing/ipctest.c
new file mode 100644
index 0000000..333d3a0
--- /dev/null
+++ b/lib/clplumbing/ipctest.c
@@ -0,0 +1,1377 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#undef _GNU_SOURCE /* in case it was defined on the command line */
+#define _GNU_SOURCE
+#include <lha_internal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+/* libgen.h: for 'basename()' on Solaris */
+#include <libgen.h>
+#include <glib.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/cl_poll.h>
+#include <clplumbing/GSource.h>
+#include <clplumbing/ipc.h>
+
+#define MAXERRORS 1000
+#define MAXERRORS_RECV 10
+
+typedef int (*TestFunc_t)(IPC_Channel*chan, int count);
+
+static int channelpair(TestFunc_t client, TestFunc_t server, int count);
+#if 0
+static void clientserverpair(IPC_Channel* channels[2]);
+#endif
+
+static int echoserver(IPC_Channel*, int repcount);
+static int echoclient(IPC_Channel*, int repcount);
+static int asyn_echoserver(IPC_Channel*, int repcount);
+static int asyn_echoclient(IPC_Channel*, int repcount);
+static int mainloop_server(IPC_Channel* chan, int repcount);
+static int mainloop_client(IPC_Channel* chan, int repcount);
+
+static int checksock(IPC_Channel* channel);
+static void checkifblocked(IPC_Channel* channel);
+
+static int (*PollFunc)(struct pollfd * fds, unsigned int, int)
+= (int (*)(struct pollfd * fds, unsigned int, int)) poll;
+static gboolean checkmsg(IPC_Message* rmsg, const char * who, int rcount);
+
+static const char *procname;
+
+static const int iter_def = 10000; /* number of iterations */
+static int verbosity; /* verbosity level */
+
+/*
+ * The ipc interface can be invoked as either:
+ * 1. pair (pipe/socketpair);
+ * 2. separate connect/accept (like server with multiple independent clients).
+ *
+ * If number of clients is given as 0, the "pair" mechanism is used,
+ * otherwise the client/server mechanism.
+ */
+/* *** CLIENTS_MAX currently 1 while coding *** */
+#define CLIENTS_MAX 1 /* max. number of independent clients */
+static int clients_def; /* number of independent clients */
+
+static int
+channelpair(TestFunc_t clientfunc, TestFunc_t serverfunc, int count)
+{
+ IPC_Channel* channels[2];
+ int rc = 0;
+ int waitstat = 0;
+
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: main process",
+ procname, (int)getpid(), __LINE__);
+ }
+ switch (fork()) {
+ case -1:
+ cl_perror("can't fork");
+ exit(1);
+ break;
+ default: /* Parent */
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: main waiting...",
+ procname, (int)getpid(), __LINE__);
+ }
+ while (wait(&waitstat) > 0) {
+ if (WIFEXITED(waitstat)) {
+ rc += WEXITSTATUS(waitstat);
+ }else{
+ rc += 1;
+ }
+ }
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: main ended rc: %d",
+ procname, (int)getpid(), __LINE__, rc);
+ }
+ if (rc > 127) {
+ rc = 127;
+ }
+ exit(rc);
+ break;
+ case 0: /* Child */
+ break;
+ }
+ /* Child continues here... */
+ if (ipc_channel_pair(channels) != IPC_OK) {
+ cl_perror("Can't create ipc channel pair");
+ exit(1);
+ }
+ checksock(channels[0]);
+ checksock(channels[1]);
+ switch (fork()) {
+ case -1:
+ cl_perror("can't fork");
+ exit(1);
+ break;
+
+ case 0: /* echo "client" Child */
+ channels[1]->ops->destroy(channels[1]);
+ channels[1] = NULL;
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: client starting...",
+ procname, (int)getpid(), __LINE__);
+ }
+ rc = clientfunc(channels[0], count);
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: client ended rc:%d",
+ procname, (int)getpid(), __LINE__, rc);
+ }
+ exit (rc > 127 ? 127 : rc);
+ break;
+
+ default:
+ break;
+ }
+ channels[0]->ops->destroy(channels[0]);
+ channels[0] = NULL;
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: server starting...",
+ procname, (int)getpid(), __LINE__);
+ }
+ rc = serverfunc(channels[1], count);
+ wait(&waitstat);
+ if (WIFEXITED(waitstat)) {
+ rc += WEXITSTATUS(waitstat);
+ }else{
+ rc += 1;
+ }
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: server ended rc:%d",
+ procname, (int)getpid(), __LINE__, rc);
+ }
+ return(rc);
+}
+
+/* server with many clients */
+static int
+clientserver(TestFunc_t clientfunc, TestFunc_t serverfunc, int count, int clients)
+{
+ IPC_Channel* channel;
+ int rc = 0;
+ int waitstat = 0;
+ struct IPC_WAIT_CONNECTION *wconn;
+ char path[] = IPC_PATH_ATTR;
+ char commpath[] = "/tmp/foobar"; /* *** CHECK/FIX: Is this OK? */
+ GHashTable * wattrs;
+ int i;
+ pid_t pid;
+
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: main process",
+ procname, (int)getpid(), __LINE__);
+ }
+
+ switch (fork()) {
+ case -1:
+ cl_perror("can't fork");
+ exit(1);
+ break;
+ default: /* Parent */
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: main waiting...",
+ procname, (int)getpid(), __LINE__);
+ }
+ while ((pid = wait(&waitstat)) > 0) {
+ if (WIFEXITED(waitstat)) {
+ rc += WEXITSTATUS(waitstat);
+ }else{
+ rc += 1;
+ }
+ }
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: main ended rc: %d",
+ procname, (int)getpid(), __LINE__, rc);
+ }
+ if (rc > 127) {
+ rc = 127;
+ }
+ exit(rc);
+ break;
+ case 0: /* Child */
+ break;
+ }
+
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d:",
+ procname, (int)getpid(), __LINE__);
+ }
+
+ /* set up a server */
+ wattrs = g_hash_table_new(g_str_hash, g_str_equal);
+ if (! wattrs) {
+ cl_perror("g_hash_table_new() failed");
+ exit(1);
+ }
+ g_hash_table_insert(wattrs, path, commpath);
+
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d:",
+ procname, (int)getpid(), __LINE__);
+ }
+
+ wconn = ipc_wait_conn_constructor(IPC_ANYTYPE, wattrs);
+ if (! wconn) {
+ cl_perror("could not establish server");
+ exit(1);
+ }
+
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d:",
+ procname, (int)getpid(), __LINE__);
+ }
+
+ /* spawn the clients */
+ for (i = 1; i <= clients; i++) {
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: fork client %d of %d",
+ procname, (int)getpid(), __LINE__, i, clients);
+ }
+ switch (fork()) {
+ case -1:
+ cl_perror("can't fork");
+ exit(1);
+ break;
+
+ case 0: /* echo "client" Child */
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: client %d starting...",
+ procname, (int)getpid(), __LINE__, i);
+ }
+ channel = ipc_channel_constructor(IPC_ANYTYPE, wattrs);
+ if (channel == NULL) {
+ cl_perror("client: channel creation failed");
+ exit(1);
+ }
+
+ rc = channel->ops->initiate_connection(channel);
+ if (rc != IPC_OK) {
+ cl_perror("channel[1] failed to connect");
+ exit(1);
+ }
+ checksock(channel);
+ rc = clientfunc(channel, count);
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: client %d ended rc:%d",
+ procname, (int)getpid(), __LINE__, rc, i);
+ }
+ exit (rc > 127 ? 127 : rc);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: server starting...",
+ procname, (int)getpid(), __LINE__);
+ }
+ /* accept on server */
+ /* ***
+ * Two problems (or more) here:
+ * 1. What to do if no incoming call pending?
+ * At present, fudge by sleeping a little so client gets started.
+ * 2. How to handle multiple clients?
+ * Would need to be able to await both new connections and
+ * data on existing connections.
+ * At present, fudge CLIENTS_MAX as 1.
+ * ***
+ */
+ sleep(1); /* *** */
+ channel = wconn->ops->accept_connection(wconn, NULL);
+ if (channel == NULL) {
+ cl_perror("server: acceptance failed");
+ }
+
+ checksock(channel);
+
+ rc = serverfunc(channel, count);
+
+ /* server finished: tidy up */
+ wconn->ops->destroy(wconn);
+
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: server ended rc:%d",
+ procname, (int)getpid(), __LINE__, rc);
+ }
+
+ /* reap the clients */
+ for (i = 1; i <= clients; i++) {
+ pid_t pid;
+
+ pid = wait(&waitstat);
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: client %d reaped:%d",
+ procname, (int)getpid(), __LINE__,
+ (int) pid, WIFEXITED(waitstat));
+ }
+ if (WIFEXITED(waitstat)) {
+ rc += WEXITSTATUS(waitstat);
+ }else{
+ rc += 1;
+ }
+ }
+
+ return(rc);
+}
+
+static void
+echomsgbody(void * body, int n, int niter, size_t * len)
+{
+ char *str = body;
+ int l;
+
+ l = snprintf(str, n-1, "String-%d", niter);
+ if (l < (n-1)) {
+ memset(&str[l], 'a', (n - (l+1)));
+ }
+ str[n-1] = '\0';
+ *len = n;
+}
+
+static void
+checkifblocked(IPC_Channel* chan)
+{
+ if (chan->ops->is_sending_blocked(chan)) {
+ cl_log(LOG_INFO, "Sending is blocked.");
+ chan->ops->resume_io(chan);
+ }
+}
+
+#ifdef CHEAT_CHECKS
+extern long SeqNums[32];
+#endif
+
+static int
+transport_tests(int iterations, int clients)
+{
+ int rc = 0;
+
+#ifdef CHEAT_CHECKS
+ memset(SeqNums, 0, sizeof(SeqNums));
+#endif
+ rc += (clients <= 0)
+ ? channelpair(echoclient, echoserver, iterations)
+ : clientserver(echoclient, echoserver, iterations, clients);
+
+#ifdef CHEAT_CHECKS
+ memset(SeqNums, 0, sizeof(SeqNums));
+#endif
+ rc += (clients <= 0)
+ ? channelpair(asyn_echoclient, asyn_echoserver, iterations)
+ : clientserver(asyn_echoclient, asyn_echoserver, iterations, clients);
+
+#ifdef CHEAT_CHECKS
+ memset(SeqNums, 0, sizeof(SeqNums));
+#endif
+ rc += (clients <= 0)
+ ? channelpair(mainloop_client, mainloop_server, iterations)
+ : clientserver(mainloop_client, mainloop_server, iterations, clients);
+
+ return rc;
+}
+
+static int data_size = 20;
+
+int
+main(int argc, char ** argv)
+{
+ int argflag, argerrs;
+ int iterations;
+ int clients;
+ int rc = 0;
+
+ /*
+ * Check and process arguments.
+ * -v: verbose
+ * -i: number of iterations
+ * -c: number of clients (invokes client/server mechanism)
+ * -s: data-size
+ */
+ procname = basename(argv[0]);
+
+ argerrs = 0;
+ iterations = iter_def;
+ clients = clients_def;
+ while ((argflag = getopt(argc, argv, "i:vuc:s:")) != EOF) {
+ switch (argflag) {
+ case 'i': /* iterations */
+ iterations = atoi(optarg);
+ break;
+ case 'v': /* verbosity */
+ verbosity++;
+ break;
+ case 'c': /* number of clients */
+ clients = atoi(optarg);
+ if (clients < 1 || clients > CLIENTS_MAX) {
+ fprintf(stderr, "number of clients out of range"
+ "(1 to %d)\n", CLIENTS_MAX);
+ argerrs++;
+ }
+ break;
+ case 's': /* data size */
+ data_size = atoi(optarg);
+ if (data_size < 0) {
+ fprintf(stderr, "data size must be >=0\n");
+ argerrs++;
+ }
+ if (data_size > MAXMSG) {
+ fprintf(stderr, "maximum data size is %d\n", MAXMSG);
+ argerrs++;
+ }
+ break;
+ default:
+ argerrs++;
+ break;
+ }
+ }
+ if (argerrs) {
+ fprintf(stderr,
+ "Usage: %s [-v] [-i iterations] [-c clients] [-s size]\n"
+ "\t-v : verbose\n"
+ "\t-i : iterations (default %d)\n"
+ "\t-c : number of clients (default %d; nonzero invokes client/server)\n"
+ "\t-s : data size (default 20 bytes)\n",
+ procname, iter_def, clients_def);
+ exit(1);
+ }
+
+ cl_log_set_entity(procname);
+ cl_log_enable_stderr(TRUE);
+
+
+
+ rc += transport_tests(iterations, clients);
+
+#if 0
+ /* Broken for the moment - need to fix it long term */
+ cl_log(LOG_INFO, "NOTE: Enabling poll(2) replacement code.");
+ PollFunc = cl_poll;
+ g_main_set_poll_func(cl_glibpoll);
+ ipc_set_pollfunc(cl_poll);
+
+ rc += transport_tests(5 * iterations, clients);
+#endif
+
+ cl_log(LOG_INFO, "TOTAL errors: %d", rc);
+
+ return (rc > 127 ? 127 : rc);
+}
+
+static int
+checksock(IPC_Channel* channel)
+{
+
+ if (!channel) {
+ cl_log(LOG_ERR, "Channel null");
+ return 1;
+ }
+ if (!IPC_ISRCONN(channel)) {
+ cl_log(LOG_ERR, "Channel status is %d"
+ ", not IPC_CONNECT", channel->ch_status);
+ return 1;
+ }
+ return 0;
+}
+
+static void
+EOFcheck(IPC_Channel* chan)
+{
+ int fd = chan->ops->get_recv_select_fd(chan);
+ struct pollfd pf[1];
+ int rc;
+
+ cl_log(LOG_INFO, "channel state: %d", chan->ch_status);
+
+ if (chan->recv_queue->current_qlen > 0) {
+ cl_log(LOG_INFO, "EOF Receive queue has %ld messages in it"
+ , (long)chan->recv_queue->current_qlen);
+ }
+ if (fd <= 0) {
+ cl_log(LOG_INFO, "EOF receive fd: %d", fd);
+ }
+
+
+ pf[0].fd = fd;
+ pf[0].events = POLLIN|POLLHUP;
+ pf[0].revents = 0;
+
+ rc = poll(pf, 1, 0);
+
+ if (rc < 0) {
+ cl_perror("failed poll(2) call in EOFcheck");
+ return;
+ }
+
+ /* Got input? */
+ if (pf[0].revents & POLLIN) {
+ cl_log(LOG_INFO, "EOF socket %d (still) has input ready (real poll)"
+ , fd);
+ }
+ if ((pf[0].revents & ~(POLLIN|POLLHUP)) != 0) {
+ cl_log(LOG_INFO, "EOFcheck poll(2) bits: 0x%lx"
+ , (unsigned long)pf[0].revents);
+ }
+ pf[0].fd = fd;
+ pf[0].events = POLLIN|POLLHUP;
+ pf[0].revents = 0;
+ rc = PollFunc(pf, 1, 0);
+ if (rc < 0) {
+ cl_perror("failed PollFunc() call in EOFcheck");
+ return;
+ }
+
+ /* Got input? */
+ if (pf[0].revents & POLLIN) {
+ cl_log(LOG_INFO, "EOF socket %d (still) has input ready (PollFunc())"
+ , fd);
+ }
+ if ((pf[0].revents & ~(POLLIN|POLLHUP)) != 0) {
+ cl_log(LOG_INFO, "EOFcheck PollFunc() bits: 0x%lx"
+ , (unsigned long)pf[0].revents);
+ }
+}
+
+static int
+echoserver(IPC_Channel* wchan, int repcount)
+{
+ char *str;
+ int j;
+ int errcount = 0;
+ IPC_Message wmsg;
+ IPC_Message* rmsg = NULL;
+
+ if (!(str = malloc(data_size))) {
+ cl_log(LOG_ERR, "Out of memory");
+ exit(1);
+ }
+
+ memset(&wmsg, 0, sizeof(wmsg));
+ wmsg.msg_private = NULL;
+ wmsg.msg_done = NULL;
+ wmsg.msg_body = str;
+ wmsg.msg_buf = NULL;
+ wmsg.msg_ch = wchan;
+
+ cl_log(LOG_INFO, "Echo server: %d reps pid %d.", repcount, getpid());
+ for (j=1; j <= repcount
+ ;++j, rmsg != NULL && (rmsg->msg_done(rmsg),1)) {
+ int rc;
+
+ echomsgbody(str, data_size, j, &(wmsg.msg_len));
+ if ((rc = wchan->ops->send(wchan, &wmsg)) != IPC_OK) {
+ cl_log(LOG_ERR
+ , "echotest: send failed %d rc iter %d"
+ , rc, j);
+ ++errcount;
+ continue;
+ }
+
+ /*fprintf(stderr, "+"); */
+ wchan->ops->waitout(wchan);
+ checkifblocked(wchan);
+ /*fprintf(stderr, "S"); */
+
+ /* Try and induce a failure... */
+ if (j == repcount) {
+ sleep(1);
+ }
+
+ while ((rc = wchan->ops->waitin(wchan)) == IPC_INTR);
+
+ if (rc != IPC_OK) {
+ cl_log(LOG_ERR
+ , "echotest server: waitin failed %d rc iter %d"
+ " errno=%d"
+ , rc, j, errno);
+ cl_perror("waitin");
+ exit(1);
+ }
+
+ /*fprintf(stderr, "-"); */
+ if ((rc = wchan->ops->recv(wchan, &rmsg)) != IPC_OK) {
+ cl_log(LOG_ERR
+ , "echotest server: recv failed %d rc iter %d"
+ " errno=%d"
+ , rc, j, errno);
+ cl_perror("recv");
+ ++errcount;
+ rmsg=NULL;
+ continue;
+ }
+ /*fprintf(stderr, "s"); */
+ if (rmsg->msg_len != wmsg.msg_len) {
+ cl_log(LOG_ERR
+ , "echotest: length mismatch [%lu,%lu] iter %d"
+ , (unsigned long)rmsg->msg_len
+ , (unsigned long)wmsg.msg_len, j);
+ ++errcount;
+ continue;
+ }
+ if (strncmp(rmsg->msg_body, wmsg.msg_body, wmsg.msg_len)
+ != 0) {
+ cl_log(LOG_ERR
+ , "echotest: data mismatch. iteration %d"
+ , j);
+ ++errcount;
+ continue;
+ }
+
+ }
+ cl_log(LOG_INFO, "echoserver: %d errors", errcount);
+#if 0
+ cl_log(LOG_INFO, "destroying channel 0x%lx", (unsigned long)wchan);
+#endif
+ wchan->ops->destroy(wchan); wchan = NULL;
+
+ free(str);
+
+ return errcount;
+}
+static int
+echoclient(IPC_Channel* rchan, int repcount)
+{
+ int j;
+ int errcount = 0;
+ IPC_Message* rmsg;
+
+
+
+ cl_log(LOG_INFO, "Echo client: %d reps pid %d."
+ , repcount, (int)getpid());
+ for (j=1; j <= repcount ;++j) {
+
+ int rc;
+
+ while ((rc = rchan->ops->waitin(rchan)) == IPC_INTR);
+
+ if (rc != IPC_OK) {
+ cl_log(LOG_ERR
+ , "echotest client: waitin failed %d rc iter %d"
+ " errno=%d"
+ , rc, j, errno);
+ cl_perror("waitin");
+ exit(1);
+ }
+ /*fprintf(stderr, "/"); */
+
+ if ((rc = rchan->ops->recv(rchan, &rmsg)) != IPC_OK) {
+ cl_log(LOG_ERR
+ , "echoclient: recv failed %d rc iter %d"
+ " errno=%d"
+ , rc, j, errno);
+ cl_perror("recv");
+ ++errcount;
+ if (errcount > MAXERRORS_RECV) {
+ cl_log(LOG_ERR,
+ "echoclient: errcount excessive: %d: abandoning",
+ errcount);
+ exit(1);
+ }
+ --j;
+ rmsg=NULL;
+ continue;
+ }
+ /*fprintf(stderr, "c"); */
+ if ((rc = rchan->ops->send(rchan, rmsg)) != IPC_OK) {
+ cl_log(LOG_ERR
+ , "echoclient: send failed %d rc iter %d"
+ , rc, j);
+ cl_log(LOG_INFO, "Message being sent: %s"
+ , (char*)rmsg->msg_body);
+ ++errcount;
+ continue;
+ }
+ /*fprintf(stderr, "%%"); */
+ rchan->ops->waitout(rchan);
+ checkifblocked(rchan);
+ /*fprintf(stderr, "C"); */
+ }
+ cl_log(LOG_INFO, "echoclient: %d errors", errcount);
+#if 0
+ cl_log(LOG_INFO, "destroying channel 0x%lx", (unsigned long)rchan);
+#endif
+ rchan->ops->destroy(rchan); rchan = NULL;
+ return errcount;
+}
+
+void dump_ipc_info(IPC_Channel* chan);
+
+static int
+checkinput(IPC_Channel* chan, const char * where, int* rdcount, int maxcount)
+{
+ IPC_Message* rmsg = NULL;
+ int errs = 0;
+ int rc;
+
+ while (chan->ops->is_message_pending(chan)
+ && errs < 10 && *rdcount < maxcount) {
+
+ if (chan->ch_status == IPC_DISCONNECT && *rdcount < maxcount){
+ cl_log(LOG_ERR
+ , "checkinput1[0x%lx %s]: EOF in iter %d"
+ , (unsigned long)chan, where, *rdcount);
+ EOFcheck(chan);
+ }
+
+ if (rmsg != NULL) {
+ rmsg->msg_done(rmsg);
+ rmsg = NULL;
+ }
+
+ if ((rc = chan->ops->recv(chan, &rmsg)) != IPC_OK) {
+ if (chan->ch_status == IPC_DISCONNECT) {
+ cl_log(LOG_ERR
+ , "checkinput2[0x%lx %s]: EOF in iter %d"
+ , (unsigned long)chan, where, *rdcount);
+ EOFcheck(chan);
+ return errs;
+ }
+ cl_log(LOG_ERR
+ , "checkinput[%s]: recv"
+ " failed: rc %d rdcount %d errno=%d"
+ , where, rc, *rdcount, errno);
+ cl_perror("recv");
+ rmsg=NULL;
+ ++errs;
+ continue;
+ }
+ *rdcount += 1;
+ if (!checkmsg(rmsg, where, *rdcount)) {
+ dump_ipc_info(chan);
+ ++errs;
+ }
+ if (*rdcount < maxcount && chan->ch_status == IPC_DISCONNECT){
+ cl_log(LOG_ERR
+ , "checkinput3[0x%lx %s]: EOF in iter %d"
+ , (unsigned long)chan, where, *rdcount);
+ EOFcheck(chan);
+ }
+
+ }
+ return errs;
+}
+
+static void
+async_high_flow_callback(IPC_Channel* ch, void* userdata)
+{
+ int* stopsending = userdata;
+
+ if (userdata == NULL){
+ cl_log(LOG_ERR, "userdata is NULL");
+ return;
+ }
+
+ *stopsending = 1;
+
+}
+
+static void
+async_low_flow_callback(IPC_Channel* ch, void* userdata)
+{
+
+ int* stopsending = userdata;
+
+ if (userdata == NULL){
+ cl_log(LOG_ERR, "userdata is NULL");
+ return;
+ }
+
+ *stopsending = 0;
+
+}
+
+
+static int
+asyn_echoserver(IPC_Channel* wchan, int repcount)
+{
+ int rdcount = 0;
+ int wrcount = 0;
+ int errcount = 0;
+ int blockedcount = 0;
+ IPC_Message* wmsg;
+ const char* w = "asyn_echoserver";
+ int stopsending = 0;
+
+ cl_log(LOG_INFO, "Asyn echo server: %d reps pid %d."
+ , repcount, (int)getpid());
+
+ (void)async_high_flow_callback;
+ (void)async_low_flow_callback;
+
+
+ wchan->ops->set_high_flow_callback(wchan, async_high_flow_callback, &stopsending);
+ wchan->ops->set_low_flow_callback(wchan, async_low_flow_callback, &stopsending);
+
+ wchan->low_flow_mark = 2;
+ wchan->high_flow_mark = 20;
+
+ while (rdcount < repcount) {
+ int rc;
+
+ while (wrcount < repcount && blockedcount < 10
+ && wchan->ch_status != IPC_DISCONNECT
+ ){
+
+ if (!stopsending){
+ ++wrcount;
+ if (wrcount > repcount) {
+ break;
+ }
+ wmsg = wchan->ops->new_ipcmsg(wchan, NULL, data_size, NULL);
+ echomsgbody(wmsg->msg_body, data_size, wrcount, &wmsg->msg_len);
+ if ((rc = wchan->ops->send(wchan, wmsg)) != IPC_OK){
+
+ cl_log(LOG_INFO, "channel sstatus in echo server is %d",
+ wchan->ch_status);
+ if (wchan->ch_status != IPC_CONNECT) {
+ cl_log(LOG_ERR
+ , "asyn_echoserver: send failed"
+ " %d rc iter %d"
+ , rc, wrcount);
+ ++errcount;
+ continue;
+ }else {/*send failed because of channel busy
+ * roll back
+ */
+ --wrcount;
+ }
+ }
+
+ if (wchan->ops->is_sending_blocked(wchan)) {
+ /* fprintf(stderr, "b"); */
+ ++blockedcount;
+ }else{
+ blockedcount = 0;
+ }
+ }
+
+
+ errcount += checkinput(wchan, w, &rdcount, repcount);
+ if (wrcount < repcount
+ && wchan->ch_status == IPC_DISCONNECT) {
+ ++errcount;
+ break;
+ }
+ }
+
+/* cl_log(LOG_INFO, "async_echoserver: wrcount =%d rdcount=%d B", wrcount, rdcount); */
+
+ wchan->ops->waitout(wchan);
+ errcount += checkinput(wchan, w, &rdcount, repcount);
+ if (wrcount >= repcount && rdcount < repcount) {
+ while ((rc = wchan->ops->waitin(wchan)) == IPC_INTR);
+
+ if (rc != IPC_OK) {
+ cl_log(LOG_ERR
+ , "asyn_echoserver: waitin()"
+ " failed %d rc rdcount %d errno=%d"
+ , rc, rdcount, errno);
+ cl_perror("waitin");
+ exit(1);
+ }
+ }
+ if (wchan->ch_status == IPC_DISCONNECT
+ && rdcount < repcount) {
+ cl_log(LOG_ERR,
+ "asyn_echoserver: EOF in iter %d (wrcount=%d)",
+ rdcount, wrcount);
+ EOFcheck(wchan);
+ ++errcount;
+ break;
+ }
+
+ blockedcount = 0;
+
+ }
+
+ cl_log(LOG_INFO, "asyn_echoserver: %d errors", errcount);
+#if 0
+ cl_log(LOG_INFO, "%d destroying channel 0x%lx", getpid(), (unsigned long)wchan);
+#endif
+ wchan->ops->destroy(wchan); wchan = NULL;
+ return errcount;
+}
+
+static int
+asyn_echoclient(IPC_Channel* chan, int repcount)
+{
+ int rdcount = 0;
+ int wrcount = 0;
+ int errcount = 0;
+ IPC_Message* rmsg;
+ int rfd = chan->ops->get_recv_select_fd(chan);
+ int wfd = chan->ops->get_send_select_fd(chan);
+ gboolean rdeqwr = (rfd == wfd);
+
+
+ cl_log(LOG_INFO, "Async Echo client: %d reps pid %d."
+ , repcount, (int)getpid());
+ ipc_set_pollfunc(PollFunc);
+
+ while (rdcount < repcount && errcount < repcount) {
+
+ int rc;
+ struct pollfd pf[2];
+ int nfd = 1;
+
+ pf[0].fd = rfd;
+ pf[0].events = POLLIN|POLLHUP;
+
+
+ if (chan->ops->is_sending_blocked(chan)) {
+ if (rdeqwr) {
+ pf[0].events |= POLLOUT;
+ }else{
+ nfd = 2;
+ pf[1].fd = wfd;
+ pf[1].events = POLLOUT|POLLHUP;
+ }
+ }
+
+ /* Have input? */
+ /* fprintf(stderr, "i"); */
+ while (chan->ops->is_message_pending(chan)
+ && rdcount < repcount) {
+ /*fprintf(stderr, "r"); */
+
+ if ((rc = chan->ops->recv(chan, &rmsg)) != IPC_OK) {
+ if (!IPC_ISRCONN(chan)) {
+ cl_log(LOG_ERR
+ , "Async echoclient: disconnect"
+ " iter %d", rdcount+1);
+ ++errcount;
+ return errcount;
+ }
+ cl_log(LOG_ERR
+ , "Async echoclient: recv"
+ " failed %d rc iter %d errno=%d"
+ , rc, rdcount+1, errno);
+ cl_perror("recv");
+ rmsg=NULL;
+ ++errcount;
+ cl_log(LOG_INFO, "sleep(1)");
+ sleep(1);
+ continue;
+ }
+ /*fprintf(stderr, "c"); */
+ ++rdcount;
+
+
+ do {
+ rc = chan->ops->send(chan, rmsg);
+
+ }while (rc != IPC_OK && chan->ch_status == IPC_CONNECT);
+
+ if (chan->ch_status != IPC_CONNECT){
+ ++errcount;
+ cl_perror("send");
+ cl_log(LOG_ERR
+ , "Async echoclient: send failed"
+ " rc %d, iter %d", rc, rdcount);
+ cl_log(LOG_INFO, "Message being sent: %s"
+ , (char*)rmsg->msg_body);
+ if (!IPC_ISRCONN(chan)) {
+ cl_log(LOG_ERR
+ , "Async echoclient: EOF(2)"
+ " iter %d", rdcount+1);
+ EOFcheck(chan);
+ return errcount;
+ }
+ continue;
+
+ }
+
+
+ ++wrcount;
+ /*fprintf(stderr, "x"); */
+ }
+ if (rdcount >= repcount) {
+ break;
+ }
+ /*
+ * At this point it is possible that the POLLOUT bit
+ * being on is no longer necessary, but this will only
+ * cause an extra (false) output poll iteration at worst...
+ * This is because (IIRC) both is_sending_blocked(), and
+ * is_message_pending() both perform a resume_io().
+ * This might be confusing, but -- oh well...
+ */
+
+ /*
+ fprintf(stderr, "P");
+ cl_log(LOG_INFO, "poll[%d, 0x%x]"
+ , pf[0].fd, pf[0].events);
+ cl_log(LOG_DEBUG, "poll[%d, 0x%x]..."
+ , pf[0].fd, pf[0].events);
+ fprintf(stderr, "%%");
+ cl_log(LOG_DEBUG, "CallingPollFunc()");
+ */
+ rc = PollFunc(pf, nfd, -1);
+
+ /* Bad poll? */
+ if (rc <= 0) {
+ cl_perror("Async echoclient: bad poll rc."
+ " %d rc iter %d", rc, rdcount);
+ ++errcount;
+ continue;
+ }
+
+ /* Error indication? */
+ if ((pf[0].revents & (POLLERR|POLLNVAL)) != 0) {
+ cl_log(LOG_ERR
+ , "Async echoclient: bad poll revents."
+ " revents: 0x%x iter %d", pf[0].revents, rdcount);
+ ++errcount;
+ continue;
+ }
+
+ /* HUP without input... Premature EOF... */
+ if ((pf[0].revents & POLLHUP)
+ && ((pf[0].revents&POLLIN) == 0)) {
+ cl_log(LOG_ERR
+ , "Async echoclient: premature pollhup."
+ " revents: 0x%x iter %d", pf[0].revents, rdcount);
+ EOFcheck(chan);
+ ++errcount;
+ continue;
+ }
+
+ /* Error indication? */
+ if (nfd > 1
+ && (pf[1].revents & (POLLERR|POLLNVAL)) != 0) {
+ cl_log(LOG_ERR
+ , "Async echoclient: bad poll revents[1]."
+ " revents: 0x%x iter %d", pf[1].revents, rdcount);
+ ++errcount;
+ continue;
+ }
+
+ /* Output unblocked (only) ? */
+ if (pf[nfd-1].revents & POLLOUT) {
+ /*fprintf(stderr, "R");*/
+ chan->ops->resume_io(chan);
+ }else if ((pf[0].revents & POLLIN) == 0) {
+ /* Neither I nor O available... */
+ cl_log(LOG_ERR
+ , "Async echoclient: bad events."
+ " revents: 0x%x iter %d", pf[0].revents, rdcount);
+ ++errcount;
+ }
+ }
+ cl_poll_ignore(rfd);
+ cl_poll_ignore(wfd);
+ cl_log(LOG_INFO, "Async echoclient: %d errors, %d reads, %d writes",
+ errcount, rdcount, wrcount);
+#if 0
+ cl_log(LOG_INFO, "%d destroying channel 0x%lx",getpid(), (unsigned long)chan);
+#endif
+
+
+ chan->ops->waitout(chan);
+
+ chan->ops->destroy(chan); chan = NULL;
+ return errcount;
+}
+
+
+struct iterinfo {
+ int wcount;
+ int rcount;
+ int errcount;
+ IPC_Channel* chan;
+ int max;
+ gboolean sendingsuspended;
+};
+
+static GMainLoop* loop = NULL;
+
+
+
+
+static gboolean
+s_send_msg(gpointer data)
+{
+ struct iterinfo*i = data;
+ IPC_Message* wmsg;
+ int rc;
+
+ ++i->wcount;
+
+ wmsg = i->chan->ops->new_ipcmsg(i->chan, NULL, data_size, NULL);
+ echomsgbody(wmsg->msg_body, data_size, i->wcount, &wmsg->msg_len);
+
+ /*cl_log(LOG_INFO, "s_send_msg: sending out %d", i->wcount);*/
+
+ if ((rc = i->chan->ops->send(i->chan, wmsg)) != IPC_OK) {
+ cl_log(LOG_ERR
+ , "s_send_msg: send failed"
+ " %d rc iter %d"
+ , rc, i->wcount);
+ cl_log(LOG_ERR
+ , "s_send_msg: channel status: %d qlen: %ld"
+ , i->chan->ch_status
+ , (long)i->chan->send_queue->current_qlen);
+ ++i->errcount;
+ if (i->chan->ch_status != IPC_CONNECT) {
+ cl_log(LOG_ERR, "s_send_msg: Exiting.");
+ return FALSE;
+ }
+ if (i->errcount >= MAXERRORS) {
+ g_main_quit(loop);
+ return FALSE;
+ }
+ }
+ return !i->sendingsuspended?i->wcount < i->max: FALSE;
+}
+
+
+
+
+static void
+mainloop_low_flow_callback(IPC_Channel* ch, void* userdata)
+{
+
+ struct iterinfo* i = (struct iterinfo*) userdata;
+
+ if (userdata == NULL){
+ cl_log(LOG_ERR, "userdata is NULL");
+ return;
+ }
+
+ if (i->sendingsuspended){
+ i->sendingsuspended = FALSE;
+ g_idle_add(s_send_msg, i);
+ }
+
+ return;
+
+}
+
+static void
+mainloop_high_flow_callback(IPC_Channel* ch, void* userdata)
+{
+ struct iterinfo* i = (struct iterinfo*) userdata;
+
+ if (userdata == NULL){
+ cl_log(LOG_ERR, "userdata is NULL");
+ return;
+ }
+
+ i->sendingsuspended = TRUE;
+
+}
+
+
+static gboolean
+s_rcv_msg(IPC_Channel* chan, gpointer data)
+{
+ struct iterinfo*i = data;
+
+ i->errcount += checkinput(chan, "s_rcv_msg", &i->rcount, i->max);
+
+ if (chan->ch_status == IPC_DISCONNECT
+ || i->rcount >= i->max || i->errcount > MAXERRORS) {
+ if (i->rcount < i->max) {
+ ++i->errcount;
+ cl_log(LOG_INFO, "Early exit from s_rcv_msg");
+ }
+ g_main_quit(loop);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+checkmsg(IPC_Message* rmsg, const char * who, int rcount)
+{
+ char *str;
+ size_t len;
+
+ if (!(str = malloc(data_size))) {
+ cl_log(LOG_ERR, "Out of memory");
+ exit(1);
+ }
+
+ echomsgbody(str, data_size, rcount, &len);
+
+ if (rmsg->msg_len != len) {
+ cl_log(LOG_ERR
+ , "checkmsg[%s]: length mismatch"
+ " [expected %u, got %lu] iteration %d"
+ , who, (unsigned)len
+ , (unsigned long)rmsg->msg_len
+ , rcount);
+ cl_log(LOG_ERR
+ , "checkmsg[%s]: expecting [%s]"
+ , who, str);
+ cl_log(LOG_ERR
+ , "checkmsg[%s]: got [%s] instead"
+ , who, (const char *)rmsg->msg_body);
+ return FALSE;
+ }
+ if (strncmp(rmsg->msg_body, str, len) != 0) {
+ cl_log(LOG_ERR
+ , "checkmsg[%s]: data mismatch"
+ ". input iteration %d"
+ , who, rcount);
+ cl_log(LOG_ERR
+ , "checkmsg[%s]: expecting [%s]"
+ , who, str);
+ cl_log(LOG_ERR
+ , "checkmsg[%s]: got [%s] instead"
+ , who, (const char *)rmsg->msg_body);
+ return FALSE;
+#if 0
+ }else if (strcmp(who, "s_rcv_msg") == 0) {
+#if 0
+
+ || strcmp(who, "s_echo_msg") == 0) {
+#endif
+ cl_log(LOG_ERR
+ , "checkmsg[%s]: data Good"
+ "! input iteration %d"
+ , who, rcount);
+#endif
+ }
+
+ free(str);
+
+ return TRUE;
+}
+
+static gboolean
+s_echo_msg(IPC_Channel* chan, gpointer data)
+{
+ struct iterinfo* i = data;
+ int rc;
+ IPC_Message* rmsg;
+
+ while (chan->ops->is_message_pending(chan)) {
+ if (chan->ch_status == IPC_DISCONNECT) {
+ break;
+ }
+
+ if ((rc = chan->ops->recv(chan, &rmsg)) != IPC_OK) {
+ cl_log(LOG_ERR
+ , "s_echo_msg: recv failed %d rc iter %d"
+ " errno=%d"
+ , rc, i->rcount+1, errno);
+ cl_perror("recv");
+ ++i->errcount;
+ goto retout;
+ }
+ i->rcount++;
+ if (!checkmsg(rmsg, "s_echo_msg", i->rcount)) {
+ ++i->errcount;
+ }
+
+
+
+ /*cl_log(LOG_INFO, "s_echo_msg: rcount= %d, wcount =%d", i->rcount, i->wcount);*/
+
+
+ do {
+ rc = chan->ops->send(chan, rmsg);
+
+ }while (rc != IPC_OK && chan->ch_status == IPC_CONNECT);
+
+ if (chan->ch_status != IPC_CONNECT){
+ cl_log(LOG_ERR,
+ "s_echo_msg: send failed %d rc iter %d qlen %ld",
+ rc, i->rcount, (long)chan->send_queue->current_qlen);
+ cl_perror("send");
+ i->errcount ++;
+
+ }
+
+ i->wcount+=1;
+ /*cl_log(LOG_INFO, "s_echo_msg: end of this ite");*/
+ }
+ retout:
+ /*fprintf(stderr, "%%");*/
+ if (i->rcount >= i->max || chan->ch_status == IPC_DISCONNECT
+ || i->errcount > MAXERRORS) {
+ chan->ops->waitout(chan);
+ g_main_quit(loop);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void
+init_iterinfo(struct iterinfo * i, IPC_Channel* chan, int max)
+{
+ memset(i, 0, sizeof(*i));
+ i->chan = chan;
+ i->max = max;
+ i->sendingsuspended = FALSE;
+}
+
+static int
+mainloop_server(IPC_Channel* chan, int repcount)
+{
+ struct iterinfo info;
+ guint sendmsgsrc;
+
+
+
+ loop = g_main_new(FALSE);
+ init_iterinfo(&info, chan, repcount);
+
+ chan->ops->set_high_flow_callback(chan, mainloop_high_flow_callback, &info);
+ chan->ops->set_low_flow_callback(chan, mainloop_low_flow_callback, &info);
+ chan->high_flow_mark = 20;
+ chan->low_flow_mark = 2;
+
+ sendmsgsrc = g_idle_add(s_send_msg, &info);
+ G_main_add_IPC_Channel(G_PRIORITY_DEFAULT, chan
+ , FALSE, s_rcv_msg, &info, NULL);
+ cl_log(LOG_INFO, "Mainloop echo server: %d reps pid %d.", repcount, (int)getpid());
+ g_main_run(loop);
+ g_main_destroy(loop);
+ g_source_remove(sendmsgsrc);
+ loop = NULL;
+ cl_log(LOG_INFO, "Mainloop echo server: %d errors", info.errcount);
+ return info.errcount;
+}
+static int
+mainloop_client(IPC_Channel* chan, int repcount)
+{
+ struct iterinfo info;
+ loop = g_main_new(FALSE);
+ init_iterinfo(&info, chan, repcount);
+ G_main_add_IPC_Channel(G_PRIORITY_DEFAULT, chan
+ , FALSE, s_echo_msg, &info, NULL);
+ cl_log(LOG_INFO, "Mainloop echo client: %d reps pid %d.", repcount, (int)getpid());
+ g_main_run(loop);
+ g_main_destroy(loop);
+ loop = NULL;
+ cl_log(LOG_INFO, "Mainloop echo client: %d errors, %d read %d written"
+ , info.errcount, info.rcount, info.wcount);
+ return info.errcount;
+}
diff --git a/lib/clplumbing/ipctransient.h b/lib/clplumbing/ipctransient.h
new file mode 100644
index 0000000..9c1746c
--- /dev/null
+++ b/lib/clplumbing/ipctransient.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2007 Andrew Beekhof <andrew@beekhof.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#undef _GNU_SOURCE /* in case it was defined on the command line */
+#define _GNU_SOURCE
+#include <lha_internal.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <glib.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/cl_poll.h>
+#include <clplumbing/GSource.h>
+#include <clplumbing/ipc.h>
+#include <clplumbing/realtime.h>
+#include <clplumbing/lsb_exitcodes.h>
+#include <errno.h>
+
+#define MAXERRORS 1000
+#define MAX_IPC_FAIL 10
+#define FIFO_LEN 1024
+
+extern const char *procname;
+
+extern const char *commdir;
+
+void trans_getargs(int argc, char **argv);
+
+void default_ipctest_input_destroy(gpointer user_data);
+
+IPC_Message * create_simple_message(const char *text, IPC_Channel *ch);
diff --git a/lib/clplumbing/ipctransientclient.c b/lib/clplumbing/ipctransientclient.c
new file mode 100644
index 0000000..080acf2
--- /dev/null
+++ b/lib/clplumbing/ipctransientclient.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <ipctransient.h>
+
+#define MAX_MESSAGES 3
+static char *messages[MAX_MESSAGES];
+
+IPC_Message *create_simple_message(const char *text, IPC_Channel *ch);
+IPC_Channel *init_client_ipctest_comms(
+ const char *child, gboolean (*dispatch)(
+ IPC_Channel* source_data, gpointer user_data),
+ void *user_data);
+gboolean transient_client_callback(IPC_Channel* server, void* private_data);
+void client_send_message(
+ const char *message_text, IPC_Channel *server_channel, int iteration);
+
+#define MAXTSTMSG 1000
+
+int
+main(int argc, char ** argv)
+{
+ int lpc =0, iteration=0;
+ GMainLoop* client_main = NULL;
+ IPC_Channel *server_channel = NULL;
+
+ trans_getargs(argc, argv);
+
+ cl_log_set_entity(procname);
+ cl_log_enable_stderr(TRUE);
+
+ /* give the server a chance to start */
+ cl_log(LOG_INFO, "#--#--#--#--# Beginning test run %d against server %d...", lpc, iteration);
+ client_main = g_main_new(FALSE);
+
+ /* connect, send messages */
+ server_channel = init_client_ipctest_comms("echo", transient_client_callback, client_main);
+
+ if(server_channel == NULL) {
+ cl_log(LOG_ERR, "[Client %d] Could not connect to server", lpc);
+ return 1;
+ }
+
+ for(lpc = 0; lpc < MAX_MESSAGES; lpc++) {
+ messages[lpc] = (char *)malloc(sizeof(char)*MAXTSTMSG);
+ }
+ snprintf(messages[0], MAXTSTMSG
+ , "%s_%ld%c", "hello", (long)getpid(), '\0');
+ snprintf(messages[1], MAXTSTMSG
+ , "%s_%ld%c", "hello_world", (long)getpid(), '\0');
+ snprintf(messages[2], MAXTSTMSG
+ , "%s_%ld%c", "hello_world_again", (long)getpid(), '\0');
+
+ for(lpc = 0; lpc < MAX_MESSAGES; lpc++) {
+ client_send_message(messages[lpc], server_channel, lpc);
+ }
+
+ server_channel->ops->waitout(server_channel);
+
+ /* wait for the reply by creating a mainloop and running it until
+ * the callbacks are invoked...
+ */
+
+ cl_log(LOG_DEBUG, "Waiting for replies from the echo server");
+ g_main_run(client_main);
+ cl_log(LOG_INFO, "[Iteration %d] Client %d completed successfully", iteration, lpc);
+
+ return 0;
+}
+
+
+IPC_Channel *
+init_client_ipctest_comms(const char *child,
+ gboolean (*dispatch)(IPC_Channel* source_data
+ ,gpointer user_data),
+ void *user_data)
+{
+ IPC_Channel *ch;
+ GHashTable * attrs;
+ int local_sock_len = 2; /* 2 = '/' + '\0' */
+ char *commpath = NULL;
+ static char path[] = IPC_PATH_ATTR;
+
+ local_sock_len += strlen(child);
+ local_sock_len += strlen(commdir);
+
+ commpath = (char*)malloc(sizeof(char)*local_sock_len);
+ if (commpath == NULL){
+ cl_log(LOG_ERR, "%s: allocating memory failed", __FUNCTION__);
+ return NULL;
+ }
+ sprintf(commpath, "%s/%s", commdir, child);
+ commpath[local_sock_len - 1] = '\0';
+
+ cl_log(LOG_DEBUG, "[Client] Attempting to talk on: %s", commpath);
+
+ attrs = g_hash_table_new(g_str_hash,g_str_equal);
+ g_hash_table_insert(attrs, path, commpath);
+ ch = ipc_channel_constructor(IPC_ANYTYPE, attrs);
+ g_hash_table_destroy(attrs);
+
+ if (ch == NULL) {
+ cl_log(LOG_ERR, "[Client] Could not access channel on: %s", commpath);
+ return NULL;
+ } else if(ch->ops->initiate_connection(ch) != IPC_OK) {
+ cl_log(LOG_ERR, "[Client] Could not init comms on: %s", commpath);
+ return NULL;
+ }
+
+ G_main_add_IPC_Channel(G_PRIORITY_LOW,
+ ch, FALSE, dispatch, user_data,
+ default_ipctest_input_destroy);
+
+ return ch;
+}
+
+
+gboolean
+transient_client_callback(IPC_Channel* server, void* private_data)
+{
+ int lpc = 0;
+ IPC_Message *msg = NULL;
+ char *buffer = NULL;
+ static int received_responses = 0;
+
+ GMainLoop *mainloop = (GMainLoop*)private_data;
+
+ while(server->ops->is_message_pending(server) == TRUE) {
+ if (server->ch_status == IPC_DISCONNECT) {
+ /* The message which was pending for us is the
+ * new status of IPC_DISCONNECT */
+ break;
+ }
+ if(server->ops->recv(server, &msg) != IPC_OK) {
+ cl_log(LOG_ERR, "[Client] Error while invoking recv()");
+ perror("[Client] Receive failure:");
+ return FALSE;
+ }
+
+ if (msg != NULL) {
+ buffer = (char*)msg->msg_body;
+ cl_log(LOG_DEBUG, "[Client] Got text [text=%s]", buffer);
+ received_responses++;
+
+ if(lpc < MAX_MESSAGES && strcmp(messages[lpc], buffer) != 0)
+ {
+ cl_log(LOG_ERR, "[Client] Received someone else's message [%s] instead of [%s]", buffer, messages[lpc]);
+ }
+ else if(lpc >= MAX_MESSAGES)
+ {
+ cl_log(LOG_ERR, "[Client] Receivedan extra message [%s]", buffer);
+ }
+
+ lpc++;
+ msg->msg_done(msg);
+ } else {
+ cl_log(LOG_ERR, "[Client] No message this time");
+ }
+ }
+
+ if(server->ch_status == IPC_DISCONNECT) {
+ cl_log(LOG_ERR, "[Client] Client received HUP");
+ return FALSE;
+ }
+
+ cl_log(LOG_DEBUG, "[Client] Processed %d IPC messages this time, %d total", lpc, received_responses);
+
+ if(received_responses > 2) {
+ cl_log(LOG_INFO, "[Client] Processed %d IPC messages, all done.", received_responses);
+ received_responses = 0;
+ g_main_quit(mainloop);
+ cl_log(LOG_INFO, "[Client] Exiting.");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void
+client_send_message(const char *message_text,
+ IPC_Channel *server_channel,
+ int iteration)
+{
+ IPC_Message *a_message = NULL;
+ if(server_channel->ch_status != IPC_CONNECT) {
+ cl_log(LOG_WARNING, "[Client %d] Channel is in state %d before sending message [%s]",
+ iteration, server_channel->ch_status, message_text);
+ return;
+ }
+
+ a_message = create_simple_message(message_text, server_channel);
+ if(a_message == NULL) {
+ cl_log(LOG_ERR, "Could not create message to send");
+ } else {
+ cl_log(LOG_DEBUG, "[Client %d] Sending message: %s", iteration, (char*)a_message->msg_body);
+ while(server_channel->ops->send(server_channel, a_message) == IPC_FAIL) {
+ cl_log(LOG_ERR, "[Client %d] IPC channel is blocked", iteration);
+ cl_shortsleep();
+ }
+
+ if(server_channel->ch_status != IPC_CONNECT) {
+ cl_log(LOG_WARNING,
+ "[Client %d] Channel is in state %d after sending message [%s]",
+ iteration, server_channel->ch_status, message_text);
+ }
+ }
+}
diff --git a/lib/clplumbing/ipctransientlib.c b/lib/clplumbing/ipctransientlib.c
new file mode 100644
index 0000000..7a6721e
--- /dev/null
+++ b/lib/clplumbing/ipctransientlib.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <ipctransient.h>
+
+/* for basename() on some OSes (e.g. Solaris) */
+#include <libgen.h>
+
+#define WORKING_DIR HA_VARLIBHBDIR
+
+const char *procname = NULL;
+
+const char *commdir = WORKING_DIR;
+
+void
+trans_getargs(int argc, char **argv)
+{
+ int argflag, argerrs;
+
+ procname = basename(argv[0]);
+
+ argerrs = 0;
+ while ((argflag = getopt(argc, argv, "C:")) != EOF) {
+ switch (argflag) {
+ case 'C': /* directory to commpath */
+ commdir = optarg;
+ break;
+ default:
+ argerrs++;
+ break;
+ }
+ }
+ if (argerrs) {
+ fprintf(stderr,
+ "Usage: %s [-C commdir]\n"
+ "\t-C : directory to commpath (default %s)\n",
+ procname, WORKING_DIR);
+ exit(1);
+ }
+
+}
+
+void
+default_ipctest_input_destroy(gpointer user_data)
+{
+ cl_log(LOG_INFO, "default_ipctest_input_destroy:received HUP");
+}
+
+IPC_Message *
+create_simple_message(const char *text, IPC_Channel *ch)
+{
+ IPC_Message *ack_msg = NULL;
+ char *copy_text = NULL;
+
+ if(text == NULL) {
+ cl_log(LOG_ERR, "ERROR: can't create IPC_Message with no text");
+ return NULL;
+ } else if(ch == NULL) {
+ cl_log(LOG_ERR, "ERROR: can't create IPC_Message with no channel");
+ return NULL;
+ }
+
+ ack_msg = (IPC_Message *)malloc(sizeof(IPC_Message));
+ if (ack_msg == NULL){
+ cl_log(LOG_ERR, "create_simple_message:"
+ "allocating memory for IPC_Message failed");
+ return NULL;
+ }
+
+ memset(ack_msg, 0, sizeof(IPC_Message));
+
+ copy_text = strdup(text);
+
+ ack_msg->msg_private = NULL;
+ ack_msg->msg_done = NULL;
+ ack_msg->msg_body = copy_text;
+ ack_msg->msg_ch = ch;
+
+ ack_msg->msg_len = strlen(text)+1;
+
+ return ack_msg;
+}
diff --git a/lib/clplumbing/ipctransientserver.c b/lib/clplumbing/ipctransientserver.c
new file mode 100644
index 0000000..d7ee61d
--- /dev/null
+++ b/lib/clplumbing/ipctransientserver.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <ipctransient.h>
+
+gboolean transient_server_callback(IPC_Channel *client, gpointer user_data);
+gboolean transient_server_connect(IPC_Channel *client_channel, gpointer user_data);
+int init_server_ipc_comms(const char *child,
+ gboolean (*channel_client_connect)(IPC_Channel *newclient, gpointer user_data),
+ void (*channel_input_destroy)(gpointer user_data),
+ gboolean usenormalpoll);
+
+int
+main(int argc, char ** argv)
+{
+ int iteration = 0;
+ GMainLoop* mainloop = NULL;
+
+ trans_getargs(argc, argv);
+
+ cl_log_set_entity(procname);
+ cl_log_enable_stderr(TRUE);
+
+ init_server_ipc_comms("echo", transient_server_connect, default_ipctest_input_destroy, FALSE);
+
+ /* wait for the reply by creating a mainloop and running it until
+ * the callbacks are invoked...
+ */
+ mainloop = g_main_new(FALSE);
+
+ cl_log(LOG_INFO, "#--#--#--# Echo Server %d is active...", iteration);
+ g_main_run(mainloop);
+ cl_log(LOG_INFO, "#--#--#--# Echo Server %d is stopped...", iteration);
+
+ return 0;
+}
+
+
+int
+init_server_ipc_comms(const char *child,
+ gboolean (*channel_client_connect)(IPC_Channel *newclient, gpointer user_data),
+ void (*channel_input_destroy)(gpointer user_data),
+ gboolean usenormalpoll)
+{
+ /* the clients wait channel is the other source of events.
+ * This source delivers the clients connection events.
+ * listen to this source at a relatively lower priority.
+ */
+ mode_t mask;
+ IPC_WaitConnection *wait_ch;
+ GHashTable * attrs;
+ int local_sock_len = 2; /* 2 = '/' + '\0' */
+ char *commpath = NULL;
+ static char path[] = IPC_PATH_ATTR;
+
+ local_sock_len += strlen(child);
+ local_sock_len += strlen(commdir);
+
+ commpath = (char*)malloc(sizeof(char)*local_sock_len);
+ if (commpath == NULL){
+ cl_log(LOG_ERR, "%s: allocating memory failed", __FUNCTION__);
+ exit(1);
+ }
+ snprintf(commpath, local_sock_len, "%s/%s", commdir, child);
+ commpath[local_sock_len - 1] = '\0';
+
+ attrs = g_hash_table_new(g_str_hash,g_str_equal);
+ g_hash_table_insert(attrs, path, commpath);
+
+ mask = umask(0);
+ wait_ch = ipc_wait_conn_constructor(IPC_ANYTYPE, attrs);
+ if (wait_ch == NULL){
+ cl_perror("[Server] Can't create wait channel of type %s", IPC_ANYTYPE);
+ exit(1);
+ }
+ mask = umask(mask);
+ g_hash_table_destroy(attrs);
+
+ G_main_add_IPC_WaitConnection(G_PRIORITY_LOW,
+ wait_ch,
+ NULL,
+ FALSE,
+ channel_client_connect,
+ wait_ch, /* user data passed to ?? */
+ channel_input_destroy);
+
+ cl_log(LOG_INFO, "[Server] Listening on: %s", commpath);
+
+/* if (!usenormalpoll) { */
+/* g_main_set_poll_func(cl_glibpoll); */
+/* ipc_set_pollfunc(cl_poll); */
+/* } */
+ return 0;
+}
+
+gboolean
+transient_server_callback(IPC_Channel *client, gpointer user_data)
+{
+ int lpc = 0;
+ IPC_Message *msg = NULL;
+ char *buffer = NULL;
+ IPC_Message *reply = NULL;
+ int llpc = 0;
+
+ cl_log(LOG_DEBUG, "channel: %p", client);
+
+ cl_log(LOG_DEBUG, "Client status %d (disconnect=%d)", client->ch_status, IPC_DISCONNECT);
+
+ while(client->ops->is_message_pending(client)) {
+ if (client->ch_status == IPC_DISCONNECT) {
+ /* The message which was pending for us is that
+ * the IPC status is now IPC_DISCONNECT */
+ break;
+ }
+ if(client->ops->recv(client, &msg) != IPC_OK) {
+ cl_perror("[Server] Receive failure");
+ return FALSE;
+ }
+
+ if (msg != NULL) {
+ lpc++;
+ buffer = (char*)g_malloc(msg->msg_len+1);
+ memcpy(buffer,msg->msg_body, msg->msg_len);
+ buffer[msg->msg_len] = '\0';
+ cl_log(LOG_DEBUG, "[Server] Got xml [text=%s]", buffer);
+
+ reply = create_simple_message(strdup(buffer), client);
+ if (!reply) {
+ cl_log(LOG_ERR, "[Server] Could allocate reply msg.");
+ return FALSE;
+ }
+
+ llpc = 0;
+ while(llpc++ < MAX_IPC_FAIL && client->ops->send(client, reply) == IPC_FAIL) {
+ cl_log(LOG_WARNING, "[Server] ipc channel blocked");
+ cl_shortsleep();
+ }
+
+ if(lpc == MAX_IPC_FAIL) {
+ cl_log(LOG_ERR, "[Server] Could not send IPC, message. Channel is dead.");
+ free(reply);
+ return FALSE;
+ }
+
+ cl_log(LOG_DEBUG, "[Server] Sent reply");
+ msg->msg_done(msg);
+ } else {
+ cl_log(LOG_ERR, "[Server] No message this time");
+ continue;
+ }
+ }
+
+ cl_log(LOG_DEBUG, "[Server] Processed %d messages", lpc);
+
+ cl_log(LOG_DEBUG, "[Server] Client status %d", client->ch_status);
+ if(client->ch_status != IPC_CONNECT) {
+ cl_log(LOG_INFO, "[Server] Server received HUP from child");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+gboolean
+transient_server_connect(IPC_Channel *client_channel, gpointer user_data)
+{
+ /* assign the client to be something, or put in a hashtable */
+ cl_log(LOG_DEBUG, "A client tried to connect... and there was much rejoicing.");
+
+ if(client_channel == NULL) {
+ cl_log(LOG_ERR, "[Server] Channel was NULL");
+ } else if(client_channel->ch_status == IPC_DISCONNECT) {
+ cl_log(LOG_ERR, "[Server] Channel was disconnected");
+ } else {
+ cl_log(LOG_DEBUG, "[Server] Client is %s %p", client_channel == NULL?"NULL":"valid", client_channel);
+ cl_log(LOG_DEBUG, "[Server] Client status %d (disconnect=%d)", client_channel->ch_status, IPC_DISCONNECT);
+
+ cl_log(LOG_DEBUG, "[Server] Adding IPC Channel to main thread.");
+ G_main_add_IPC_Channel(G_PRIORITY_LOW,
+ client_channel,
+ FALSE,
+ transient_server_callback,
+ NULL,
+ default_ipctest_input_destroy);
+ }
+
+ return TRUE;
+}
diff --git a/lib/clplumbing/longclock.c b/lib/clplumbing/longclock.c
new file mode 100644
index 0000000..594c9c5
--- /dev/null
+++ b/lib/clplumbing/longclock.c
@@ -0,0 +1,275 @@
+/*
+ * Longclock operations
+ *
+ * Copyright (c) 2002 International Business Machines
+ * Author: Alan Robertson <alanr@unix.sh>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <lha_internal.h>
+#include <unistd.h>
+#include <sys/times.h>
+#include <errno.h>
+#include <clplumbing/longclock.h>
+#include <clplumbing/cl_log.h>
+
+static unsigned Hz = 0;
+static longclock_t Lc_Hz;
+static double d_Hz;
+
+
+const longclock_t zero_longclock = 0UL;
+
+#ifndef CLOCK_T_IS_LONG_ENOUGH
+# undef time_longclock
+#endif
+
+#ifdef HAVE_LONGCLOCK_ARITHMETIC
+# undef msto_longclock
+# undef longclockto_ms
+# undef secsto_longclock
+# undef add_longclock
+# undef sub_longclock
+# undef cmp_longclock
+#endif
+
+
+unsigned
+hz_longclock(void)
+{
+ if (Hz == 0) {
+ /* Compute various hz-related constants */
+
+ Hz = sysconf(_SC_CLK_TCK);
+ Lc_Hz = (longclock_t)Hz;
+ d_Hz = (double) Hz;
+ }
+ return Hz;
+}
+
+#ifdef TIMES_ALLOWS_NULL_PARAM
+# define TIMES_PARAM NULL
+#else
+ static struct tms dummy_longclock_tms_struct;
+# define TIMES_PARAM &dummy_longclock_tms_struct
+#endif
+
+unsigned long
+cl_times(void) /* Make times(2) behave rationally on Linux */
+{
+ clock_t ret;
+#ifndef DISABLE_TIMES_KLUDGE
+ int save_errno = errno;
+
+ /*
+ * times(2) really returns an unsigned value ...
+ *
+ * We don't check to see if we got back the error value (-1), because
+ * the only possibility for an error would be if the address of
+ * longclock_dummy_tms_struct was invalid. Since it's a
+ * compiler-generated address, we assume that errors are impossible.
+ * And, unfortunately, it is quite possible for the correct return
+ * from times(2) to be exactly (clock_t)-1. Sigh...
+ *
+ */
+ errno = 0;
+#endif /* DISABLE_TIMES_KLUDGE */
+ ret = times(TIMES_PARAM);
+
+#ifndef DISABLE_TIMES_KLUDGE
+/*
+ * This is to work around a bug in the system call interface
+ * for times(2) found in glibc on Linux (and maybe elsewhere)
+ * It changes the return values from -1 to -4096 all into
+ * -1 and then dumps the -(return value) into errno.
+ *
+ * This totally bizarre behavior seems to be widespread in
+ * versions of Linux and glibc.
+ *
+ * Many thanks to Wolfgang Dumhs <wolfgang.dumhs (at) gmx.at>
+ * for finding and documenting this bizarre behavior.
+ */
+ if (errno != 0) {
+ ret = (clock_t) (-errno);
+ }
+ errno = save_errno;
+#endif /* DISABLE_TIMES_KLUDGE */
+
+ /* sizeof(long) may be larger than sizeof(clock_t).
+ * Don't jump from 0x7fffffff to 0xffffffff80000000
+ * because of sign extension.
+ * We do expect sizeof(clock_t) <= sizeof(long), however.
+ */
+ BUILD_BUG_ON(sizeof(clock_t) > sizeof(unsigned long));
+#define CLOCK_T_MAX (~0UL >> (8*(sizeof(unsigned long) - sizeof(clock_t))))
+ return (unsigned long)ret & CLOCK_T_MAX;
+}
+
+#ifdef CLOCK_T_IS_LONG_ENOUGH
+longclock_t
+time_longclock(void)
+{
+ /* See note below about deliberately ignoring errors... */
+ return (longclock_t)cl_times();
+}
+
+#else /* clock_t is shorter than 64 bits */
+
+#define BITSPERBYTE 8
+#define WRAPSHIFT (BITSPERBYTE*sizeof(clock_t))
+#define WRAPAMOUNT (((longclock_t) 1) << WRAPSHIFT)
+#define MINJUMP ((CLOCK_T_MAX/100UL)*99UL)
+
+longclock_t
+time_longclock(void)
+{
+ /* Internal note: This updates the static fields; care should be
+ * taken to not call a function like cl_log (which internally
+ * calls time_longclock() as well) just before this happens,
+ * because then this can recurse infinitely; that is why the
+ * cl_log call is where it is; found by Simon Graham. */
+ static gboolean calledbefore = FALSE;
+ static unsigned long lasttimes = 0L;
+ static unsigned long callcount = 0L;
+ static longclock_t lc_wrapcount = 0L;
+ unsigned long timesval;
+
+ ++callcount;
+
+ timesval = cl_times();
+
+ if (calledbefore && timesval < lasttimes) {
+ unsigned long jumpbackby = lasttimes - timesval;
+
+ if (jumpbackby < MINJUMP) {
+ /* Kernel weirdness */
+ cl_log(LOG_CRIT
+ , "%s: clock_t from times(2) appears to"
+ " have jumped backwards (in error)!"
+ , __FUNCTION__);
+ cl_log(LOG_CRIT
+ , "%s: old value was %lu"
+ ", new value is %lu, diff is %lu, callcount %lu"
+ , __FUNCTION__
+ , (unsigned long)lasttimes
+ , (unsigned long)timesval
+ , (unsigned long)jumpbackby
+ , callcount);
+ /* Assume jump back was the error and ignore it */
+ /* (i.e., hope it goes away) */
+ }else{
+ /* Normal looking wraparound */
+ /* update last time BEFORE loging as log call
+ can call this routine recursively leading
+ to double update of wrapcount! */
+
+ lasttimes = timesval;
+ lc_wrapcount += WRAPAMOUNT;
+
+ cl_log(LOG_INFO
+ , "%s: clock_t wrapped around (uptime)."
+ , __FUNCTION__);
+ }
+ }
+ else {
+ lasttimes = timesval;
+ calledbefore = TRUE;
+ }
+ return (lc_wrapcount | timesval);
+}
+#endif /* ! CLOCK_T_IS_LONG_ENOUGH */
+
+longclock_t
+msto_longclock(unsigned long ms)
+{
+ unsigned long secs = ms / 1000UL;
+ unsigned long msec = ms % 1000;
+ longclock_t result;
+
+ (void)(Hz == 0 && hz_longclock());
+
+ if (ms == 0) {
+ return (longclock_t)0UL;
+ }
+ result = secs * Lc_Hz + (msec * Lc_Hz)/1000;
+
+ if (result == 0) {
+ result = 1;
+ }
+ return result;
+}
+
+longclock_t
+secsto_longclock(unsigned long Secs)
+{
+ longclock_t secs = Secs;
+
+ (void)(Hz == 0 && hz_longclock());
+
+ return secs * Lc_Hz;
+}
+
+longclock_t
+dsecsto_longclock(double v)
+{
+ (void)(Hz == 0 && hz_longclock());
+
+ return (longclock_t) ((v * d_Hz)+0.5);
+
+}
+
+unsigned long
+longclockto_ms(longclock_t t)
+{
+ (void)(Hz == 0 && hz_longclock());
+
+ if (t == 0) {
+ return 0UL;
+ }
+ return (unsigned long) ((t*1000UL)/Lc_Hz);
+}
+#ifndef CLOCK_T_IS_LONG_ENOUGH
+long
+longclockto_long(longclock_t t)
+{
+ return ((long)(t));
+}
+
+longclock_t
+add_longclock(longclock_t l, longclock_t r)
+{
+ return l + r;
+}
+
+longclock_t
+sub_longclock(longclock_t l, longclock_t r)
+{
+ return l - r;
+}
+
+int
+cmp_longclock(longclock_t l, longclock_t r)
+{
+ if (l < r) {
+ return -1;
+ }
+ if (l > r) {
+ return 1;
+ }
+ return 0;
+}
+#endif /* CLOCK_T_IS_LONG_ENOUGH */
diff --git a/lib/clplumbing/md5.c b/lib/clplumbing/md5.c
new file mode 100644
index 0000000..b893483
--- /dev/null
+++ b/lib/clplumbing/md5.c
@@ -0,0 +1,335 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ *
+ * Cleaned up for heartbeat by
+ * Mitja Sarp <mitja@lysator.liu.se>
+ * Sun Jiang Dong <sunjd@cn.ibm.com>
+ * Pan Jia Ming <jmltc@cn.ibm.com>
+ *
+ */
+
+#include <lha_internal.h>
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <stdio.h> /* for sprintf() */
+#include <string.h> /* for memcpy() */
+#include <sys/types.h> /* for stupid systems */
+#include <netinet/in.h> /* for ntohl() */
+#include <clplumbing/cl_log.h>
+#include <clplumbing/md5.h>
+
+#define MD5_DIGESTSIZE 16
+#define MD5_BLOCKSIZE 64
+
+typedef struct MD5Context_st {
+ uint32_t buf[4];
+ uint32_t bytes[2];
+ uint32_t in[16];
+}MD5Context;
+
+#define md5byte unsigned char
+
+struct MD5Context {
+ uint32_t buf[4];
+ uint32_t bytes[2];
+ uint32_t in[16];
+};
+
+void MD5Init(MD5Context *context);
+void MD5Update(MD5Context *context, md5byte const *buf, unsigned len);
+void MD5Final(unsigned char digest[16], MD5Context *context);
+void MD5Transform(uint32_t buf[4], uint32_t const in[16]);
+
+
+#ifdef CONFIG_BIG_ENDIAN
+static inline void byteSwap(uint32_t * buf, uint32_t len);
+
+static inline void
+byteSwap(uint32_t * buf, uint32_t len)
+{
+ int i;
+ for (i = 0; i < len; i ++) {
+ uint32_t tmp = buf[i];
+ buf[i] = ( (uint32_t) ((unsigned char *) &tmp)[0] ) |
+ (((uint32_t) ((unsigned char *) &tmp)[1]) << 8) |
+ (((uint32_t) ((unsigned char *) &tmp)[2]) << 16) |
+ (((uint32_t) ((unsigned char *) &tmp)[3]) << 24);
+ }
+}
+#elif defined(CONFIG_LITTLE_ENDIAN)
+ #define byteSwap(buf,words)
+#else
+ #error "Neither CONFIG_BIG_ENDIAN nor CONFIG_LITTLE_ENDIAN defined!"
+#endif
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void
+MD5Init(MD5Context *ctx)
+{
+ ctx->buf[0] = 0x67452301ul;
+ ctx->buf[1] = 0xefcdab89ul;
+ ctx->buf[2] = 0x98badcfeul;
+ ctx->buf[3] = 0x10325476ul;
+
+ ctx->bytes[0] = 0;
+ ctx->bytes[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void
+MD5Update(MD5Context *ctx, md5byte const *buf, unsigned len)
+{
+ uint32_t t;
+
+ /* Update byte count */
+
+ t = ctx->bytes[0];
+ if ((ctx->bytes[0] = t + len) < t)
+ ctx->bytes[1]++; /* Carry from low to high */
+
+ t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */
+ if (t > len) {
+ memcpy((md5byte *)ctx->in + 64 - t, buf, len);
+ return;
+ }
+ /* First chunk is an odd size */
+ memcpy((md5byte *)ctx->in + 64 - t, buf, t);
+ byteSwap(ctx->in, 16);
+ MD5Transform(ctx->buf, ctx->in);
+ buf += t;
+ len -= t;
+
+ /* Process data in 64-byte chunks */
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ byteSwap(ctx->in, 16);
+ MD5Transform(ctx->buf, ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+ memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void
+MD5Final(md5byte digest[16], MD5Context *ctx)
+{
+ int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */
+ md5byte *p = (md5byte *)ctx->in + count;
+
+ /* Set the first char of padding to 0x80. There is always room. */
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 56 bytes (-8..55) */
+ count = 56 - 1 - count;
+
+ if (count < 0) { /* Padding forces an extra block */
+ memset(p, 0, count + 8);
+ byteSwap(ctx->in, 16);
+ MD5Transform(ctx->buf, ctx->in);
+ p = (md5byte *)ctx->in;
+ count = 56;
+ }
+ memset(p, 0, count);
+ byteSwap(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ctx->in[14] = ctx->bytes[0] << 3;
+ ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29;
+ MD5Transform(ctx->buf, ctx->in);
+
+ byteSwap(ctx->buf, 16);
+ memcpy(digest, ctx->buf, 16);
+ memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */
+}
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) ((x) ^ (y) ^ (z))
+#define F4(x, y, z) ((y) ^ ((x) | ~(z)))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f,w,x,y,z,in,s) \
+ (w += f(x,y,z) + (in), (w) = ((w)<<(s) | (w)>>(32-(s))) + (x))
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void
+MD5Transform(uint32_t buf[4], uint32_t const in[16])
+{
+ register uint32_t a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478ul, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756ul, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070dbul, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceeeul, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faful, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62aul, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613ul, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501ul, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8ul, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7aful, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1ul, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7beul, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122ul, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193ul, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438eul, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821ul, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562ul, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340ul, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51ul, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aaul, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105dul, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453ul, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681ul, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8ul, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6ul, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6ul, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87ul, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14edul, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905ul, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8ul, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9ul, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8aul, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942ul, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681ul, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122ul, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380cul, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44ul, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9ul, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60ul, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70ul, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6ul, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127faul, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085ul, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05ul, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039ul, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5ul, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8ul, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665ul, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244ul, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97ul, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7ul, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039ul, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3ul, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92ul, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47dul, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1ul, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4ful, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0ul, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314ul, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1ul, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82ul, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235ul, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bbul, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391ul, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+int MD5( const unsigned char *data
+ , unsigned long len
+ , unsigned char *digest)
+{
+ MD5Context context;
+
+ MD5Init(&context);
+ MD5Update(&context, data, len);
+ MD5Final(digest, &context);
+
+ return 0;
+}
+
+int HMAC( const unsigned char * key
+ , unsigned int key_len
+ , const unsigned char * text
+ , unsigned long textlen
+ , unsigned char * digest)
+{
+ MD5Context context;
+ /* inner padding - key XORd with ipad */
+ unsigned char k_ipad[65];
+ /* outer padding - * key XORd with opad */
+ unsigned char k_opad[65];
+ unsigned char tk[MD5_DIGESTSIZE];
+ int i;
+
+ /* if key is longer than MD5_BLOCKSIZE bytes reset it to key=MD5(key) */
+ if (key_len > MD5_BLOCKSIZE) {
+ MD5Context tctx;
+ MD5Init(&tctx);
+ MD5Update(&tctx, (const unsigned char *)key, key_len);
+ MD5Final(tk, &tctx);
+
+ key = (unsigned char *)tk;
+ key_len = MD5_DIGESTSIZE;
+ }
+ /* start out by storing key in pads */
+ memset(k_ipad, 0, sizeof k_ipad);
+ memset(k_opad, 0, sizeof k_opad);
+ memcpy(k_ipad, key, key_len);
+ memcpy(k_opad, key, key_len);
+
+ /* XOR key with ipad and opad values */
+ for (i=0; i<MD5_BLOCKSIZE; i++) {
+ k_ipad[i] ^= 0x36;
+ k_opad[i] ^= 0x5c;
+ }
+ /* perform inner MD5 */
+ MD5Init(&context); /* init context for 1st pass */
+ MD5Update(&context, k_ipad, MD5_BLOCKSIZE); /* start with inner pad */
+ MD5Update(&context, text, textlen); /* then text of datagram */
+ MD5Final(digest, &context); /* finish up 1st pass */
+
+ /* perform outer MD5 */
+ MD5Init(&context); /* init context for 2nd pass */
+ MD5Update(&context, k_opad, MD5_BLOCKSIZE); /* start with outer pad */
+ MD5Update(&context, digest, MD5_DIGESTSIZE); /* then results of 1st hash */
+
+ MD5Final(digest, &context); /* finish up 2nd pass */
+
+ return 0;
+}
diff --git a/lib/clplumbing/mkstemp_mode.c b/lib/clplumbing/mkstemp_mode.c
new file mode 100644
index 0000000..69c080b
--- /dev/null
+++ b/lib/clplumbing/mkstemp_mode.c
@@ -0,0 +1,56 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <lha_internal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <clplumbing/mkstemp_mode.h>
+
+
+/*
+ * A slightly safer version of mkstemp(3)
+ *
+ * In this version, the file is initially created mode 0, and then chmod-ed
+ * to the requested permissions. This guarantees that the file is never
+ * open to others beyond the specified permissions at any time.
+ */
+int
+mkstemp_mode(char* template, mode_t filemode)
+{
+
+ mode_t maskval;
+ int fd;
+
+ maskval = umask(0777);
+
+ /* created file should now be mode 0000 */
+ fd = mkstemp(template);
+
+ umask(maskval); /* cannot fail :-) */
+
+ if (fd >= 0) {
+ if (chmod(template, filemode) < 0) {
+ int save = errno;
+ close(fd);
+ errno = save;
+ fd = -1;
+ }
+ }
+ return fd;
+}
diff --git a/lib/clplumbing/netstring_test.c b/lib/clplumbing/netstring_test.c
new file mode 100644
index 0000000..1f498ec
--- /dev/null
+++ b/lib/clplumbing/netstring_test.c
@@ -0,0 +1,255 @@
+/*
+ * netstring_test: Test program for testing the heartbeat binary/struct API
+ *
+ * Copyright (C) 2000 Guochun Shi <gshi@ncsa.uiuc.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <lha_internal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/cl_signal.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <hb_api_core.h>
+#include <hb_api.h>
+
+/*
+ * A heartbeat API test program...
+ */
+
+void NodeStatus(const char * node, const char * status, void * private);
+void LinkStatus(const char * node, const char *, const char *, void*);
+void gotsig(int nsig);
+
+void
+NodeStatus(const char * node, const char * status, void * private)
+{
+ cl_log(LOG_NOTICE, "Status update: Node %s now has status %s"
+ , node, status);
+}
+
+void
+LinkStatus(const char * node, const char * lnk, const char * status
+, void * private)
+{
+ cl_log(LOG_NOTICE, "Link Status update: Link %s/%s now has status %s"
+ , node, lnk, status);
+}
+
+int quitnow = 0;
+void gotsig(int nsig)
+{
+ (void)nsig;
+ quitnow = 1;
+}
+
+#define BUFSIZE 16
+extern int netstring_format;
+
+int
+main(int argc, char ** argv)
+{
+ struct ha_msg* reply;
+ struct ha_msg* pingreq = NULL;
+ unsigned fmask;
+ ll_cluster_t* hb;
+ int msgcount=0;
+ char databuf[BUFSIZE];
+ int i;
+#if 0
+ char * ctmp;
+ const char * cval;
+ int j;
+#endif
+
+ netstring_format = 0;
+
+ cl_log_set_entity(argv[0]);
+ cl_log_enable_stderr(TRUE);
+ cl_log_set_facility(LOG_USER);
+ hb = ll_cluster_new("heartbeat");
+ cl_log(LOG_INFO, "PID=%ld", (long)getpid());
+ cl_log(LOG_INFO, "Signing in with heartbeat");
+ if (hb->llc_ops->signon(hb, "ping")!= HA_OK) {
+ cl_log(LOG_ERR, "Cannot sign on with heartbeat");
+ cl_log(LOG_ERR, "REASON: %s", hb->llc_ops->errmsg(hb));
+ exit(1);
+ }
+
+ if (hb->llc_ops->set_nstatus_callback(hb, NodeStatus, NULL) !=HA_OK){
+ cl_log(LOG_ERR, "Cannot set node status callback");
+ cl_log(LOG_ERR, "REASON: %s", hb->llc_ops->errmsg(hb));
+ exit(2);
+ }
+
+ if (hb->llc_ops->set_ifstatus_callback(hb, LinkStatus, NULL)!=HA_OK){
+ cl_log(LOG_ERR, "Cannot set if status callback");
+ cl_log(LOG_ERR, "REASON: %s", hb->llc_ops->errmsg(hb));
+ exit(3);
+ }
+
+#if 0
+ fmask = LLC_FILTER_RAW;
+#else
+ fmask = LLC_FILTER_DEFAULT;
+#endif
+ /* This isn't necessary -- you don't need this call - it's just for testing... */
+ cl_log(LOG_INFO, "Setting message filter mode");
+ if (hb->llc_ops->setfmode(hb, fmask) != HA_OK) {
+ cl_log(LOG_ERR, "Cannot set filter mode");
+ cl_log(LOG_ERR, "REASON: %s", hb->llc_ops->errmsg(hb));
+ exit(4);
+ }
+
+ CL_SIGINTERRUPT(SIGINT, 1);
+ CL_SIGNAL(SIGINT, gotsig);
+
+ pingreq = ha_msg_new(0);
+ ha_msg_add(pingreq, F_TYPE, "ping");
+ {
+ struct ha_msg *childmsg;
+ struct ha_msg *grandchildmsg;
+
+ for(i = 0 ;i < BUFSIZE;i ++){
+ databuf[i] = 1 + i ;
+ }
+ databuf[4] = 0;
+
+ ha_msg_addbin(pingreq, "data",databuf , BUFSIZE);
+
+
+ childmsg = ha_msg_new(0);
+ ha_msg_add(childmsg, "name","testchild");
+ ha_msg_addbin(childmsg, "data",databuf , BUFSIZE);
+
+ grandchildmsg = ha_msg_new(0);
+ ha_msg_add(grandchildmsg, "name","grandchild");
+ ha_msg_addstruct(childmsg, "child",grandchildmsg);
+
+ if( ha_msg_addstruct(pingreq, "child", childmsg) != HA_OK){
+ cl_log(LOG_ERR, "adding a child message to the message failed");
+ exit(1);
+ }
+
+ }
+
+ cl_log(LOG_INFO, "printing out the pingreq message:");
+
+ ha_log_message(pingreq);
+ if (hb->llc_ops->sendclustermsg(hb, pingreq) == HA_OK) {
+ cl_log(LOG_INFO, "Sent ping request to cluster");
+ }else{
+ cl_log(LOG_ERR, "PING request FAIL to cluster");
+ }
+ errno = 0;
+ for(; !quitnow && (reply=hb->llc_ops->readmsg(hb, 1)) != NULL;) {
+ const char * type;
+ const char * orig;
+ ++msgcount;
+ if ((type = ha_msg_value(reply, F_TYPE)) == NULL) {
+ type = "?";
+ }
+ if ((orig = ha_msg_value(reply, F_ORIG)) == NULL) {
+ orig = "?";
+ }
+ cl_log(LOG_INFO, " ");
+ cl_log(LOG_NOTICE, "Got message %d of type [%s] from [%s]"
+ , msgcount, type, orig);
+
+ if (strcmp(type, "ping") ==0) {
+ int datalen = 0;
+ const char *data;
+ struct ha_msg *childmsg;
+
+ cl_log(LOG_INFO, "****************************************");
+ ha_log_message(reply);
+
+ data = cl_get_binary(reply, "data", &datalen);
+ if(data){
+ cl_log(LOG_INFO, " ");
+ cl_log(LOG_INFO, "%d of data received,data=%s", datalen,data);
+ for(i = 0; i < datalen; i++){
+ if( databuf[i] != data[i]){
+ cl_log(LOG_ERR, "data does not match at %d",i);
+ break;
+ }
+ }
+ if(i == datalen){
+ cl_log(LOG_INFO,"data matches");
+ }
+ }else {
+ cl_log(LOG_WARNING, "cl_get_binary failed");
+ }
+
+ childmsg = cl_get_struct(reply,"child");
+ if(childmsg){
+ cl_log(LOG_INFO, " ");
+ cl_log(LOG_INFO, "child message found");
+ ha_log_message(childmsg);
+ }else{
+ cl_log(LOG_WARNING, "cl_get_struct failed");
+ }
+
+ }
+
+#if 1
+ {
+ struct ha_msg *cpmsg;
+ cl_log(LOG_INFO, " ");
+ cl_log(LOG_INFO, "****************************************************");
+ cl_log(LOG_INFO, "Testing ha_msg_copy():");
+ cpmsg = ha_msg_copy(reply);
+ cl_log(LOG_INFO, " ");
+ cl_log(LOG_INFO, "orginal message is :");
+ cl_log(LOG_INFO, " ");
+ ha_log_message(reply);
+ cl_log(LOG_INFO, " ");
+ cl_log(LOG_INFO, "copied message is: ");
+ cl_log(LOG_INFO, " ");
+ ha_log_message(cpmsg);
+ ha_msg_del(cpmsg);
+ }
+
+ ha_msg_del(reply); reply=NULL;
+#endif
+ }
+
+ if (!quitnow) {
+ cl_log(LOG_ERR, "read_hb_msg returned NULL");
+ cl_log(LOG_ERR, "REASON: %s", hb->llc_ops->errmsg(hb));
+ }
+ if (hb->llc_ops->signoff(hb, TRUE) != HA_OK) {
+ cl_log(LOG_ERR, "Cannot sign off from heartbeat.");
+ cl_log(LOG_ERR, "REASON: %s", hb->llc_ops->errmsg(hb));
+ exit(10);
+ }
+ if (hb->llc_ops->delete(hb) != HA_OK) {
+ cl_log(LOG_ERR, "Cannot delete API object.");
+ cl_log(LOG_ERR, "REASON: %s", hb->llc_ops->errmsg(hb));
+ exit(11);
+ }
+ return 0;
+}
diff --git a/lib/clplumbing/ocf_ipc.c b/lib/clplumbing/ocf_ipc.c
new file mode 100644
index 0000000..c243934
--- /dev/null
+++ b/lib/clplumbing/ocf_ipc.c
@@ -0,0 +1,594 @@
+/*
+ *
+ * ocf_ipc.c: IPC abstraction implementation.
+ *
+ *
+ * Copyright (c) 2002 Xiaoxiang Liu <xiliu@ncsa.uiuc.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <lha_internal.h>
+#include <clplumbing/ipc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/poll.h>
+#include <clplumbing/cl_log.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <grp.h>
+
+static int num_pool_allocated = 0;
+static int num_pool_freed = 0;
+
+#ifdef IPC_TIME_DEBUG
+struct ha_msg;
+void cl_log_message (int log_level, const struct ha_msg *m);
+int timediff(longclock_t t1, longclock_t t2);
+void ha_msg_del(struct ha_msg* msg);
+void ipc_time_debug(IPC_Channel* ch, IPC_Message* ipcmsg, int whichpos);
+#endif
+
+struct IPC_WAIT_CONNECTION * socket_wait_conn_new(GHashTable* ch_attrs);
+struct IPC_CHANNEL * socket_client_channel_new(GHashTable* ch_attrs);
+
+int (*ipc_pollfunc_ptr)(struct pollfd*, unsigned int, int)
+= (int (*)(struct pollfd*, unsigned int, int)) poll;
+
+/* Set the IPC poll function to the given function */
+void
+ipc_set_pollfunc(int (*pf)(struct pollfd*, unsigned int, int))
+{
+ ipc_pollfunc_ptr = pf;
+}
+
+struct IPC_WAIT_CONNECTION *
+ipc_wait_conn_constructor(const char * ch_type, GHashTable* ch_attrs)
+{
+ if (strcmp(ch_type, "domain_socket") == 0
+ || strcmp(ch_type, IPC_UDS_CRED) == 0
+ || strcmp(ch_type, IPC_ANYTYPE) == 0
+ || strcmp(ch_type, IPC_DOMAIN_SOCKET) == 0) {
+ return socket_wait_conn_new(ch_attrs);
+ }
+ return NULL;
+}
+
+struct IPC_CHANNEL *
+ipc_channel_constructor(const char * ch_type, GHashTable* ch_attrs)
+{
+ if (strcmp(ch_type, "domain_socket") == 0
+ || strcmp(ch_type, IPC_UDS_CRED) == 0
+ || strcmp(ch_type, IPC_ANYTYPE) == 0
+ || strcmp(ch_type, IPC_DOMAIN_SOCKET) == 0) {
+
+ return socket_client_channel_new(ch_attrs);
+ }
+ return NULL;
+}
+
+static int
+gnametonum(const char * gname, int gnlen)
+{
+ char grpname[64];
+ struct group* grp;
+
+ if (isdigit((int) gname[0])) {
+ return atoi(gname);
+ }
+ if (gnlen >= (int)sizeof(grpname)) {
+ return -1;
+ }
+ strncpy(grpname, gname, gnlen);
+ grpname[gnlen] = EOS;
+ if ((grp = getgrnam(grpname)) == NULL) {
+ cl_log(LOG_ERR
+ , "Invalid group name [%s]", grpname);
+ return -1;
+ }
+ return (int)grp->gr_gid;
+}
+
+static int
+unametonum(const char * lname, int llen)
+{
+ char loginname[64];
+ struct passwd* pwd;
+
+ if (llen >= (int)sizeof(loginname)) {
+ cl_log(LOG_ERR
+ , "user id name [%s] is too long", loginname);
+ return -1;
+ }
+ strncpy(loginname, lname, llen);
+ loginname[llen] = EOS;
+
+ if (isdigit((int) loginname[0])) {
+ return atoi(loginname);
+ }
+ if ((pwd = getpwnam(loginname)) == NULL) {
+ cl_log(LOG_ERR
+ , "Invalid user id name [%s]", loginname);
+ return -1;
+ }
+ return (int)pwd->pw_uid;
+}
+
+static GHashTable*
+make_id_table(const char * list, int listlen, int (*map)(const char *, int))
+{
+ GHashTable* ret;
+ const char * id;
+ const char * lastid = list + listlen;
+ int idlen;
+ int idval;
+ static int one = 1;
+
+ ret = g_hash_table_new(g_direct_hash, g_direct_equal);
+
+ id = list;
+ while (id < lastid && *id != EOS) {
+ idlen = strcspn(id, ",");
+ if (id+idlen >= lastid) {
+ idlen = (lastid - id);
+ }
+ idval = map(id, idlen);
+ if (idval < 0) {
+ g_hash_table_destroy(ret);
+ return NULL;
+ }
+#if 0
+ cl_log(LOG_DEBUG
+ , "Adding [ug]id %*s [%d] to authorization g_hash_table"
+ , idlen, id, idval);
+#endif
+ g_hash_table_insert(ret, GUINT_TO_POINTER(idval), &one);
+ id += idlen;
+ if (id < lastid) {
+ id += strspn(id, ",");
+ }
+ }
+ return ret;
+}
+
+struct IPC_AUTH*
+ipc_str_to_auth(const char* uidlist, int uidlen, const char* gidlist, int gidlen)
+{
+ struct IPC_AUTH* auth;
+
+ auth = malloc(sizeof(struct IPC_AUTH));
+ if (auth == NULL) {
+ cl_log(LOG_ERR, "Out of memory for IPC_AUTH");
+ return NULL;
+ }
+
+ memset(auth, 0, sizeof(*auth));
+
+ if (uidlist) {
+ auth->uid = make_id_table(uidlist, uidlen, unametonum);
+ if (auth->uid == NULL) {
+ cl_log(LOG_ERR,
+ "Bad uid list [%*s]",
+ uidlen, uidlist);
+ goto errout;
+ }
+ }
+ if (gidlist) {
+ auth->gid = make_id_table(gidlist, gidlen, gnametonum);
+ if (auth->gid == NULL) {
+ cl_log(LOG_ERR ,
+ "Bad gid list [%*s]",
+ gidlen, gidlist);
+ goto errout;
+ }
+ }
+ return auth;
+
+ errout:
+ if (auth->uid) {
+ g_hash_table_destroy(auth->uid);
+ auth->uid = NULL;
+ }
+ if (auth->gid) {
+ g_hash_table_destroy(auth->gid);
+ auth->gid = NULL;
+ }
+ free(auth);
+ auth = NULL;
+ return NULL;
+}
+
+struct IPC_AUTH *
+ipc_set_auth(uid_t * a_uid, gid_t * a_gid, int num_uid, int num_gid)
+{
+ struct IPC_AUTH *temp_auth;
+ int i;
+ static int v = 1;
+
+ temp_auth = malloc(sizeof(struct IPC_AUTH));
+ if (temp_auth == NULL) {
+ cl_log(LOG_ERR, "%s: memory allocation failed",__FUNCTION__);
+ return NULL;
+ }
+ temp_auth->uid = g_hash_table_new(g_direct_hash, g_direct_equal);
+ temp_auth->gid = g_hash_table_new(g_direct_hash, g_direct_equal);
+
+ if (num_uid > 0) {
+ for (i=0; i<num_uid; i++) {
+ g_hash_table_insert(temp_auth->uid, GINT_TO_POINTER((gint)a_uid[i])
+ , &v);
+ }
+ }
+
+ if (num_gid > 0) {
+ for (i=0; i<num_gid; i++) {
+ g_hash_table_insert(temp_auth->gid, GINT_TO_POINTER((gint)a_gid[i])
+ , &v);
+ }
+ }
+
+ return temp_auth;
+}
+
+void
+ipc_destroy_auth(struct IPC_AUTH *auth)
+{
+ if (auth != NULL) {
+ if (auth->uid) {
+ g_hash_table_destroy(auth->uid);
+ }
+ if (auth->gid) {
+ g_hash_table_destroy(auth->gid);
+ }
+ free((void *)auth);
+ }
+}
+
+static void
+ipc_bufpool_display(struct ipc_bufpool* pool)
+{
+ if (pool == NULL) {
+ return;
+ }
+ cl_log(LOG_INFO, "pool: refcount=%d, startpos=%p, currpos=%p,"
+ "consumepos=%p, endpos=%p, size=%d",
+ pool->refcount, pool->startpos,
+ pool->currpos, pool->consumepos,
+ pool->endpos, pool->size);
+}
+
+void
+ipc_bufpool_dump_stats(void)
+{
+ cl_log(LOG_INFO, "num_pool_allocated=%d, num_pool_freed=%d, diff=%d",
+ num_pool_allocated,
+ num_pool_freed,
+ num_pool_allocated - num_pool_freed);
+}
+
+#define POOLHDR_SIZE \
+ (sizeof(struct ipc_bufpool) + 2*sizeof(struct SOCKET_MSG_HEAD))
+
+struct ipc_bufpool*
+ipc_bufpool_new(int size)
+{
+ struct ipc_bufpool* pool;
+ int totalsize;
+
+ /* there are memories for two struct SOCKET_MSG_HEAD
+ * one for the big message, the other one for the next
+ * message. This code prevents allocating
+ * <big memory> <4k> <big memory><4k> ...
+ * from happening when a client sends big messages
+ * constantly*/
+
+ totalsize = size + POOLHDR_SIZE;
+
+ if (totalsize < POOL_SIZE) {
+ totalsize = POOL_SIZE;
+ }
+
+ if (totalsize > MAXMSG + POOLHDR_SIZE) {
+ cl_log(LOG_INFO, "ipc_bufpool_new: "
+ "asking for buffer with size %d; "
+ "corrupted data len???", totalsize);
+ return NULL;
+ }
+
+ pool = (struct ipc_bufpool*)malloc(totalsize+1);
+ if (pool == NULL) {
+ cl_log(LOG_ERR, "%s: memory allocation failed", __FUNCTION__);
+ return NULL;
+ }
+ memset(pool, 0, totalsize);
+ pool->refcount = 1;
+ pool->startpos = pool->currpos = pool->consumepos =
+ ((char*)pool) + sizeof(struct ipc_bufpool);
+
+ pool->endpos = ((char*)pool) + totalsize;
+ pool->size = totalsize;
+
+ num_pool_allocated ++ ;
+
+ return pool;
+}
+
+void
+ipc_bufpool_del(struct ipc_bufpool* pool)
+{
+ if (pool == NULL) {
+ return;
+ }
+
+ if (pool->refcount > 0) {
+ cl_log(LOG_ERR," ipc_bufpool_del:"
+ " IPC buffer pool reference count > 0");
+ return;
+ }
+
+ memset(pool, 0, pool->size);
+ free(pool);
+ num_pool_freed ++ ;
+}
+
+int
+ipc_bufpool_spaceleft(struct ipc_bufpool* pool)
+{
+ if( pool == NULL) {
+ cl_log(LOG_ERR, "ipc_bufpool_spaceleft:"
+ " invalid input argument");
+ return 0;
+ }
+ return pool->endpos - pool->currpos;
+}
+
+/* brief free the memory space allocated to msg and destroy msg. */
+
+static void
+ipc_bufpool_msg_done(struct IPC_MESSAGE * msg)
+{
+ struct ipc_bufpool* pool;
+
+ if (msg == NULL) {
+ cl_log(LOG_ERR, "ipc_bufpool_msg_done: invalid input");
+ return;
+ }
+
+ pool = (struct ipc_bufpool*)msg->msg_private;
+
+ ipc_bufpool_unref(pool);
+ free(msg);
+}
+
+static struct IPC_MESSAGE*
+ipc_bufpool_msg_new(void)
+{
+ struct IPC_MESSAGE * temp_msg;
+
+ temp_msg = malloc(sizeof(struct IPC_MESSAGE));
+ if (temp_msg == NULL) {
+ cl_log(LOG_ERR, "ipc_bufpool_msg_new:"
+ "allocating new msg failed");
+ return NULL;
+ }
+
+ memset(temp_msg, 0, sizeof(struct IPC_MESSAGE));
+
+ return temp_msg;
+}
+
+static void
+ipcmsg_display(IPC_Message* ipcmsg)
+{
+ if (ipcmsg == NULL) {
+ cl_log(LOG_ERR, "ipcmsg is NULL");
+ return;
+ }
+ cl_log(LOG_INFO, "ipcmsg: msg_len=%lu, msg_buf=%p, msg_body=%p,"
+ "msg_done=%p, msg_private=%p, msg_ch=%p",
+ (unsigned long)ipcmsg->msg_len,
+ ipcmsg->msg_buf,
+ ipcmsg->msg_body,
+ ipcmsg->msg_done,
+ ipcmsg->msg_private,
+ ipcmsg->msg_ch);
+}
+
+/* after a recv call, we have new data
+ * in the pool buf, we need to update our
+ * pool struct to consume it
+ *
+ */
+
+int
+ipc_bufpool_update(struct ipc_bufpool* pool,
+ struct IPC_CHANNEL * ch,
+ int msg_len,
+ IPC_Queue* rqueue)
+{
+ IPC_Message* ipcmsg;
+ struct SOCKET_MSG_HEAD localhead;
+ struct SOCKET_MSG_HEAD* head = &localhead;
+ int nmsgs = 0 ;
+
+ if (rqueue == NULL) {
+ cl_log(LOG_ERR, "ipc_update_bufpool:"
+ "invalid input");
+ return 0;
+ }
+
+ pool->currpos += msg_len;
+
+ while(TRUE) {
+ /*not enough data for head*/
+ if ((int)(pool->currpos - pool->consumepos) < (int)ch->msgpad) {
+ break;
+ }
+
+ memcpy(head, pool->consumepos, sizeof(struct SOCKET_MSG_HEAD));
+
+ if (head->magic != HEADMAGIC) {
+ GList* last = g_list_last(rqueue->queue);
+ cl_log(LOG_ERR, "ipc_bufpool_update: "
+ "magic number in head does not match. "
+ "Something very bad happened, farside pid =%d",
+ ch->farside_pid);
+ cl_log(LOG_ERR, "magic=%x, expected value=%x", head->magic, HEADMAGIC);
+ ipc_bufpool_display(pool);
+ cl_log(LOG_INFO, "nmsgs=%d", nmsgs);
+ /*print out the last message in queue*/
+ if (last) {
+ IPC_Message* m = (IPC_Message*)last;
+ ipcmsg_display(m);
+ }
+ return -1;
+ }
+
+ if ( head->msg_len > MAXMSG) {
+ cl_log(LOG_ERR, "ipc_update_bufpool:"
+ "msg length is corruptted(%d)",
+ head->msg_len);
+ break;
+ }
+
+ if (pool->consumepos + ch->msgpad + head->msg_len
+ > pool->currpos) {
+ break;
+ }
+
+ ipcmsg = ipc_bufpool_msg_new();
+ if (ipcmsg == NULL) {
+ cl_log(LOG_ERR, "ipc_update_bufpool:"
+ "allocating memory for new ipcmsg failed");
+ break;
+
+ }
+ ipcmsg->msg_buf = pool->consumepos;
+ ipcmsg->msg_body = pool->consumepos + ch->msgpad;
+ ipcmsg->msg_len = head->msg_len;
+ ipcmsg->msg_private = pool;
+ ipcmsg->msg_done = ipc_bufpool_msg_done;
+#ifdef IPC_TIME_DEBUG
+ ipc_time_debug(ch,ipcmsg, MSGPOS_RECV);
+#endif
+ rqueue->queue = g_list_append(rqueue->queue, ipcmsg);
+ rqueue->current_qlen ++;
+ nmsgs++;
+
+ pool->consumepos += ch->msgpad + head->msg_len;
+ ipc_bufpool_ref(pool);
+ }
+ return nmsgs;
+}
+
+gboolean
+ipc_bufpool_full(struct ipc_bufpool* pool,
+ struct IPC_CHANNEL* ch,
+ int* dataspaceneeded)
+{
+ struct SOCKET_MSG_HEAD localhead;
+ struct SOCKET_MSG_HEAD* head = &localhead;
+
+ *dataspaceneeded = 0;
+ /* not enough space for head */
+ if ((int)(pool->endpos - pool->consumepos) < (int)ch->msgpad) {
+ return TRUE;
+ }
+
+ /*enough space for head*/
+ if ((int)(pool->currpos - pool->consumepos) >= (int)ch->msgpad) {
+ memcpy(head, pool->consumepos, sizeof(struct SOCKET_MSG_HEAD));
+
+ /* not enough space for data*/
+ if ( pool->consumepos + ch->msgpad + head->msg_len >= pool->endpos) {
+ *dataspaceneeded = head->msg_len;
+ return TRUE;
+ }
+ }
+
+ /* Either we are sure we have enough space
+ * or we cannot tell because we have not received
+ * head yet. But we are sure we have enough space
+ * for head
+ */
+ return FALSE;
+}
+
+int
+ipc_bufpool_partial_copy(struct ipc_bufpool* dstpool,
+ struct ipc_bufpool* srcpool)
+{
+ struct SOCKET_MSG_HEAD localhead;
+ struct SOCKET_MSG_HEAD *head = &localhead;
+ int space_needed;
+ int nbytes;
+
+ if (dstpool == NULL
+ || srcpool == NULL) {
+ cl_log(LOG_ERR, "ipc_bufpool_partial_ipcmsg_cp:"
+ "invalid input");
+ return IPC_FAIL;
+ }
+
+ if (srcpool->currpos - srcpool->consumepos >=
+ (ssize_t)sizeof(struct SOCKET_MSG_HEAD)) {
+
+ memcpy(head, srcpool->consumepos, sizeof(struct SOCKET_MSG_HEAD));
+ space_needed = head->msg_len + sizeof(*head);
+
+ if (space_needed > ipc_bufpool_spaceleft(dstpool)) {
+ cl_log(LOG_ERR, "ipc_bufpool_partial_ipcmsg_cp:"
+ " not enough space left in dst pool,spaced needed=%d",
+ space_needed);
+ return IPC_FAIL;
+ }
+ }
+
+ nbytes = srcpool->currpos - srcpool->consumepos;
+ memcpy(dstpool->consumepos, srcpool->consumepos,nbytes);
+
+ srcpool->currpos = srcpool->consumepos;
+ dstpool->currpos = dstpool->consumepos + nbytes;
+
+ return IPC_OK;
+}
+
+void
+ipc_bufpool_ref(struct ipc_bufpool* pool)
+{
+ if (pool == NULL) {
+ cl_log(LOG_ERR, "ref_pool:"
+ " invalid input");
+ return;
+ }
+ pool->refcount ++;
+}
+
+void
+ipc_bufpool_unref(struct ipc_bufpool* pool)
+{
+ if (pool == NULL) {
+ cl_log(LOG_ERR, "unref_pool:"
+ " invalid input");
+ return;
+ }
+ pool->refcount --;
+ if (pool->refcount <= 0) {
+ ipc_bufpool_del(pool);
+ }
+}
diff --git a/lib/clplumbing/proctrack.c b/lib/clplumbing/proctrack.c
new file mode 100644
index 0000000..f6a9df2
--- /dev/null
+++ b/lib/clplumbing/proctrack.c
@@ -0,0 +1,515 @@
+/*
+ * Process tracking object.
+ *
+ * Copyright (c) 2002 International Business Machines
+ * Author: Alan Robertson <alanr@unix.sh>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <lha_internal.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <time.h>
+#include <clplumbing/proctrack.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/uids.h>
+#include <clplumbing/cl_signal.h>
+#include <clplumbing/Gmain_timeout.h>
+
+#define DEBUGPROCTRACK debugproctrack
+
+
+int debugproctrack = 0;
+static int LoggingIsEnabled = 1;
+static GHashTable* ProcessTable = NULL;
+static void InitProcTable(void);
+static void ForEachProcHelper(gpointer key, gpointer value
+, void * helper);
+static gboolean TrackedProcTimeoutFunction(gpointer p);
+
+static void
+InitProcTable()
+{
+ if (ProcessTable) {
+ return;
+ }
+
+ ProcessTable = g_hash_table_new(g_direct_hash, g_direct_equal);
+}
+
+/* Create/Log a new tracked process */
+void
+NewTrackedProc(pid_t pid, int isapgrp, ProcTrackLogType loglevel
+, void * privatedata, ProcTrack_ops* ops)
+{
+ ProcTrack* p = g_new(ProcTrack, 1);
+
+ InitProcTable();
+ p->pid = pid;
+ p->isapgrp = isapgrp;
+ p->loglevel = loglevel;
+ p->privatedata = privatedata;
+ p->ops = ops;
+ p->startticks = time_longclock();
+ p->starttime = time(NULL);
+ p->timerid = 0;
+ p->timeoutseq = -1;
+ p->killinfo = NULL;
+
+ g_hash_table_insert(ProcessTable, GINT_TO_POINTER(pid), p);
+
+ /* Tell them that someone registered a process */
+ if (p->ops->procregistered) {
+ p->ops->procregistered(p);
+ }
+}
+
+static struct signal_info_s {
+ int signo;
+ const char * sigdefine;
+ const char* sigwords;
+} signal_info [] = {
+
+#ifdef SIGHUP
+ {SIGHUP, "SIGHUP", "Hangup"},
+#endif
+#ifdef SIGINT
+ {SIGINT, "SIGINT", "Interrupt"},
+#endif
+#ifdef SIGQUIT
+ {SIGQUIT, "SIGQUIT", "Quit"},
+#endif
+#ifdef SIGILL
+ {SIGILL, "SIGILL", "Illegal instruction"},
+#endif
+#ifdef SIGTRAP
+ {SIGTRAP, "SIGTRAP", "Trace"},
+#endif
+#ifdef SIGABRT
+ {SIGABRT, "SIGABRT", "Abort"},
+#endif
+#ifdef SIGIOT
+ {SIGIOT, "SIGIOT", "IOT trap"},
+#endif
+#ifdef SIGBUS
+ {SIGBUS, "SIGBUS", "BUS error"},
+#endif
+#ifdef SIGFPE
+ {SIGFPE, "SIGFPE", "Floating-point exception"},
+#endif
+#ifdef SIGKILL
+ {SIGKILL, "SIGKILL", "Kill, unblockable"},
+#endif
+#ifdef SIGUSR1
+ {SIGUSR1, "SIGUSR1", "User-defined signal 1"},
+#endif
+#ifdef SIGSEGV
+ {SIGSEGV, "SIGSEGV", "Segmentation violation"},
+#endif
+#ifdef SIGUSR2
+ {SIGUSR2, "SIGUSR2", "User-defined signal 2"},
+#endif
+#ifdef SIGPIPE
+ {SIGPIPE, "SIGPIPE", "Broken pipe (POSIX)"},
+#endif
+#ifdef SIGALRM
+ {SIGALRM, "SIGALRM", "Alarm clock (POSIX)"},
+#endif
+#ifdef SIGTERM
+ {SIGTERM, "SIGTERM", "Termination (ANSI)"},
+#endif
+#ifdef SIGSTKFLT
+ {SIGSTKFLT, "SIGSTKFLT", "Stack fault"},
+#endif
+#ifdef SIGCHLD
+ {SIGCHLD, "SIGCHLD", "Child status has changed"},
+#endif
+#ifdef SIGCLD
+ {SIGCLD, "SIGCLD ", "Child status has changed"},
+#endif
+#ifdef SIGCONT
+ {SIGCONT, "SIGCONT", "Continue"},
+#endif
+#ifdef SIGSTOP
+ {SIGSTOP, "SIGSTOP", "Stop, unblockable"},
+#endif
+#ifdef SIGTSTP
+ {SIGTSTP, "SIGTSTP", "Keyboard stop"},
+#endif
+#ifdef SIGTTIN
+ {SIGTTIN, "SIGTTIN", "Background read from tty"},
+#endif
+#ifdef SIGTTOU
+ {SIGTTOU, "SIGTTOU", "Background write to tty"},
+#endif
+#ifdef SIGURG
+ {SIGURG, "SIGURG ", "Urgent condition on socket"},
+#endif
+#ifdef SIGXCPU
+ {SIGXCPU, "SIGXCPU", "CPU limit exceeded"},
+#endif
+#ifdef SIGXFSZ
+ {SIGXFSZ, "SIGXFSZ", "File size limit exceeded"},
+#endif
+#ifdef SIGVTALRM
+ {SIGVTALRM, "SIGVTALRM", "Virtual alarm clock"},
+#endif
+#ifdef SIGPROF
+ {SIGPROF, "SIGPROF", "Profiling alarm clock"},
+#endif
+#ifdef SIGWINCH
+ {SIGWINCH, "SIGWINCH", "Window size change"},
+#endif
+#ifdef SIGPOLL
+ {SIGPOLL, "SIGPOLL", "Pollable event occurred"},
+#endif
+#ifdef SIGIO
+ {SIGIO, "SIGIO", "I/O now possible"},
+#endif
+#ifdef SIGPWR
+ {SIGPWR, "SIGPWR", "Power failure restart"},
+#endif
+#ifdef SIGSYS
+ {SIGSYS, "SIGSYS", "Bad system call"},
+#endif
+};
+static const char *
+signal_name(int signo, const char ** sigdescription)
+{
+ int j;
+ for (j=0; j < DIMOF(signal_info); ++j) {
+ if (signal_info[j].signo == signo) {
+ if (sigdescription) {
+ *sigdescription = signal_info[j].sigwords;
+ }
+ return signal_info[j].sigdefine;
+ }
+ }
+ if (sigdescription) {
+ *sigdescription = NULL;
+ }
+ return NULL;
+}
+
+/* returns TRUE if 'pid' was registered */
+int
+ReportProcHasDied(int pid, int status)
+{
+ ProcTrack* p;
+ int signo=0;
+ int deathbyexit=0;
+ int deathbysig=0;
+ int exitcode=0;
+ int doreport = 0;
+ int debugreporting = 0;
+ const char * type;
+ ProcTrackLogType level;
+#ifdef WCOREDUMP
+ int didcoredump = 0;
+#endif
+ if ((p = GetProcInfo(pid)) == NULL) {
+ if (DEBUGPROCTRACK) {
+ cl_log(LOG_DEBUG
+ , "Process %d died (%d) but is not tracked."
+ , pid, status);
+ }
+ type = "untracked process";
+ level = PT_LOGNONE;
+ }else{
+ type = p->ops->proctype(p);
+ level = p->loglevel;
+ }
+
+ if (WIFEXITED(status)) {
+ deathbyexit=1;
+ exitcode = WEXITSTATUS(status);
+ }else if (WIFSIGNALED(status)) {
+ deathbysig=1;
+ signo = WTERMSIG(status);
+ doreport=1;
+ }
+ switch(level) {
+ case PT_LOGVERBOSE: doreport=1;
+ break;
+
+ case PT_LOGNONE: doreport = 0;
+ break;
+
+ case PT_LOGNORMAL: break;
+ }
+
+ if (!LoggingIsEnabled) {
+ doreport = 0;
+ }
+#ifdef WCOREDUMP
+ if (WCOREDUMP(status)) {
+ /* Force a report on all core dumping processes */
+ didcoredump=1;
+ doreport=1;
+ }
+#endif
+ if (DEBUGPROCTRACK && !doreport) {
+ doreport = 1;
+ debugreporting = 1;
+ }
+
+ if (doreport) {
+ if (deathbyexit) {
+ cl_log((exitcode == 0 ? LOG_INFO : LOG_WARNING)
+ , "Managed %s process %d exited with return code %d."
+ , type, pid, exitcode);
+ }else if (deathbysig) {
+ const char * signame = NULL;
+ const char * sigwords = NULL;
+ int logtype;
+ signame = signal_name(signo, &sigwords);
+ logtype = (debugreporting ? LOG_INFO : LOG_WARNING);
+ /*
+ * Processes being killed isn't an error if
+ * we're only logging because of debugging.
+ */
+ if (signame && sigwords) {
+ cl_log(logtype
+ , "Managed %s process %d killed by"
+ " signal %d [%s - %s]."
+ , type, pid, signo
+ , signame, sigwords);
+ }else{
+ cl_log(logtype
+ , "Managed %s process %d killed by signal %d."
+ , type, pid, signo);
+ }
+ }else{
+ cl_log(LOG_ERR, "Managed %s process %d went away"
+ " strangely (!)"
+ , type, pid);
+ }
+ }
+#ifdef WCOREDUMP
+ if (didcoredump) {
+ /* We report ALL core dumps without exception */
+ cl_log(LOG_ERR, "Managed %s process %d dumped core"
+ , type, pid);
+ }
+#endif
+
+ if (p) {
+ RemoveTrackedProcTimeouts(pid);
+ /*
+ * From clplumbing/proctrack.h:
+ * (ProcTrack* p, int status, int signo, int exitcode
+ * , int waslogged);
+ */
+ p->ops->procdied(p, status, signo, exitcode, doreport);
+ if (p->privatedata) {
+ /* They may have forgotten to free something... */
+ cl_log(LOG_ERR, "Managed %s process %d did not"
+ " clean up private data!"
+ , type, pid);
+ }
+ g_hash_table_remove(ProcessTable, GINT_TO_POINTER(pid));
+ g_free(p);
+ }
+
+ return doreport;
+}
+
+/* Return information associated with the given PID (or NULL) */
+ProcTrack*
+GetProcInfo(pid_t pid)
+{
+ return (ProcessTable
+ ? g_hash_table_lookup(ProcessTable, GINT_TO_POINTER(pid))
+ : NULL);
+}
+
+/* "info" is 0-terminated (terminated by a 0 signal) */
+int
+SetTrackedProcTimeouts(pid_t pid, ProcTrackKillInfo* info)
+{
+ long mstimeout;
+ ProcTrack* pinfo;
+ pinfo = GetProcInfo(pid);
+
+ if (pinfo == NULL) {
+ return 0;
+ }
+
+ pinfo->timeoutseq = 0;
+ pinfo->killinfo = info;
+ mstimeout = pinfo->killinfo[0].mstimeout;
+ pinfo->timerid = Gmain_timeout_add(mstimeout
+ , TrackedProcTimeoutFunction
+ , GINT_TO_POINTER(pid));
+ return pinfo->timerid;
+}
+
+void
+RemoveTrackedProcTimeouts(pid_t pid)
+{
+ ProcTrack* pinfo;
+ pinfo = GetProcInfo(pid);
+
+ if (pinfo == NULL) {
+ return;
+ }
+
+ if (pinfo->killinfo && pinfo->timerid) {
+ Gmain_timeout_remove(pinfo->timerid);
+ }
+ pinfo->killinfo = NULL;
+ pinfo->timerid = 0;
+}
+
+static gboolean
+TrackedProcTimeoutFunction(gpointer p)
+{
+ /* This is safe - Pids are relatively small ints */
+ pid_t pid = POINTER_TO_SIZE_T(p); /*pointer cast as int*/
+ ProcTrack* pinfo;
+ int nsig;
+ long mstimeout;
+ int hadprivs;
+
+ pinfo = GetProcInfo(pid);
+
+ if (pinfo == NULL) {
+ cl_log(LOG_ERR, "%s: bad pinfo in call (pid %d)", __FUNCTION__, pid);
+ return FALSE;
+ }
+ if (pinfo->timeoutseq < 0 || pinfo->killinfo == NULL) {
+ cl_log(LOG_ERR
+ , "%s: bad call (pid %d): killinfo (%d, 0x%lx)"
+ , __FUNCTION__, pid
+ , pinfo->timeoutseq
+ , (unsigned long)POINTER_TO_SIZE_T(pinfo->killinfo));
+ return FALSE;
+ }
+
+ pinfo->timerid = 0;
+ nsig = pinfo->killinfo[pinfo->timeoutseq].signalno;
+
+ if (nsig == 0) {
+ if (CL_PID_EXISTS(pid)) {
+ cl_log(LOG_ERR
+ , "%s: %s process (PID %d) will not die!"
+ , __FUNCTION__
+ , pinfo->ops->proctype(pinfo)
+ , (int)pid);
+ }
+ return FALSE;
+ }
+ pinfo->timeoutseq++;
+ cl_log(LOG_WARNING, "%s process (PID %d) timed out (try %d)"
+ ". Killing with signal %s (%d)."
+ , pinfo->ops->proctype(pinfo), (int)pid
+ , pinfo->timeoutseq
+ , signal_name(nsig, NULL)
+ , nsig);
+
+ if (pinfo->isapgrp && nsig > 0) {
+ pid = -pid;
+ }
+
+ if (!(hadprivs = cl_have_full_privs())) {
+ return_to_orig_privs();
+ }
+ if (kill(pid, nsig) < 0) {
+ if (errno == ESRCH) {
+ /* Mission accomplished! */
+ cl_log(LOG_INFO, "%s process (PID %d) died before killing (try %d)"
+ , pinfo->ops->proctype(pinfo), (int)pid
+ , pinfo->timeoutseq);
+ return FALSE;
+ }else{
+ cl_perror("%s: kill(%d,%d) failed"
+ , __FUNCTION__, pid, nsig);
+ }
+ }
+ if (!hadprivs) {
+ return_to_dropped_privs();
+ }
+ mstimeout = pinfo->killinfo[pinfo->timeoutseq].mstimeout;
+ pinfo->timerid = Gmain_timeout_add(mstimeout
+ , TrackedProcTimeoutFunction
+ , p);
+ if (pinfo->timerid <= 0) {
+ cl_log(LOG_ERR, "%s: Could not add new kill timer [%u]"
+ , __FUNCTION__, pinfo->timerid);
+ kill(pid, SIGKILL);
+ }
+ if (debugproctrack) {
+ cl_log(LOG_DEBUG, "%s process (PID %d) scheduled to be killed again"
+ " (try %d) in %ld ms [timerid %u]"
+ , pinfo->ops->proctype(pinfo), (int)pid
+ , pinfo->timeoutseq
+ , mstimeout
+ , pinfo->timerid);
+ }
+ return FALSE;
+}
+
+/* Helper struct to allow us to stuff 3 args into one ;-) */
+struct prochelper {
+ ProcTrack_ops* type;
+ ProcTrackFun fun;
+ void* data;
+};
+
+/* Helper function to call user's function with right args... */
+static void
+ForEachProcHelper(gpointer key, gpointer value, void * helper)
+{
+ ProcTrack* p = value;
+ struct prochelper* ph = helper;
+
+ if (ph->type != NULL && ph->type != p->ops) {
+ return;
+ }
+
+ ph->fun(p, ph->data);
+}
+/*
+ * Iterate over the set of tracked processes.
+ * If proctype is NULL, then walk through them all, otherwise only those
+ * of the given type.
+ */
+void
+ForEachProc(ProcTrack_ops* proctype, ProcTrackFun f, void * data)
+{
+ struct prochelper ph;
+
+ InitProcTable();
+ ph.fun = f;
+ ph.type = proctype;
+ ph.data = data;
+ g_hash_table_foreach(ProcessTable, ForEachProcHelper, &ph);
+}
+
+void
+DisableProcLogging()
+{
+ LoggingIsEnabled = 0;
+}
+
+void
+EnableProcLogging()
+{
+ LoggingIsEnabled = 1;
+}
diff --git a/lib/clplumbing/realtime.c b/lib/clplumbing/realtime.c
new file mode 100644
index 0000000..9271204
--- /dev/null
+++ b/lib/clplumbing/realtime.c
@@ -0,0 +1,354 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <lha_internal.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stddef.h>
+/* The BSD's do not use malloc.h directly. */
+/* They use stdlib.h instead */
+#ifndef BSD
+#ifdef HAVE_MALLOC_H
+# include <malloc.h>
+#endif
+#endif
+#include <unistd.h>
+#ifdef _POSIX_MEMLOCK
+# include <sys/mman.h>
+# include <sys/time.h>
+# include <sys/resource.h>
+#endif
+#ifdef _POSIX_PRIORITY_SCHEDULING
+# include <sched.h>
+#endif
+#include <string.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/realtime.h>
+#include <clplumbing/uids.h>
+#include <time.h>
+#include <errno.h>
+
+static gboolean cl_realtimepermitted = TRUE;
+static void cl_rtmalloc_setup(void);
+
+#define HOGRET 0xff
+/*
+ * Slightly wacko recursive function to touch requested amount
+ * of stack so we have it pre-allocated inside our realtime code
+ * as per suggestion from mlockall(2)
+ */
+#ifdef _POSIX_MEMLOCK
+static unsigned char
+cl_stack_hogger(unsigned char * inbuf, int kbytes)
+{
+ unsigned char buf[1024];
+
+ if (inbuf == NULL) {
+ memset(buf, HOGRET, sizeof(buf));
+ }else{
+ memcpy(buf, inbuf, sizeof(buf));
+ }
+
+ if (kbytes > 0) {
+ return cl_stack_hogger(buf, kbytes-1);
+ }else{
+ return buf[sizeof(buf)-1];
+ }
+/* #else
+ return HOGRET;
+*/
+}
+#endif
+/*
+ * We do things this way to hopefully defeat "smart" malloc code which
+ * handles large mallocs as special cases using mmap().
+ */
+static void
+cl_malloc_hogger(int kbytes)
+{
+ long size = kbytes * 1024;
+ int chunksize = 1024;
+ long nchunks = (int)(size / chunksize);
+ int chunkbytes = nchunks * sizeof(void *);
+ void** chunks;
+ int j;
+
+#ifdef HAVE_MALLOPT
+# ifdef M_MMAP_MAX
+ /* Keep malloc from using mmap */
+ mallopt(M_MMAP_MAX, 0);
+#endif
+# ifdef M_TRIM_THRESHOLD
+ /* Keep malloc from giving memory back to the system */
+ mallopt(M_TRIM_THRESHOLD, -1);
+# endif
+#endif
+ chunks=malloc(chunkbytes);
+ if (chunks == NULL) {
+ cl_log(LOG_INFO, "Could not preallocate (%d) bytes"
+ , chunkbytes);
+ return;
+ }
+ memset(chunks, 0, chunkbytes);
+
+ for (j=0; j < nchunks; ++j) {
+ chunks[j] = malloc(chunksize);
+ if (chunks[j] == NULL) {
+ cl_log(LOG_INFO, "Could not preallocate (%d) bytes"
+ , chunksize);
+ }else{
+ memset(chunks[j], 0, chunksize);
+ }
+ }
+ for (j=0; j < nchunks; ++j) {
+ if (chunks[j]) {
+ free(chunks[j]);
+ chunks[j] = NULL;
+ }
+ }
+ free(chunks);
+ chunks = NULL;
+}
+
+/*
+ * Make us behave like a soft real-time process.
+ * We need scheduling priority and being locked in memory.
+ * If you ask us nicely, we'll even grow the stack and heap
+ * for you before locking you into memory ;-).
+ */
+void
+cl_make_realtime(int spolicy, int priority, int stackgrowK, int heapgrowK)
+{
+#ifdef DEFAULT_REALTIME_POLICY
+ struct sched_param sp;
+ int staticp;
+#endif
+
+ if (heapgrowK > 0) {
+ cl_malloc_hogger(heapgrowK);
+ }
+
+#ifdef _POSIX_MEMLOCK
+ if (stackgrowK > 0) {
+ unsigned char ret;
+ if ((ret=cl_stack_hogger(NULL, stackgrowK)) != HOGRET) {
+ cl_log(LOG_INFO, "Stack hogger failed 0x%x"
+ , ret);
+ }
+ }
+#endif
+ cl_rtmalloc_setup();
+
+ if (!cl_realtimepermitted) {
+ cl_log(LOG_INFO
+ , "Request to set pid %ld to realtime ignored."
+ , (long)getpid());
+ return;
+ }
+
+#ifdef DEFAULT_REALTIME_POLICY
+ if (spolicy < 0) {
+ spolicy = DEFAULT_REALTIME_POLICY;
+ }
+
+ if (priority <= 0) {
+ priority = sched_get_priority_min(spolicy);
+ }
+
+ if (priority > sched_get_priority_max(spolicy)) {
+ priority = sched_get_priority_max(spolicy);
+ }
+
+
+ if ((staticp=sched_getscheduler(0)) < 0) {
+ cl_perror("unable to get scheduler parameters.");
+ }else{
+ memset(&sp, 0, sizeof(sp));
+ sp.sched_priority = priority;
+
+ if (sched_setscheduler(0, spolicy, &sp) < 0) {
+ cl_perror("Unable to set scheduler parameters.");
+ }
+ }
+#endif
+
+#if defined _POSIX_MEMLOCK
+# ifdef RLIMIT_MEMLOCK
+# define THRESHOLD(lim) (((lim))/2)
+ {
+ unsigned long growsize = ((stackgrowK+heapgrowK)*1024);
+ struct rlimit memlocklim;
+
+ getrlimit(RLIMIT_MEMLOCK, &memlocklim); /* Allow for future added fields */
+ memlocklim.rlim_max = RLIM_INFINITY;
+ memlocklim.rlim_cur = RLIM_INFINITY;
+ /* Try and remove memory locking limits -- if we can */
+ if (setrlimit(RLIMIT_MEMLOCK, &memlocklim) < 0) {
+ /* Didn't work - get what we can */
+ getrlimit(RLIMIT_MEMLOCK, &memlocklim);
+ memlocklim.rlim_cur = memlocklim.rlim_max;
+ setrlimit(RLIMIT_MEMLOCK, &memlocklim);
+ }
+
+ /* Could we get 'enough' ? */
+ /* (this is a guess - might not be right if we're not root) */
+ if (getrlimit(RLIMIT_MEMLOCK, &memlocklim) >= 0
+ && memlocklim.rlim_cur != RLIM_INFINITY
+ && (growsize >= THRESHOLD(memlocklim.rlim_cur))) {
+ cl_log(LOG_ERR
+ , "Cannot lock ourselves into memory: System limits"
+ " on locked-in memory are too small.");
+ return;
+ }
+ }
+# endif /*RLIMIT_MEMLOCK*/
+ if (mlockall(MCL_CURRENT|MCL_FUTURE) >= 0) {
+ if (ANYDEBUG) {
+ cl_log(LOG_DEBUG, "pid %d locked in memory.", (int) getpid());
+ }
+
+ } else if(errno == ENOSYS) {
+ const char *err = strerror(errno);
+ cl_log(LOG_WARNING, "Unable to lock pid %d in memory: %s",
+ (int) getpid(), err);
+
+ } else {
+ cl_perror("Unable to lock pid %d in memory", (int) getpid());
+ }
+#endif
+}
+
+void
+cl_make_normaltime(void)
+{
+#ifdef DEFAULT_REALTIME_POLICY
+ struct sched_param sp;
+
+ memset(&sp, 0, sizeof(sp));
+ sp.sched_priority = sched_get_priority_min(SCHED_OTHER);
+ if (sched_setscheduler(0, SCHED_OTHER, &sp) < 0) {
+ cl_perror("unable to (re)set scheduler parameters.");
+ }
+#endif
+#ifdef _POSIX_MEMLOCK
+ /* Not strictly necessary. */
+ munlockall();
+#endif
+}
+
+void
+cl_disable_realtime(void)
+{
+ cl_realtimepermitted = FALSE;
+}
+
+void
+cl_enable_realtime(void)
+{
+ cl_realtimepermitted = TRUE;
+}
+
+/* Give up the CPU for a little bit */
+/* This is similar to sched_yield() but allows lower prio processes to run */
+int
+cl_shortsleep(void)
+{
+ static const struct timespec req = {0,2000001L};
+
+ return nanosleep(&req, NULL);
+}
+
+
+static int post_rt_morecore_count = 0;
+static unsigned long init_malloc_arena = 0L;
+
+#ifdef HAVE_MALLINFO
+# define MALLOC_TOTALSIZE() (((unsigned long)mallinfo().arena)+((unsigned long)mallinfo().hblkhd))
+#else
+# define MALLOC_TOTALSIZE() (0L)
+#endif
+
+
+
+/* Return the number of times we went after more core */
+int
+cl_nonrealtime_malloc_count(void)
+{
+ return post_rt_morecore_count;
+}
+unsigned long
+cl_nonrealtime_malloc_size(void)
+{
+ return (MALLOC_TOTALSIZE() - init_malloc_arena);
+}
+/* Log the number of times we went after more core */
+void
+cl_realtime_malloc_check(void)
+{
+ static int lastcount = 0;
+ static unsigned long oldarena = 0UL;
+
+ if (oldarena == 0UL) {
+ oldarena = init_malloc_arena;
+ }
+
+ if (post_rt_morecore_count > lastcount) {
+
+ if (MALLOC_TOTALSIZE() > oldarena) {
+
+ cl_log(LOG_WARNING,
+ "Performed %d more non-realtime malloc calls.",
+ post_rt_morecore_count - lastcount);
+
+ cl_log(LOG_INFO,
+ "Total non-realtime malloc bytes: %ld",
+ MALLOC_TOTALSIZE() - init_malloc_arena);
+ oldarena = MALLOC_TOTALSIZE();
+
+ }
+
+ lastcount = post_rt_morecore_count;
+ }
+}
+
+#ifdef HAVE___DEFAULT_MORECORE
+
+static void (*our_save_morecore_hook)(void) = NULL;
+static void cl_rtmalloc_morecore_fun(void);
+
+static void
+cl_rtmalloc_morecore_fun(void)
+{
+ post_rt_morecore_count++;
+ if (our_save_morecore_hook) {
+ our_save_morecore_hook();
+ }
+}
+#endif
+
+static void
+cl_rtmalloc_setup(void)
+{
+ static gboolean inityet = FALSE;
+ if (!inityet) {
+ init_malloc_arena = MALLOC_TOTALSIZE();
+#ifdef HAVE___DEFAULT_MORECORE
+ our_save_morecore_hook = __after_morecore_hook;
+ __after_morecore_hook = cl_rtmalloc_morecore_fun;
+ inityet = TRUE;
+#endif
+ }
+ }
diff --git a/lib/clplumbing/replytrack.c b/lib/clplumbing/replytrack.c
new file mode 100644
index 0000000..8c7c38e
--- /dev/null
+++ b/lib/clplumbing/replytrack.c
@@ -0,0 +1,643 @@
+
+/*
+ * Reply tracking library.
+ *
+ * Copyright (c) 2007 Alan Robertson
+ * Author: Alan Robertson <alanr@unix.sh>
+ *
+ ******************************************************************
+ * This library is useful for tracking replies to multicast messages
+ * sent to cluster members. It tracks incremental membership changes
+ * according to any desired criteria, and then keeps track of when
+ * the last expected reply is received according to the dynamically
+ * updated membership as of when the message was sent out.
+ ******************************************************************
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <lha_internal.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <memory.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/replytrack.h>
+#include <clplumbing/Gmain_timeout.h>
+
+/*
+ * These are the only data items that go in our GHashTables
+ */
+struct rt_node_info {
+ char * nodename;
+ cl_uuid_t nodeid;
+};
+
+struct node_tables {
+
+ GHashTable* uuidmap; /* Keyed by uuid */
+ int uuidcount;
+ GHashTable* namemap; /* Keyed by nodename*/
+ int namecount;
+};
+struct _nodetrack {
+ struct node_tables nt;
+ int refcount;
+ nodetrack_callback_t callback;
+ gpointer user_data;
+ nodetrack_callback_t extra_callback;
+ gpointer ext_data;
+};
+
+/*
+ * Things we use to track outstanding replies
+ * This is the structure used by the replytrack_t typedef
+ */
+struct _replytrack {
+ replytrack_callback_t callback;
+ gpointer user_data;
+ unsigned timerid;
+ struct node_tables tables;
+ gboolean expectingmore;
+ nodetrack_t* membership;
+};
+
+struct _nodetrack_intersection {
+ nodetrack_t** tables;
+ int ntables;
+ nodetrack_callback_t callback;
+ gpointer user_data;
+ nodetrack_t* intersection;
+};
+
+static cl_uuid_t nulluuid;
+static int nodetrack_t_count = 0;
+static int replytrack_t_count = 0;
+static int replytrack_intersection_t_count = 0;
+
+static struct rt_node_info *
+rt_node_info_new(const char * nodename, cl_uuid_t nodeid)
+{
+ struct rt_node_info* ret;
+
+ if (!nodename) {
+ return NULL;
+ }
+ ret = MALLOCT(struct rt_node_info);
+
+ if (!ret) {
+ return ret;
+ }
+ ret->nodename = strdup(nodename);
+ if (!ret->nodename) {
+ free(ret);
+ ret = NULL;
+ return ret;
+ }
+ ret->nodeid = nodeid;
+ return ret;
+}
+
+static void
+rt_node_info_del(struct rt_node_info * ni)
+{
+ if (ni != NULL) {
+ if (ni->nodename != NULL) {
+ free(ni->nodename);
+ }
+ memset(ni, 0, sizeof(*ni));
+ free(ni);
+ }
+}
+
+/*
+ * namehash cannot be NULL, idhash cannot be NULL, and nodename cannot be NULL
+ *
+ * 'id' can be a NULL uuid, in which case it goes into only the name table
+ * 'nodename' can change over time - in which case we update our tables.
+ * It is possible for one nodename to have more than one uuid.
+ * We allow for that.
+ *
+ * Changing the uuid but keeping the nodename the same is considered to be
+ * adding a new node with the same nodename.
+ * Exception: A node with a null uuid is presumed to have acquired a proper
+ * uuid if it is later seen with a non-null UUID
+ */
+
+static gboolean
+del_node_from_hashtables(struct node_tables *t
+, const char * nodename, cl_uuid_t id)
+{
+ struct rt_node_info * entry;
+ if (cl_uuid_is_null(&id)) {
+ if ((entry = g_hash_table_lookup(t->namemap,nodename))!=NULL){
+ g_hash_table_remove(t->namemap, nodename);
+ rt_node_info_del(entry);
+ t->namecount--;
+ }
+ return TRUE;
+ }
+ if ((entry=g_hash_table_lookup(t->uuidmap, &id)) != NULL) {
+ g_hash_table_remove(t->uuidmap, &id);
+ rt_node_info_del(entry);
+ t->uuidcount--;
+ }
+ return TRUE;
+}
+
+
+static gboolean
+add_node_to_hashtables(struct node_tables * t
+, const char * nodename, cl_uuid_t id)
+{
+ struct rt_node_info* idinfo = NULL;
+
+ if (cl_uuid_is_null(&id)) {
+ /* Supplied uuid is the null UUID - insert in name table */
+ struct rt_node_info* ninfo;
+ if (g_hash_table_lookup(t->namemap, nodename) == NULL) {
+ if (NULL == (ninfo = rt_node_info_new(nodename, id))){
+ goto outofmem;
+ }
+ g_hash_table_insert(t->namemap,ninfo->nodename,ninfo);
+ t->namecount++;
+ }
+ return TRUE;
+ }
+
+ /* Supplied uuid is not the null UUID */
+
+ if (g_hash_table_lookup(t->uuidmap,&id) == NULL) {
+ /* See if a corresponding name is in name map */
+ /* If so, delete it - assume uuid was missing before */
+
+ if (g_hash_table_lookup(t->namemap, nodename) != NULL) {
+ del_node_from_hashtables(t, nodename, nulluuid);
+ }
+ /* Not yet in our uuid hash table */
+ idinfo = rt_node_info_new(nodename, id);
+ if (idinfo == NULL) {
+ goto outofmem;
+ }
+ g_hash_table_insert(t->uuidmap, &idinfo->nodeid, idinfo);
+ t->uuidcount++;
+ }
+ return TRUE;
+outofmem:
+ cl_log(LOG_ERR, "%s: out of memory", __FUNCTION__);
+ return FALSE;
+}
+
+static gboolean
+create_new_hashtables(struct node_tables*t)
+{
+ t->namemap = g_hash_table_new(g_str_hash, g_str_equal);
+ if (t->namemap == NULL) {
+ return FALSE;
+ }
+ t->uuidmap = g_hash_table_new(cl_uuid_g_hash, cl_uuid_g_equal);
+ if (t->uuidmap == NULL) {
+ g_hash_table_destroy(t->namemap);
+ t->namemap = NULL;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+hashtable_destroy_rt_node_info(gpointer key, gpointer rti, gpointer unused)
+{
+ rt_node_info_del(rti);
+ rti = key = NULL;
+ return TRUE;
+}
+
+static void
+destroy_map_hashtable(GHashTable*t)
+{
+ g_hash_table_foreach_remove(t, hashtable_destroy_rt_node_info,NULL);
+ g_hash_table_destroy(t);
+ t = NULL;
+}
+
+struct tablehelp {
+ struct node_tables* t;
+ gboolean ret;
+};
+
+static void
+copy_hashtables_helper(gpointer key_unused, gpointer value
+, gpointer user_data)
+{
+ struct tablehelp * th = user_data;
+ struct rt_node_info* ni = value;
+ if (!add_node_to_hashtables(th->t, ni->nodename, ni->nodeid)) {
+ th->ret = FALSE;
+ }
+}
+
+static gboolean
+copy_hashtables(struct node_tables* tin, struct node_tables* tout)
+{
+ struct tablehelp newtables;
+ if (!create_new_hashtables(tout)){
+ return FALSE;
+ }
+ newtables.t = tout;
+ newtables.ret = TRUE;
+
+ g_hash_table_foreach(tout->namemap,copy_hashtables_helper,&newtables);
+ if (!newtables.ret) {
+ return FALSE;
+ }
+ g_hash_table_foreach(tout->uuidmap,copy_hashtables_helper,&newtables);
+ return newtables.ret;
+}
+
+static gboolean mbr_inityet = FALSE;
+static void
+init_global_membership(void)
+{
+ if (mbr_inityet) {
+ return;
+ }
+ mbr_inityet = TRUE;
+ memset(&nulluuid, 0, sizeof(nulluuid));
+}
+
+gboolean /* Call us when an expected replier joins / comes up */
+nodetrack_nodeup(nodetrack_t * mbr, const char * node, cl_uuid_t uuid)
+{
+ gboolean ret;
+ ret = add_node_to_hashtables(&mbr->nt, node, uuid);
+ if (ret && mbr->callback) {
+ mbr->callback(mbr, node, uuid, NODET_UP, mbr->user_data);
+ }
+ if (mbr->extra_callback) {
+ mbr->extra_callback(mbr, node, uuid, NODET_UP,mbr->ext_data);
+ }
+ return ret;
+}
+
+gboolean /* Call us when an expected replier goes down / away */
+nodetrack_nodedown(nodetrack_t* mbr, const char* node, cl_uuid_t uuid)
+{
+ if (mbr->callback) {
+ mbr->callback(mbr, node, uuid, NODET_DOWN, mbr->user_data);
+ }
+ if (mbr->extra_callback) {
+ mbr->extra_callback(mbr, node,uuid,NODET_DOWN,mbr->ext_data);
+ }
+ return del_node_from_hashtables(&mbr->nt, node, uuid);
+}
+
+/* This function calls the user's timeout callback */
+static gboolean
+replytrack_timeout_helper(gpointer rldata)
+{
+ replytrack_t* rl = rldata;
+ rl->expectingmore = FALSE;
+ rl->timerid = 0;
+ if (rl->callback) {
+ rl->callback(rl, rl->user_data, REPLYT_TIMEOUT);
+ }
+ return FALSE;
+}
+
+replytrack_t* /* replytrack_t constructor */
+replytrack_new(nodetrack_t * membership
+, replytrack_callback_t callback
+, unsigned long timeout_ms
+, gpointer user_data)
+{
+ replytrack_t* ret = MALLOCT(replytrack_t);
+ if (!ret) {
+ return ret;
+ }
+ if (!copy_hashtables(&membership->nt, &ret->tables)) {
+ free(ret);
+ ret = NULL;
+ return ret;
+ }
+ replytrack_t_count++;
+ ret->membership = membership;
+ ret->membership->refcount++;
+ ret->callback = callback;
+ ret->user_data = user_data;
+ ret->expectingmore = TRUE;
+ ret->timerid = 0;
+ if (timeout_ms != 0 && callback != NULL) {
+ ret->timerid = Gmain_timeout_add(timeout_ms
+ , replytrack_timeout_helper, ret);
+ }
+ return ret;
+}
+
+void /* replytrack_t destructor */
+replytrack_del(replytrack_t * rl)
+{
+ rl->membership->refcount--;
+ replytrack_t_count++;
+ if (rl->expectingmore && rl->timerid > 0) {
+ cl_log(LOG_INFO
+ , "%s: destroying replytrack while still expecting"
+ " %d replies"
+ , __FUNCTION__
+ , (rl->tables.namecount + rl->tables.uuidcount));
+ }
+ if (rl->timerid > 0) {
+ g_source_remove(rl->timerid);
+ rl->timerid = 0;
+ }
+ destroy_map_hashtable(rl->tables.namemap);
+ rl->tables.namemap=NULL;
+ destroy_map_hashtable(rl->tables.uuidmap);
+ rl->tables.uuidmap=NULL;
+ memset(&rl, 0, sizeof(rl));
+ free(rl);
+ rl=NULL;
+}
+
+gboolean /* Call replytrack_gotreply when you receive an expected reply */
+replytrack_gotreply(replytrack_t*rl, const char * node, cl_uuid_t uuid)
+{
+ gboolean lastone;
+ del_node_from_hashtables(&rl->tables, node, uuid);
+ lastone = (rl->tables.namecount + rl->tables.uuidcount) == 0;
+ if (lastone) {
+ rl->expectingmore = FALSE;
+ if (rl->timerid > 0) {
+ g_source_remove(rl->timerid);
+ rl->timerid = 0;
+ }
+ if (rl->callback){
+ rl->callback(rl, rl->user_data, REPLYT_ALLRCVD);
+ }
+ }
+ return lastone;
+}
+
+struct replytrack_iterator_data {
+ replytrack_t* rlist;
+ replytrack_iterator_t f;
+ int count;
+ gpointer user_data;
+};
+
+
+static void /* g_hash_table user-level iteration helper */
+replytrack_iterator_helper(gpointer key_unused, gpointer entry
+, gpointer user_data)
+{
+ struct replytrack_iterator_data* ri = user_data;
+ struct rt_node_info* ni = entry;
+ if (ri && ri->rlist) {
+ ++ri->count;
+ if (ri->f) {
+ ri->f(ri->rlist, ri->user_data
+ , ni->nodename, ni->nodeid);
+ }
+ }
+}
+
+
+
+int /* iterate through the outstanding expected replies */
+replytrack_outstanding_iterate(replytrack_t* rl
+, replytrack_iterator_t i, gpointer user_data)
+{
+ struct replytrack_iterator_data id;
+ id.rlist = rl;
+ id.f = i;
+ id.count = 0;
+ id.user_data = user_data;
+ g_hash_table_foreach(rl->tables.namemap, replytrack_iterator_helper
+ , &id);
+ g_hash_table_foreach(rl->tables.uuidmap, replytrack_iterator_helper
+ , &id);
+ if (id.count != (rl->tables.namecount + rl->tables.uuidcount)) {
+ cl_log(LOG_ERR
+ , "%s: iteration count %d disagrees with"
+ " (namecount %d+uuidcount %d)"
+ , __FUNCTION__, id.count
+ , rl->tables.namecount,rl->tables.uuidcount);
+ }
+ return id.count;
+}
+int /* return count of outstanding expected replies */
+replytrack_outstanding_count(replytrack_t* rl)
+{
+ return (rl->tables.namecount + rl->tables.uuidcount);
+}
+
+nodetrack_t*
+nodetrack_new(nodetrack_callback_t callback, gpointer user_data)
+{
+ nodetrack_t* ret = MALLOCT(nodetrack_t);
+ if (!mbr_inityet) {
+ init_global_membership();
+ }
+ if (!ret) {
+ return ret;
+ }
+ nodetrack_t_count++;
+ ret->refcount = 0;
+ if (!create_new_hashtables(&ret->nt)) {
+ free(ret);
+ ret = NULL;
+ }
+ ret->user_data = user_data;
+ ret->callback = callback;
+ ret->extra_callback = NULL;
+ ret->ext_data = NULL;
+ return ret;
+}
+void
+nodetrack_del(nodetrack_t * np)
+{
+ if (np->refcount) {
+ cl_log(LOG_ERR
+ , "%s: reply tracking reference count is %d"
+ , __FUNCTION__, np->refcount);
+ }
+ nodetrack_t_count--;
+ destroy_map_hashtable(np->nt.namemap);
+ np->nt.namemap=NULL;
+ destroy_map_hashtable(np->nt.uuidmap);
+ np->nt.uuidmap=NULL;
+ memset(np, 0, sizeof(*np));
+ free(np);
+}
+
+gboolean
+nodetrack_ismember(nodetrack_t* mbr, const char * name, cl_uuid_t u)
+{
+ if (cl_uuid_is_null(&u)) {
+ return(g_hash_table_lookup(mbr->nt.namemap, name) != NULL);
+ }
+ return (g_hash_table_lookup(mbr->nt.uuidmap, &u) != NULL);
+}
+
+struct nodetrack_iterator_data {
+ nodetrack_t* rlist;
+ nodetrack_iterator_t f;
+ int count;
+ gpointer user_data;
+};
+static void /* g_hash_table user-level iteration helper */
+nodetrack_iterator_helper(gpointer key_unused, gpointer entry
+, gpointer user_data)
+{
+ struct nodetrack_iterator_data* ri = user_data;
+ struct rt_node_info* ni = entry;
+ if (ri && ri->rlist) {
+ ++ri->count;
+ if (ri->f) {
+ ri->f(ri->rlist, ri->user_data
+ , ni->nodename, ni->nodeid);
+ }
+ }
+}
+
+int /* iterate through the outstanding expected replies */
+nodetrack_iterate(nodetrack_t* rl
+, nodetrack_iterator_t i, gpointer user_data)
+{
+ struct nodetrack_iterator_data id;
+ id.rlist = rl;
+ id.f = i;
+ id.count = 0;
+ id.user_data = user_data;
+ g_hash_table_foreach(rl->nt.namemap, nodetrack_iterator_helper
+ , &id);
+ g_hash_table_foreach(rl->nt.uuidmap, nodetrack_iterator_helper
+ , &id);
+ if (id.count != (rl->nt.namecount + rl->nt.uuidcount)) {
+ cl_log(LOG_ERR
+ , "%s: iteration count %d disagrees with"
+ " (namecount %d+uuidcount %d)"
+ , __FUNCTION__, id.count
+ , rl->nt.namecount,rl->nt.uuidcount);
+ }
+ return id.count;
+}
+static void
+intersection_callback
+( nodetrack_t * mbr
+, const char * node
+, cl_uuid_t u
+, nodetrack_change_t reason
+, gpointer user_data)
+{
+ nodetrack_intersection_t* it = user_data;
+ int j;
+ gboolean allfound = TRUE;
+
+ if (reason == NODET_DOWN) {
+ if (nodetrack_ismember(it->intersection, node, u)) {
+ nodetrack_nodedown(it->intersection,node,u);
+ }
+ return;
+ }
+ for (j=0; j < it->ntables && allfound; ++j) {
+ if (nodetrack_ismember(it->tables[j], node, u)) {
+ allfound = FALSE;
+ }
+ }
+ if (allfound) {
+ nodetrack_nodeup(it->intersection, node, u);
+ }
+}
+
+struct li_helper {
+ nodetrack_intersection_t* i;
+ gboolean result;
+};
+
+static void
+intersection_init_iterator(nodetrack_t* nt
+, gpointer ghelp
+, const char* node
+, cl_uuid_t uuid)
+{
+ struct li_helper* help = ghelp;
+ gboolean allfound = TRUE;
+ int j;
+
+ for (j=1; allfound && j < help->i->ntables; ++j) {
+ if (!nodetrack_ismember(help->i->tables[j]
+ , node, uuid)) {
+ allfound = FALSE;
+ }
+ }
+ if (allfound) {
+ nodetrack_nodeup(help->i->intersection, node, uuid);
+ }
+}
+
+nodetrack_intersection_t*
+nodetrack_intersection_new(nodetrack_t** tables, int ntables
+, nodetrack_callback_t callback, gpointer user_data)
+{
+ nodetrack_intersection_t* ret;
+ int j;
+ ret = MALLOCT(nodetrack_intersection_t);
+ if (!ret) {
+ return ret;
+ }
+ ret->intersection = nodetrack_new(callback, user_data);
+ if (!ret->intersection) {
+ free(ret);
+ ret = NULL;
+ return ret;
+ }
+ ret->tables = tables;
+ ret->ntables = ntables;
+ ret->callback = callback;
+ ret->user_data = user_data;
+ for (j=0; j < ntables; ++j) {
+ tables[j]->refcount ++;
+ tables[j]->ext_data = ret;
+ tables[j]->extra_callback = intersection_callback;
+ }
+ /* Initialize the intersection membership list */
+ nodetrack_iterate(tables[0], intersection_init_iterator, ret);
+ replytrack_intersection_t_count++;
+ return ret;
+}
+void
+nodetrack_intersection_del(nodetrack_intersection_t* p)
+{
+ int j;
+
+ for (j=0; j < p->ntables; ++j) {
+ p->tables[j]->refcount ++;
+ }
+ nodetrack_del(p->intersection);
+ p->intersection = NULL;
+ memset(p, 0, sizeof(*p));
+ free(p);
+ p = NULL;
+ replytrack_intersection_t_count--;
+}
+
+nodetrack_t*
+nodetrack_intersection_table(nodetrack_intersection_t*p)
+{
+ return p->intersection;
+}
diff --git a/lib/clplumbing/setproctitle.c b/lib/clplumbing/setproctitle.c
new file mode 100644
index 0000000..ffc5481
--- /dev/null
+++ b/lib/clplumbing/setproctitle.c
@@ -0,0 +1,235 @@
+/*
+ * setproctitle.c
+ *
+ * The code in this file, setproctitle.c is heavily based on code from
+ * proftpd, please see the licening information below.
+ *
+ * This file added to the heartbeat tree by Horms <horms@vergenet.net>
+ *
+ * Code to portably change the title of a programme as displayed
+ * by ps(1).
+ *
+ * heartbeat: Linux-HA heartbeat code
+ *
+ * Copyright (C) 1999,2000,2001 Alan Robertson <alanr@unix.sh>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * ProFTPD - FTP server daemon
+ * Copyright (c) 1997, 1998 Public Flood Software
+ * Copyright (C) 1999, 2000 MacGyver aka Habeeb J. Dihu <macgyver@tos.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu
+ * and other respective copyright holders give permission to link this program
+ * with OpenSSL, and distribute the resulting executable, without including
+ * the source code for OpenSSL in the source distribution.
+ */
+
+#include <lha_internal.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdarg.h>
+
+#define PF_ARGV_NONE 0
+#define PF_ARGV_NEW 1
+#define PF_ARGV_WRITEABLE 2
+#define PF_ARGV_PSTAT 3
+#define PF_ARGV_PSSTRINGS 4
+
+#if PF_ARGV_TYPE == PF_ARGV_PSTAT
+# include <pstat.h>
+#endif
+
+#include <clplumbing/setproctitle.h>
+
+#if PF_ARGV_TYPE != PF_ARGV_NONE
+static char **Argv = NULL;
+static char *LastArgv = NULL;
+#endif /* PF_ARGV_TYPE != PF_ARGV_NONE */
+
+extern char **environ;
+
+#ifdef HAVE___PROGNAME
+extern char *__progname;
+extern char *__progname_full;
+#endif /* HAVE___PROGNAME */
+
+int
+init_set_proc_title(int argc, char *argv[], char *envp[])
+{
+#if PF_ARGV_TYPE == PF_ARGV_NONE
+ return 0;
+#else
+ int i;
+ int envpsize;
+ char **p;
+
+ /* Move the environment so setproctitle can use the space.
+ */
+ for(i = envpsize = 0; envp[i] != NULL; i++) {
+ envpsize += strlen(envp[i]) + 1;
+ }
+
+ p = (char **) malloc((i + 1) * sizeof(char *));
+ if (p == NULL) {
+ return -1;
+ }
+
+ environ = p;
+
+ for(i = 0; envp[i] != NULL; i++) {
+ environ[i] = strdup(envp[i]);
+ if(environ[i] == NULL) {
+ goto error_environ;
+ }
+ }
+ environ[i] = NULL;
+
+ Argv = argv;
+
+ for(i = 0; i < argc; i++) {
+ if(!i || (LastArgv + 1 == argv[i]))
+ LastArgv = argv[i] + strlen(argv[i]);
+ }
+
+ for(i = 0; envp[i] != NULL; i++) {
+ if((LastArgv + 1) == envp[i]) {
+ LastArgv = envp[i] + strlen(envp[i]);
+ }
+ }
+
+#ifdef HAVE___PROGNAME
+ /* Set the __progname and __progname_full variables so glibc and
+ * company don't go nuts. - MacGyver
+ */
+
+ __progname = strdup("heartbeat");
+ if (__progname == NULL) {
+ goto error_environ;
+ }
+ __progname_full = strdup(argv[0]);
+ if (__progname_full == NULL) {
+ goto error_environ;
+ }
+#endif /* HAVE___PROGNAME */
+
+ return 0;
+
+error_environ:
+ for(i = 0; environ[i] != NULL; i++) {
+ free(environ[i]);
+ }
+ free(environ);
+ return -1;
+#endif /* PF_ARGV_TYPE == PF_ARGV_NONE */
+}
+
+void set_proc_title(const char *fmt,...)
+{
+#if PF_ARGV_TYPE != PF_ARGV_NONE
+ va_list msg;
+ static char statbuf[BUFSIZ];
+
+#ifndef HAVE_SETPROCTITLE
+#if PF_ARGV_TYPE == PF_ARGV_PSTAT
+ union pstun pst;
+#endif /* PF_ARGV_PSTAT */
+ int i,maxlen = (LastArgv - Argv[0]) - 2;
+ char *p;
+#endif /* HAVE_SETPROCTITLE */
+
+ va_start(msg,fmt);
+
+ memset(statbuf, 0, sizeof(statbuf));
+
+
+#ifdef HAVE_SETPROCTITLE
+# if __FreeBSD__ >= 4 && !defined(FREEBSD4_0) && !defined(FREEBSD4_1)
+ /* FreeBSD's setproctitle() automatically prepends the process name. */
+ vsnprintf(statbuf, sizeof(statbuf), fmt, msg);
+
+# else /* FREEBSD4 */
+ /* Manually append the process name for non-FreeBSD platforms. */
+ vsnprintf(statbuf + strlen(statbuf), sizeof(statbuf) - strlen(statbuf),
+ fmt, msg);
+
+# endif /* FREEBSD4 */
+ setproctitle("%s", statbuf);
+
+#else /* HAVE_SETPROCTITLE */
+ /* Manually append the process name for non-setproctitle() platforms. */
+ vsnprintf(statbuf + strlen(statbuf), sizeof(statbuf) - strlen(statbuf),
+ fmt, msg);
+
+#endif /* HAVE_SETPROCTITLE */
+
+ va_end(msg);
+
+#ifdef HAVE_SETPROCTITLE
+ return;
+#else
+ i = strlen(statbuf);
+
+#if PF_ARGV_TYPE == PF_ARGV_NEW
+ /* We can just replace argv[] arguments. Nice and easy.
+ */
+ Argv[0] = statbuf;
+ Argv[1] = NULL;
+#endif /* PF_ARGV_NEW */
+
+#if PF_ARGV_TYPE == PF_ARGV_WRITEABLE
+ /* We can overwrite individual argv[] arguments. Semi-nice.
+ */
+ snprintf(Argv[0], maxlen, "%s", statbuf);
+ p = &Argv[0][i];
+
+ while(p < LastArgv)
+ *p++ = '\0';
+ Argv[1] = NULL;
+#endif /* PF_ARGV_WRITEABLE */
+
+#if PF_ARGV_TYPE == PF_ARGV_PSTAT
+ pst.pst_command = statbuf;
+ pstat(PSTAT_SETCMD, pst, i, 0, 0);
+#endif /* PF_ARGV_PSTAT */
+
+#if PF_ARGV_TYPE == PF_ARGV_PSSTRINGS
+ PS_STRINGS->ps_nargvstr = 1;
+ PS_STRINGS->ps_argvstr = statbuf;
+#endif /* PF_ARGV_PSSTRINGS */
+
+#endif /* HAVE_SETPROCTITLE */
+
+#endif /* PF_ARGV_TYPE != PF_ARGV_NONE */
+}
diff --git a/lib/clplumbing/timers.c b/lib/clplumbing/timers.c
new file mode 100644
index 0000000..c3e99da
--- /dev/null
+++ b/lib/clplumbing/timers.c
@@ -0,0 +1,119 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <lha_internal.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <clplumbing/timers.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/cl_signal.h>
+#include <clplumbing/longclock.h>
+
+int
+setmsrepeattimer(long ms)
+{
+ long secs = ms / 1000L;
+ long usecs = (ms % 1000L)*1000L;
+ struct itimerval nexttime =
+ { {secs, usecs} /* Repeat Interval */
+ , {secs, usecs} /* Timer Value */
+ };
+
+#if 0
+ cl_log(LOG_DEBUG, "Setting repeating timer for %ld ms"
+ , ms);
+#endif
+
+
+ /* Is this right??? */
+ CL_IGNORE_SIG(SIGALRM);
+ return setitimer(ITIMER_REAL, &nexttime, NULL);
+}
+
+int
+setmsalarm(long ms)
+{
+ long secs = ms / 1000L;
+ long usecs = (ms % 1000L)*1000L;
+ struct itimerval nexttime =
+ { {0L, 0L} /* Repeat Interval */
+ , {secs, usecs} /* Timer Value */
+ };
+
+ return setitimer(ITIMER_REAL, &nexttime, NULL);
+}
+
+int
+cancelmstimer(void)
+{
+ struct itimerval nexttime =
+ { {0L, 0L} /* Repeat Interval */
+ , {0L, 0L} /* Timer Value */
+ };
+ return setitimer(ITIMER_REAL, &nexttime, NULL);
+}
+
+
+static int alarmpopped = 0;
+
+static void
+st_timer_handler(int nsig)
+{
+ ++alarmpopped;
+}
+
+/*
+ * Pretty simple:
+ * 1) Set up SIGALRM signal handler
+ * 2) set alarmpopped to FALSE;
+ * 2) Record current time
+ * 3) Call setmsalarm(ms)
+ * 4) Call pause(2)
+ * 5) Call cancelmstimer()
+ * 6) Reset signal handler
+ * 7) See if SIGALRM happened
+ * if so: return zero
+ * if not: get current time, and compute milliseconds left 'til signal
+ * should arrive, and return that...
+ */
+long
+mssleep(long ms)
+{
+ struct sigaction saveaction;
+ longclock_t start;
+ longclock_t finish;
+ unsigned long elapsedms;
+
+ memset(&saveaction, 0, sizeof(saveaction));
+
+ cl_signal_set_simple_handler(SIGALRM, st_timer_handler, &saveaction);
+ alarmpopped = 0;
+ start = time_longclock();
+ setmsalarm(ms);
+ pause();
+ cancelmstimer();
+ cl_signal_set_simple_handler(SIGALRM, saveaction.sa_handler, &saveaction);
+ if (alarmpopped) {
+ return 0;
+ }
+
+ finish = time_longclock();
+ elapsedms = longclockto_ms(sub_longclock(finish, start));
+ return ms - elapsedms;
+}
diff --git a/lib/clplumbing/transient-test.sh b/lib/clplumbing/transient-test.sh
new file mode 100755
index 0000000..7da88bf
--- /dev/null
+++ b/lib/clplumbing/transient-test.sh
@@ -0,0 +1,120 @@
+#!/bin/sh
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+#
+####FIXME
+# Known problems within this testing:
+# 1. Doesn't reflect "someone else's message" problems into error count.
+# 2. Path to "ipctransient{client,server}" not flexible enough.
+
+no_logs=0
+exit_on_error=1
+num_servers=10
+num_clients=10
+client_time=2
+commpath_args=""
+
+cmd=`basename $0`
+USAGE="Usage: $cmd [-c clients] [-s servers] [-t timetowait] [-C commpath]"
+
+while getopts c:s:C:t: c
+do
+ case $c in
+ c)
+ num_clients=$OPTARG
+ ;;
+ s)
+ num_servers=$OPTARG
+ ;;
+ t)
+ client_time=$OPTARG
+ ;;
+ C)
+ commpath_args="-$c $OPTARG"
+ ;;
+ \?)
+ echo $USAGE
+ exit 2
+ ;;
+ esac
+done
+shift `expr $OPTIND - 1`
+
+total_failed=0
+
+server_failed=0
+server_loop_cnt=0
+while [ $server_loop_cnt != $num_servers ]; do
+ echo "############ DEBUG: Starting server iter $server_loop_cnt"
+ if [ $no_logs = 1 ]; then
+ ./ipctransientserver $commpath_args > /dev/null 2>&1 &
+ else
+ ./ipctransientserver $commpath_args &
+ fi
+ server_pid=$!
+
+ sleep 5
+
+ client_failed=0
+ client_loop_cnt=0
+ while [ $client_loop_cnt != $num_clients ]; do
+ sleep 5
+ echo "############ DEBUG: Starting client iter $client_loop_cnt"
+ if [ $no_logs = 1 ]; then
+ ./ipctransientclient $commpath_args > /dev/null 2>&1 &
+ else
+ ./ipctransientclient $commpath_args &
+ fi
+ client_pid=$!
+ sleep $client_time
+ if [ $exit_on_error = 1 ];then
+ kill -0 $client_pid > /dev/null 2>&1
+ else
+ kill -9 $client_pid > /dev/null 2>&1
+ fi
+ rc=$?
+ if [ $rc = 0 ]; then
+ echo "############ ERROR: Iter $client_loop_cnt failed to receive all messages"
+ client_failed=`expr $client_failed + 1`
+ if [ $exit_on_error = 1 ];then
+ echo "terminating after first error..."
+ exit 0
+ fi
+ else
+ echo "############ INFO: Iter $client_loop_cnt passed"
+ fi
+
+ client_loop_cnt=`expr $client_loop_cnt + 1`;
+ done
+ server_loop_cnt=`expr $server_loop_cnt + 1`;
+ total_failed=`expr $total_failed + $client_failed`
+ kill -9 $server_pid > /dev/null 2>&1
+ rc=$?
+ if [ $rc = 0 ]; then
+ echo "############ ERROR: Server was already dead"
+ server_failed=`expr $server_failed + 1`
+ fi
+done
+
+total_failed=`expr $total_failed + $server_failed`
+
+if [ $total_failed = 0 ]; then
+ echo "INFO: All tests passed"
+else
+ echo "ERROR: $total_failed tests failed"
+fi
+
+exit $total_failed
diff --git a/lib/clplumbing/uids.c b/lib/clplumbing/uids.c
new file mode 100644
index 0000000..0727e1d
--- /dev/null
+++ b/lib/clplumbing/uids.c
@@ -0,0 +1,140 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <lha_internal.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <clplumbing/uids.h>
+#include <clplumbing/coredumps.h>
+#define NOBODY "nobody"
+
+#if defined(HAVE_SETEUID) && defined(HAVE_SETEGID) && \
+ defined(_POSIX_SAVED_IDS)
+# define CAN_DROP_PRIVS 1
+
+#endif
+
+
+
+
+#ifndef CAN_DROP_PRIVS
+ int drop_privs(uid_t uid, gid_t gid) { return 0; }
+ int return_to_orig_privs(void) { return 0; }
+ int return_to_dropped_privs(void) { return 0; }
+ int cl_have_full_privs(void) { return 0; }
+#else
+
+static int anysaveduid = 0;
+static uid_t nobodyuid=-1;
+static gid_t nobodygid=-1;
+static uid_t poweruid=-1;
+static gid_t powergid=-1;
+static int privileged_state = 1;
+
+/* WARNING: uids are unsigned! */
+#define WANT_NOBODY(uid) ((uid) == 0)
+
+int /* Become nobody - and remember our original privileges */
+drop_privs(uid_t uid, gid_t gid)
+{
+ int rc;
+ gid_t curgid = getgid();
+
+ if (!anysaveduid) {
+ poweruid=getuid();
+ powergid=curgid;
+ }
+
+ if (WANT_NOBODY(uid)) {
+ struct passwd* p;
+
+ p = getpwnam(NOBODY);
+
+ if (p == NULL) {
+ return -1;
+ }
+ uid = p->pw_uid;
+ gid = p->pw_gid;
+ }
+ if (setegid(gid) < 0) {
+ return -1;
+ }
+ rc = seteuid(uid);
+
+ if (rc >= 0) {
+ anysaveduid = 1;
+ nobodyuid=uid;
+ nobodygid=gid;
+ privileged_state = 0;
+ }else{
+ /* Attempt to recover original privileges */
+ int err = errno;
+ setegid(curgid);
+ errno = err;
+ }
+ cl_untaint_coredumps();
+ return rc;
+}
+
+int /* Return to our original privileges (if any) */
+return_to_orig_privs(void)
+{
+ int rc;
+ if (!anysaveduid) {
+ return 0;
+ }
+ if (seteuid(poweruid) < 0) {
+ return -1;
+ }
+ privileged_state = 1;
+ rc = setegid(powergid);
+ /*
+ * Sad but true, for security reasons we can't call
+ * cl_untaint_coredumps() here - because it might cause an
+ * leak of confidential information for some applications.
+ * So, the applications need to use either cl_untaint_coredumps()
+ * when they change privileges, or they need to call
+ * cl_set_all_coredump_signal_handlers() to handle core dump
+ * signals and set their privileges to maximum before core
+ * dumping. See the comments in coredumps.c for more details.
+ */
+ return rc;
+}
+
+int /* Return to "nobody" level of privs (if any) */
+return_to_dropped_privs(void)
+{
+ int rc;
+
+ if (!anysaveduid) {
+ return 0;
+ }
+ setegid(nobodygid);
+ privileged_state = 0;
+ rc = seteuid(nobodyuid);
+ /* See note above about dumping core */
+ return rc;
+}
+
+/* Return TRUE if we have full privileges at the moment */
+int
+cl_have_full_privs(void)
+{
+ return privileged_state != 0;
+}
+#endif
diff --git a/lib/lrm/Makefile.am b/lib/lrm/Makefile.am
new file mode 100644
index 0000000..815f92f
--- /dev/null
+++ b/lib/lrm/Makefile.am
@@ -0,0 +1,36 @@
+#
+# Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+# Copyright (c) 2004 International Business Machines
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \
+ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl
+
+lrmdir = $(localstatedir)/lib/heartbeat/lrm
+COMMONLIBS = $(top_builddir)/lib/clplumbing/libplumb.la \
+ $(GLIBLIB)
+
+lib_LTLIBRARIES = liblrm.la
+liblrm_la_SOURCES = lrm_msg.c clientlib.c racommon.c
+liblrm_la_LDFLAGS = -version-info 2:0:0 $(COMMONLIBS)
+liblrm_la_CFLAGS = $(INCLUDES)
+
+install-exec-local:
+ $(mkinstalldirs) $(DESTDIR)$(lrmdir)
+ -chgrp $(GLUE_DAEMON_GROUP) $(DESTDIR)/$(lrmdir)
+ chmod 770 $(DESTDIR)/$(lrmdir)
diff --git a/lib/lrm/clientlib.c b/lib/lrm/clientlib.c
new file mode 100644
index 0000000..78dcdc8
--- /dev/null
+++ b/lib/lrm/clientlib.c
@@ -0,0 +1,1612 @@
+/*
+ * Client Library for Local Resource Manager API.
+ *
+ * Author: Huang Zhen <zhenh@cn.ibm.com>
+ * Copyright (c) 2004 International Business Machines
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <lha_internal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include <glib.h>
+#include <clplumbing/ipc.h>
+#include <ha_msg.h>
+#include <lrm/lrm_api.h>
+
+#include <lrm/lrm_msg.h>
+
+/* FIXME: Notice: this define should be replaced when merge to the whole pkg*/
+#define LRM_MAXPIDLEN 256
+#define LRM_ID "lrm"
+
+#define LOG_FAIL_create_lrm_msg(msg_type) \
+ cl_log(LOG_ERR, "%s(%d): failed to create a %s message with " \
+ "function create_lrm_msg." \
+ , __FUNCTION__, __LINE__, msg_type)
+
+#define LOG_FAIL_create_lrm_rsc_msg(msg_type) \
+ cl_log(LOG_ERR, "%s(%d): failed to create a %s message with " \
+ "function create_lrm_rsc_msg." \
+ , __FUNCTION__, __LINE__, msg_type)
+
+#define LOG_FAIL_receive_reply(msg_type) \
+ cl_log(LOG_ERR, "%s(%d): failed to receive a reply message of %s." \
+ , __FUNCTION__, __LINE__, msg_type)
+
+#define LOG_FAIL_SEND_MSG(msg_type, chan_name) \
+ cl_log(LOG_ERR, "%s(%d): failed to send a %s message to lrmd " \
+ "via %s channel." \
+ , __FUNCTION__, __LINE__, msg_type, chan_name)
+
+#define LOG_GOT_FAIL_RET(priority, msg_type) \
+ cl_log(priority, "%s(%d): got a return code HA_FAIL from " \
+ "a reply message of %s with function get_ret_from_msg." \
+ , __FUNCTION__, __LINE__, msg_type)
+
+#define LOG_BASIC_ERROR(apiname) \
+ cl_log(LOG_ERR, "%s(%d): %s failed." \
+ , __FUNCTION__, __LINE__, apiname)
+
+#define LOG_FAIL_GET_MSG_FIELD(priority, field_name, msg) \
+ {cl_log(priority, "%s(%d): failed to get the value " \
+ "of field %s from a ha_msg" \
+ , __FUNCTION__, __LINE__, field_name); \
+ cl_log(LOG_INFO, "%s: Message follows:", __FUNCTION__); \
+ cl_log_message(LOG_INFO, (msg)); \
+ }
+
+/* declare the functions used by the lrm_ops structure*/
+static int lrm_signon (ll_lrm_t* lrm, const char * app_name);
+static int lrm_signoff (ll_lrm_t*);
+static int lrm_delete (ll_lrm_t*);
+static int lrm_set_lrm_callback (ll_lrm_t* lrm,
+ lrm_op_done_callback_t op_done_callback_func);
+static GList* lrm_get_rsc_class_supported (ll_lrm_t* lrm);
+static GList* lrm_get_rsc_type_supported (ll_lrm_t* lrm, const char* class);
+static GList* lrm_get_rsc_provider_supported (ll_lrm_t* lrm
+ ,const char* class, const char* type);
+static char* lrm_get_rsc_type_metadata(ll_lrm_t* lrm, const char* class
+ ,const char* type, const char* provider);
+static GHashTable* lrm_get_all_type_metadata(ll_lrm_t*, const char* class);
+static GList* lrm_get_all_rscs (ll_lrm_t* lrm);
+static lrm_rsc_t* lrm_get_rsc (ll_lrm_t* lrm, const char* rsc_id);
+static int lrm_add_rsc (ll_lrm_t*, const char* id, const char* class
+ ,const char* type, const char* provider
+ ,GHashTable* parameter);
+static int lrm_delete_rsc (ll_lrm_t*, const char* id);
+static int lrm_fail_rsc (ll_lrm_t* lrm, const char* rsc_id, const int fail_rc
+ ,const char* fail_reason);
+static int lrm_set_lrmd_param (ll_lrm_t* lrm, const char* name, const char *value);
+static char* lrm_get_lrmd_param (ll_lrm_t* lrm, const char* name);
+static IPC_Channel* lrm_ipcchan (ll_lrm_t*);
+static int lrm_msgready (ll_lrm_t*);
+static int lrm_rcvmsg (ll_lrm_t*, int blocking);
+static struct lrm_ops lrm_ops_instance =
+{
+ lrm_signon,
+ lrm_signoff,
+ lrm_delete,
+ lrm_set_lrm_callback,
+ lrm_set_lrmd_param,
+ lrm_get_lrmd_param,
+ lrm_get_rsc_class_supported,
+ lrm_get_rsc_type_supported,
+ lrm_get_rsc_provider_supported,
+ lrm_get_rsc_type_metadata,
+ lrm_get_all_type_metadata,
+ lrm_get_all_rscs,
+ lrm_get_rsc,
+ lrm_add_rsc,
+ lrm_delete_rsc,
+ lrm_fail_rsc,
+ lrm_ipcchan,
+ lrm_msgready,
+ lrm_rcvmsg
+};
+/* declare the functions used by the lrm_rsc_ops structure*/
+static int rsc_perform_op (lrm_rsc_t*, lrm_op_t* op);
+static int rsc_cancel_op (lrm_rsc_t*, int call_id);
+static int rsc_flush_ops (lrm_rsc_t*);
+static GList* rsc_get_cur_state (lrm_rsc_t*, state_flag_t* cur_state);
+static lrm_op_t* rsc_get_last_result (lrm_rsc_t*, const char* op_type);
+static gint compare_call_id(gconstpointer a, gconstpointer b);
+
+static struct rsc_ops rsc_ops_instance =
+{
+ rsc_perform_op,
+ rsc_cancel_op,
+ rsc_flush_ops,
+ rsc_get_cur_state,
+ rsc_get_last_result
+};
+
+
+/* define the internal data used by the client library*/
+static int is_signed_on = FALSE;
+static IPC_Channel* ch_cmd = NULL;
+static IPC_Channel* ch_cbk = NULL;
+static lrm_op_done_callback_t op_done_callback = NULL;
+
+/* define some utility functions*/
+static int get_ret_from_ch(IPC_Channel* ch);
+static int get_ret_from_msg(struct ha_msg* msg);
+static struct ha_msg* op_to_msg (lrm_op_t* op);
+static lrm_op_t* msg_to_op(struct ha_msg* msg);
+static void free_op (lrm_op_t* op);
+
+/* define of the api functions*/
+ll_lrm_t*
+ll_lrm_new (const char * llctype)
+{
+ ll_lrm_t* lrm;
+
+ /* check the parameter*/
+ if (0 != STRNCMP_CONST(llctype, LRM_ID)) {
+ cl_log(LOG_ERR, "ll_lrm_new: wrong parameter");
+ return NULL;
+ }
+
+ /* alloc memory for lrm*/
+ if (NULL == (lrm = (ll_lrm_t*) g_new(ll_lrm_t,1))) {
+ cl_log(LOG_ERR, "ll_lrm_new: can not allocate memory");
+ return NULL;
+ }
+ /* assign the ops*/
+ lrm->lrm_ops = &lrm_ops_instance;
+
+ return lrm;
+}
+
+static int
+lrm_signon (ll_lrm_t* lrm, const char * app_name)
+{
+
+ GHashTable* ch_cmd_attrs;
+ GHashTable* ch_cbk_attrs;
+
+ struct ha_msg* msg;
+
+ char path[] = IPC_PATH_ATTR;
+ char cmd_path[] = LRM_CMDPATH;
+ char callback_path[] = LRM_CALLBACKPATH;
+
+ /* check parameters*/
+ if (NULL == lrm || NULL == app_name) {
+ cl_log(LOG_ERR, "lrm_signon: wrong parameter");
+ return HA_FAIL;
+ }
+
+ /* if already signed on, sign off first*/
+ if (is_signed_on) {
+ cl_log(LOG_WARNING,
+ "lrm_signon: the client is alreay signed on, re-sign");
+ lrm_signoff(lrm);
+ }
+
+ /* create the command ipc channel to lrmd*/
+ ch_cmd_attrs = g_hash_table_new(g_str_hash, g_str_equal);
+ g_hash_table_insert(ch_cmd_attrs, path, cmd_path);
+ ch_cmd = ipc_channel_constructor(IPC_ANYTYPE, ch_cmd_attrs);
+ g_hash_table_destroy(ch_cmd_attrs);
+
+ if (NULL == ch_cmd){
+ lrm_signoff(lrm);
+ cl_log(LOG_WARNING,
+ "lrm_signon: can not connect to lrmd for cmd channel");
+ return HA_FAIL;
+ }
+
+ if (IPC_OK != ch_cmd->ops->initiate_connection(ch_cmd)) {
+ lrm_signoff(lrm);
+ cl_log(LOG_WARNING,
+ "lrm_signon: can not initiate connection");
+ return HA_FAIL;
+ }
+
+ /* construct the reg msg*/
+ if (NULL == (msg = create_lrm_reg_msg(app_name))) {
+ lrm_signoff(lrm);
+ cl_log(LOG_ERR,"lrm_signon: failed to create a register message");
+ return HA_FAIL;
+ }
+
+ /* send the msg*/
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ lrm_signoff(lrm);
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(REGISTER, "ch_cmd");
+ return HA_FAIL;
+ }
+ /* parse the return msg*/
+ if (HA_OK != get_ret_from_ch(ch_cmd)) {
+ ha_msg_del(msg);
+ lrm_signoff(lrm);
+ LOG_FAIL_receive_reply(REGISTER);
+ return HA_FAIL;
+ }
+
+ /* create the callback ipc channel to lrmd*/
+ ch_cbk_attrs = g_hash_table_new(g_str_hash, g_str_equal);
+ g_hash_table_insert(ch_cbk_attrs, path, callback_path);
+ ch_cbk = ipc_channel_constructor(IPC_ANYTYPE,ch_cbk_attrs);
+ g_hash_table_destroy(ch_cbk_attrs);
+
+ if (NULL == ch_cbk) {
+ ha_msg_del(msg);
+ lrm_signoff(lrm);
+ cl_log(LOG_ERR, "lrm_signon: failed to construct a callback "
+ "channel to lrmd");
+ return HA_FAIL;
+ }
+
+ if (IPC_OK != ch_cbk->ops->initiate_connection(ch_cbk)) {
+ ha_msg_del(msg);
+ lrm_signoff(lrm);
+ cl_log(LOG_ERR,
+ "lrm_signon: failed to initiate the callback channel.");
+ return HA_FAIL;
+ }
+ /* send the msg*/
+ if (HA_OK != msg2ipcchan(msg,ch_cbk)) {
+ lrm_signoff(lrm);
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(REGISTER, "ch_cbk");
+ return HA_FAIL;
+ }
+ ha_msg_del(msg);
+ /* parse the return msg*/
+ if (HA_OK != get_ret_from_ch(ch_cbk)) {
+ lrm_signoff(lrm);
+ LOG_FAIL_receive_reply(REGISTER);
+ return HA_FAIL;
+ }
+ /* ok, we sign on sucessfully now*/
+ is_signed_on = TRUE;
+ return HA_OK;
+}
+
+static int
+lrm_signoff (ll_lrm_t* lrm)
+{
+ /* close channels */
+ if (NULL != ch_cmd) {
+ if (IPC_ISWCONN(ch_cmd)) {
+ ch_cmd->ops->destroy(ch_cmd);
+ }
+ ch_cmd = NULL;
+ }
+ if (NULL != ch_cbk) {
+ if (IPC_ISWCONN(ch_cbk)) {
+ ch_cbk->ops->destroy(ch_cbk);
+ }
+ ch_cbk = NULL;
+ }
+ is_signed_on = FALSE;
+
+ return HA_OK;
+}
+
+static int
+lrm_delete (ll_lrm_t* lrm)
+{
+ /* check the parameter */
+ if (NULL == lrm) {
+ cl_log(LOG_ERR,"lrm_delete: the parameter is a null pointer.");
+ return HA_FAIL;
+ }
+ g_free(lrm);
+
+ return HA_OK;
+}
+
+static int
+lrm_set_lrm_callback (ll_lrm_t* lrm,
+ lrm_op_done_callback_t op_done_callback_func)
+
+{
+ op_done_callback = op_done_callback_func;
+
+ return HA_OK;
+}
+
+static GList*
+lrm_get_rsc_class_supported (ll_lrm_t* lrm)
+{
+ struct ha_msg* msg;
+ struct ha_msg* ret;
+ GList* class_list = NULL;
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd)
+ {
+ cl_log(LOG_ERR,
+ "lrm_get_rsc_class_supported: ch_cmd is a null pointer.");
+ return NULL;
+ }
+ /* create the get ra type message */
+ msg = create_lrm_msg(GETRSCCLASSES);
+ if ( NULL == msg) {
+ LOG_FAIL_create_lrm_msg(GETRSCCLASSES);
+ return NULL;
+ }
+
+ /* send the msg to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(GETRSCCLASSES, "ch_cmd");
+ return NULL;
+ }
+ ha_msg_del(msg);
+ /* get the return message */
+ ret = msgfromIPC(ch_cmd, MSG_ALLOWINTR);
+ if (NULL == ret) {
+ LOG_FAIL_receive_reply(GETRSCCLASSES);
+ return NULL;
+ }
+ /* get the return code of the message */
+ if (HA_OK != get_ret_from_msg(ret)) {
+ LOG_GOT_FAIL_RET(LOG_WARNING, GETRSCCLASSES);
+ ha_msg_del(ret);
+ return NULL;
+ }
+ /* get the ra type list from message */
+ class_list = ha_msg_value_str_list(ret,F_LRM_RCLASS);
+
+ ha_msg_del(ret);
+
+ return class_list;
+}
+static GList*
+lrm_get_rsc_type_supported (ll_lrm_t* lrm, const char* rclass)
+{
+ struct ha_msg* msg;
+ struct ha_msg* ret;
+ GList* type_list = NULL;
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd)
+ {
+ cl_log(LOG_ERR, "%s(%d): ch_cmd is null."
+ , __FUNCTION__, __LINE__);
+
+ return NULL;
+ }
+ /* create the get ra type message */
+ msg = create_lrm_msg(GETRSCTYPES);
+ if ( NULL == msg) {
+ LOG_FAIL_create_lrm_msg(GETRSCTYPES);
+ return NULL;
+ }
+ if ( HA_OK != ha_msg_add(msg, F_LRM_RCLASS, rclass)) {
+ ha_msg_del(msg);
+ LOG_BASIC_ERROR("ha_msg_add");
+ return NULL;
+ }
+
+ /* send the msg to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(GETRSCTYPES, "ch_cmd");
+ return NULL;
+ }
+ ha_msg_del(msg);
+ /* get the return message */
+ ret = msgfromIPC(ch_cmd, MSG_ALLOWINTR);
+ if (NULL == ret) {
+ LOG_FAIL_receive_reply(GETRSCTYPES);
+ return NULL;
+ }
+ /* get the return code of the message */
+ if (HA_OK != get_ret_from_msg(ret)) {
+ LOG_GOT_FAIL_RET(LOG_ERR, GETRSCTYPES);
+ ha_msg_del(ret);
+ return NULL;
+ }
+ /* get the ra type list from message */
+ type_list = ha_msg_value_str_list(ret,F_LRM_RTYPES);
+
+ ha_msg_del(ret);
+
+ return type_list;
+}
+static GList*
+lrm_get_rsc_provider_supported (ll_lrm_t* lrm, const char* class, const char* type)
+{
+ struct ha_msg* msg;
+ struct ha_msg* ret;
+ GList* provider_list = NULL;
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd)
+ {
+ cl_log(LOG_ERR,
+ "lrm_get_rsc_provider_supported: ch_mod is null.");
+ return NULL;
+ }
+ /* create the get ra providers message */
+ msg = create_lrm_msg(GETPROVIDERS);
+ if ( NULL == msg) {
+ LOG_FAIL_create_lrm_msg(GETPROVIDERS);
+ return NULL;
+ }
+ if (HA_OK != ha_msg_add(msg, F_LRM_RCLASS, class)
+ || HA_OK != ha_msg_add(msg, F_LRM_RTYPE, type)) {
+ ha_msg_del(msg);
+ LOG_BASIC_ERROR("ha_msg_add");
+ return NULL;
+ }
+
+ /* send the msg to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(GETPROVIDERS, "ch_cmd");
+ return NULL;
+ }
+ ha_msg_del(msg);
+ /* get the return message */
+ ret = msgfromIPC(ch_cmd, MSG_ALLOWINTR);
+ if (NULL == ret) {
+ LOG_FAIL_receive_reply(GETPROVIDERS);
+ return NULL;
+ }
+ /* get the return code of the message */
+ if (HA_OK != get_ret_from_msg(ret)) {
+ LOG_GOT_FAIL_RET(LOG_ERR, GETPROVIDERS);
+ ha_msg_del(ret);
+ return NULL;
+ }
+ /* get the ra provider list from message */
+ provider_list = ha_msg_value_str_list(ret,F_LRM_RPROVIDERS);
+
+ ha_msg_del(ret);
+
+ return provider_list;
+}
+
+/*
+ * lrm_get_all_type_metadatas():
+ * The key of the hash table is in the format "type:provider"
+ * The value of the hash table is the metadata.
+ */
+static GHashTable*
+lrm_get_all_type_metadata (ll_lrm_t* lrm, const char* rclass)
+{
+ GHashTable* metas = g_hash_table_new_full(g_str_hash, g_str_equal
+ , g_free, g_free);
+ GList* types = lrm_get_rsc_type_supported (lrm, rclass);
+ GList* providers = NULL;
+ GList* cur_type = NULL;
+ GList* cur_provider = NULL;
+
+ cur_type = g_list_first(types);
+ while (cur_type != NULL)
+ {
+ const char* type;
+ char key[MAXLENGTH];
+ type = (const char*) cur_type->data;
+ providers = lrm_get_rsc_provider_supported(lrm, rclass, type);
+ cur_provider = g_list_first(providers);
+ while (cur_provider != NULL) {
+ const char* meta;
+ const char* provider;
+ provider = (const char*) cur_provider->data;
+ meta = lrm_get_rsc_type_metadata(lrm,rclass,type,provider);
+ if (NULL == meta) {
+ cur_provider = g_list_next(cur_provider);
+ continue;
+ }
+ snprintf(key,MAXLENGTH, "%s:%s",type,provider);
+ key[MAXLENGTH-1]='\0';
+ g_hash_table_insert(metas,g_strdup(key),g_strdup(meta));
+ cur_provider = g_list_next(cur_provider);
+ }
+ lrm_free_str_list(providers);
+ cur_type=g_list_next(cur_type);
+ }
+ lrm_free_str_list(types);
+ return metas;
+}
+
+static char*
+lrm_get_rsc_type_metadata (ll_lrm_t* lrm, const char* rclass, const char* rtype,
+ const char* provider)
+{
+ struct ha_msg* msg;
+ struct ha_msg* ret;
+ const char* tmp = NULL;
+ char* metadata = NULL;
+
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd)
+ {
+ cl_log(LOG_ERR,
+ "lrm_get_rsc_type_metadata: ch_mod is null.");
+ return NULL;
+ }
+ /* create the get ra type message */
+ msg = create_lrm_msg(GETRSCMETA);
+ if (NULL == msg ) {
+ LOG_FAIL_create_lrm_msg(GETRSCMETA);
+ return NULL;
+ }
+
+ if (HA_OK != ha_msg_add(msg, F_LRM_RCLASS, rclass)
+ || HA_OK != ha_msg_add(msg, F_LRM_RTYPE, rtype)){
+ ha_msg_del(msg);
+ LOG_BASIC_ERROR("ha_msg_add");
+ return NULL;
+ }
+
+ if( provider ) {
+ if (HA_OK != ha_msg_add(msg, F_LRM_RPROVIDER, provider)) {
+ LOG_BASIC_ERROR("ha_msg_add");
+ ha_msg_del(msg);
+ return NULL;
+ }
+ }
+
+ /* send the msg to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(GETRSCMETA, "ch_cmd");
+ return NULL;
+ }
+ ha_msg_del(msg);
+ /* get the return message */
+ ret = msgfromIPC(ch_cmd, MSG_ALLOWINTR);
+ if (NULL == ret) {
+ LOG_FAIL_receive_reply(GETRSCMETA);
+ return NULL;
+ }
+ /* get the return code of the message */
+ if (HA_OK != get_ret_from_msg(ret)) {
+ LOG_GOT_FAIL_RET(LOG_ERR, GETRSCMETA);
+ ha_msg_del(ret);
+ return NULL;
+ }
+
+ /* get the metadata from message */
+ tmp = cl_get_string(ret, F_LRM_METADATA);
+ if (NULL!=tmp) {
+ metadata = g_strdup(tmp);
+ }
+ ha_msg_del(ret);
+
+ return metadata;
+}
+
+static GList*
+lrm_get_all_rscs (ll_lrm_t* lrm)
+{
+ struct ha_msg* msg = NULL;
+ struct ha_msg* ret = NULL;
+ GList* rid_list = NULL;
+
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd) {
+ cl_log(LOG_ERR, "lrm_get_all_rscs: ch_mod is null.");
+ return NULL;
+ }
+ /* create the msg of get all resource */
+ msg = create_lrm_msg(GETALLRCSES);
+ if ( NULL == msg) {
+ LOG_FAIL_create_lrm_msg(GETALLRCSES);
+ return NULL;
+ }
+ /* send the msg to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(GETALLRCSES, "ch_cmd");
+ return NULL;
+ }
+ ha_msg_del(msg);
+ /* get the return msg */
+ ret = msgfromIPC(ch_cmd, MSG_ALLOWINTR);
+ if (NULL == ret) {
+ LOG_FAIL_receive_reply(GETALLRCSES);
+ return NULL;
+ }
+ /* get the return code of msg */
+ if (HA_OK != get_ret_from_msg(ret)) {
+ LOG_GOT_FAIL_RET(LOG_ERR, GETALLRCSES);
+ ha_msg_del(ret);
+ return NULL;
+ }
+ /* get the rsc_id list from msg */
+ rid_list = ha_msg_value_str_list(ret,F_LRM_RID);
+
+ ha_msg_del(ret);
+ /* return the id list */
+ return rid_list;
+
+}
+
+static lrm_rsc_t*
+lrm_get_rsc (ll_lrm_t* lrm, const char* rsc_id)
+{
+ struct ha_msg* msg = NULL;
+ struct ha_msg* ret = NULL;
+ lrm_rsc_t* rsc = NULL;
+
+ /* check whether the rsc_id is available */
+ if (strlen(rsc_id) >= RID_LEN) {
+ cl_log(LOG_ERR, "lrm_get_rsc: rsc_id is too long.");
+ return NULL;
+ }
+
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd) {
+ cl_log(LOG_ERR, "lrm_get_rsc: ch_mod is null.");
+ return NULL;
+ }
+ /* create the msg of get resource */
+ msg = create_lrm_rsc_msg(rsc_id, GETRSC);
+ if ( NULL == msg) {
+ LOG_FAIL_create_lrm_rsc_msg(GETRSC);
+ return NULL;
+ }
+ /* send the msg to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(GETRSC, "ch_cmd");
+ return NULL;
+ }
+ ha_msg_del(msg);
+ /* get the return msg from lrmd */
+ ret = msgfromIPC(ch_cmd, MSG_ALLOWINTR);
+ if (NULL == ret) {
+ LOG_FAIL_receive_reply(GETRSC);
+ return NULL;
+ }
+ /* get the return code of return message */
+ if (HA_OK != get_ret_from_msg(ret)) {
+ ha_msg_del(ret);
+ return NULL;
+ }
+ /* create a new resource structure */
+ rsc = g_new(lrm_rsc_t, 1);
+
+ /* fill the field of resource with the data from msg */
+ rsc->id = g_strdup(ha_msg_value(ret, F_LRM_RID));
+ rsc->type = g_strdup(ha_msg_value(ret, F_LRM_RTYPE));
+ rsc->class = g_strdup(ha_msg_value(ret, F_LRM_RCLASS));
+ rsc->provider = g_strdup(ha_msg_value(ret, F_LRM_RPROVIDER));
+ rsc->params = ha_msg_value_str_table(ret,F_LRM_PARAM);
+
+ rsc->ops = &rsc_ops_instance;
+ ha_msg_del(ret);
+ /* return the new resource */
+ return rsc;
+}
+
+static int
+lrm_fail_rsc (ll_lrm_t* lrm, const char* rsc_id, const int fail_rc
+, const char* fail_reason)
+{
+ struct ha_msg* msg;
+
+ /* check whether the rsc_id is available */
+ if (NULL == rsc_id || RID_LEN <= strlen(rsc_id)) {
+ cl_log(LOG_ERR, "%s: wrong parameter rsc_id.", __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd) {
+ cl_log(LOG_ERR, "%s: ch_mod is null.", __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ /* create the message */
+ msg = create_lrm_rsc_msg(rsc_id,FAILRSC);
+ if (NULL == msg) {
+ LOG_FAIL_create_lrm_rsc_msg(FAILRSC);
+ return HA_FAIL;
+ }
+ if ((fail_reason && HA_OK != ha_msg_add(msg,F_LRM_FAIL_REASON,fail_reason))
+ || HA_OK != ha_msg_add_int(msg, F_LRM_ASYNCMON_RC, fail_rc)
+ ) {
+ ha_msg_del(msg);
+ LOG_BASIC_ERROR("ha_msg_add");
+ return HA_FAIL;
+ }
+ /* send to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(FAILRSC, "ch_cmd");
+ return HA_FAIL;
+ }
+ ha_msg_del(msg);
+ /* check the result */
+ if (HA_OK != get_ret_from_ch(ch_cmd)) {
+ LOG_GOT_FAIL_RET(LOG_ERR, FAILRSC);
+ return HA_FAIL;
+ }
+
+ return HA_OK;
+}
+
+static int
+lrm_set_lrmd_param(ll_lrm_t* lrm, const char* name, const char *value)
+{
+ struct ha_msg* msg;
+
+ if (!name || !value) {
+ cl_log(LOG_ERR, "%s: no parameter name or value", __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd) {
+ cl_log(LOG_ERR, "%s: ch_mod is null.", __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ /* create the message */
+ msg = create_lrm_msg(SETLRMDPARAM);
+ if (NULL == msg) {
+ LOG_FAIL_create_lrm_rsc_msg(SETLRMDPARAM);
+ return HA_FAIL;
+ }
+ if (HA_OK != ha_msg_add(msg,F_LRM_LRMD_PARAM_NAME,name)
+ || HA_OK != ha_msg_add(msg,F_LRM_LRMD_PARAM_VAL,value)) {
+ ha_msg_del(msg);
+ LOG_BASIC_ERROR("ha_msg_add");
+ return HA_FAIL;
+ }
+ /* send to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(FAILRSC, "ch_cmd");
+ return HA_FAIL;
+ }
+ ha_msg_del(msg);
+ /* check the result */
+ if (HA_OK != get_ret_from_ch(ch_cmd)) {
+ LOG_GOT_FAIL_RET(LOG_ERR, FAILRSC);
+ return HA_FAIL;
+ }
+
+ return HA_OK;
+}
+
+static char*
+lrm_get_lrmd_param (ll_lrm_t* lrm, const char *name)
+{
+ struct ha_msg* msg = NULL;
+ struct ha_msg* ret = NULL;
+ const char* value = NULL;
+ char* v2;
+
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd) {
+ cl_log(LOG_ERR, "lrm_get_rsc: ch_mod is null.");
+ return NULL;
+ }
+ /* create the msg of get resource */
+ msg = create_lrm_msg(GETLRMDPARAM);
+ if ( NULL == msg) {
+ LOG_FAIL_create_lrm_msg(GETLRMDPARAM);
+ return NULL;
+ }
+ if (HA_OK != ha_msg_add(msg,F_LRM_LRMD_PARAM_NAME,name)) {
+ ha_msg_del(msg);
+ LOG_BASIC_ERROR("ha_msg_add");
+ return NULL;
+ }
+ /* send the msg to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(GETLRMDPARAM, "ch_cmd");
+ return NULL;
+ }
+ ha_msg_del(msg);
+ /* get the return msg from lrmd */
+ ret = msgfromIPC(ch_cmd, MSG_ALLOWINTR);
+ if (NULL == ret) {
+ LOG_FAIL_receive_reply(GETLRMDPARAM);
+ return NULL;
+ }
+ /* get the return code of return message */
+ if (HA_OK != get_ret_from_msg(ret)) {
+ ha_msg_del(ret);
+ return NULL;
+ }
+ value = ha_msg_value(ret,F_LRM_LRMD_PARAM_VAL);
+ if (!value) {
+ LOG_FAIL_GET_MSG_FIELD(LOG_ERR, F_LRM_LRMD_PARAM_VAL, ret);
+ ha_msg_del(ret);
+ return NULL;
+ }
+ v2 = g_strdup(value);
+ ha_msg_del(ret);
+ return v2;
+}
+
+static int
+lrm_add_rsc (ll_lrm_t* lrm, const char* rsc_id, const char* class
+, const char* type, const char* provider, GHashTable* parameter)
+{
+ struct ha_msg* msg;
+
+ /* check whether the rsc_id is available */
+ if (NULL == rsc_id || RID_LEN <= strlen(rsc_id)) {
+ cl_log(LOG_ERR, "lrm_add_rsc: wrong parameter rsc_id.");
+ return HA_FAIL;
+ }
+
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd) {
+ cl_log(LOG_ERR, "lrm_add_rsc: ch_mod is null.");
+ return HA_FAIL;
+ }
+
+ /* create the message of add resource */
+ msg = create_lrm_addrsc_msg(rsc_id, class, type, provider, parameter);
+ if ( NULL == msg) {
+ cl_log(LOG_ERR, "%s(%d): failed to create a ADDSRC message "
+ "with function create_lrm_addrsc_msg"
+ , __FUNCTION__, __LINE__);
+ return HA_FAIL;
+ }
+ /* send to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(ADDRSC, "ch_cmd");
+ return HA_FAIL;
+ }
+ ha_msg_del(msg);
+ /* check the result */
+ if (HA_OK != get_ret_from_ch(ch_cmd)) {
+ LOG_GOT_FAIL_RET(LOG_ERR, ADDRSC);
+ return HA_FAIL;
+ }
+
+ return HA_OK;
+}
+
+static int
+lrm_delete_rsc (ll_lrm_t* lrm, const char* rsc_id)
+{
+ struct ha_msg* msg = NULL;
+ int rc;
+
+ /* check whether the rsc_id is available */
+ if (NULL == rsc_id || RID_LEN <= strlen(rsc_id)) {
+ cl_log(LOG_ERR, "lrm_delete_rsc: wrong parameter rsc_id.");
+ return HA_FAIL;
+ }
+
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd) {
+ cl_log(LOG_ERR, "lrm_delete_rsc: ch_mod is null.");
+ return HA_FAIL;
+ }
+
+ /* create the msg of del resource */
+ msg = create_lrm_rsc_msg(rsc_id, DELRSC);
+ if ( NULL == msg) {
+ LOG_FAIL_create_lrm_rsc_msg(DELRSC);
+ return HA_FAIL;
+ }
+ /* send the msg to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(DELRSC, "ch_cmd");
+ return HA_FAIL;
+ }
+ ha_msg_del(msg);
+ /* check the response of the msg */
+ rc = get_ret_from_ch(ch_cmd);
+ if (rc != HA_OK && rc != HA_RSCBUSY) {
+ LOG_GOT_FAIL_RET(LOG_ERR, DELRSC);
+ return HA_FAIL;
+ }
+
+ return rc;
+}
+
+static IPC_Channel*
+lrm_ipcchan (ll_lrm_t* lrm)
+{
+ if (NULL == ch_cbk) {
+ cl_log(LOG_ERR,
+ "lrm_inputfd: callback channel is null.");
+ return NULL;
+ }
+
+ return ch_cbk;
+}
+
+static gboolean
+lrm_msgready (ll_lrm_t* lrm)
+{
+ if (NULL == ch_cbk) {
+ cl_log(LOG_ERR,
+ "lrm_msgready: callback channel is null.");
+ return FALSE;
+ }
+ return ch_cbk->ops->is_message_pending(ch_cbk);
+}
+
+static int
+lrm_rcvmsg (ll_lrm_t* lrm, int blocking)
+{
+ struct ha_msg* msg = NULL;
+ lrm_op_t* op = NULL;
+ int msg_count = 0;
+
+ /* if it is not blocking mode and no message in the channel, return */
+ if ((!lrm_msgready(lrm)) && (!blocking)) {
+ cl_log(LOG_DEBUG,
+ "lrm_rcvmsg: no message and non-block.");
+ return msg_count;
+ }
+ /* wait until message ready */
+ if (!lrm_msgready(lrm)) {
+ ch_cbk->ops->waitin(ch_cbk);
+ }
+ while (lrm_msgready(lrm)) {
+ if (ch_cbk->ch_status == IPC_DISCONNECT) {
+ return msg_count;
+ }
+ /* get the message */
+ msg = msgfromIPC(ch_cbk, MSG_ALLOWINTR);
+ if (msg == NULL) {
+ cl_log(LOG_WARNING,
+ "%s(%d): receive a null message with msgfromIPC."
+ , __FUNCTION__, __LINE__);
+ return msg_count;
+ }
+ msg_count++;
+
+ op = msg_to_op(msg);
+ if (NULL!=op && NULL!=op_done_callback) {
+ (*op_done_callback)(op);
+ }
+ free_op(op);
+ ha_msg_del(msg);
+ }
+
+ return msg_count;
+}
+
+/* following are the functions for rsc_ops */
+static int
+rsc_perform_op (lrm_rsc_t* rsc, lrm_op_t* op)
+{
+ int rc = 0;
+ struct ha_msg* msg = NULL;
+ char* rsc_id;
+
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd
+ || NULL == rsc
+ || NULL == rsc->id
+ || NULL == op
+ || NULL == op->op_type) {
+ cl_log(LOG_ERR,
+ "rsc_perform_op: wrong parameters.");
+ return HA_FAIL;
+ }
+ /* create the msg of perform op */
+ rsc_id = op->rsc_id;
+ op->rsc_id = rsc->id;
+ msg = op_to_msg(op);
+ op->rsc_id = rsc_id;
+ if ( NULL == msg) {
+ cl_log(LOG_ERR, "rsc_perform_op: failed to create a message "
+ "with function op_to_msg");
+ return HA_FAIL;
+ }
+ /* send it to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(PERFORMOP, "ch_cmd");
+ return HA_FAIL;
+ }
+ ha_msg_del(msg);
+
+ /* check return code, the return code is the call_id of the op */
+ rc = get_ret_from_ch(ch_cmd);
+ return rc;
+}
+
+static int
+rsc_cancel_op (lrm_rsc_t* rsc, int call_id)
+{
+ int rc;
+ struct ha_msg* msg = NULL;
+
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd) {
+ cl_log(LOG_ERR, "rsc_cancel_op: ch_mod is null.");
+ return HA_FAIL;
+ }
+ /* check parameter */
+ if (NULL == rsc) {
+ cl_log(LOG_ERR, "rsc_cancel_op: parameter rsc is null.");
+ return HA_FAIL;
+ }
+ /* create the msg of flush ops */
+ msg = create_lrm_rsc_msg(rsc->id,CANCELOP);
+ if (NULL == msg) {
+ LOG_FAIL_create_lrm_rsc_msg(CANCELOP);
+ return HA_FAIL;
+ }
+ if (HA_OK != ha_msg_add_int(msg, F_LRM_CALLID, call_id)) {
+ LOG_BASIC_ERROR("ha_msg_add_int");
+ ha_msg_del(msg);
+ return HA_FAIL;
+ }
+
+ /* send the msg to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(CANCELOP, "ch_cmd");
+ return HA_FAIL;
+ }
+ ha_msg_del(msg);
+
+ rc = get_ret_from_ch(ch_cmd);
+
+ return rc;
+}
+
+static int
+rsc_flush_ops (lrm_rsc_t* rsc)
+{
+ int rc;
+ struct ha_msg* msg = NULL;
+
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd) {
+ cl_log(LOG_ERR, "rsc_flush_ops: ch_mod is null.");
+ return HA_FAIL;
+ }
+ /* check parameter */
+ if (NULL == rsc) {
+ cl_log(LOG_ERR, "rsc_flush_ops: parameter rsc is null.");
+ return HA_FAIL;
+ }
+ /* create the msg of flush ops */
+ msg = create_lrm_rsc_msg(rsc->id,FLUSHOPS);
+ if ( NULL == msg) {
+ LOG_FAIL_create_lrm_rsc_msg(CANCELOP);
+ return HA_FAIL;
+ }
+ /* send the msg to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(FLUSHOPS, "ch_cmd");
+ return HA_FAIL;
+ }
+ ha_msg_del(msg);
+
+ rc = get_ret_from_ch(ch_cmd);
+
+ return rc>0?rc:HA_FAIL;
+}
+static gint
+compare_call_id(gconstpointer a, gconstpointer b)
+{
+ const lrm_op_t* opa = (const lrm_op_t*)a;
+ const lrm_op_t* opb = (const lrm_op_t*)b;
+ return opa->call_id - opb->call_id;
+}
+static GList*
+rsc_get_cur_state (lrm_rsc_t* rsc, state_flag_t* cur_state)
+{
+ GList* op_list = NULL, * tmplist = NULL;
+ struct ha_msg* msg = NULL;
+ struct ha_msg* ret = NULL;
+ struct ha_msg* op_msg = NULL;
+ lrm_op_t* op = NULL;
+ int state;
+ int op_count, i;
+
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd) {
+ cl_log(LOG_ERR, "rsc_get_cur_state: ch_mod is null.");
+ return NULL;
+ }
+ /* check paramter */
+ if (NULL == rsc) {
+ cl_log(LOG_ERR, "rsc_get_cur_state: parameter rsc is null.");
+ return NULL;
+ }
+ /* create the msg of get current state of resource */
+ msg = create_lrm_rsc_msg(rsc->id,GETRSCSTATE);
+ if ( NULL == msg) {
+ LOG_FAIL_create_lrm_rsc_msg(GETRSCSTATE);
+ return NULL;
+ }
+ /* send the msg to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(GETRSCSTATE, "ch_cmd");
+ return NULL;
+ }
+ ha_msg_del(msg);
+
+ /* get the return msg */
+ ret = msgfromIPC(ch_cmd, MSG_ALLOWINTR);
+ if (NULL == ret) {
+ LOG_FAIL_receive_reply(GETRSCSTATE);
+ return NULL;
+ }
+
+ /* get the state of the resource from the message */
+ if (HA_OK != ha_msg_value_int(ret, F_LRM_STATE, &state)) {
+ LOG_FAIL_GET_MSG_FIELD(LOG_ERR, F_LRM_STATE, ret);
+ ha_msg_del(ret);
+ return NULL;
+ }
+ *cur_state = (state_flag_t)state;
+ /* the first msg includes the count of pending ops. */
+ if (HA_OK != ha_msg_value_int(ret, F_LRM_OPCNT, &op_count)) {
+ LOG_FAIL_GET_MSG_FIELD(LOG_WARNING, F_LRM_OPCNT, ret);
+ ha_msg_del(ret);
+ return NULL;
+ }
+ ha_msg_del(ret);
+ for (i = 0; i < op_count; i++) {
+ /* one msg for one op */
+ op_msg = msgfromIPC(ch_cmd, MSG_ALLOWINTR);
+
+ if (NULL == op_msg) {
+ cl_log(LOG_WARNING, "%s(%d): failed to receive a "
+ "(pending operation) message from lrmd."
+ , __FUNCTION__, __LINE__);
+ continue;
+ }
+ op = msg_to_op(op_msg);
+ /* add msg to the return list */
+
+ if (NULL != op) {
+ op_list = g_list_append(op_list, op);
+ }
+ else {
+ cl_log(LOG_WARNING, "%s(%d): failed to make a operation "
+ "from a message with function msg_to_op"
+ , __FUNCTION__, __LINE__);
+ }
+ ha_msg_del(op_msg);
+ }
+ op_list = g_list_sort(op_list, compare_call_id);
+
+ /* Delete the duplicate op for call_id */
+#if 0
+ cl_log(LOG_WARNING, "Before uniquing");
+ tmplist = g_list_first(op_list);
+ while (tmplist != NULL) {
+ cl_log(LOG_WARNING, "call_id=%d", ((lrm_op_t*)(tmplist->data))->call_id);
+ tmplist = g_list_next(tmplist);
+ }
+#endif
+
+ tmplist = g_list_first(op_list);
+ while (tmplist != NULL) {
+ if (NULL != g_list_previous(tmplist)) {
+ if (((lrm_op_t*)(g_list_previous(tmplist)->data))->call_id
+ == ((lrm_op_t*)(tmplist->data))->call_id) {
+ op_list = g_list_remove_link (op_list, tmplist);
+ free_op((lrm_op_t *)tmplist->data);
+ g_list_free_1(tmplist);
+ tmplist = g_list_first(op_list);
+ }
+ }
+ tmplist = g_list_next(tmplist);
+ }
+
+#if 0
+ cl_log(LOG_WARNING, "After uniquing");
+ while (tmplist != NULL) {
+ cl_log(LOG_WARNING, "call_id=%d", ((lrm_op_t*)(tmplist->data))->call_id);
+ tmplist = g_list_next(tmplist);
+ }
+#endif
+
+ return op_list;
+}
+
+static lrm_op_t*
+rsc_get_last_result (lrm_rsc_t* rsc, const char* op_type)
+{
+ struct ha_msg* msg = NULL;
+ struct ha_msg* ret = NULL;
+ lrm_op_t* op = NULL;
+ int opcount = 0;
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd) {
+ cl_log(LOG_ERR, "rsc_get_last_result: ch_mod is null.");
+ return NULL;
+ }
+ /* check parameter */
+ if (NULL == rsc) {
+ cl_log(LOG_ERR, "rsc_get_last_result: parameter rsc is null.");
+ return NULL;
+ }
+ /* create the msg of get last op */
+ msg = create_lrm_rsc_msg(rsc->id,GETLASTOP);
+ if (NULL == msg) {
+ LOG_FAIL_create_lrm_rsc_msg(GETLASTOP);
+ return NULL;
+ }
+ if (HA_OK != ha_msg_add(msg, F_LRM_RID, rsc->id)) {
+ LOG_BASIC_ERROR("ha_msg_add");
+ ha_msg_del(msg);
+ return NULL;
+ }
+ if (HA_OK != ha_msg_add(msg, F_LRM_OP, op_type)) {
+ LOG_BASIC_ERROR("ha_msg_add");
+ ha_msg_del(msg);
+ return NULL;
+ }
+
+ /* send the msg to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(GETLASTOP, "ch_cmd");
+ return NULL;
+ }
+
+ /* get the return msg */
+ ret = msgfromIPC(ch_cmd, MSG_ALLOWINTR);
+ if (NULL == ret) {
+ LOG_FAIL_receive_reply(GETLASTOP);
+ ha_msg_del(msg);
+ return NULL;
+ }
+ if (HA_OK != ha_msg_value_int(ret,F_LRM_OPCNT, &opcount)) {
+ op = NULL;
+ }
+ else if ( 1 == opcount ) {
+ op = msg_to_op(ret);
+ }
+ ha_msg_del(msg);
+ ha_msg_del(ret);
+ return op;
+}
+/*
+ * following are the implements of the utility functions
+ */
+lrm_op_t*
+lrm_op_new(void)
+{
+ lrm_op_t* op;
+
+ op = g_new0(lrm_op_t, 1);
+ op->op_status = LRM_OP_PENDING;
+ return op;
+}
+
+static lrm_op_t*
+msg_to_op(struct ha_msg* msg)
+{
+ lrm_op_t* op;
+ const char* op_type;
+ const char* app_name;
+ const char* rsc_id;
+ const char* fail_reason;
+ const char* output;
+ const void* user_data;
+
+ op = lrm_op_new();
+
+ /* op->timeout, op->interval, op->target_rc, op->call_id*/
+ if (HA_OK != ha_msg_value_int(msg,F_LRM_TIMEOUT, &op->timeout)
+ || HA_OK != ha_msg_value_int(msg,F_LRM_INTERVAL, &op->interval)
+ || HA_OK != ha_msg_value_int(msg,F_LRM_TARGETRC, &op->target_rc)
+ || HA_OK != ha_msg_value_int(msg,F_LRM_DELAY, &op->start_delay)
+ || HA_OK != ha_msg_value_int(msg,F_LRM_CALLID, &op->call_id)) {
+ LOG_BASIC_ERROR("ha_msg_value_int");
+ free_op(op);
+ return NULL;
+ }
+
+ /* op->op_status */
+ if (HA_OK !=
+ ha_msg_value_int(msg, F_LRM_OPSTATUS, (int*)&op->op_status)) {
+ LOG_FAIL_GET_MSG_FIELD(LOG_WARNING, F_LRM_OPSTATUS, msg);
+ op->op_status = LRM_OP_PENDING;
+ }
+
+ /* if it finished successfully */
+ if (LRM_OP_DONE == op->op_status ) {
+ /* op->rc */
+ if (HA_OK != ha_msg_value_int(msg, F_LRM_RC, &op->rc)) {
+ free_op(op);
+ LOG_FAIL_GET_MSG_FIELD(LOG_ERR, F_LRM_RC, msg);
+ return NULL;
+ }
+ /* op->output */
+ output = cl_get_string(msg, F_LRM_DATA);
+ if (NULL != output){
+ op->output = g_strdup(output);
+ }
+ else {
+ op->output = NULL;
+ }
+ } else if(op->op_status == LRM_OP_PENDING) {
+ op->rc = EXECRA_STATUS_UNKNOWN;
+
+ } else {
+ op->rc = EXECRA_EXEC_UNKNOWN_ERROR;
+ }
+
+
+ /* op->app_name */
+ app_name = ha_msg_value(msg, F_LRM_APP);
+ if (NULL == app_name) {
+ LOG_FAIL_GET_MSG_FIELD(LOG_ERR, F_LRM_APP, msg);
+ free_op(op);
+ return NULL;
+ }
+ op->app_name = g_strdup(app_name);
+
+
+ /* op->op_type */
+ op_type = ha_msg_value(msg, F_LRM_OP);
+ if (NULL == op_type) {
+ LOG_FAIL_GET_MSG_FIELD(LOG_ERR, F_LRM_OP, msg);
+ free_op(op);
+ return NULL;
+ }
+ op->op_type = g_strdup(op_type);
+
+ /* op->rsc_id */
+ rsc_id = ha_msg_value(msg, F_LRM_RID);
+ if (NULL == rsc_id) {
+ LOG_FAIL_GET_MSG_FIELD(LOG_ERR, F_LRM_RID, msg);
+ free_op(op);
+ return NULL;
+ }
+ op->rsc_id = g_strdup(rsc_id);
+
+ /* op->fail_reason present only on async failures */
+ fail_reason = ha_msg_value(msg, F_LRM_FAIL_REASON);
+ if (fail_reason) {
+ op->fail_reason = g_strdup(fail_reason);
+ }
+
+ /* op->user_data */
+ user_data = cl_get_string(msg, F_LRM_USERDATA);
+
+ if (NULL != user_data) {
+ op->user_data = g_strdup(user_data);
+ }
+
+ /* time_stamps */
+ if (ha_msg_value_ul(msg, F_LRM_T_RUN, &op->t_run) != HA_OK
+ || ha_msg_value_ul(msg, F_LRM_T_RCCHANGE, &op->t_rcchange) != HA_OK
+ || ha_msg_value_ul(msg, F_LRM_EXEC_TIME, &op->exec_time) != HA_OK
+ || ha_msg_value_ul(msg, F_LRM_QUEUE_TIME, &op->queue_time) != HA_OK) {
+ /* cl_log(LOG_WARNING
+ , "%s:%d: failed to get the timing information"
+ , __FUNCTION__, __LINE__);
+ */
+ }
+
+ /* op->params */
+ op->params = ha_msg_value_str_table(msg, F_LRM_PARAM);
+
+ ha_msg_value_int(msg, F_LRM_RSCDELETED, &op->rsc_deleted);
+
+ return op;
+}
+
+static struct ha_msg*
+op_to_msg (lrm_op_t* op)
+{
+ struct ha_msg* msg = ha_msg_new(15);
+ if (!msg) {
+ LOG_BASIC_ERROR("ha_msg_new");
+ return NULL;
+ }
+
+ if (HA_OK != ha_msg_add(msg, F_LRM_TYPE, PERFORMOP)
+ || HA_OK != ha_msg_add(msg, F_LRM_RID, op->rsc_id)
+ || HA_OK != ha_msg_add(msg, F_LRM_OP, op->op_type)
+ || HA_OK != ha_msg_add_int(msg, F_LRM_TIMEOUT, op->timeout)
+ || HA_OK != ha_msg_add_int(msg, F_LRM_INTERVAL, op->interval)
+ || HA_OK != ha_msg_add_int(msg, F_LRM_DELAY, op->start_delay)
+ || HA_OK != ha_msg_add_int(msg, F_LRM_COPYPARAMS, op->copyparams)
+ || HA_OK != ha_msg_add_ul(msg, F_LRM_T_RUN,op->t_run)
+ || HA_OK != ha_msg_add_ul(msg, F_LRM_T_RCCHANGE, op->t_rcchange)
+ || HA_OK != ha_msg_add_ul(msg, F_LRM_EXEC_TIME, op->exec_time)
+ || HA_OK != ha_msg_add_ul(msg, F_LRM_QUEUE_TIME, op->queue_time)
+ || HA_OK != ha_msg_add_int(msg, F_LRM_TARGETRC, op->target_rc)
+ || ( op->app_name && (HA_OK != ha_msg_add(msg, F_LRM_APP, op->app_name)))
+ || ( op->user_data && (HA_OK != ha_msg_add(msg,F_LRM_USERDATA,op->user_data)))
+ || ( op->params && (HA_OK != ha_msg_add_str_table(msg,F_LRM_PARAM,op->params)))) {
+ LOG_BASIC_ERROR("op_to_msg conversion failed");
+ ha_msg_del(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+static int
+get_ret_from_ch(IPC_Channel* ch)
+{
+ int ret;
+ struct ha_msg* msg;
+
+ msg = msgfromIPC(ch, MSG_ALLOWINTR);
+
+ if (NULL == msg) {
+ cl_log(LOG_ERR
+ , "%s(%d): failed to receive message with function msgfromIPC"
+ , __FUNCTION__, __LINE__);
+ return HA_FAIL;
+ }
+ if (HA_OK != ha_msg_value_int(msg, F_LRM_RET, &ret)) {
+ LOG_FAIL_GET_MSG_FIELD(LOG_ERR, F_LRM_RET, msg);
+ ha_msg_del(msg);
+ return HA_FAIL;
+ }
+ ha_msg_del(msg);
+ return ret;
+}
+
+static int
+get_ret_from_msg(struct ha_msg* msg)
+{
+ int ret;
+
+ if (NULL == msg) {
+ cl_log(LOG_ERR, "%s(%d): the parameter is a NULL pointer."
+ , __FUNCTION__, __LINE__);
+ return HA_FAIL;
+ }
+ if (HA_OK != ha_msg_value_int(msg, F_LRM_RET, &ret)) {
+ LOG_FAIL_GET_MSG_FIELD(LOG_ERR, F_LRM_RET, msg);
+ return HA_FAIL;
+ }
+ return ret;
+}
+static void
+free_op (lrm_op_t* op)
+{
+ if (NULL == op) {
+ return;
+ }
+ if (NULL != op->op_type) {
+ g_free(op->op_type);
+ }
+ if (NULL != op->output) {
+ g_free(op->output);
+ }
+ if (NULL != op->rsc_id) {
+ g_free(op->rsc_id);
+ }
+ if (NULL != op->app_name) {
+ g_free(op->app_name);
+ }
+ if (NULL != op->user_data) {
+ g_free(op->user_data);
+ }
+ if (NULL != op->params) {
+ free_str_table(op->params);
+ }
+ g_free(op);
+}
+
+void lrm_free_op(lrm_op_t* op) {
+ free_op(op);
+}
+void lrm_free_rsc(lrm_rsc_t* rsc) {
+ if (NULL == rsc) {
+ return;
+ }
+ if (NULL != rsc->id) {
+ g_free(rsc->id);
+ }
+ if (NULL != rsc->type) {
+ g_free(rsc->type);
+ }
+ if (NULL != rsc->class) {
+ g_free(rsc->class);
+ }
+ if (NULL != rsc->provider) {
+ g_free(rsc->provider);
+ }
+ if (NULL != rsc->params) {
+ free_str_table(rsc->params);
+ }
+ g_free(rsc);
+}
+void lrm_free_str_list(GList* list) {
+ GList* item;
+ if (NULL == list) {
+ return;
+ }
+ item = g_list_first(list);
+ while (NULL != item) {
+ if (NULL != item->data) {
+ g_free(item->data);
+ }
+ list = g_list_delete_link(list, item);
+ item = g_list_first(list);
+ }
+}
+void lrm_free_op_list(GList* list) {
+ GList* item;
+ if (NULL == list) {
+ return;
+ }
+ item = g_list_first(list);
+ while (NULL != item) {
+ if (NULL != item->data) {
+ free_op((lrm_op_t*)item->data);
+ }
+ list = g_list_delete_link(list, item);
+ item = g_list_first(list);
+ }
+}
+void lrm_free_str_table(GHashTable* table) {
+ if (NULL != table) {
+ free_str_table(table);
+ }
+}
+
+const char *
+execra_code2string(uniform_ret_execra_t code)
+{
+ switch(code) {
+ case EXECRA_EXEC_UNKNOWN_ERROR:
+ return "unknown exec error";
+ case EXECRA_NO_RA:
+ return "no RA";
+ case EXECRA_OK:
+ return "ok";
+ case EXECRA_UNKNOWN_ERROR:
+ return "unknown error";
+ case EXECRA_INVALID_PARAM:
+ return "invalid parameter";
+ case EXECRA_UNIMPLEMENT_FEATURE:
+ return "unimplemented feature";
+ case EXECRA_INSUFFICIENT_PRIV:
+ return "insufficient privileges";
+ case EXECRA_NOT_INSTALLED:
+ return "not installed";
+ case EXECRA_NOT_CONFIGURED:
+ return "not configured";
+ case EXECRA_NOT_RUNNING:
+ return "not running";
+ /* For status command only */
+ case EXECRA_RUNNING_MASTER:
+ return "master";
+ case EXECRA_FAILED_MASTER:
+ return "master (failed)";
+ case EXECRA_RA_DEAMON_DEAD1:
+ return "status: daemon dead";
+ case EXECRA_RA_DEAMON_DEAD2:
+ return "status: daemon dead";
+ case EXECRA_RA_DEAMON_STOPPED:
+ return "status: daemon stopped";
+ case EXECRA_STATUS_UNKNOWN:
+ return "status: unknown";
+ default:
+ break;
+ }
+
+ return "<unknown>";
+}
diff --git a/lib/lrm/lrm_msg.c b/lib/lrm/lrm_msg.c
new file mode 100644
index 0000000..fdd3b3f
--- /dev/null
+++ b/lib/lrm/lrm_msg.c
@@ -0,0 +1,212 @@
+/*
+ * Message Functions For Local Resource Manager
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ * By Huang Zhen <zhenh@cn.ibm.com> 2004/2/13
+ *
+ */
+#include <lha_internal.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <clplumbing/cl_log.h>
+#include <ha_msg.h>
+#include <lrm/lrm_api.h>
+#include <lrm/lrm_msg.h>
+#define LOG_BASIC_ERROR(apiname) \
+ cl_log(LOG_ERR, "%s(%d): %s failed.", __FUNCTION__, __LINE__, apiname)
+
+const lrm_op_t lrm_zero_op; /* Default initialized to zeros */
+
+static void
+copy_pair(gpointer key, gpointer value, gpointer user_data)
+{
+ GHashTable* taget_table = (GHashTable*)user_data;
+ g_hash_table_insert(taget_table, g_strdup(key), g_strdup(value));
+}
+
+GHashTable*
+copy_str_table(GHashTable* src_table)
+{
+ GHashTable* target_table = NULL;
+
+ if ( NULL == src_table) {
+ return NULL;
+ }
+ target_table = g_hash_table_new(g_str_hash, g_str_equal);
+ g_hash_table_foreach(src_table, copy_pair, target_table);
+ return target_table;
+}
+
+static void
+merge_pair(gpointer key, gpointer value, gpointer user_data)
+{
+ GHashTable *merged = (GHashTable*)user_data;
+
+ if (g_hash_table_lookup(merged, key)) {
+ return;
+ }
+
+ g_hash_table_insert(merged, g_strdup(key), g_strdup(value));
+}
+
+GHashTable*
+merge_str_tables(GHashTable* old, GHashTable* new)
+{
+ GHashTable* merged = NULL;
+ if ( NULL == old ) {
+ return copy_str_table(new);
+ }
+ if ( NULL == new ) {
+ return copy_str_table(old);
+ }
+ merged = copy_str_table(new);
+ g_hash_table_foreach(old, merge_pair, merged);
+ return merged;
+}
+
+static gboolean
+free_pair(gpointer key, gpointer value, gpointer user_data)
+{
+ g_free(key);
+ g_free(value);
+ return TRUE;
+}
+
+void
+free_str_table(GHashTable* hash_table)
+{
+ g_hash_table_foreach_remove(hash_table, free_pair, NULL);
+ g_hash_table_destroy(hash_table);
+}
+
+
+
+struct ha_msg*
+create_lrm_msg (const char* msg)
+{
+ struct ha_msg* ret;
+ if ((NULL == msg) || (0 == strlen(msg))) {
+ return NULL;
+ }
+
+ ret = ha_msg_new(1);
+ if (HA_OK != ha_msg_add(ret, F_LRM_TYPE, msg)) {
+ ha_msg_del(ret);
+ LOG_BASIC_ERROR("ha_msg_add");
+ return NULL;
+ }
+
+ return ret;
+}
+
+struct ha_msg*
+create_lrm_reg_msg(const char* app_name)
+{
+ struct ha_msg* ret;
+ if ((NULL == app_name) || (0 == strlen(app_name))) {
+ return NULL;
+ }
+
+ ret = ha_msg_new(5);
+
+ if(HA_OK != ha_msg_add(ret, F_LRM_TYPE, REGISTER)
+ || HA_OK != ha_msg_add(ret, F_LRM_APP, app_name)
+ || HA_OK != ha_msg_add_int(ret, F_LRM_PID, getpid())
+ || HA_OK != ha_msg_add_int(ret, F_LRM_GID, getegid())
+ || HA_OK != ha_msg_add_int(ret, F_LRM_UID, getuid())) {
+ ha_msg_del(ret);
+ LOG_BASIC_ERROR("ha_msg_add");
+ return NULL;
+ }
+
+ return ret;
+}
+
+struct ha_msg*
+create_lrm_addrsc_msg(const char* rid, const char* class, const char* type,
+ const char* provider, GHashTable* params)
+{
+ struct ha_msg* msg;
+ if (NULL==rid||NULL==class||NULL==type) {
+ return NULL;
+ }
+
+ msg = ha_msg_new(5);
+ if(HA_OK != ha_msg_add(msg, F_LRM_TYPE, ADDRSC)
+ || HA_OK != ha_msg_add(msg, F_LRM_RID, rid)
+ || HA_OK != ha_msg_add(msg, F_LRM_RCLASS, class)
+ || HA_OK != ha_msg_add(msg, F_LRM_RTYPE, type)) {
+ ha_msg_del(msg);
+ LOG_BASIC_ERROR("ha_msg_add");
+ return NULL;
+ }
+
+ if( provider ) {
+ if (HA_OK != ha_msg_add(msg, F_LRM_RPROVIDER, provider)) {
+ ha_msg_del(msg);
+ LOG_BASIC_ERROR("ha_msg_add");
+ return NULL;
+ }
+ }
+
+ if ( params ) {
+ if (HA_OK != ha_msg_add_str_table(msg,F_LRM_PARAM,params)) {
+ ha_msg_del(msg);
+ LOG_BASIC_ERROR("ha_msg_add");
+ return NULL;
+ }
+ }
+ return msg;
+}
+
+
+struct ha_msg*
+create_lrm_rsc_msg(const char* rid, const char* msg)
+{
+ struct ha_msg* ret;
+ if ((NULL == rid) ||(NULL == msg) || (0 == strlen(msg))) {
+ return NULL;
+ }
+
+ ret = ha_msg_new(2);
+ if(HA_OK != ha_msg_add(ret, F_LRM_TYPE, msg)
+ || HA_OK != ha_msg_add(ret, F_LRM_RID, rid)) {
+ ha_msg_del(ret);
+ LOG_BASIC_ERROR("ha_msg_add");
+ return NULL;
+ }
+ return ret;
+}
+
+
+
+struct ha_msg*
+create_lrm_ret(int ret, int fields)
+{
+ struct ha_msg* msg = ha_msg_new(fields);
+ if(HA_OK != ha_msg_add(msg, F_LRM_TYPE, RETURN)
+ || HA_OK != ha_msg_add_int(msg, F_LRM_RET, ret)) {
+ ha_msg_del(msg);
+ LOG_BASIC_ERROR("ha_msg_add");
+ return NULL;
+ }
+ return msg;
+}
+
diff --git a/lib/lrm/racommon.c b/lib/lrm/racommon.c
new file mode 100644
index 0000000..2670f05
--- /dev/null
+++ b/lib/lrm/racommon.c
@@ -0,0 +1,178 @@
+/*
+ * Common functions for LRM interface to resource agents
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * File: racommon.c
+ * Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+ * Copyright (c) 2004 International Business Machines
+ *
+ */
+
+
+#include <lha_internal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <dirent.h>
+#include <libgen.h> /* Add it for compiling on OSX */
+#include <glib.h>
+#include <sys/stat.h>
+#include <clplumbing/cl_log.h>
+#include <lrm/raexec.h>
+#include <lrm/racommon.h>
+
+void
+get_ra_pathname(const char* class_path, const char* type, const char* provider,
+ char pathname[])
+{
+ char* type_dup;
+ char* base_name;
+
+ type_dup = g_strndup(type, RA_MAX_NAME_LENGTH);
+ if (type_dup == NULL) {
+ cl_log(LOG_ERR, "No enough memory to allocate.");
+ pathname[0] = '\0';
+ return;
+ }
+
+ base_name = basename(type_dup);
+
+ if ( strncmp(type, base_name, RA_MAX_NAME_LENGTH) == 0 ) {
+ /*the type does not include path*/
+ if (provider) {
+ snprintf(pathname, RA_MAX_NAME_LENGTH, "%s/%s/%s",
+ class_path, provider, type);
+ }else{
+ snprintf(pathname, RA_MAX_NAME_LENGTH, "%s/%s",
+ class_path,type);
+ }
+ }else{
+ /*the type includes path, just copy it to pathname*/
+ if ( *type == '/' ) {
+ g_strlcpy(pathname, type, RA_MAX_NAME_LENGTH);
+ } else {
+ *pathname = '\0';
+ cl_log(LOG_ERR, "%s: relative paths not allowed: %s",
+ __FUNCTION__, type);
+ }
+ }
+
+ g_free(type_dup);
+}
+
+/*
+ * Description: Filter a file.
+ * Return Value:
+ * TRUE: the file is qualified.
+ * FALSE: the file is unqualified.
+ * Notes: A qualifed file is a regular file with execute bits
+ * which does not start with '.'
+ */
+gboolean
+filtered(char * file_name)
+{
+ struct stat buf;
+ char *s;
+
+ if ( stat(file_name, &buf) != 0 ) {
+ return FALSE;
+ }
+ if ( ((s = strrchr(file_name,'/')) && *(s+1) == '.')
+ || *file_name == '.' ) {
+ return FALSE;
+ }
+
+ if ( S_ISREG(buf.st_mode)
+ && ( ( buf.st_mode & S_IXUSR ) || ( buf.st_mode & S_IXGRP )
+ || ( buf.st_mode & S_IXOTH ) ) ) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+int
+get_runnable_list(const char* class_path, GList ** rsc_info)
+{
+ struct dirent **namelist;
+ int file_num;
+
+ if ( rsc_info == NULL ) {
+ cl_log(LOG_ERR, "Parameter error: get_resource_list");
+ return -2;
+ }
+
+ if ( *rsc_info != NULL ) {
+ cl_log(LOG_ERR, "Parameter error: get_resource_list."\
+ "will cause memory leak.");
+ *rsc_info = NULL;
+ }
+
+ file_num = scandir(class_path, &namelist, NULL, alphasort);
+ if (file_num < 0) {
+ cl_log(LOG_ERR, "scandir failed in RA plugin");
+ return -2;
+ } else{
+ while (file_num--) {
+ char tmp_buffer[FILENAME_MAX+1];
+
+ tmp_buffer[0] = '\0';
+ tmp_buffer[FILENAME_MAX] = '\0';
+ snprintf(tmp_buffer, FILENAME_MAX, "%s/%s",
+ class_path, namelist[file_num]->d_name );
+ if ( filtered(tmp_buffer) == TRUE ) {
+ *rsc_info = g_list_append(*rsc_info,
+ g_strdup(namelist[file_num]->d_name));
+ }
+ free(namelist[file_num]);
+ }
+ free(namelist);
+ }
+ return g_list_length(*rsc_info);
+}
+
+int
+get_failed_exec_rc(void)
+{
+ int rc;
+
+ switch (errno) { /* see execve(2) */
+ case ENOENT: /* No such file or directory */
+ case EISDIR: /* Is a directory */
+ rc = EXECRA_NOT_INSTALLED;
+ break;
+ case EACCES: /* permission denied (various errors) */
+ rc = EXECRA_INSUFFICIENT_PRIV;
+ break;
+ default:
+ rc = EXECRA_EXEC_UNKNOWN_ERROR;
+ break;
+ }
+ return rc;
+}
+
+void
+closefiles(void)
+{
+ int fd;
+
+ /* close all descriptors except stdin/out/err and channels to logd */
+ for (fd = getdtablesize() - 1; fd > STDERR_FILENO; fd--) {
+ /*if (!cl_log_is_logd_fd(fd))*/
+ close(fd);
+ }
+}
diff --git a/lib/pils/Makefile.am b/lib/pils/Makefile.am
new file mode 100644
index 0000000..d47c6c7
--- /dev/null
+++ b/lib/pils/Makefile.am
@@ -0,0 +1,57 @@
+#
+# pils: Linux-HA heartbeat code
+#
+# Copyright (C) 2001 Alan Robertson
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \
+ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \
+ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl
+
+
+AM_CFLAGS = @CFLAGS@
+
+## include files
+#pkginclude_HEADERS = $(top_srcdir)/include/pils/plugin.h \
+# $(top_srcdir)/include/pils/interface.h
+
+## binaries
+#sbin_PROGRAMS = main
+
+
+#main_SOURCES = main.c
+
+#main_LDADD = libpils.la @LIBLTDL@ \
+# $(GLIBLIB) \
+# $(top_builddir)/replace/libreplace.la
+#main_LDFLAGS = @LIBADD_DL@ @LIBLTDL@ -export-dynamic @DLOPEN_FORCE_FLAGS@
+
+
+## libraries
+
+lib_LTLIBRARIES = libpils.la
+
+plugindir = $(libdir)/@HB_PKG@/plugins/test
+plugin_LTLIBRARIES = test.la
+
+libpils_la_SOURCES = pils.c
+libpils_la_LDFLAGS = -version-info 2:0:0
+libpils_la_LIBADD = $(top_builddir)/replace/libreplace.la \
+ @LIBLTDL@ $(GLIBLIB)
+test_la_SOURCES = test.c
+test_la_LDFLAGS = -export-dynamic -module -avoid-version
diff --git a/lib/pils/main.c b/lib/pils/main.c
new file mode 100644
index 0000000..32faceb
--- /dev/null
+++ b/lib/pils/main.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2001 Alan Robertson <alanr@unix.sh>
+ * This software licensed under the GNU LGPL.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <lha_internal.h>
+#include <stdio.h>
+#include <pils/generic.h>
+
+#define MOD "/home/alanr/modules"
+
+GHashTable* test1functions = NULL;
+
+long one = 1;
+long two = 2;
+long three = 3;
+long four = 4;
+
+static int TestCallBack
+( GenericPILCallbackType t
+, PILPluginUniv* univ
+, const char * iftype
+, const char * ifname
+, void* userptr
+);
+
+static PILGenericIfMgmtRqst RegRqsts [] =
+ { {"test", &test1functions, &one, TestCallBack, &two},
+ {NULL, NULL, NULL, NULL, NULL}
+};
+
+int
+main(int argc, char ** argv)
+{
+ PILPluginUniv * u;
+ PIL_rc rc;
+ int j;
+
+
+ u = NewPILPluginUniv(MOD);
+ /* PILSetDebugLevel(u, NULL, NULL, 0); */
+ PILLogMemStats();
+
+
+ if ((rc = PILLoadPlugin(u, "InterfaceMgr", "generic", &RegRqsts))
+ != PIL_OK) {
+ fprintf(stderr, "generic plugin load Error = [%s]\n"
+ , lt_dlerror());
+ /*exit(1);*/
+ }
+ /* PILSetDebugLevel(u, NULL, NULL, 0); */
+
+ for (j=0; j < 10; ++j) {
+ PILLogMemStats();
+ fprintf(stderr, "****Loading plugin test/test\n");
+ if ((rc = PILLoadPlugin(u, "test", "test", NULL)) != PIL_OK) {
+ printf("ERROR: test plugin load error = [%d/%s]\n"
+ , rc, lt_dlerror());
+ }
+ PILLogMemStats();
+ fprintf(stderr, "****UN-loading plugin test/test\n");
+ if ((rc = PILIncrIFRefCount(u, "test", "test", -1))!= PIL_OK){
+ printf("ERROR: test plugin UNload error = [%d/%s]\n"
+ , rc, lt_dlerror());
+ }
+ }
+ PILLogMemStats();
+ DelPILPluginUniv(u); u = NULL;
+ PILLogMemStats();
+
+ return 0;
+}
+
+
+static int
+TestCallBack
+( GenericPILCallbackType t
+, PILPluginUniv* univ
+, const char * iftype
+, const char * ifname
+, void* userptr)
+{
+ char cbbuf[32];
+
+ switch(t) {
+ case PIL_REGISTER:
+ snprintf(cbbuf, sizeof(cbbuf), "PIL_REGISTER");
+ break;
+
+ case PIL_UNREGISTER:
+ snprintf(cbbuf, sizeof(cbbuf), "PIL_UNREGISTER");
+ break;
+
+ default:
+ snprintf(cbbuf, sizeof(cbbuf), "type [%d?]", t);
+ break;
+ }
+
+ fprintf(stderr, "Callback: (%s, univ: 0x%lx, module: %s/%s, user ptr: 0x%lx (%ld))\n"
+ , cbbuf
+ , (unsigned long) univ
+ , iftype, ifname
+ , (unsigned long)userptr
+ , (*((long *)userptr)));
+ return PIL_OK;
+}
+
diff --git a/lib/pils/pils.c b/lib/pils/pils.c
new file mode 100644
index 0000000..4243b22
--- /dev/null
+++ b/lib/pils/pils.c
@@ -0,0 +1,2152 @@
+/*
+ * Copyright (C) 2001 Alan Robertson <alanr@unix.sh>
+ * This software licensed under the GNU LGPL.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <lha_internal.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <sys/stat.h>
+
+/* Dumbness... */
+#define time FooTimeParameter
+#define index FooIndexParameter
+# include <glib.h>
+#undef time
+#undef index
+
+
+#define ENABLE_PIL_DEFS_PRIVATE
+#define ENABLE_PLUGIN_MANAGER_PRIVATE
+
+#ifndef STRLEN_CONST
+# define STRLEN_CONST(con) (sizeof(con)-1)
+#endif
+
+#include <pils/interface.h>
+
+#define NEW(type) (g_new(type,1))
+#define ZAP(obj) memset(obj, 0, sizeof(*obj))
+#define DELETE(obj) {g_free(obj); obj = NULL;}
+
+#ifdef LTDL_SHLIB_EXT
+# define PLUGINSUFFIX LTDL_SHLIB_EXT
+#else
+# define PLUGINSUFFIX ".so"
+#endif
+
+static int PluginDebugLevel = 0;
+
+#define DEBUGPLUGIN (PluginDebugLevel > 0)
+
+
+
+static PIL_rc InterfaceManager_plugin_init(PILPluginUniv* univ);
+
+static char** PILPluginTypeListPlugins(PILPluginType* pitype, int* picount);
+static PILInterface* FindIF(PILPluginUniv* universe, const char *iftype
+, const char * ifname);
+static PIL_rc PluginExists(const char * PluginPath);
+static char * PILPluginPath(PILPluginUniv* universe, const char * plugintype
+, const char * pluginname);
+
+
+void DelPILPluginUniv(PILPluginUniv*);
+/*
+ * These RmA* functions primarily called from hash_table_foreach,
+ * functions, so they have gpointer arguments. When calling
+ * them by hand, take special care to pass the right argument types.
+ *
+ * They all follow the same calling sequence though. It is:
+ * String name"*" type object
+ * "*" type object with the name given by 1st argument
+ * NULL
+ *
+ * For example:
+ * RmAPILPluginType takes
+ * string name
+ * PILPluginType* object with the given name.
+ */
+static gboolean RmAPILPluginType
+( gpointer pitname /* Name of this plugin type */
+, gpointer pitype /* PILPluginType* */
+, gpointer notused
+);
+static void RemoveAPILPluginType(PILPluginType*);
+
+static PILPluginType* NewPILPluginType
+( PILPluginUniv* pluginuniv
+, const char * plugintype
+);
+static void DelPILPluginType(PILPluginType*);
+/*
+ * These RmA* functions primarily called from hash_table_foreach,
+ * so they have gpointer arguments. When calling
+ * them by hand, take special care to pass the right argument types.
+ */
+static gboolean RmAPILPlugin
+( gpointer piname /* Name of this plugin */
+, gpointer plugin /* PILPlugin* */
+, gpointer notused
+);
+static void RemoveAPILPlugin(PILPlugin*);
+
+
+static PILPlugin* NewPILPlugin(PILPluginType* pitype
+ , const char * plugin_name
+ , lt_dlhandle dlhand
+ , PILPluginInitFun PluginSym);
+static void DelPILPlugin(PILPlugin*);
+
+struct MemStat {
+ unsigned long news;
+ unsigned long frees;
+};
+
+static struct PluginStats {
+ struct MemStat plugin;
+ struct MemStat pitype;
+ struct MemStat piuniv;
+ struct MemStat interface;
+ struct MemStat interfacetype;
+ struct MemStat interfaceuniv;
+}PILstats;
+
+#define STATNEW(t) {PILstats.t.news ++; }
+#define STATFREE(t) {PILstats.t.frees ++; }
+
+
+
+static PILInterfaceUniv* NewPILInterfaceUniv(PILPluginUniv*);
+static void DelPILInterfaceUniv(PILInterfaceUniv*);
+/*
+ * These RmA* functions primarily called from hash_table_foreach, but
+ * not necessarily, so they have gpointer arguments. When calling
+ * them by hand, take special care to pass the right argument types.
+ */
+static gboolean RmAPILInterfaceType
+( gpointer iftypename /* Name of this interface type */
+, gpointer iftype /* PILInterfaceType* */
+, gpointer notused
+);
+static void RemoveAPILInterfaceType(PILInterfaceType*, PILInterfaceType*);
+
+static PILInterfaceType* NewPILInterfaceType
+( PILInterfaceUniv*
+, const char * typename
+, void* ifexports, void* user_data
+);
+static void DelPILInterfaceType(PILInterfaceType*);
+/*
+ * These RmA* functions are designed to be called from
+ * hash_table_foreach, so they have gpointer arguments. When calling
+ * them by hand, take special care to pass the right argument types.
+ * They can be called from other places safely also.
+ */
+static gboolean RmAPILInterface
+( gpointer ifname /* Name of this interface */
+, gpointer plugin /* PILInterface* */
+, gpointer notused
+);
+static PIL_rc RemoveAPILInterface(PILInterface*);
+static void DelPILPluginType(PILPluginType*);
+
+static PILInterface* NewPILInterface
+( PILInterfaceType* interfacetype
+, const char* interfacename
+, void * exports
+, PILInterfaceFun closefun
+, void* ud_interface
+, PILPlugin* loading_plugin /* The plugin that loaded us */
+);
+static void DelPILInterface(PILInterface*);
+static PIL_rc close_ifmgr_interface(PILInterface*, void*);
+
+
+
+
+/*
+ * For consistency, we show up as a plugin in our our system.
+ *
+ * Here are our exports as a plugin.
+ *
+ */
+static const char * PIL_PILPluginVersion(void);
+static void PIL_PILPluginClose (PILPlugin*);
+void PILpisysSetDebugLevel (int level);
+int PILpisysGetDebugLevel(void);
+static const char * PIL_PILPluginLicense (void);
+static const char * PIL_PILPluginLicenseUrl (void);
+
+static const PILPluginOps PluginExports =
+{ PIL_PILPluginVersion
+, PILpisysGetDebugLevel
+, PILpisysSetDebugLevel
+, PIL_PILPluginLicense
+, PIL_PILPluginLicenseUrl
+, PIL_PILPluginClose
+};
+
+/* Prototypes for the functions that we export to every plugin */
+static PIL_rc PILregister_plugin(PILPlugin* piinfo, const PILPluginOps* mops);
+static PIL_rc PILunregister_plugin(PILPlugin* piinfo);
+static PIL_rc
+PILRegisterInterface
+( PILPlugin* piinfo
+, const char * interfacetype /* Type of interface */
+, const char * interfacename /* Name of interface */
+, void* Ops /* Ops exported by this interface */
+, PILInterfaceFun closefunc /* Ops exported by this interface */
+, PILInterface** interfaceid /* Interface id (OP) */
+, void** Imports /* Functions imported by */
+ /* this interface (OP) */
+, void* ud_interface /* interface user data */
+);
+static PIL_rc PILunregister_interface(PILInterface* interfaceid);
+static void PILLog(PILLogLevel priority, const char * fmt, ...)
+ G_GNUC_PRINTF(2,3);
+
+
+/*
+ * This is the set of functions that we export to every plugin
+ *
+ * That also makes it the set of functions that every plugin imports.
+ *
+ */
+
+static PILPluginImports PILPluginImportSet =
+{ PILregister_plugin /* register_plugin */
+, PILunregister_plugin /* unregister_plugin */
+, PILRegisterInterface /* register_interface */
+, RemoveAPILInterface /* unregister_interface */
+, PILLoadPlugin /* load_plugin */
+, PILLog /* Logging function */
+, g_malloc /* Malloc function */
+, g_realloc /* realloc function */
+, g_free /* Free function */
+, g_strdup /* Strdup function */
+};
+
+static PIL_rc ifmgr_register_interface(PILInterface* newif
+ , void** imports);
+static PIL_rc ifmgr_unregister_interface(PILInterface* interface);
+
+/*
+ * For consistency, the master interface manager is a interface in the
+ * system. Below is our set of exported Interface functions.
+ *
+ * Food for thought: This is the interface manager whose name is
+ * interface. This makes it the Interface Interface interface ;-)
+ * (or the Interface/Interface interface if you prefer)
+ */
+
+static PILInterfaceOps IfExports =
+{ ifmgr_register_interface
+, ifmgr_unregister_interface
+};
+
+
+
+/*
+ * Below is the set of functions we export to every interface manager.
+ */
+
+static int IfRefCount(PILInterface * ifh);
+static int IfIncrRefCount(PILInterface*eifinfo,int plusminus);
+static int PluginIncrRefCount(PILPlugin*eifinfo,int plusminus);
+#if 0
+static int PluginRefCount(PILPlugin * ifh);
+#endif
+static void IfForceUnregister(PILInterface *eifinfo);
+static void IfForEachClientRemove(PILInterface* manangerif
+ , gboolean(*f)(PILInterface* clientif, void * other)
+ , void* other);
+
+static PILInterfaceImports IFManagerImports =
+{ IfRefCount
+, IfIncrRefCount
+, IfForceUnregister
+, IfForEachClientRemove
+};
+static void PILValidatePlugin(gpointer key, gpointer plugin, gpointer pitype);
+static void PILValidatePluginType(gpointer key, gpointer pitype, gpointer piuniv);
+static void PILValidatePluginUniv(gpointer key, gpointer pitype, gpointer);
+static void PILValidateInterface(gpointer key, gpointer interface, gpointer iftype);
+static void PILValidateInterfaceType(gpointer key, gpointer iftype, gpointer ifuniv);
+static void PILValidateInterfaceUniv(gpointer key, gpointer puniv, gpointer);
+
+/*****************************************************************************
+ *
+ * This code is for managing plugins, and interacting with them...
+ *
+ ****************************************************************************/
+
+static PILPlugin*
+NewPILPlugin( PILPluginType* pitype
+ , const char * plugin_name
+ , lt_dlhandle dlhand
+ , PILPluginInitFun PluginSym)
+{
+ PILPlugin* ret = NEW(PILPlugin);
+
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "NewPILPlugin(0x%lx)", (unsigned long)ret);
+ }
+
+ STATNEW(plugin);
+ ret->MagicNum = PIL_MAGIC_PLUGIN;
+ ret->plugin_name = g_strdup(plugin_name);
+ ret->plugintype = pitype;
+ ret->refcnt = 0;
+ ret->dlhandle = dlhand;
+ ret->dlinitfun = PluginSym;
+ PILValidatePlugin(ret->plugin_name, ret, pitype);
+ return ret;
+}
+static void
+DelPILPlugin(PILPlugin*pi)
+{
+
+ if (pi->refcnt > 0) {
+ PILLog(PIL_INFO, "DelPILPlugin: Non-zero refcnt");
+ }
+
+ if (pi->dlhandle) {
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "Closing dlhandle for (%s/%s)"
+ , pi->plugintype->plugintype, pi->plugin_name);
+ }
+ lt_dlclose(pi->dlhandle);
+ }else if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "NO dlhandle for (%s/%s)!"
+ , pi->plugintype->plugintype, pi->plugin_name);
+ }
+ DELETE(pi->plugin_name);
+ ZAP(pi);
+ DELETE(pi);
+ STATFREE(plugin);
+}
+
+
+static PILPluginType dummymlpitype =
+{ PIL_MAGIC_PLUGINTYPE
+, NULL /*plugintype*/
+, NULL /*piuniv*/
+, NULL /*Plugins*/
+, PILPluginTypeListPlugins /* listplugins */
+};
+
+static PILPluginType*
+NewPILPluginType(PILPluginUniv* pluginuniv
+ , const char * plugintype
+)
+{
+ PILPluginType* ret = NEW(PILPluginType);
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "NewPILPlugintype(0x%lx)", (unsigned long)ret);
+ }
+ STATNEW(pitype);
+
+ *ret = dummymlpitype;
+
+ ret->plugintype = g_strdup(plugintype);
+ ret->piuniv = pluginuniv;
+ ret->Plugins = g_hash_table_new(g_str_hash, g_str_equal);
+ g_hash_table_insert(pluginuniv->PluginTypes
+ , g_strdup(ret->plugintype), ret);
+ PILValidatePluginType(ret->plugintype, ret, pluginuniv);
+ return ret;
+}
+static void
+DelPILPluginType(PILPluginType*pitype)
+{
+ PILValidatePluginType(NULL, pitype, NULL);
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "DelPILPluginType(%s)", pitype->plugintype);
+ }
+
+ STATFREE(pitype);
+ g_hash_table_foreach_remove(pitype->Plugins, RmAPILPlugin, NULL);
+ g_hash_table_destroy(pitype->Plugins);
+ DELETE(pitype->plugintype);
+ ZAP(pitype);
+ DELETE(pitype);
+}
+/*
+ * These RmA* functions primarily called from hash_table_foreach,
+ * so they have gpointer arguments. This *not necessarily* clause
+ * is why they do the g_hash_table_lookup_extended call instead of
+ * just deleting the key. When called from outside, the key *
+ * may not be pointing at the key to actually free, but a copy
+ * of the key.
+ */
+static gboolean
+RmAPILPlugin /* IsA GHFunc: required for g_hash_table_foreach_remove() */
+( gpointer piname /* Name of this plugin */
+, gpointer plugin /* PILPlugin* */
+, gpointer notused
+)
+{
+ PILPlugin* Plugin = plugin;
+ PILPluginType* Pitype = Plugin->plugintype;
+
+ PILValidatePlugin(piname, plugin, NULL);
+ PILValidatePluginType(NULL, Pitype, NULL);
+ g_assert(IS_PILPLUGIN(Plugin));
+
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "RmAPILPlugin(%s/%s)", Pitype->plugintype
+ , Plugin->plugin_name);
+ }
+ /* Normally called from g_hash_table_foreachremove or equivalent */
+
+ DelPILPlugin(plugin);
+ DELETE(piname);
+ return TRUE;
+}
+
+static void
+RemoveAPILPlugin(PILPlugin*Plugin)
+{
+ PILPluginType* Pitype = Plugin->plugintype;
+ gpointer key;
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "RemoveAPILPlugin(%s/%s)"
+ , Pitype->plugintype
+ , Plugin->plugin_name);
+ }
+ if (g_hash_table_lookup_extended(Pitype->Plugins
+ , Plugin->plugin_name, &key, (void*)&Plugin)) {
+
+ g_hash_table_remove(Pitype->Plugins, key);
+ RmAPILPlugin(key, Plugin, NULL);
+ key = NULL;
+ Plugin = NULL;
+
+ }else{
+ g_assert_not_reached();
+ }
+ if (g_hash_table_size(Pitype->Plugins) == 0) {
+ RemoveAPILPluginType(Pitype);
+ /* Pitype is now invalid */
+ Pitype = NULL;
+ }
+}
+
+PILPluginUniv*
+NewPILPluginUniv(const char * basepluginpath)
+{
+ PILPluginUniv* ret = NEW(PILPluginUniv);
+
+ /* The delimiter separating search path components */
+ const char* path_delim = G_SEARCHPATH_SEPARATOR_S;
+ char * fullpath;
+
+ STATNEW(piuniv);
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "NewPILPluginUniv(0x%lx)"
+ , (unsigned long)ret);
+ }
+ if (!g_path_is_absolute(basepluginpath)) {
+ DELETE(ret);
+ return(ret);
+ }
+ ret->MagicNum = PIL_MAGIC_PLUGINUNIV;
+ fullpath = g_strdup_printf("%s%s%s", basepluginpath
+ , path_delim, PILS_BASE_PLUGINDIR);
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG
+ , "PILS: Plugin path = %s", fullpath);
+ }
+
+ /* Separate the root directory PATH into components */
+ ret->rootdirlist = g_strsplit(fullpath, path_delim, 100);
+ g_free(fullpath);
+
+ ret->PluginTypes = g_hash_table_new(g_str_hash, g_str_equal);
+ ret->imports = &PILPluginImportSet;
+ ret->ifuniv = NewPILInterfaceUniv(ret);
+ PILValidatePluginUniv(NULL, ret, NULL);
+ return ret;
+}
+
+/* Change memory allocation functions immediately after creating universe */
+void
+PilPluginUnivSetMemalloc(PILPluginUniv* u
+, gpointer (*allocfun)(glib_size_t size)
+, gpointer (*reallocfun)(gpointer ptr, glib_size_t size)
+, void (*freefun)(void* space)
+, char* (*strdupfun)(const char *s))
+{
+ u->imports->alloc = allocfun;
+ u->imports->mrealloc = reallocfun;
+ u->imports->mfree = freefun;
+ u->imports->mstrdup = strdupfun;
+}
+
+
+/* Change logging functions - preferably right after creating universe */
+void
+PilPluginUnivSetLog(PILPluginUniv* u
+, void (*logfun) (PILLogLevel priority, const char * fmt, ...))
+{
+ u->imports->log = logfun;
+}
+
+void
+DelPILPluginUniv(PILPluginUniv* piuniv)
+{
+
+
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "DelPILPluginUniv(0x%lx)"
+ , (unsigned long)piuniv);
+ }
+ STATFREE(piuniv);
+ PILValidatePluginUniv(NULL, piuniv, NULL);
+ DelPILInterfaceUniv(piuniv->ifuniv);
+ piuniv->ifuniv = NULL;
+ g_hash_table_foreach_remove(piuniv->PluginTypes
+ , RmAPILPluginType, NULL);
+ g_hash_table_destroy(piuniv->PluginTypes);
+ g_strfreev(piuniv->rootdirlist);
+ ZAP(piuniv);
+ DELETE(piuniv);
+}
+
+/*
+ * These RmA* functions primarily called from hash_table_foreach,
+ * so they have gpointer arguments. This *not necessarily* clause
+ * is why they do the g_hash_table_lookup_extended call instead of
+ * just deleting the key. When called from outside, the key *
+ * may not be pointing at the key to actually free, but a copy
+ * of the key.
+ */
+static gboolean /* IsA GHFunc: required for g_hash_table_foreach_remove() */
+RmAPILPluginType
+( gpointer pitname /* Name of this plugin type "real" key */
+, gpointer pitype /* PILPluginType* */
+, gpointer notused
+)
+{
+ PILPluginType* Plugintype = pitype;
+
+ g_assert(IS_PILPLUGINTYPE(Plugintype));
+ PILValidatePluginType(pitname, pitype, NULL);
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "RmAPILPluginType(%s)"
+ , Plugintype->plugintype);
+ }
+ /*
+ * This function is usually but not always called by
+ * g_hash_table_foreach_remove()
+ */
+
+ DelPILPluginType(pitype);
+ DELETE(pitname);
+ return TRUE;
+}
+static void
+RemoveAPILPluginType(PILPluginType*Plugintype)
+{
+ PILPluginUniv* Pluginuniv = Plugintype->piuniv;
+ gpointer key;
+ if (g_hash_table_lookup_extended(Pluginuniv->PluginTypes
+ , Plugintype->plugintype, &key, (void*)&Plugintype)) {
+
+ g_hash_table_remove(Pluginuniv->PluginTypes, key);
+ RmAPILPluginType(key, Plugintype, NULL);
+ }else{
+ g_assert_not_reached();
+ }
+}
+
+/*
+ * InterfaceManager_plugin_init: Initialize the handling of
+ * "Interface Manager" interfaces.
+ *
+ * There are a few potential bootstrapping problems here ;-)
+ *
+ */
+static PIL_rc
+InterfaceManager_plugin_init(PILPluginUniv* univ)
+{
+ PILPluginImports* imports = univ->imports;
+ PILPluginType* pitype;
+ PILInterface* ifinfo;
+ PILInterfaceType* iftype;
+ void* dontcare;
+ PILPlugin* ifmgr_plugin;
+ PIL_rc rc;
+
+
+ iftype = NewPILInterfaceType(univ->ifuniv, PI_IFMANAGER, &IfExports
+ , NULL);
+
+ g_hash_table_insert(univ->ifuniv->iftypes
+ , g_strdup(PI_IFMANAGER), iftype);
+
+ pitype = NewPILPluginType(univ, PI_IFMANAGER);
+
+ g_hash_table_insert(univ->PluginTypes
+ , g_strdup(PI_IFMANAGER), pitype);
+
+ ifmgr_plugin= NewPILPlugin(pitype, PI_IFMANAGER, NULL, NULL);
+
+ g_hash_table_insert(pitype->Plugins
+ , g_strdup(PI_IFMANAGER), ifmgr_plugin);
+
+ /* We can call register_plugin, since it doesn't depend on us... */
+ rc = imports->register_plugin(ifmgr_plugin, &PluginExports);
+ if (rc != PIL_OK) {
+ PILLog(PIL_CRIT, "register_plugin() failed in init: %s"
+ , PIL_strerror(rc));
+ return(rc);
+ }
+ /*
+ * Now, we're registering interfaces, and are into some deep
+ * Catch-22 if do it the "easy" way, since our code is
+ * needed in order to support interface loading for the type of
+ * interface we are (a Interface interface).
+ *
+ * So, instead of calling imports->register_interface(), we have to
+ * do the work ourselves here...
+ *
+ * Since no one should yet be registered to handle Interface
+ * interfaces, we need to bypass the hash table handler lookup
+ * that register_interface would do and call the function that
+ * register_interface would call...
+ *
+ */
+
+ /* The first argument is the PILInterfaceType* */
+ ifinfo = NewPILInterface(iftype, PI_IFMANAGER, &IfExports
+ , close_ifmgr_interface, NULL, NULL);
+ ifinfo->ifmanager = iftype->ifmgr_ref = ifinfo;
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "InterfaceManager_plugin_init(0x%lx/%s)"
+ , (unsigned long)ifinfo, ifinfo->interfacename);
+ }
+ PILValidatePluginUniv(NULL, univ, NULL);
+ ifmgr_register_interface(ifinfo, &dontcare);
+ PILValidatePluginUniv(NULL, univ, NULL);
+
+ return(PIL_OK);
+}/*InterfaceManager_plugin_init*/
+
+
+/* Return current IfIf "plugin" version (not very interesting for us) */
+static const char *
+PIL_PILPluginVersion(void)
+{
+ return("1.0");
+}
+
+/* Return current IfIf debug level */
+int
+PILpisysGetDebugLevel(void)
+{
+ return(PluginDebugLevel);
+}
+
+/* Set current IfIf debug level */
+void
+PILpisysSetDebugLevel (int level)
+{
+ PluginDebugLevel = level;
+}
+struct set_debug_helper {
+ const char * pitype;
+ const char * piname;
+ int level;
+};
+
+static void
+PILSetDebugLeveltoPlugin(gpointer key, gpointer plugin, gpointer Helper)
+{
+ PILPlugin* p = plugin;
+ struct set_debug_helper* helper = Helper;
+
+ p->pluginops->setdebuglevel(helper->level);
+}
+
+static void
+PILSetDebugLevelbyType(const void * key, gpointer plugintype, gpointer Helper)
+{
+ struct set_debug_helper* helper = Helper;
+
+
+ PILPluginType* t = plugintype;
+
+ if (helper->piname == NULL) {
+ g_hash_table_foreach(t->Plugins, PILSetDebugLeveltoPlugin
+ , helper);
+ }else{
+ PILPlugin* p = g_hash_table_lookup(t->Plugins
+ , helper->piname);
+ if (p != NULL) {
+ p->pluginops->setdebuglevel(helper->level);
+ }
+ }
+}
+
+void
+PILSetDebugLevel(PILPluginUniv* u, const char * pitype, const char * piname
+, int level)
+{
+ struct set_debug_helper helper = {pitype, piname, level};
+
+ if (u == NULL) {
+ return;
+ }
+
+ if (pitype == NULL) {
+ g_hash_table_foreach(u->PluginTypes
+ /*
+ * Reason for this next cast:
+ * SetDebugLevelbyType takes const gpointer
+ * arguments, unlike a GHFunc which doesn't.
+ */
+ , (GHFunc)PILSetDebugLevelbyType
+ , &helper);
+ }else{
+ PILPluginType* t = g_hash_table_lookup(u->PluginTypes
+ , pitype);
+ if (t != NULL) {
+ PILSetDebugLevelbyType(pitype, t, &helper);
+ }
+ }
+}
+
+
+int
+PILGetDebugLevel(PILPluginUniv* u, const char * pitype, const char * piname)
+{
+ PILPluginType* t;
+ PILPlugin* p;
+ if ( u == NULL
+ || pitype == NULL
+ || (t = g_hash_table_lookup(u->PluginTypes, pitype)) == NULL
+ || (p = g_hash_table_lookup(t->Plugins, piname)) == NULL) {
+ return -1;
+ }
+ return p->pluginops->getdebuglevel();
+}
+
+/* Close/shutdown our PILPlugin (the interface manager interface plugin) */
+/* All our interfaces will have already been shut down and unregistered */
+static void
+PIL_PILPluginClose (PILPlugin* plugin)
+{
+}
+static const char *
+PIL_PILPluginLicense (void)
+{
+ return LICENSE_LGPL;
+}
+static const char *
+PIL_PILPluginLicenseUrl (void)
+{
+ return URL_LGPL;
+}
+
+/*****************************************************************************
+ *
+ * This code is for managing interfaces, and interacting with them...
+ *
+ ****************************************************************************/
+
+
+static PILInterface*
+NewPILInterface(PILInterfaceType* interfacetype
+ , const char* interfacename
+ , void * exports
+ , PILInterfaceFun closefun
+ , void* ud_interface
+ , PILPlugin* loading_plugin)
+{
+ PILInterface* ret = NULL;
+ PILInterface* look = NULL;
+
+
+ if ((look = g_hash_table_lookup(interfacetype->interfaces
+ , interfacename)) != NULL) {
+ PILLog(PIL_DEBUG, "Deleting PILInterface!");
+ DelPILInterface(look);
+ }
+ ret = NEW(PILInterface);
+ STATNEW(interface);
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "NewPILInterface(0x%lx)", (unsigned long)ret);
+ }
+
+ if (ret) {
+ ret->MagicNum = PIL_MAGIC_INTERFACE;
+ ret->interfacetype = interfacetype;
+ ret->exports = exports;
+ ret->ud_interface = ud_interface;
+ ret->interfacename = g_strdup(interfacename);
+ ret->ifmanager = interfacetype->ifmgr_ref;
+ ret->loadingpi = loading_plugin;
+ g_hash_table_insert(interfacetype->interfaces
+ , g_strdup(ret->interfacename), ret);
+
+ ret->if_close = closefun;
+ ret->refcnt = 1;
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "NewPILInterface(0x%lx:%s/%s)*** user_data: 0x%p *******"
+ , (unsigned long)ret
+ , interfacetype->typename
+ , ret->interfacename
+ , ud_interface);
+ }
+ }
+ return ret;
+}
+static void
+DelPILInterface(PILInterface* intf)
+{
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "DelPILInterface(0x%lx/%s)"
+ , (unsigned long)intf, intf->interfacename);
+ }
+ STATFREE(interface);
+ DELETE(intf->interfacename);
+ ZAP(intf);
+ DELETE(intf);
+}
+
+static PILInterfaceType*
+NewPILInterfaceType(PILInterfaceUniv*univ, const char * typename
+, void* ifeports, void* user_data)
+{
+ PILInterfaceType* ifmgr_types;
+ PILInterface* ifmgr_ref;
+ PILInterfaceType* ret = NEW(PILInterfaceType);
+
+
+ STATNEW(interfacetype);
+ ret->MagicNum = PIL_MAGIC_INTERFACETYPE;
+ ret->typename = g_strdup(typename);
+ ret->interfaces = g_hash_table_new(g_str_hash, g_str_equal);
+ ret->ud_if_type = user_data;
+ ret->universe = univ;
+ ret->ifmgr_ref = NULL;
+
+ /* Now find the pointer to our if type in the Interface Universe */
+ if ((ifmgr_types = g_hash_table_lookup(univ->iftypes, PI_IFMANAGER))
+ != NULL) {
+ if ((ifmgr_ref=g_hash_table_lookup(ifmgr_types->interfaces
+ , typename)) != NULL) {
+ ret->ifmgr_ref = ifmgr_ref;
+ }else {
+ g_assert(strcmp(typename, PI_IFMANAGER) == 0);
+ }
+ }else {
+ g_assert(strcmp(typename, PI_IFMANAGER) == 0);
+ }
+
+ /* Insert ourselves into our parent's table */
+ g_hash_table_insert(univ->iftypes, g_strdup(typename), ret);
+ return ret;
+}
+static void
+DelPILInterfaceType(PILInterfaceType*ift)
+{
+ PILInterfaceUniv* u = ift->universe;
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "DelPILInterfaceType(%s)"
+ , ift->typename);
+ }
+ STATFREE(interfacetype);
+
+ PILValidateInterfaceUniv(NULL, u, NULL);
+
+ /*
+ * RmAPILInterface refuses to remove the interface for the
+ * Interface manager, because it must be removed last.
+ *
+ * Otherwise we won't be able to unregister interfaces
+ * for other types of objects, and we'll be very confused.
+ */
+
+ g_hash_table_foreach_remove(ift->interfaces, RmAPILInterface, NULL);
+
+ PILValidateInterfaceUniv(NULL, u, NULL);
+
+ if (g_hash_table_size(ift->interfaces) > 0) {
+ gpointer key, iftype;
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG
+ , "DelPILInterfaceType(%s): table size (%d)"
+ , ift->typename, g_hash_table_size(ift->interfaces));
+ }
+ if (g_hash_table_lookup_extended(ift->interfaces
+ , PI_IFMANAGER, &key, &iftype)) {
+ DelPILInterface((PILInterface*)iftype);
+ DELETE(key);
+ }
+ }
+ DELETE(ift->typename);
+ g_hash_table_destroy(ift->interfaces);
+ ZAP(ift);
+ DELETE(ift);
+}
+
+/*
+ * These RmA* functions primarily called from hash_table_foreach,
+ * so they have gpointer arguments. This *not necessarily* clause
+ * is why they do the g_hash_table_lookup_extended call instead of
+ * just deleting the key. When called from outside, the key *
+ * may not be pointing at the key to actually free, but a copy
+ * of the key.
+ */
+static gboolean /* IsAGHFunc: required for g_hash_table_foreach_remove() */
+RmAPILInterface
+( gpointer ifname /* Name of this interface */
+, gpointer intf /* PILInterface* */
+, gpointer notused
+)
+{
+ PILInterface* If = intf;
+ PILInterfaceType* Iftype = If->interfacetype;
+
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "RmAPILInterface(0x%lx/%s)"
+ , (unsigned long)If, If->interfacename);
+ }
+ g_assert(IS_PILINTERFACE(If));
+
+ /*
+ * Don't remove the master interface manager this way, or
+ * Somebody will have a cow...
+ */
+ if (If == If->ifmanager) {
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "RmAPILInterface: skipping (%s)"
+ , If->interfacename);
+ }
+ return FALSE;
+ }
+ PILValidateInterface(ifname, If, Iftype);
+ PILValidateInterfaceType(NULL, Iftype, NULL);
+
+ /*
+ * This function is usually but not always called by
+ * g_hash_table_foreach_remove()
+ */
+
+ PILunregister_interface(If);
+ PILValidateInterface(ifname, If, Iftype);
+ PILValidateInterfaceType(NULL, Iftype, NULL);
+ DELETE(ifname);
+ DelPILInterface(If);
+ return TRUE;
+}
+static PIL_rc
+RemoveAPILInterface(PILInterface* pif)
+{
+ PILInterfaceType* Iftype = pif->interfacetype;
+ gpointer key;
+
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "RemoveAPILInterface(0x%lx/%s)"
+ , (unsigned long)pif, pif->interfacename);
+ }
+ if (g_hash_table_lookup_extended(Iftype->interfaces
+ , pif->interfacename, &key, (void*)&pif)) {
+ g_assert(IS_PILINTERFACE(pif));
+ g_hash_table_remove(Iftype->interfaces, key);
+ RmAPILInterface(key, pif, NULL);
+ }else{
+ g_assert_not_reached();
+ }
+
+ if (g_hash_table_size(Iftype->interfaces) == 0) {
+ /* The generic plugin handler doesn't want us to
+ * delete it's types...
+ */
+ if (Iftype->ifmgr_ref->refcnt <= 1) {
+ RemoveAPILInterfaceType(Iftype, NULL);
+ }
+ }
+ return PIL_OK;
+}
+
+
+/* Register a Interface Interface (Interface manager) */
+static PIL_rc
+ifmgr_register_interface(PILInterface* intf
+, void** imports)
+{
+ PILInterfaceType* ift = intf->interfacetype;
+ PILInterfaceUniv* ifuniv = ift->universe;
+ PILInterfaceOps* ifops; /* Ops vector for InterfaceManager */
+
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG
+ , "Registering Implementation manager for"
+ " Interface type '%s'"
+ , intf->interfacename);
+ }
+
+ ifops = intf->exports;
+ if (ifops->RegisterInterface == NULL
+ || ifops->UnRegisterInterface == NULL) {
+ PILLog(PIL_DEBUG, "ifmgr_register_interface(%s)"
+ ": NULL exported function pointer"
+ , intf->interfacename);
+ return PIL_INVAL;
+ }
+
+ *imports = &IFManagerImports;
+
+ if(g_hash_table_lookup(ifuniv->iftypes, intf->interfacename) == NULL){
+ /* It registers itself into ifuniv automatically */
+ NewPILInterfaceType(ifuniv,intf->interfacename, &IfExports
+ , NULL);
+ }
+ return PIL_OK;
+}
+
+static gboolean
+RemoveAllClients(PILInterface*interface, void * managerif)
+{
+ /*
+ * Careful! We can't remove ourselves this way...
+ * This gets taken care of as a special case in DelPILInterfaceUniv...
+ */
+ if (managerif == interface) {
+ return FALSE;
+ }
+ PILunregister_interface(interface);
+ return TRUE;
+}
+
+/* Unconditionally unregister a interface manager (InterfaceMgr Interface) */
+static PIL_rc
+ifmgr_unregister_interface(PILInterface* interface)
+{
+ /*
+ * We need to unregister every interface we manage
+ */
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "ifmgr_unregister_interface(%s)"
+ , interface->interfacename);
+ }
+
+ IfForEachClientRemove(interface, RemoveAllClients, interface);
+ return PIL_OK;
+}
+
+/* Called to close the Interface manager for type Interface */
+static PIL_rc
+close_ifmgr_interface(PILInterface* us, void* ud_interface)
+{
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "close_ifmgr_interface(%s)"
+ , us->interfacename);
+ }
+ /* Nothing much to do */
+ return PIL_OK;
+}
+
+/* Return the reference count for this interface */
+static int
+IfRefCount(PILInterface * eifinfo)
+{
+ return eifinfo->refcnt;
+}
+
+/* Modify the reference count for this interface */
+static int
+IfIncrRefCount(PILInterface*eifinfo, int plusminus)
+{
+ if(DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "IfIncrRefCount(%d + %d )"
+ , eifinfo->refcnt, plusminus);
+ }
+ eifinfo->refcnt += plusminus;
+ if (eifinfo->refcnt <= 0) {
+ eifinfo->refcnt = 0;
+ /* Unregister this interface. */
+ RemoveAPILInterface(eifinfo);
+ return 0;
+ }
+ return eifinfo->refcnt;
+}
+
+#if 0
+static int
+PluginRefCount(PILPlugin * pi)
+{
+ return pi->refcnt;
+}
+#endif
+
+static int
+PluginIncrRefCount(PILPlugin*pi, int plusminus)
+{
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "PluginIncrRefCount(%d + %d )"
+ , pi->refcnt, plusminus);
+ }
+ pi->refcnt += plusminus;
+ if (pi->refcnt <= 0) {
+ pi->refcnt = 0;
+ RemoveAPILPlugin(pi);
+ return 0;
+ }
+ return pi->refcnt;
+}
+
+static PILInterface*
+FindIF(PILPluginUniv* universe, const char *iftype, const char * ifname)
+{
+ PILInterfaceUniv* puniv;
+ PILInterfaceType* ptype;
+
+ if (universe == NULL || (puniv = universe->ifuniv) == NULL
+ || (ptype=g_hash_table_lookup(puniv->iftypes, iftype))==NULL){
+ return NULL;
+ }
+ return g_hash_table_lookup(ptype->interfaces, ifname);
+}
+
+PIL_rc
+PILIncrIFRefCount(PILPluginUniv* mu
+, const char * interfacetype
+, const char * interfacename
+, int plusminus)
+{
+ PILInterface* intf = FindIF(mu, interfacetype, interfacename);
+
+ if (intf) {
+ g_assert(IS_PILINTERFACE(intf));
+ IfIncrRefCount(intf, plusminus);
+ return PIL_OK;
+ }
+ return PIL_NOPLUGIN;
+}
+
+int
+PILGetIFRefCount(PILPluginUniv* mu
+, const char * interfacetype
+, const char * interfacename)
+{
+ PILInterface* intf = FindIF(mu, interfacetype, interfacename);
+
+ return IfRefCount(intf);
+}
+
+static void
+IfForceUnregister(PILInterface *id)
+{
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "IfForceUnRegister(%s)"
+ , id->interfacename);
+ }
+ RemoveAPILInterface(id);
+}
+
+struct f_e_c_helper {
+ gboolean(*fun)(PILInterface* clientif, void * passalong);
+ void* passalong;
+};
+
+static gboolean IfForEachClientHelper(gpointer key
+, gpointer iftype, gpointer helper_v);
+
+static gboolean
+IfForEachClientHelper(gpointer unused, gpointer iftype, gpointer v)
+{
+ struct f_e_c_helper* s = (struct f_e_c_helper*)v;
+
+ g_assert(IS_PILINTERFACE((PILInterface*)iftype));
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "IfForEachClientHelper(%s)"
+ , ((PILInterface*)iftype)->interfacename);
+ }
+
+ return s->fun((PILInterface*)iftype, s->passalong);
+}
+
+
+static void
+IfForEachClientRemove
+( PILInterface* mgrif
+, gboolean(*f)(PILInterface* clientif, void * passalong)
+, void* passalong /* usually PILInterface* */
+)
+{
+ PILInterfaceType* mgrt;
+ PILInterfaceUniv* u;
+ const char * ifname;
+ PILInterfaceType* clientt;
+
+ struct f_e_c_helper h = {f, passalong};
+
+
+ if (mgrif == NULL || (mgrt = mgrif->interfacetype) == NULL
+ || (u = mgrt->universe) == NULL
+ || (ifname = mgrif->interfacename) == NULL) {
+ PILLog(PIL_WARN, "bad parameters to IfForEachClientRemove");
+ return;
+ }
+
+ if ((clientt = g_hash_table_lookup(u->iftypes, ifname)) == NULL) {
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG
+ , "Interface manager [%s/%s] has no clients"
+ , PI_IFMANAGER, ifname);
+ }
+ return;
+ };
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "IfForEachClientRemove(%s:%s)"
+ , mgrt->typename, clientt->typename);
+ }
+ if (clientt->ifmgr_ref != mgrif) {
+ PILLog(PIL_WARN, "Bad ifmgr_ref ptr in PILInterfaceType");
+ return;
+ }
+
+ g_hash_table_foreach_remove(clientt->interfaces, IfForEachClientHelper
+ , &h);
+}
+
+static PIL_rc
+PILregister_plugin(PILPlugin* piinfo, const PILPluginOps* commonops)
+{
+ piinfo->pluginops = commonops;
+
+ return PIL_OK;
+}
+
+static PIL_rc
+PILunregister_plugin(PILPlugin* piinfo)
+{
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "PILunregister_plugin(%s)"
+ , piinfo->plugin_name);
+ }
+ RemoveAPILPlugin(piinfo);
+ return PIL_OK;
+}
+
+/* General logging function (not really UPPILS-specific) */
+static void
+PILLog(PILLogLevel priority, const char * format, ...)
+{
+ va_list args;
+ GLogLevelFlags flags;
+
+ switch(priority) {
+ case PIL_FATAL: flags = G_LOG_LEVEL_ERROR;
+ break;
+ case PIL_CRIT: flags = G_LOG_LEVEL_CRITICAL;
+ break;
+
+ default: /* FALL THROUGH... */
+ case PIL_WARN: flags = G_LOG_LEVEL_WARNING;
+ break;
+
+ case PIL_INFO: flags = G_LOG_LEVEL_INFO;
+ break;
+ case PIL_DEBUG: flags = G_LOG_LEVEL_DEBUG;
+ break;
+ };
+ va_start (args, format);
+ g_logv (G_LOG_DOMAIN, flags, format, args);
+ va_end (args);
+}
+
+static const char * PIL_strerrmsgs [] =
+{ "Success"
+, "Invalid Parameters"
+, "Bad plugin/interface type"
+, "Duplicate entry (plugin/interface name/type)"
+, "Oops happens"
+, "No such plugin/interface/interface type"
+};
+
+const char *
+PIL_strerror(PIL_rc rc)
+{
+ int irc = (int) rc;
+ static char buf[128];
+
+ if (irc < 0 || irc >= DIMOF(PIL_strerrmsgs)) {
+ snprintf(buf, sizeof(buf), "return code %d (?)", irc);
+ return buf;
+ }
+ return PIL_strerrmsgs[irc];
+}
+
+/*
+ * Returns the PATHname of the file containing the requested plugin
+ * This file handles PATH-like semantics from the rootdirlist.
+ * It is also might be the right place to put alias handing in the future...
+ */
+static char *
+PILPluginPath(PILPluginUniv* universe, const char * plugintype
+, const char * pluginname)
+{
+ char * PluginPath = NULL;
+ char ** spath_component;
+
+ for (spath_component = universe->rootdirlist; *spath_component
+ ; ++ spath_component) {
+
+ if (PluginPath) {
+ g_free(PluginPath); PluginPath=NULL;
+ }
+
+ PluginPath = g_strdup_printf("%s%s%s%s%s%s"
+ , *spath_component
+ , G_DIR_SEPARATOR_S
+ , plugintype
+ , G_DIR_SEPARATOR_S
+ , pluginname
+ , PLUGINSUFFIX);
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG
+ , "PILS: Looking for %s/%s => [%s]"
+ , plugintype, pluginname, PluginPath);
+ }
+
+ if (PluginExists(PluginPath) == PIL_OK) {
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG
+ , "Plugin path for %s/%s => [%s]"
+ , plugintype, pluginname, PluginPath);
+ }
+ return PluginPath;
+ }
+ /* FIXME: Put alias file processing here... */
+ }
+
+ /* Can't find 'em all... */
+ return PluginPath;
+}
+
+static PIL_rc
+PluginExists(const char * PluginPath)
+{
+ /* Make sure we can read and execute the plugin file */
+ /* This test is nice, because dlopen reasons aren't return codes */
+
+ if (access(PluginPath, R_OK) != 0) {
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "Plugin file %s does not exist"
+ , PluginPath);
+ }
+ return PIL_NOPLUGIN;
+ }
+ return PIL_OK;
+}
+
+/* Return PIL_OK if the given plugin exists */
+PIL_rc
+PILPluginExists(PILPluginUniv* piuniv
+, const char * plugintype
+, const char * pluginname)
+{
+ PIL_rc rc;
+ char * path = PILPluginPath(piuniv, plugintype, pluginname);
+
+ if (path == NULL) {
+ return PIL_INVAL;
+ }
+ rc = PluginExists(path);
+ DELETE(path);
+ return rc;
+}
+
+/*
+ * PILLoadPlugin() - loads a plugin into memory and calls the
+ * initial() entry point in the plugin.
+ *
+ *
+ * Method:
+ *
+ * Construct file name of plugin.
+ * See if plugin exists. If not, fail with PIL_NOPLUGIN.
+ *
+ * Search Universe for plugin type
+ * If found, search plugin type for pluginname
+ * if found, fail with PIL_EXIST.
+ * Otherwise,
+ * Create new Plugin type structure
+ * Use lt_dlopen() on plugin to get lt_dlhandle for it.
+ *
+ * Construct the symbol name of the initialization function.
+ *
+ * Use lt_dlsym() to find the pointer to the init function.
+ *
+ * Call the initialization function.
+ */
+PIL_rc
+PILLoadPlugin(PILPluginUniv* universe, const char * plugintype
+, const char * pluginname
+, void* plugin_user_data)
+{
+ PIL_rc rc;
+ char * PluginPath;
+ char * PluginSym;
+ PILPluginType* pitype;
+ PILPlugin* piinfo;
+ lt_dlhandle dlhand;
+ PILPluginInitFun initfun;
+
+ PluginPath = PILPluginPath(universe, plugintype, pluginname);
+
+ if ((rc=PluginExists(PluginPath)) != PIL_OK) {
+ DELETE(PluginPath);
+ return rc;
+ }
+
+ if((pitype=g_hash_table_lookup(universe->PluginTypes, plugintype))
+ != NULL) {
+ if ((piinfo = g_hash_table_lookup
+ ( pitype->Plugins, pluginname)) != NULL) {
+
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "Plugin %s already loaded"
+ , PluginPath);
+ }
+ DELETE(PluginPath);
+ return PIL_EXIST;
+ }
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "PluginType %s already present"
+ , plugintype);
+ }
+ }else{
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "Creating PluginType for %s"
+ , plugintype);
+ }
+ /* Create a new PILPluginType object */
+ pitype = NewPILPluginType(universe, plugintype);
+ }
+
+ g_assert(pitype != NULL);
+
+ /*
+ * At this point, we have a PILPluginType object and our
+ * plugin name is not listed in it.
+ */
+
+ dlhand = lt_dlopen(PluginPath);
+
+ if (!dlhand) {
+ PILLog(PIL_WARN
+ , "lt_dlopen() failure on plugin %s/%s [%s]."
+ " Reason: [%s]"
+ , plugintype, pluginname
+ , PluginPath
+ , lt_dlerror());
+ DELETE(PluginPath);
+ return PIL_NOPLUGIN;
+ }
+ DELETE(PluginPath);
+ /* Construct the magic init function symbol name */
+ PluginSym = g_strdup_printf(PIL_FUNC_FMT
+ , plugintype, pluginname);
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "Plugin %s/%s init function: %s"
+ , plugintype, pluginname
+ , PluginSym);
+ }
+
+ initfun = lt_dlsym(dlhand, PluginSym);
+
+ if (initfun == NULL) {
+ PILLog(PIL_WARN
+ , "Plugin %s/%s init function (%s) not found"
+ , plugintype, pluginname, PluginSym);
+ DELETE(PluginSym);
+ lt_dlclose(dlhand); dlhand=NULL;
+ DelPILPluginType(pitype);
+ return PIL_NOPLUGIN;
+ }
+ DELETE(PluginSym);
+ /*
+ * Construct the new PILPlugin object
+ */
+ piinfo = NewPILPlugin(pitype, pluginname, dlhand, initfun);
+ g_assert(piinfo != NULL);
+ g_hash_table_insert(pitype->Plugins, g_strdup(piinfo->plugin_name), piinfo);
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "Plugin %s/%s loaded and constructed."
+ , plugintype, pluginname);
+ }
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "Calling init function in plugin %s/%s."
+ , plugintype, pluginname);
+ }
+ /* Save away the user_data for later */
+ piinfo->ud_plugin = plugin_user_data;
+ /* initfun is allowed to change ud_plugin if they want */
+ initfun(piinfo, universe->imports, plugin_user_data);
+
+ return PIL_OK;
+}/*PILLoadPlugin*/
+
+
+
+#define REPORTERR(msg) PILLog(PIL_CRIT, "%s", msg)
+
+/*
+ * Register an interface.
+ *
+ * This function is exported to plugins for their use.
+ */
+static PIL_rc
+PILRegisterInterface(PILPlugin* piinfo
+, const char * interfacetype /* Type of interface */
+, const char * interfacename /* Name of interface */
+, void* Ops /* Info (functions) exported
+ by this interface */
+, PILInterfaceFun close_func /* Close function for interface */
+, PILInterface** interfaceid /* Interface id (OP) */
+, void** Imports /* Functions imported by
+ this interface (OP) */
+, void* ud_interface /* Optional user_data */
+)
+{
+ PILPluginUniv* piuniv; /* Universe this plugin is in */
+ PILPluginType* pitype; /* Type of this plugin */
+ PILInterfaceUniv* ifuniv; /* Universe this interface is in */
+ PILInterfaceType*iftype; /* Type of this interface */
+ PILInterface* ifinfo; /* Info about this Interface */
+
+ PILInterfaceType*ifmgrtype; /* PILInterfaceType for PI_IFMANAGER */
+ PILInterface* ifmgrinfo; /* Interf info for "interfacetype" */
+ const PILInterfaceOps* ifops; /* Ops vector for InterfaceManager */
+ /* of type "interfacetype" */
+ PIL_rc rc;
+
+ if ( piinfo == NULL
+ || (pitype = piinfo->plugintype) == NULL
+ || (piuniv = pitype->piuniv) == NULL
+ || (ifuniv = piuniv->ifuniv) == NULL
+ || ifuniv->iftypes == NULL
+ ) {
+ REPORTERR("bad parameters to PILRegisterInterface");
+ return PIL_INVAL;
+ }
+
+ /* Now we have lots of info, but not quite enough... */
+
+ if ((iftype = g_hash_table_lookup(ifuniv->iftypes, interfacetype))
+ == NULL) {
+
+ /* Try to autoload the needed interface handler */
+ rc = PILLoadPlugin(piuniv, PI_IFMANAGER, interfacetype, NULL);
+
+ /* See if the interface handler loaded like we expect */
+ if ((iftype = g_hash_table_lookup(ifuniv->iftypes
+ , interfacetype)) == NULL) {
+ return PIL_BADTYPE;
+ }
+ }
+ if ((ifinfo = g_hash_table_lookup(iftype->interfaces, interfacename))
+ != NULL) {
+ g_warning("Attempt to register duplicate interface: %s/%s"
+ , interfacetype, interfacename);
+ return PIL_EXIST;
+ }
+ /*
+ * OK... Now we know it is valid, and isn't registered...
+ * Let's locate the InterfaceManager registrar for this type
+ */
+ if ((ifmgrtype = g_hash_table_lookup(ifuniv->iftypes, PI_IFMANAGER))
+ == NULL) {
+ REPORTERR("No " PI_IFMANAGER " type!");
+ return PIL_OOPS;
+ }
+ if ((ifmgrinfo = g_hash_table_lookup(ifmgrtype->interfaces
+ , interfacetype)) == NULL) {
+ PILLog(PIL_CRIT
+ , "No interface manager for given type (%s) !"
+ , interfacetype);
+ return PIL_BADTYPE;
+ }
+
+ ifops = ifmgrinfo->exports;
+
+ /* Now we have all the information anyone could possibly want ;-) */
+
+ ifinfo = NewPILInterface(iftype, interfacename, Ops
+ , close_func, ud_interface, piinfo);
+
+ g_assert(ifmgrinfo == ifinfo->ifmanager);
+ *interfaceid = ifinfo;
+
+ /* Call the registration function for our interface type */
+ rc = ifops->RegisterInterface(ifinfo, Imports);
+
+
+ /* Increment reference count of interface manager */
+ IfIncrRefCount(ifmgrinfo, 1);
+
+ /* Increment the ref count of the plugin that loaded us */
+ PluginIncrRefCount(piinfo, 1);
+
+ if (rc != PIL_OK) {
+ RemoveAPILInterface(ifinfo);
+ }
+ return rc;
+}
+
+/*
+ * Method:
+ *
+ * Verify interface is valid.
+ *
+ * Call interface close function.
+ *
+ * Call interface manager unregister function
+ *
+ * Call RmAPILInterface to remove from InterfaceType table, and
+ * free interface object.
+ *
+ */
+
+static PIL_rc
+PILunregister_interface(PILInterface* id)
+{
+ PILInterfaceType* t;
+ PILInterfaceUniv* u;
+ PIL_rc rc;
+ PILInterface* ifmgr_info; /* Pointer to our interface handler */
+ const PILInterfaceOps* exports; /* InterfaceManager operations for
+ * the type of interface we are
+ */
+
+ if ( id == NULL
+ || (t = id->interfacetype) == NULL
+ || (u = t->universe) == NULL
+ || id->interfacename == NULL) {
+ PILLog(PIL_WARN, "PILunregister_interface: bad interfaceid");
+ return PIL_INVAL;
+ }
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "PILunregister_interface(%s/%s)"
+ , t->typename, id->interfacename);
+ }
+ PILValidateInterface(NULL, id, t);
+ PILValidateInterfaceType(NULL, t, u);
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "Calling InterfaceClose on %s/%s"
+ , t->typename, id->interfacename);
+ }
+
+ /* Call the close function supplied by the interface */
+
+ if ((id->if_close != NULL)
+ && ((rc=id->if_close(id, id->ud_interface)) != PIL_OK)) {
+ PILLog(PIL_WARN, "InterfaceClose on %s/%s returned %s"
+ , t->typename, id->interfacename
+ , PIL_strerror(rc));
+ } else {
+ rc = PIL_OK;
+ }
+
+ /* Find the InterfaceManager that manages us */
+ ifmgr_info = t->ifmgr_ref;
+
+ g_assert(ifmgr_info != NULL);
+
+ /* Find the exported functions from that IFIF */
+ exports = ifmgr_info->exports;
+
+ g_assert(exports != NULL && exports->UnRegisterInterface != NULL);
+
+ /* Call the interface manager unregister function */
+ exports->UnRegisterInterface(id);
+
+ /* Decrement reference count of interface manager */
+ IfIncrRefCount(ifmgr_info, -1);
+ /* This may make ifmgr_info invalid */
+ ifmgr_info = NULL;
+
+ /* Decrement the reference count of the plugin that loaded us */
+ PluginIncrRefCount(id->loadingpi, -1);
+
+ return rc;
+}
+
+static PILInterfaceUniv*
+NewPILInterfaceUniv(PILPluginUniv* piuniv)
+{
+ PILInterfaceUniv* ret = NEW(PILInterfaceUniv);
+ static int ltinityet = 0;
+
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "NewPILInterfaceUniv(0x%lx)"
+ , (unsigned long)ret);
+ }
+ if (!ltinityet) {
+ ltinityet=1;
+ lt_dlinit();
+ }
+ STATNEW(interfaceuniv);
+ ret->MagicNum = PIL_MAGIC_INTERFACEUNIV;
+ /* Make the two universes point at each other */
+ ret->piuniv = piuniv;
+ piuniv->ifuniv = ret;
+
+ ret->iftypes = g_hash_table_new(g_str_hash, g_str_equal);
+
+ InterfaceManager_plugin_init(piuniv);
+ return ret;
+}
+
+static void
+DelPILInterfaceUniv(PILInterfaceUniv* ifuniv)
+{
+ PILInterfaceType* ifmgrtype;
+ g_assert(ifuniv!= NULL && ifuniv->iftypes != NULL);
+ PILValidateInterfaceUniv(NULL, ifuniv, NULL);
+
+ STATFREE(interfaceuniv);
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "DelPILInterfaceUniv(0x%lx)"
+ , (unsigned long) ifuniv);
+ }
+ g_hash_table_foreach_remove(ifuniv->iftypes, RmAPILInterfaceType, NULL);
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "DelPILInterfaceUniv: final cleanup");
+ }
+ ifmgrtype = g_hash_table_lookup(ifuniv->iftypes, PI_IFMANAGER);
+ RemoveAPILInterfaceType(ifmgrtype, ifmgrtype);
+ /*
+ * FIXME! need to delete the interface for PI_IFMANAGER last
+ * Right now, it seems to happen last, but I think that's
+ * coincidence...
+ */
+ g_hash_table_destroy(ifuniv->iftypes);
+ ZAP(ifuniv);
+ DELETE(ifuniv);
+}
+
+/*
+ * These RmA* functions primarily called from hash_table_foreach,
+ * so they have gpointer arguments. This *not necessarily* clause
+ * is why they do the g_hash_table_lookup_extended call instead of
+ * just deleting the key. When called from outside, the key
+ * may not be pointing at the key to actually free, but a copy
+ * of the key.
+ */
+static gboolean /* IsA GHFunc: required for g_hash_table_foreach_remove() */
+RmAPILInterfaceType
+( gpointer typename /* Name of this interface type */
+, gpointer iftype /* PILInterfaceType* */
+, gpointer notused
+)
+{
+ PILInterfaceType* Iftype = iftype;
+ PILInterfaceUniv* Ifuniv = Iftype->universe;
+
+ /*
+ * We are not always called by g_hash_table_foreach_remove()
+ */
+
+ g_assert(IS_PILINTERFACETYPE(Iftype));
+ PILValidateInterfaceUniv(NULL, Ifuniv, NULL);
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "RmAPILInterfaceType(%s)"
+ , (char*)typename);
+ }
+ if (iftype != notused
+ && strcmp(Iftype->typename, PI_IFMANAGER) == 0) {
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "RmAPILInterfaceType: skipping (%s)"
+ , (char*)typename);
+ }
+ return FALSE;
+ }
+
+ DelPILInterfaceType(iftype);
+ DELETE(typename);
+
+ return TRUE;
+}
+
+static void
+RemoveAPILInterfaceType(PILInterfaceType*Iftype, PILInterfaceType* t2)
+{
+ PILInterfaceUniv* Ifuniv = Iftype->universe;
+ gpointer key;
+
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "RemoveAPILInterfaceType(%s)"
+ , Iftype->typename);
+ }
+ if (t2 != Iftype
+ && strcmp(Iftype->typename, PI_IFMANAGER) == 0) {
+ PILLog(PIL_DEBUG, "RemoveAPILInterfaceType: skipping (%s)"
+ , Iftype->typename);
+ return;
+ }
+ if (g_hash_table_lookup_extended(Ifuniv->iftypes
+ , Iftype->typename, &key, (gpointer)&Iftype)) {
+
+ g_hash_table_remove(Ifuniv->iftypes, key);
+ RmAPILInterfaceType(key, Iftype, t2);
+ }else{
+ g_assert_not_reached();
+ }
+}
+
+/*
+ * We need to write more functions: These include...
+ *
+ * Plugin functions:
+ *
+ * PILPluginPath() - returns path name for a given plugin
+ *
+ * PILPluginTypeList() - returns list of plugins of a given type
+ *
+ */
+static void free_dirlist(struct dirent** dlist, int n);
+
+static int qsort_string_cmp(const void *a, const void *b);
+
+
+static void
+free_dirlist(struct dirent** dlist, int n)
+{
+ int j;
+ for (j=0; j < n; ++j) {
+ if (dlist[j]) {
+ free(dlist[j]);
+ dlist[j] = NULL;
+ }
+ }
+ free(dlist);
+}
+
+static int
+qsort_string_cmp(const void *a, const void *b)
+{
+ return(strcmp(*(const char * const *)a, *(const char * const *)b));
+}
+
+#define FREE_DIRLIST(dlist, n) {free_dirlist(dlist, n); dlist = NULL;}
+
+static int
+so_select (const struct dirent *dire)
+{
+
+ const char obj_end [] = PLUGINSUFFIX;
+ const char *end = &dire->d_name[strlen(dire->d_name)
+ - (STRLEN_CONST(obj_end))];
+
+
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "In so_select: %s.", dire->d_name);
+ }
+ if (end < dire->d_name) {
+ return 0;
+ }
+ if (strcmp(end, obj_end) == 0) {
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "FILE %s looks like a plugin name."
+ , dire->d_name);
+ }
+ return 1;
+ }
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG
+ , "FILE %s Doesn't look like a plugin name [%s] "
+ "%zd %zd %s."
+ , dire->d_name, end
+ , sizeof(obj_end), strlen(dire->d_name)
+ , &dire->d_name[strlen(dire->d_name)
+ - (STRLEN_CONST(obj_end))]);
+ }
+
+ return 0;
+}
+
+/* Return (sorted) list of available plugin names */
+static char**
+PILPluginTypeListPlugins(PILPluginType* pitype
+, int * picount /* Can be NULL ... */)
+{
+ const char * piclass = pitype->plugintype;
+ unsigned plugincount = 0;
+ char ** result = NULL;
+ int initoff = 0;
+ char ** pelem;
+
+ /* Return all the plugins in all the directories in the PATH */
+
+ for (pelem=pitype->piuniv->rootdirlist; *pelem; ++pelem) {
+ int j;
+ GString* path;
+ int dircount;
+ struct dirent** files;
+
+
+ path = g_string_new(*pelem);
+ g_assert(piclass != NULL);
+ if (piclass) {
+ if (g_string_append_c(path, G_DIR_SEPARATOR) == NULL
+ || g_string_append(path, piclass) == NULL) {
+ g_string_free(path, 1); path = NULL;
+ return(NULL);
+ }
+ }
+
+ files = NULL;
+ errno = 0;
+ dircount = scandir(path->str, &files
+ , SCANSEL_CAST so_select, NULL);
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "PILS: Examining directory [%s]"
+ ": [%d] files matching [%s] suffix found."
+ , path->str, dircount, PLUGINSUFFIX);
+ }
+ g_string_free(path, 1); path=NULL;
+
+ if (dircount <= 0) {
+ if (files != NULL) {
+ FREE_DIRLIST(files, dircount);
+ files = NULL;
+ }
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG
+ , "PILS: skipping empty directory"
+ " in PILPluginTypeListPlugins()");
+ }
+ continue;
+ }
+
+ initoff = plugincount;
+ plugincount += dircount;
+ if (result == NULL) {
+ result = (char **) g_malloc((plugincount+1)*sizeof(char *));
+ }else{
+ result = (char **) g_realloc(result
+ , (plugincount+1)*sizeof(char *));
+ }
+
+ for (j=0; j < dircount; ++j) {
+ char* s;
+ unsigned slen = strlen(files[j]->d_name)
+ - STRLEN_CONST(PLUGINSUFFIX);
+
+ s = g_malloc(slen+1);
+ strncpy(s, files[j]->d_name, slen);
+ s[slen] = EOS;
+ result[initoff+j] = s;
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "PILS: plugin [%s] found"
+ , s);
+ }
+ }
+ FREE_DIRLIST(files, dircount);
+ files = NULL;
+ }
+
+ if (picount != NULL) {
+ *picount = plugincount;
+ }
+ if (result) {
+ result[plugincount] = NULL;
+ /* Return them in sorted order... */
+ qsort(result, plugincount, sizeof(char *), qsort_string_cmp);
+ }else{
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "PILS: NULL return"
+ " from PILPluginTypeListPlugins()");
+ }
+ }
+
+
+ return result;
+}
+/* Return (sorted) list of available plugin names */
+char**
+PILListPlugins(PILPluginUniv* u, const char * pitype
+, int * picount /* Can be NULL ... */)
+{
+ PILPluginType* t;
+ if ((t = g_hash_table_lookup(u->PluginTypes, pitype)) == NULL) {
+ if (picount) {
+ *picount = 0;
+ }
+ t = NewPILPluginType(u, pitype);
+ if (!t) {
+ return NULL;
+ }
+ }
+ return PILPluginTypeListPlugins(t, picount);
+}
+
+void
+PILFreePluginList(char ** pluginlist)
+{
+ char ** ml = pluginlist;
+
+ if (!ml) {
+ return;
+ }
+
+ while (*ml != NULL) {
+ DELETE(*ml);
+ }
+ DELETE(pluginlist);
+}
+
+
+static void
+PILValidatePlugin(gpointer key, gpointer plugin, gpointer pitype)
+{
+ const char * Key = key;
+ const PILPlugin * Plugin = plugin;
+
+ g_assert(IS_PILPLUGIN(Plugin));
+
+ g_assert(Key == NULL || strcmp(Key, Plugin->plugin_name) == 0);
+
+ g_assert (Plugin->refcnt >= 0 );
+
+ /* g_assert (Plugin->pluginops != NULL ); */
+ g_assert (strcmp(Key, PI_IFMANAGER) == 0 || Plugin->dlinitfun != NULL );
+ g_assert (strcmp(Plugin->plugin_name, PI_IFMANAGER) == 0
+ || Plugin->dlhandle != NULL);
+ g_assert(Plugin->plugintype != NULL);
+ g_assert(IS_PILPLUGINTYPE(Plugin->plugintype));
+ g_assert(pitype == NULL || pitype == Plugin->plugintype);
+}
+
+static void
+PILValidatePluginType(gpointer key, gpointer pitype, gpointer piuniv)
+{
+ char * Key = key;
+ PILPluginType * Pitype = pitype;
+ PILPluginUniv * Muniv = piuniv;
+
+ g_assert(IS_PILPLUGINTYPE(Pitype));
+ g_assert(Muniv == NULL || IS_PILPLUGINUNIV(Muniv));
+ g_assert(Key == NULL || strcmp(Key, Pitype->plugintype) == 0);
+ g_assert(IS_PILPLUGINUNIV(Pitype->piuniv));
+ g_assert(piuniv == NULL || piuniv == Pitype->piuniv);
+ g_assert(Pitype->Plugins != NULL);
+ g_hash_table_foreach(Pitype->Plugins, PILValidatePlugin, Pitype);
+}
+static void
+PILValidatePluginUniv(gpointer key, gpointer piuniv, gpointer dummy)
+{
+ PILPluginUniv * Muniv = piuniv;
+
+ g_assert(IS_PILPLUGINUNIV(Muniv));
+ g_assert(Muniv->rootdirlist != NULL);
+ g_assert(Muniv->imports != NULL);
+ g_hash_table_foreach(Muniv->PluginTypes, PILValidatePluginType, piuniv);
+ PILValidateInterfaceUniv(NULL, Muniv->ifuniv, piuniv);
+}
+static void
+PILValidateInterface(gpointer key, gpointer interface, gpointer iftype)
+{
+ char * Key = key;
+ PILInterface* Interface = interface;
+ g_assert(IS_PILINTERFACE(Interface));
+ g_assert(Key == NULL || strcmp(Key, Interface->interfacename) == 0);
+ g_assert(IS_PILINTERFACETYPE(Interface->interfacetype));
+ g_assert(iftype == NULL || iftype == Interface->interfacetype);
+ g_assert(Interface->ifmanager!= NULL);
+ g_assert(IS_PILINTERFACE(Interface->ifmanager));
+ g_assert(strcmp(Interface->interfacetype->typename
+ , Interface->ifmanager->interfacename)== 0);
+ g_assert(Interface->exports != NULL);
+}
+static void
+PILValidateInterfaceType(gpointer key, gpointer iftype, gpointer ifuniv)
+{
+ char * Key = key;
+ PILInterfaceType* Iftype = iftype;
+ g_assert(IS_PILINTERFACETYPE(Iftype));
+ g_assert(Key == NULL || strcmp(Key, Iftype->typename) == 0);
+ g_assert(ifuniv == NULL || Iftype->universe == ifuniv);
+ g_assert(Iftype->interfaces != NULL);
+ g_assert(Iftype->ifmgr_ref != NULL);
+ g_assert(IS_PILINTERFACE(Iftype->ifmgr_ref));
+ g_assert(Key == NULL || strcmp(Key, Iftype->ifmgr_ref->interfacename) == 0);
+
+ g_hash_table_foreach(Iftype->interfaces, PILValidateInterface, iftype);
+}
+static void
+PILValidateInterfaceUniv(gpointer key, gpointer ifuniv, gpointer piuniv)
+{
+ PILInterfaceUniv* Ifuniv = ifuniv;
+ PILPluginUniv* Pluginuniv = piuniv;
+ g_assert(IS_PILINTERFACEUNIV(Ifuniv));
+ g_assert(Pluginuniv == NULL || IS_PILPLUGINUNIV(Pluginuniv));
+ g_assert(piuniv == NULL || piuniv == Ifuniv->piuniv);
+ g_hash_table_foreach(Ifuniv->iftypes, PILValidateInterfaceType, ifuniv);
+}
+
+#define PRSTAT(type) { \
+ PILLog(PIL_INFO, "Plugin system objects (" #type "): " \
+ "\tnew %ld free \%ld current %ld" \
+ , PILstats.type.news \
+ , PILstats.type.frees \
+ , PILstats.type.news - PILstats.type.frees); \
+}
+void
+PILLogMemStats(void)
+{
+ PRSTAT(plugin);
+ PRSTAT(pitype);
+ PRSTAT(piuniv);
+ PRSTAT(interface);
+ PRSTAT(interfacetype);
+ PRSTAT(interfaceuniv);
+}
+
+/*
+ * Function for logging with the given logging function
+ * The reason why it's here is so we can get printf arg checking
+ * You can't get that when you call a function pointer directly.
+ */
+void
+PILCallLog(PILLogFun logfun, PILLogLevel priority, const char * fmt, ...)
+{
+ va_list args;
+ char * str;
+ int err = errno;
+
+ va_start (args, fmt);
+ str = g_strdup_vprintf(fmt, args);
+ va_end (args);
+ logfun(priority, "%s", str);
+ g_free(str);
+ errno = err;
+}
diff --git a/lib/pils/test.c b/lib/pils/test.c
new file mode 100644
index 0000000..c2cdb26
--- /dev/null
+++ b/lib/pils/test.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2001 Alan Robertson <alanr@unix.sh>
+ * This software licensed under the GNU LGPL.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+/*
+ * Sample Interface manager.
+ */
+#define PIL_PLUGINTYPE test
+#define PIL_PLUGINTYPENAME "test"
+#define PIL_PLUGIN test
+#define PIL_PLUGINNAME "test"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+
+/* We are a interface manager... */
+#define ENABLE_PLUGIN_MANAGER_PRIVATE
+
+#include <pils/interface.h>
+
+PIL_PLUGIN_BOILERPLATE("1.0", DebugFlag, Ourclose)
+
+/*
+ * Places to store information gotten during registration.
+ */
+static const PILPluginImports* OurPIImports; /* Imported plugin funs */
+static PILPlugin* OurPlugin; /* Our plugin info */
+static PILInterfaceImports* OurIfImports; /* Interface imported funs */
+static PILInterface* OurIf; /* Pointer to interface info */
+
+static void
+Ourclose (PILPlugin* us)
+{
+}
+
+/*
+ * Our Interface Manager interfaces - exported to the universe!
+ *
+ * (or at least the interface management universe ;-).
+ *
+ */
+static PILInterfaceOps OurIfOps = {
+ /* FIXME -- put some in here !! */
+};
+
+PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, PILPluginImports* imports, void*);
+
+static PIL_rc
+IfClose(PILInterface*intf, void* ud_interface)
+{
+ OurPIImports->log(PIL_INFO, "In Ifclose (test plugin)");
+ return PIL_OK;
+}
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, PILPluginImports* imports, void *user_ptr)
+{
+ PIL_rc ret;
+ /*
+ * Force compiler to check our parameters...
+ */
+ PILPluginInitFun fun = &PIL_PLUGIN_INIT; (void)fun;
+
+
+ OurPIImports = imports;
+ OurPlugin = us;
+
+ imports->log(PIL_INFO, "Plugin %s: user_ptr = %lx"
+ , PIL_PLUGINNAME, (unsigned long)user_ptr);
+
+ imports->log(PIL_INFO, "Registering ourselves as a plugin");
+
+ /* Register as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ imports->log(PIL_INFO, "Registering our interfaces");
+
+ /* Register our interfaces */
+ ret = imports->register_interface
+ ( us
+ , PIL_PLUGINTYPENAME
+ , PIL_PLUGINNAME
+ , &OurIfOps /* Exported interface operations */
+ , IfClose /* Interface Close function */
+ , &OurIf
+ , (void*)&OurIfImports
+ , NULL);
+ imports->log(PIL_INFO, "test init function: returning %d"
+ , ret);
+
+ return ret;
+}
diff --git a/lib/plugins/InterfaceMgr/HBauth.c b/lib/plugins/InterfaceMgr/HBauth.c
new file mode 100644
index 0000000..eae22cf
--- /dev/null
+++ b/lib/plugins/InterfaceMgr/HBauth.c
@@ -0,0 +1,171 @@
+/*
+ * Heartbeat authentication interface manager
+ *
+ * Copyright 2001 Alan Robertson <alanr@unix.sh>
+ * Licensed under the GNU Lesser General Public License
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+ */
+#define PIL_PLUGINTYPE InterfaceMgr
+#define PIL_PLUGIN HBauth
+
+#define PIN(f) #f
+#define PIN2(f) PIN(f)
+#define PIN3 PIN2(PIL_PLUGIN)
+#define PIT PIN2(PIL_PLUGINTYPE)
+
+/* We are a interface manager... */
+#define ENABLE_PLUGIN_MANAGER_PRIVATE
+
+#include <lha_internal.h>
+#include <pils/interface.h>
+#include <stdio.h>
+
+PIL_PLUGIN_BOILERPLATE2("1.0", AuthDebugFlag)
+
+
+/*
+ * Places to store information gotten during registration.
+ */
+static const PILPluginImports* AuthPIImports; /* Imported plugin fcns */
+static PILPlugin* AuthPlugin; /* Our plugin info */
+static PILInterfaceImports* AuthIfImports; /* Interface imported fcns */
+static PILInterface* AuthIf; /* Our Auth Interface info */
+
+/* Our exported auth interface management functions */
+static PIL_rc RegisterAuthIF(PILInterface* ifenv, void** imports);
+
+static PIL_rc UnregisterAuthIF(PILInterface*iifinfo);
+
+/*
+ * Our Interface Manager interfaces - exported to the universe!
+ *
+ * (or at least to the interface management universe ;-).
+ *
+ * These are the interfaces which are used to manage our
+ * client authentication interfaces
+ *
+ */
+static PILInterfaceOps AuthIfOps =
+{ RegisterAuthIF
+, UnregisterAuthIF
+};
+
+
+PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, PILPluginImports* imports, void*);
+
+/*
+ * Our user_ptr is presumed to point at a GHashTable for us
+ * to put plugin into when they show up, and drop from when
+ * they disappear.
+ *
+ * We need to think more carefully about the way for us to get
+ * the user_ptr from the global environment.
+ *
+ * We need to think more carefully about how interface registration
+ * etc. interact with plugin loading, reference counts, etc. and how
+ * the application that uses us (i.e., heartbeat) interacts with us.
+ *
+ * Issues include:
+ * - freeing all memory,
+ * - making sure things are all cleaned up correctly
+ * - Thread-safety?
+ *
+ * I think the global system should handle thread-safety.
+ */
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, PILPluginImports* imports, void *user_ptr)
+{
+ PIL_rc ret;
+ /*
+ * Force compiler to check our parameters...
+ */
+ PILPluginInitFun fun = &PIL_PLUGIN_INIT; (void)fun;
+
+
+ if (user_ptr == NULL) {
+ imports->log(PIL_CRIT
+ , "Interface Manager %s requires non-NULL "
+ " user pointer (to GHashTable) at initialization"
+ , PIN3);
+ return PIL_INVAL;
+ }
+
+ AuthPIImports = imports;
+ AuthPlugin = us;
+
+ /* Register as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+
+ /* Register our interfaces */
+ ret = imports->register_interface(us
+ , PIT
+ , PIN3
+ , &AuthIfOps
+ , NULL
+ , &AuthIf /* Our interface object pointer */
+ , (void**)&AuthIfImports /* Interface-imported functions */
+ , user_ptr);
+ return ret;
+}
+
+/*
+ * We get called for every authentication interface that gets registered.
+ *
+ * It's our job to make the authentication interface that's
+ * registering with us available to the system.
+ *
+ * We do that by adding it to a g_hash_table of authentication
+ * plugin. The rest of the system takes it from there...
+ * The key is the authentication method, and the data
+ * is a pointer to the functions the method exports.
+ * It's a piece of cake ;-)
+ */
+static PIL_rc
+RegisterAuthIF(PILInterface* intf, void** imports)
+{
+ GHashTable* authtbl = intf->ifmanager->ud_interface;
+
+ g_assert(authtbl != NULL);
+
+ /* Reference count should now be one */
+ g_assert(intf->refcnt == 1);
+ g_hash_table_insert(authtbl, intf->interfacename, intf->exports);
+
+ return PIL_OK;
+}
+
+/* Unregister a client authentication interface -
+ * We get called from the interface mgmt sys when someone requests that
+ * a interface be unregistered.
+ */
+static PIL_rc
+UnregisterAuthIF(PILInterface*intf)
+{
+ GHashTable* authtbl = intf->ifmanager->ud_interface;
+ g_assert(authtbl != NULL);
+
+ intf->refcnt--;
+ g_assert(intf->refcnt >= 0);
+ if (intf->refcnt <= 0) {
+ g_hash_table_remove(authtbl, intf->interfacetype);
+ }
+ return PIL_OK;
+}
+
diff --git a/lib/plugins/InterfaceMgr/Makefile.am b/lib/plugins/InterfaceMgr/Makefile.am
new file mode 100644
index 0000000..86b88d1
--- /dev/null
+++ b/lib/plugins/InterfaceMgr/Makefile.am
@@ -0,0 +1,33 @@
+#
+# InterfaceMgr: Interface manager plugins for Linux-HA
+#
+# Copyright (C) 2001 Alan Robertson
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \
+ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \
+ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl \
+ -I$(top_builddir)/lib/upmls -I$(top_srcdir)/lib/upmls
+
+## libraries
+
+plugindir = $(libdir)/@HB_PKG@/plugins/InterfaceMgr
+plugin_LTLIBRARIES = generic.la
+
+generic_la_SOURCES = generic.c
+generic_la_LDFLAGS = -export-dynamic -module -avoid-version
diff --git a/lib/plugins/InterfaceMgr/generic.c b/lib/plugins/InterfaceMgr/generic.c
new file mode 100644
index 0000000..6ddad3b
--- /dev/null
+++ b/lib/plugins/InterfaceMgr/generic.c
@@ -0,0 +1,452 @@
+/*
+ *
+ * Generic interface (implementation) manager
+ *
+ * Copyright 2001 Alan Robertson <alanr@unix.sh>
+ * Licensed under the GNU Lesser General Public License
+ *
+ * This manager will manage any number of types of interfaces.
+ *
+ * This means that when any implementations of our client interfaces register
+ * or unregister, it is us that makes their interfaces show up in the outside
+ * world.
+ *
+ * And, of course, we have to do this in a very generic way, since we have
+ * no idea about the client programs or interface types, or anything else.
+ *
+ * We do that by getting a parameter passed to us which tell us the names
+ * of the interface types we want to manage, and the address of a GHashTable
+ * for each type that we put the implementation in when they register
+ * themselves.
+ *
+ * So, each type of interface that we manage gets its own private
+ * GHashTable of the implementations of that type that are currently
+ * registered.
+ *
+ * For example, if we manage communication modules, their exported
+ * interfaces will be registered in a hash table. If we manage
+ * authentication modules, they'll have their (separate) hash table that
+ * their exported interfaces are registered in.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define PIL_PLUGINTYPE InterfaceMgr
+#define PIL_PLUGINTYPE_S "InterfaceMgr"
+#define PIL_PLUGIN generic
+#define PIL_PLUGIN_S "generic"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+
+/* We are an interface manager... */
+#define ENABLE_PLUGIN_MANAGER_PRIVATE
+#define ENABLE_PIL_DEFS_PRIVATE
+
+#include <lha_internal.h>
+#include <pils/generic.h>
+
+#include <stdio.h>
+
+PIL_PLUGIN_BOILERPLATE("1.0", GenDebugFlag, CloseGeneralPluginManager)
+
+/*
+ * Key is interface type, value is a PILGenericIfMgmtRqst.
+ * The key is g_strdup()ed, but the struct is not copied.
+ */
+
+static gboolean FreeAKey(gpointer key, gpointer value, gpointer data);
+
+/*
+ * Places to store information gotten during registration.
+ */
+static const PILPluginImports* GenPIImports; /* Imported plugin fcns */
+static PILPlugin* GenPlugin; /* Our plugin info */
+static PILInterfaceImports* GenIfImports; /* Interface imported fcns */
+
+/* Our exported generic interface management functions */
+static PIL_rc RegisterGenIF(PILInterface* ifenv, void** imports);
+
+static PIL_rc UnregisterGenIF(PILInterface*iifinfo);
+
+static PIL_rc CloseGenInterfaceManager(PILInterface*, void* info);
+
+/*
+ * Our Interface Manager interfaces - exported to the universe!
+ *
+ * (or at least to the interface management universe ;-).
+ *
+ * These are the interfaces which are used to manage our
+ * client implementations
+ */
+static PILInterfaceOps GenIfOps =
+{ RegisterGenIF
+, UnregisterGenIF
+};
+
+
+PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, PILPluginImports* imports, void*);
+
+/*
+ * Our user_ptr is presumed to point to NULL-terminated array of
+ * PILGenericIfMgmtRqst structs.
+ *
+ * These requests have pointers to GHashTables for us
+ * to put plugins into when they show up, and drop from when
+ * they disappear.
+ *
+ * Issues include:
+ * - freeing all memory,
+ * - making sure things are all cleaned up correctly
+ * - Thread-safety?
+ *
+ * IMHO the global system should handle thread-safety.
+ */
+static PIL_rc AddAnInterfaceType(PILPlugin*us, GHashTable* MasterTable, PILGenericIfMgmtRqst* req);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, PILPluginImports* imports, void *user_ptr)
+{
+ PIL_rc ret;
+ PILGenericIfMgmtRqst* user_req;
+ PILGenericIfMgmtRqst* curreq;
+ GHashTable* MasterTable = NULL;
+ /*
+ * Force the compiler to check our parameters...
+ */
+ PILPluginInitFun fun = &PIL_PLUGIN_INIT; (void)fun;
+
+
+ GenPIImports = imports;
+
+ if (GenDebugFlag) {
+ PILCallLog(GenPIImports->log, PIL_DEBUG
+ , "IF manager %s: initializing.", PIL_PLUGIN_S);
+ }
+
+ if (user_ptr == NULL) {
+ PILCallLog(GenPIImports->log, PIL_CRIT
+ , "%s Interface Manager requires non-NULL "
+ " PILGenericIfMgmtRqst user pointer at initialization."
+ , PIL_PLUGIN_S);
+ return PIL_INVAL;
+ }
+
+ GenPlugin = us;
+
+ if (GenDebugFlag) {
+ PILCallLog(GenPIImports->log, PIL_DEBUG
+ , "IF manager %s: registering as a plugin."
+ , PIL_PLUGIN_S);
+ }
+
+ user_req = user_ptr;
+ MasterTable = g_hash_table_new(g_str_hash, g_str_equal);
+ us->ud_plugin = MasterTable; /* Override passed value */
+
+ /* Register ourselves as a plugin */
+
+ if ((ret = imports->register_plugin(us, &OurPIExports)) != PIL_OK) {
+ PILCallLog(imports->log, PIL_CRIT
+ , "IF manager %s unable to register as plugin (%s)"
+ , PIL_PLUGIN_S, PIL_strerror(ret));
+
+ return ret;
+ }
+
+ /*
+ * Register to manage implementations
+ * for all the interface types we've been asked to manage.
+ */
+
+ for(curreq = user_req; curreq->iftype != NULL; ++curreq) {
+ PIL_rc newret;
+
+ newret = AddAnInterfaceType(us, MasterTable, curreq);
+
+ if (newret != PIL_OK) {
+ ret = newret;
+ }
+ }
+
+ /*
+ * Our plugin and all our registered plugin types
+ * have ud_plugin pointing at MasterTable.
+ */
+
+ return ret;
+}
+
+static PIL_rc
+AddAnInterfaceType(PILPlugin*us, GHashTable* MasterTable, PILGenericIfMgmtRqst* req)
+{
+ PIL_rc rc;
+ PILInterface* GenIf; /* Our Generic Interface info*/
+
+ g_assert(MasterTable != NULL);
+ g_hash_table_insert(MasterTable, g_strdup(req->iftype), req);
+
+ if (req->ifmap == NULL) {
+ PILCallLog(GenPIImports->log, PIL_CRIT
+ , "IF manager %s: iftype %s has NULL"
+ " ifmap pointer address."
+ , PIL_PLUGIN_S, req->iftype);
+ return PIL_INVAL;
+ }
+ if ((*req->ifmap) != NULL) {
+ PILCallLog(GenPIImports->log, PIL_CRIT
+ , "IF manager %s: iftype %s GHashTable pointer"
+ " was not initialized to NULL"
+ , PIL_PLUGIN_S, req->iftype);
+ return PIL_INVAL;
+ }
+
+ if (GenDebugFlag) {
+ PILCallLog(GenPIImports->log, PIL_DEBUG
+ , "IF manager %s: registering ourselves"
+ " to manage interface type %s"
+ , PIL_PLUGIN_S, req->iftype);
+ PILCallLog(GenPIImports->log, PIL_DEBUG
+ , "%s IF manager: ifmap: 0x%lx callback: 0x%lx"
+ " imports: 0x%lx"
+ , PIL_PLUGIN_S
+ , (unsigned long)req->ifmap
+ , (unsigned long)req->callback
+ , (unsigned long)req->importfuns);
+ }
+
+ /* Create the hash table to communicate with this client */
+ *(req->ifmap) = g_hash_table_new(g_str_hash, g_str_equal);
+
+ rc = GenPIImports->register_interface(us
+ , PIL_PLUGINTYPE_S
+ , req->iftype /* the iftype we're managing here */
+ , &GenIfOps
+ , CloseGenInterfaceManager
+ , &GenIf
+ , (void*)&GenIfImports
+ , MasterTable); /* Point ud_interface to MasterTable */
+
+ /* We don't ever want to be unloaded... */
+ GenIfImports->ModRefCount(GenIf, +100);
+
+ if (rc != PIL_OK) {
+ PILCallLog(GenPIImports->log, PIL_CRIT
+ , "Generic interface manager %s: unable to register"
+ " to manage interface type %s: %s"
+ , PIL_PLUGIN_S, req->iftype
+ , PIL_strerror(rc));
+ }
+ return rc;
+}
+
+static void
+CloseGeneralPluginManager(PILPlugin* us)
+{
+
+ GHashTable* MasterTable = us->ud_plugin;
+ int count;
+
+ g_assert(MasterTable != NULL);
+
+ /*
+ * All our clients have already been shut down automatically
+ * This is the final shutdown for us...
+ */
+
+
+ /* There *shouldn't* be any keys in there ;-) */
+
+ if ((count=g_hash_table_size(MasterTable)) > 0) {
+
+ /* But just in case there are... */
+ g_hash_table_foreach_remove(MasterTable, FreeAKey, NULL);
+ }
+ g_hash_table_destroy(MasterTable);
+ us->ud_plugin = NULL;
+ return;
+}
+
+/*
+ * We get called for every time an implementation registers itself as
+ * implementing one of the kinds of interfaces we manage.
+ *
+ * It's our job to make the implementation that's
+ * registering with us available to the system.
+ *
+ * We do that by adding it to a GHashTable for its interface type
+ * Our users in the rest of the system takes it from there...
+ *
+ * The key to the GHashTable is the implementation name, and the data is
+ * a pointer to the information the implementation exports.
+ *
+ * It's a piece of cake ;-)
+ */
+static PIL_rc
+RegisterGenIF(PILInterface* intf, void** imports)
+{
+ PILGenericIfMgmtRqst* ifinfo;
+ GHashTable* MasterTable = intf->ifmanager->ud_interface;
+
+ g_assert(MasterTable != NULL);
+
+ /* Reference count should now be one */
+ if (GenDebugFlag) {
+ PILCallLog(GenPIImports->log, PIL_DEBUG
+ , "%s IF manager: interface %s/%s registering."
+ , PIL_PLUGIN_S, intf->interfacetype->typename
+ , intf->interfacename);
+ }
+ g_assert(intf->refcnt == 1);
+ /*
+ * We need to add it to the table that goes with this particular
+ * type of interface.
+ */
+ if ((ifinfo = g_hash_table_lookup(MasterTable
+ , intf->interfacetype->typename)) != NULL) {
+ GHashTable* ifmap = *(ifinfo->ifmap);
+
+ g_hash_table_insert(ifmap, intf->interfacename,intf->exports);
+ if (GenDebugFlag) {
+ PILCallLog(GenPIImports->log, PIL_DEBUG
+ , "%s IF manager: Inserted interface [%s] in hash"
+ " table @ 0x%08lx"
+ , PIL_PLUGIN_S, intf->interfacename
+ , (unsigned long)ifmap);
+ PILCallLog(GenPIImports->log, PIL_DEBUG
+ , "%s IF manager: Exports are here: 0x%08x"
+ , PIL_PLUGIN_S
+ , GPOINTER_TO_UINT(intf->exports));
+ }
+
+ if (ifinfo->callback != NULL) {
+ PILInterfaceType* t = intf->interfacetype;
+
+ if (GenDebugFlag) {
+ PILCallLog(GenPIImports->log, PIL_DEBUG
+ , "%s IF manager: callback 0x%lx"
+ , PIL_PLUGIN_S
+ , (unsigned long)ifinfo->callback);
+ }
+ ifinfo->callback(PIL_REGISTER
+ , t->universe->piuniv, intf->interfacename
+ , t->typename, ifinfo->userptr);
+ }
+
+ *imports = ifinfo->importfuns;
+
+ return PIL_OK;
+
+ }else{
+ PILCallLog(GenPIImports->log, PIL_WARN
+ , "RegisterGenIF: interface type %s not found"
+ , intf->interfacename);
+ }
+ return PIL_INVAL;
+}
+
+/* Unregister an implementation -
+ * We get called from the interface management system when someone
+ * has requested that an implementation of a client interface be
+ * unregistered.
+ */
+static PIL_rc
+UnregisterGenIF(PILInterface*intf)
+{
+ GHashTable* MasterTable = intf->ifmanager->ud_interface;
+ PILGenericIfMgmtRqst* ifinfo;
+
+ g_assert(MasterTable != NULL);
+ g_assert(intf->refcnt >= 0);
+ /*
+ * Go through the "master table" and find client table,
+ * notify client we're about to remove this entry, then
+ * then remove this entry from it.
+ */
+ if (GenDebugFlag) {
+ PILCallLog(GenPIImports->log, PIL_DEBUG
+ , "%s IF manager: unregistering interface %s/%s."
+ , PIL_PLUGIN_S, intf->interfacetype->typename
+ , intf->interfacename);
+ }
+ if ((ifinfo = g_hash_table_lookup(MasterTable
+ , intf->interfacetype->typename)) != NULL) {
+
+ GHashTable* ifmap = *(ifinfo->ifmap);
+
+ if (ifinfo->callback != NULL) {
+ PILInterfaceType* t = intf->interfacetype;
+ if (GenDebugFlag) {
+ PILCallLog(GenPIImports->log, PIL_DEBUG
+ , "%s IF manager: callback 0x%lx"
+ , PIL_PLUGIN_S
+ , (unsigned long)ifinfo->callback);
+ }
+ ifinfo->callback(PIL_UNREGISTER
+ , t->universe->piuniv, intf->interfacename
+ , t->typename, ifinfo->userptr);
+ }
+
+ /* Remove the client entry from master table */
+ g_hash_table_remove(ifmap, intf->interfacename);
+
+ }else{
+ PILCallLog(GenPIImports->log, PIL_WARN
+ , "UnregisterGenIF: interface type %s not found"
+ , intf->interfacename);
+ return PIL_INVAL;
+ }
+ return PIL_OK;
+}
+
+/*
+ * Close down the generic interface manager.
+ */
+static PIL_rc
+CloseGenInterfaceManager(PILInterface*intf, void* info)
+{
+ void* key;
+ void* data;
+ GHashTable* MasterTable = intf->ud_interface;
+
+ if (GenDebugFlag) {
+ PILCallLog(GenPIImports->log, PIL_INFO
+ , "In CloseGenInterFaceManager on %s/%s (MasterTable: 0x%08lx)"
+ , intf->interfacetype->typename, intf->interfacename
+ , (unsigned long)MasterTable);
+ }
+
+
+ g_assert(MasterTable != NULL);
+ if (g_hash_table_lookup_extended(MasterTable
+ , intf->interfacename, &key, &data)) {
+ PILGenericIfMgmtRqst* ifinfo = data;
+ g_hash_table_destroy(*(ifinfo->ifmap));
+ *(ifinfo->ifmap) = NULL;
+ g_hash_table_remove(MasterTable, key);
+ g_free(key);
+ }else{
+ g_assert_not_reached();
+ }
+ return PIL_OK;
+}
+
+static gboolean
+FreeAKey(gpointer key, gpointer value, gpointer data)
+{
+ g_free(key);
+ return TRUE;
+}
diff --git a/lib/plugins/Makefile.am b/lib/plugins/Makefile.am
new file mode 100644
index 0000000..21827cd
--- /dev/null
+++ b/lib/plugins/Makefile.am
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2008 Andrew Beekhof
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+
+MAINTAINERCLEANFILES = Makefile.in
+SUBDIRS = InterfaceMgr stonith lrm compress
diff --git a/lib/plugins/compress/Makefile.am b/lib/plugins/compress/Makefile.am
new file mode 100644
index 0000000..3a3193a
--- /dev/null
+++ b/lib/plugins/compress/Makefile.am
@@ -0,0 +1,52 @@
+#
+# InterfaceMgr: Interface manager plugins for Linux-HA
+#
+# Copyright (C) 2001 Alan Robertson
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+if BUILD_ZLIB_COMPRESS_MODULE
+zlibmodule = zlib.la
+endif
+
+if BUILD_BZ2_COMPRESS_MODULE
+bz2module = bz2.la
+endif
+
+SUBDIRS =
+
+INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \
+ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \
+ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl \
+ -I$(top_builddir)/lib/upmls -I$(top_srcdir)/lib/upmls
+
+AM_CFLAGS = @CFLAGS@
+
+## libraries
+
+halibdir = $(libdir)/@HB_PKG@
+plugindir = $(halibdir)/plugins/compress
+plugin_LTLIBRARIES = $(zlibmodule) $(bz2module)
+
+zlib_la_SOURCES = zlib.c
+zlib_la_LDFLAGS = -export-dynamic -module -avoid-version -lz
+zlib_la_LIBADD = $(top_builddir)/replace/libreplace.la
+
+bz2_la_SOURCES = bz2.c
+bz2_la_LDFLAGS = -export-dynamic -module -avoid-version -lbz2
+bz2_la_LIBADD = $(top_builddir)/replace/libreplace.la
+
diff --git a/lib/plugins/compress/bz2.c b/lib/plugins/compress/bz2.c
new file mode 100644
index 0000000..2eab116
--- /dev/null
+++ b/lib/plugins/compress/bz2.c
@@ -0,0 +1,142 @@
+ /* bz2.c: compression module using bz2 for heartbeat.
+ *
+ * Copyright (C) 2005 Guochun Shi <gshi@ncsa.uiuc.edu>
+ *
+ * SECURITY NOTE: It would be very easy for someone to masquerade as the
+ * device that you're pinging. If they don't know the password, all they can
+ * do is echo back the packets that you're sending out, or send out old ones.
+ * This does mean that if you're using such an approach, that someone could
+ * make you think you have quorum when you don't during a cluster partition.
+ * The danger in that seems small, but you never know ;-)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+
+
+#define PIL_PLUGINTYPE HB_COMPRESS_TYPE
+#define PIL_PLUGINTYPE_S HB_COMPRESS_TYPE_S
+#define PIL_PLUGIN bz2
+#define PIL_PLUGIN_S "bz2"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <lha_internal.h>
+#include <stdio.h>
+#include <pils/plugin.h>
+#include <compress.h>
+#include <bzlib.h>
+#include <clplumbing/cl_log.h>
+#include <string.h>
+
+
+static struct hb_compress_fns bz2Ops;
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static struct hb_media_imports* OurImports;
+static void* interfprivate;
+
+#define LOG PluginImports->log
+#define MALLOC PluginImports->alloc
+#define STRDUP PluginImports->mstrdup
+#define FREE PluginImports->mfree
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &bz2Ops
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , interfprivate);
+}
+
+static int
+bz2_compress(char* dest, size_t* destlen,
+ const char* _src, size_t srclen)
+{
+ int ret;
+ char* src;
+ unsigned int tmpdestlen;
+
+ memcpy(&src, &_src, sizeof(char*));
+
+ tmpdestlen = *destlen;
+ ret = BZ2_bzBuffToBuffCompress(dest, &tmpdestlen, src, srclen, 1, 0, 30);
+ if (ret != BZ_OK){
+ cl_log(LOG_ERR, "%s: compression failed",
+ __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ *destlen = tmpdestlen;
+
+ return HA_OK;
+}
+
+static int
+bz2_decompress(char* dest, size_t* destlen,
+ const char* _src, size_t srclen)
+{
+
+ int ret;
+ char* src;
+ unsigned int tmpdestlen;
+
+ memcpy(&src, &_src, sizeof(char*));
+
+ tmpdestlen = *destlen;
+ ret = BZ2_bzBuffToBuffDecompress(dest, &tmpdestlen, src, srclen, 1, 0);
+ if (ret != BZ_OK){
+ cl_log(LOG_ERR, "%s: decompression failed",
+ __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ *destlen = tmpdestlen;
+
+ return HA_OK;
+}
+
+static const char*
+bz2_getname(void)
+{
+ return "bz2";
+}
+
+static struct hb_compress_fns bz2Ops ={
+ bz2_compress,
+ bz2_decompress,
+ bz2_getname,
+};
diff --git a/lib/plugins/compress/zlib.c b/lib/plugins/compress/zlib.c
new file mode 100644
index 0000000..5958966
--- /dev/null
+++ b/lib/plugins/compress/zlib.c
@@ -0,0 +1,135 @@
+ /* zlib.c: compression module using zlib for heartbeat.
+ *
+ * Copyright (C) 2005 Guochun Shi <gshi@ncsa.uiuc.edu>
+ *
+ * SECURITY NOTE: It would be very easy for someone to masquerade as the
+ * device that you're pinging. If they don't know the password, all they can
+ * do is echo back the packets that you're sending out, or send out old ones.
+ * This does mean that if you're using such an approach, that someone could
+ * make you think you have quorum when you don't during a cluster partition.
+ * The danger in that seems small, but you never know ;-)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+
+
+#define PIL_PLUGINTYPE HB_COMPRESS_TYPE
+#define PIL_PLUGINTYPE_S HB_COMPRESS_TYPE_S
+#define PIL_PLUGIN zlib
+#define PIL_PLUGIN_S "zlib"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <lha_internal.h>
+#include <pils/plugin.h>
+#include <compress.h>
+#include <zlib.h>
+#include <clplumbing/cl_log.h>
+#include <string.h>
+
+
+static struct hb_compress_fns zlibOps;
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static struct hb_media_imports* OurImports;
+static void* interfprivate;
+
+#define LOG PluginImports->log
+#define MALLOC PluginImports->alloc
+#define STRDUP PluginImports->mstrdup
+#define FREE PluginImports->mfree
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &zlibOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , interfprivate);
+}
+
+static int
+zlib_compress(char* dest, size_t* _destlen,
+ const char* src, size_t _srclen)
+{
+ int ret;
+ uLongf destlen = *_destlen;
+ uLongf srclen = _srclen;
+
+ ret = compress((Bytef *)dest, &destlen, (const Bytef *)src, srclen);
+ if (ret != Z_OK){
+ cl_log(LOG_ERR, "%s: compression failed",
+ __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ *_destlen = destlen;
+ return HA_OK;
+
+}
+
+static int
+zlib_decompress(char* dest, size_t* _destlen,
+ const char* src, size_t _srclen)
+{
+
+ int ret;
+ uLongf destlen = *_destlen;
+ uLongf srclen = _srclen;
+
+ ret = uncompress((Bytef *)dest, &destlen, (const Bytef *)src, srclen);
+ if (ret != Z_OK){
+ cl_log(LOG_ERR, "%s: decompression failed",
+ __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ *_destlen = destlen;
+
+ return HA_OK;
+}
+
+static const char*
+zlib_getname(void)
+{
+ return "zlib";
+}
+
+static struct hb_compress_fns zlibOps ={
+ zlib_compress,
+ zlib_decompress,
+ zlib_getname,
+};
diff --git a/lib/plugins/lrm/Makefile.am b/lib/plugins/lrm/Makefile.am
new file mode 100644
index 0000000..fd24579
--- /dev/null
+++ b/lib/plugins/lrm/Makefile.am
@@ -0,0 +1,58 @@
+#
+# Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+# Copyright (c) 2004 International Business Machines
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+if UPSTART
+SUBDIRS = dbus
+endif
+
+LRM_DIR = lrm
+INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \
+ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \
+ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl
+if UPSTART
+INCLUDES += $(DBUS_CFLAGS)
+endif
+
+halibdir = $(libdir)/@HB_PKG@
+havarlibdir = $(localstatedir)/lib/@HB_PKG@
+COMMONLIBS = $(top_builddir)/lib/clplumbing/libplumb.la \
+ $(top_builddir)/lib/lrm/liblrm.la \
+ $(GLIBLIB)
+
+plugindir = $(halibdir)/plugins/RAExec
+
+plugin_LTLIBRARIES = lsb.la ocf.la heartbeat.la
+if UPSTART
+plugin_LTLIBRARIES += upstart.la
+endif
+
+lsb_la_SOURCES = raexeclsb.c
+lsb_la_LDFLAGS = -L$(top_builddir)/lib/pils -lpils -export-dynamic -module -avoid-version
+
+ocf_la_SOURCES = raexecocf.c
+ocf_la_LDFLAGS = -L$(top_builddir)/lib/pils -lpils -export-dynamic -module -avoid-version
+
+heartbeat_la_SOURCES = raexechb.c
+heartbeat_la_LDFLAGS = -L$(top_builddir)/lib/pils -lpils -export-dynamic -module -avoid-version
+
+if UPSTART
+upstart_la_SOURCES = raexecupstart.c upstart-dbus.c
+upstart_la_LDFLAGS = -L$(top_builddir)/lib/pils -lpils -export-dynamic -module -avoid-version \
+ $(DBUS_LIBS)
+endif
diff --git a/lib/plugins/lrm/dbus/Makefile.am b/lib/plugins/lrm/dbus/Makefile.am
new file mode 100644
index 0000000..ec93436
--- /dev/null
+++ b/lib/plugins/lrm/dbus/Makefile.am
@@ -0,0 +1,16 @@
+if UPSTART
+BINDINGS=Upstart_Instance.h \
+ Upstart_Job.h \
+ Upstart.h
+
+all-local:
+ for header in $(BINDINGS); do \
+ input=com.ubuntu.`echo $$header | sed 's/\.h//' | tr _ .`.xml; \
+ $(DBUS_BINDING_TOOL) --mode=glib-client $$input > $$header; \
+ done
+
+clean-local:
+ rm -f $(BINDINGS)
+
+EXTRA_DIST = *.xml
+endif
diff --git a/lib/plugins/lrm/dbus/com.ubuntu.Upstart.Instance.xml b/lib/plugins/lrm/dbus/com.ubuntu.Upstart.Instance.xml
new file mode 100644
index 0000000..d4f7ab2
--- /dev/null
+++ b/lib/plugins/lrm/dbus/com.ubuntu.Upstart.Instance.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- upstart
+
+ com.ubuntu.Upstart.Instance.xml - interface definition for interface
+ objects
+
+ Copyright © 2009 Canonical Ltd.
+ Author: Scott James Remnant <scott@netsplit.com>.
+
+ This file is free software; Canonical Ltd gives unlimited permission
+ to copy and/or distribute it, with or without modifications, as long
+ as this notice is preserved.
+
+ Communication and interaction with Upstart through this interface is
+ permitted without restriction.
+ -->
+
+<!DOCTYPE node PUBLIC
+ "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+
+<node>
+ <interface name="com.ubuntu.Upstart0_6.Instance">
+ <!-- Methods to directly control instances. Unlike the equivalent methods
+ for a Job, these are not permitted to pass or set environment. -->
+ <method name="Start">
+ <annotation name="com.netsplit.Nih.Method.Async" value="true" />
+ <arg name="wait" type="b" direction="in" />
+ </method>
+ <method name="Stop">
+ <annotation name="com.netsplit.Nih.Method.Async" value="true" />
+ <arg name="wait" type="b" direction="in" />
+ </method>
+ <method name="Restart">
+ <annotation name="com.netsplit.Nih.Method.Async" value="true" />
+ <arg name="wait" type="b" direction="in" />
+ </method>
+
+ <!-- Basic information about an Instance -->
+ <property name="name" type="s" access="read" />
+ <property name="goal" type="s" access="read" />
+ <property name="state" type="s" access="read" />
+ <property name="processes" type="a(si)" access="read" />
+ </interface>
+</node>
diff --git a/lib/plugins/lrm/dbus/com.ubuntu.Upstart.Job.xml b/lib/plugins/lrm/dbus/com.ubuntu.Upstart.Job.xml
new file mode 100644
index 0000000..27f47a1
--- /dev/null
+++ b/lib/plugins/lrm/dbus/com.ubuntu.Upstart.Job.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- upstart
+
+ com.ubuntu.Upstart.Job.xml - interface definition for job objects
+
+ Copyright © 2009 Canonical Ltd.
+ Author: Scott James Remnant <scott@netsplit.com>.
+
+ This file is free software; Canonical Ltd gives unlimited permission
+ to copy and/or distribute it, with or without modifications, as long
+ as this notice is preserved.
+
+ Communication and interaction with Upstart through this interface is
+ permitted without restriction.
+ -->
+
+<!DOCTYPE node PUBLIC
+ "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+
+<node>
+ <interface name="com.ubuntu.Upstart0_6.Job">
+ <!-- Get object paths for instances, while you can figure these out too,
+ it's still better form to use these -->
+ <method name="GetInstance">
+ <arg name="env" type="as" direction="in" />
+ <arg name="instance" type="o" direction="out" />
+ </method>
+ <method name="GetInstanceByName">
+ <arg name="name" type="s" direction="in" />
+ <arg name="instance" type="o" direction="out" />
+ </method>
+ <method name="GetAllInstances">
+ <arg name="instances" type="ao" direction="out" />
+ </method>
+
+ <!-- Signals for changes to the instance list for a job -->
+ <signal name="InstanceAdded">
+ <arg name="instance" type="o" />
+ </signal>
+ <signal name="InstanceRemoved">
+ <arg name="instance" type="o" />
+ </signal>
+
+ <!-- Job control; the environment arguments are used for both instance
+ selection and for passing environment to the processes of the job. -->
+ <method name="Start">
+ <annotation name="com.netsplit.Nih.Method.Async" value="true" />
+ <arg name="env" type="as" direction="in" />
+ <arg name="wait" type="b" direction="in" />
+ <arg name="instance" type="o" direction="out" />
+ </method>
+ <method name="Stop">
+ <annotation name="com.netsplit.Nih.Method.Async" value="true" />
+ <arg name="env" type="as" direction="in" />
+ <arg name="wait" type="b" direction="in" />
+ </method>
+ <method name="Restart">
+ <annotation name="com.netsplit.Nih.Method.Async" value="true" />
+ <arg name="env" type="as" direction="in" />
+ <arg name="wait" type="b" direction="in" />
+ <arg name="instance" type="o" direction="out" />
+ </method>
+
+ <!-- Basic information about a Job -->
+ <property name="name" type="s" access="read" />
+ <property name="description" type="s" access="read" />
+ <property name="author" type="s" access="read" />
+ <property name="version" type="s" access="read" />
+ </interface>
+</node>
diff --git a/lib/plugins/lrm/dbus/com.ubuntu.Upstart.xml b/lib/plugins/lrm/dbus/com.ubuntu.Upstart.xml
new file mode 100644
index 0000000..a4331cd
--- /dev/null
+++ b/lib/plugins/lrm/dbus/com.ubuntu.Upstart.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- upstart
+
+ com.ubuntu.Upstart.xml - interface definition for manager object
+
+ Copyright © 2009 Canonical Ltd.
+ Author: Scott James Remnant <scott@netsplit.com>.
+
+ This file is free software; Canonical Ltd gives unlimited permission
+ to copy and/or distribute it, with or without modifications, as long
+ as this notice is preserved.
+
+ Communication and interaction with Upstart through this interface is
+ permitted without restriction.
+ -->
+
+<!DOCTYPE node PUBLIC
+ "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+
+<node name="/com/ubuntu/Upstart">
+ <interface name="com.ubuntu.Upstart0_6">
+ <!-- Reload all configuration sources -->
+ <method name="ReloadConfiguration">
+ </method>
+
+ <!-- Get object paths for jobs, while you can figure them out, it's
+ better form to use these -->
+ <method name="GetJobByName">
+ <arg name="name" type="s" direction="in" />
+ <arg name="job" type="o" direction="out" />
+ </method>
+ <method name="GetAllJobs">
+ <arg name="jobs" type="ao" direction="out" />
+ </method>
+
+ <!-- Signals for changes to the job list -->
+ <signal name="JobAdded">
+ <arg name="job" type="o" />
+ </signal>
+ <signal name="JobRemoved">
+ <arg name="job" type="o" />
+ </signal>
+
+ <!-- Event emission -->
+ <method name="EmitEvent">
+ <annotation name="com.netsplit.Nih.Method.Async" value="true" />
+ <arg name="name" type="s" direction="in" />
+ <arg name="env" type="as" direction="in" />
+ <arg name="wait" type="b" direction="in" />
+ </method>
+
+ <!-- Basic information about Upstart -->
+ <property name="version" type="s" access="read" />
+ <property name="log_priority" type="s" access="readwrite" />
+ </interface>
+</node>
diff --git a/lib/plugins/lrm/raexechb.c b/lib/plugins/lrm/raexechb.c
new file mode 100644
index 0000000..f9f1eb9
--- /dev/null
+++ b/lib/plugins/lrm/raexechb.c
@@ -0,0 +1,416 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * File: raexechb.c
+ * Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+ * Copyright (c) 2004 International Business Machines
+ *
+ * This code implements the Resource Agent Plugin Module for LSB style.
+ * It's a part of Local Resource Manager. Currently it's used by lrmd only.
+ */
+
+#include <lha_internal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <dirent.h>
+#include <libgen.h>
+#include <glib.h>
+#include <clplumbing/cl_log.h>
+#include <pils/plugin.h>
+#include <lrm/raexec.h>
+
+#define PIL_PLUGINTYPE RA_EXEC_TYPE
+#define PIL_PLUGIN heartbeat
+#define PIL_PLUGINTYPE_S "RAExec"
+#define PIL_PLUGIN_S "heartbeat"
+#define PIL_PLUGINLICENSE LICENSE_PUBDOM
+#define PIL_PLUGINLICENSEURL URL_PUBDOM
+
+static const char * RA_PATH = HB_RA_DIR;
+
+static const char meta_data_template[] =
+"<?xml version=\"1.0\"?>\n"
+"<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"
+"<resource-agent name=\"%s\">\n"
+"<version>1.0</version>\n"
+"<longdesc lang=\"en\">\n"
+"%s"
+"</longdesc>\n"
+"<shortdesc lang=\"en\">%s</shortdesc>\n"
+"<parameters>\n"
+"<parameter name=\"1\" unique=\"1\" required=\"0\">\n"
+"<longdesc lang=\"en\">\n"
+"This argument will be passed as the first argument to the "
+"heartbeat resource agent (assuming it supports one)\n"
+"</longdesc>\n"
+"<shortdesc lang=\"en\">argv[1]</shortdesc>\n"
+"<content type=\"string\" default=\" \" />\n"
+"</parameter>\n"
+"<parameter name=\"2\" unique=\"1\" required=\"0\">\n"
+"<longdesc lang=\"en\">\n"
+"This argument will be passed as the second argument to the "
+"heartbeat resource agent (assuming it supports one)\n"
+"</longdesc>\n"
+"<shortdesc lang=\"en\">argv[2]</shortdesc>\n"
+"<content type=\"string\" default=\" \" />\n"
+"</parameter>\n"
+"<parameter name=\"3\" unique=\"1\" required=\"0\">\n"
+"<longdesc lang=\"en\">\n"
+"This argument will be passed as the third argument to the "
+"heartbeat resource agent (assuming it supports one)\n"
+"</longdesc>\n"
+"<shortdesc lang=\"en\">argv[3]</shortdesc>\n"
+"<content type=\"string\" default=\" \" />\n"
+"</parameter>\n"
+"<parameter name=\"4\" unique=\"1\" required=\"0\">\n"
+"<longdesc lang=\"en\">\n"
+"This argument will be passed as the fourth argument to the "
+"heartbeat resource agent (assuming it supports one)\n"
+"</longdesc>\n"
+"<shortdesc lang=\"en\">argv[4]</shortdesc>\n"
+"<content type=\"string\" default=\" \" />\n"
+"</parameter>\n"
+"<parameter name=\"5\" unique=\"1\" required=\"0\">\n"
+"<longdesc lang=\"en\">\n"
+"This argument will be passed as the fifth argument to the "
+"heartbeat resource agent (assuming it supports one)\n"
+"</longdesc>\n"
+"<shortdesc lang=\"en\">argv[5]</shortdesc>\n"
+"<content type=\"string\" default=\" \" />\n"
+"</parameter>\n"
+"</parameters>\n"
+"<actions>\n"
+"<action name=\"start\" timeout=\"15\" />\n"
+"<action name=\"stop\" timeout=\"15\" />\n"
+"<action name=\"status\" timeout=\"15\" />\n"
+"<action name=\"monitor\" timeout=\"15\" interval=\"15\" start-delay=\"15\" />\n"
+"<action name=\"meta-data\" timeout=\"5\" />\n"
+"</actions>\n"
+"<special tag=\"heartbeart\">\n"
+"</special>\n"
+"</resource-agent>\n";
+
+/* The begin of exported function list */
+static int execra(const char * rsc_id,
+ const char * rsc_type,
+ const char * provider,
+ const char * op_type,
+ const int timeout,
+ GHashTable * params);
+
+static uniform_ret_execra_t map_ra_retvalue(int ret_execra
+ , const char * op_type, const char * std_output);
+static int get_resource_list(GList ** rsc_info);
+static char* get_resource_meta(const char* rsc_type, const char* provider);
+static int get_provider_list(const char* ra_type, GList ** providers);
+
+/* The end of exported function list */
+
+/* The begin of internal used function & data list */
+#define MAX_PARAMETER_NUM 40
+typedef char * RA_ARGV[MAX_PARAMETER_NUM];
+
+static const int MAX_LENGTH_OF_RSCNAME = 40,
+ MAX_LENGTH_OF_OPNAME = 40;
+
+static int prepare_cmd_parameters(const char * rsc_type, const char * op_type,
+ GHashTable * params, RA_ARGV params_argv);
+/* The end of internal function & data list */
+
+/* Rource agent execution plugin operations */
+static struct RAExecOps raops =
+{ execra,
+ map_ra_retvalue,
+ get_resource_list,
+ get_provider_list,
+ get_resource_meta
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static void* OurImports;
+static void* interfprivate;
+static int idebuglevel = 0;
+
+/*
+ * Our plugin initialization and registration function
+ * It gets called when the plugin gets loaded.
+ */
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ if (getenv(HADEBUGVAL) != NULL && atoi(getenv(HADEBUGVAL)) > 0 ) {
+ idebuglevel = atoi(getenv(HADEBUGVAL));
+ cl_log(LOG_DEBUG, "LRM debug level set to %d", idebuglevel);
+ }
+
+ /* Register our interfaces */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S, PIL_PLUGIN_S,
+ &raops, NULL, &OurInterface, &OurImports,
+ interfprivate);
+}
+
+/*
+ * Real work starts here ;-)
+ */
+
+static int
+execra( const char * rsc_id, const char * rsc_type, const char * provider,
+ const char * op_type, const int timeout, GHashTable * params)
+{
+ RA_ARGV params_argv;
+ char ra_pathname[RA_MAX_NAME_LENGTH];
+ uniform_ret_execra_t exit_value;
+ GString * debug_info;
+ char * optype_tmp = NULL;
+ int index_tmp = 0;
+
+ /* How to generate the meta-data? There is nearly no value
+ * information in meta-data build up in current way.
+ * Should directly add meta-data to the script itself?
+ */
+ if ( 0 == STRNCMP_CONST(op_type, "meta-data") ) {
+ printf("%s", get_resource_meta(rsc_type, provider));
+ exit(0);
+ }
+
+ /* To simulate the 'monitor' operation with 'status'.
+ * Now suppose there is no 'monitor' operation for heartbeat scripts.
+ */
+ if ( 0 == STRNCMP_CONST(op_type, "monitor") ) {
+ optype_tmp = g_strdup("status");
+ } else {
+ optype_tmp = g_strdup(op_type);
+ }
+
+ /* Prepare the call parameter */
+ if (0 > prepare_cmd_parameters(rsc_type, optype_tmp, params, params_argv)) {
+ cl_log(LOG_ERR, "HB RA: Error of preparing parameters");
+ g_free(optype_tmp);
+ return -1;
+ }
+ g_free(optype_tmp);
+
+ get_ra_pathname(RA_PATH, rsc_type, NULL, ra_pathname);
+
+ /* let this log show only high loglevel. */
+ if (idebuglevel > 1) {
+ debug_info = g_string_new("");
+ do {
+ g_string_append(debug_info, params_argv[index_tmp]);
+ g_string_append(debug_info, " ");
+ } while (params_argv[++index_tmp] != NULL);
+ debug_info->str[debug_info->len-1] = '\0';
+
+ cl_log(LOG_DEBUG, "RA instance %s executing: heartbeat::%s"
+ , rsc_id, debug_info->str);
+
+ g_string_free(debug_info, TRUE);
+ }
+
+ closefiles(); /* don't leak open files */
+ execv(ra_pathname, params_argv);
+ cl_perror("(%s:%s:%d) execv failed for %s"
+ , __FILE__, __FUNCTION__, __LINE__, ra_pathname);
+
+ switch (errno) {
+ case ENOENT: /* No such file or directory */
+ case EISDIR: /* Is a directory */
+ exit_value = EXECRA_NOT_INSTALLED;
+ break;
+ default:
+ exit_value = EXECRA_EXEC_UNKNOWN_ERROR;
+ }
+ exit(exit_value);
+}
+
+static int
+prepare_cmd_parameters(const char * rsc_type, const char * op_type,
+ GHashTable * params_ht, RA_ARGV params_argv)
+{
+ int tmp_len, index;
+ int ht_size = 0;
+ int param_num = 0;
+ char buf_tmp[20];
+ void * value_tmp;
+
+ if (params_ht) {
+ ht_size = g_hash_table_size(params_ht);
+ }
+ if ( ht_size+3 > MAX_PARAMETER_NUM ) {
+ cl_log(LOG_ERR, "Too many parameters");
+ return -1;
+ }
+
+ /* Now suppose the parameter format stored in Hashtabe is as like as
+ * key="1", value="-Wl,soname=test"
+ * Moreover, the key is supposed as a string transfered from an integer.
+ * It may be changed in the future.
+ */
+ /* Notice: if ht_size==0, no actual arguments except op_type */
+ for (index = 1; index <= ht_size; index++ ) {
+ snprintf(buf_tmp, sizeof(buf_tmp), "%d", index);
+ value_tmp = g_hash_table_lookup(params_ht, buf_tmp);
+ /* suppose the key is consecutive */
+ if ( value_tmp == NULL ) {
+/* cl_log(LOG_WARNING, "Parameter ordering error in"\
+ "prepare_cmd_parameters, raexeclsb.c");
+ cl_log(LOG_WARNING, "search key=%s.", buf_tmp);
+*/ continue;
+ }
+ param_num ++;
+ params_argv[param_num] = g_strdup((char *)value_tmp);
+ }
+
+ tmp_len = strnlen(rsc_type, MAX_LENGTH_OF_RSCNAME);
+ params_argv[0] = g_strndup(rsc_type, tmp_len);
+ /* Add operation code as the last argument */
+ tmp_len = strnlen(op_type, MAX_LENGTH_OF_OPNAME);
+ params_argv[param_num+1] = g_strndup(op_type, tmp_len);
+ /* Add the teminating NULL pointer */
+ params_argv[param_num+2] = NULL;
+ return 0;
+}
+
+static uniform_ret_execra_t
+map_ra_retvalue(int ret_execra, const char * op_type, const char * std_output)
+{
+
+ /* Now there is no formal related specification for Heartbeat RA
+ * scripts. Temporarily deal as LSB init script.
+ */
+ /* Except op_type equals 'status', the UNIFORM_RET_EXECRA is compatible
+ with LSB standard.
+ */
+ const char * stop_pattern1 = "*stopped*",
+ * stop_pattern2 = "*not*running*",
+ * running_pattern1 = "*running*",
+ * running_pattern2 = "*OK*";
+ char * lower_std_output = NULL;
+
+ if(ret_execra == EXECRA_NOT_INSTALLED) {
+ return ret_execra;
+ }
+
+ if ( 0 == STRNCMP_CONST(op_type, "status")
+ || 0 == STRNCMP_CONST(op_type, "monitor")) {
+ if (std_output == NULL ) {
+ cl_log(LOG_WARNING, "No status output from the (hb) resource agent.");
+ return EXECRA_NOT_RUNNING;
+ }
+
+ if (idebuglevel) {
+ cl_log(LOG_DEBUG, "RA output was: [%s]", std_output);
+ }
+
+ lower_std_output = g_ascii_strdown(std_output, -1);
+
+ if ( TRUE == g_pattern_match_simple(stop_pattern1
+ , lower_std_output) || TRUE ==
+ g_pattern_match_simple(stop_pattern2
+ , lower_std_output) ) {
+ if (idebuglevel) {
+ cl_log(LOG_DEBUG
+ , "RA output [%s] matched stopped pattern"
+ " [%s] or [%s]"
+ , std_output
+ , stop_pattern1
+ , stop_pattern2);
+ }
+ ret_execra = EXECRA_NOT_RUNNING; /* stopped */
+ } else if ( TRUE == g_pattern_match_simple(running_pattern1
+ , lower_std_output) || TRUE ==
+ g_pattern_match_simple(running_pattern2
+ , std_output) ) {
+ if (idebuglevel) {
+ cl_log(LOG_DEBUG
+ , "RA output [%s] matched running"
+ " pattern [%s] or [%s]"
+ , std_output, running_pattern1
+ , running_pattern2);
+ }
+ ret_execra = EXECRA_OK; /* running */
+ } else {
+ /* It didn't say it was running - must be stopped */
+ cl_log(LOG_DEBUG, "RA output [%s] didn't match any pattern"
+ , std_output);
+ ret_execra = EXECRA_NOT_RUNNING; /* stopped */
+ }
+ g_free(lower_std_output);
+ }
+ /* For non-status operation return code */
+ if (ret_execra < 0) {
+ ret_execra = EXECRA_UNKNOWN_ERROR;
+ }
+ return ret_execra;
+}
+
+static int
+get_resource_list(GList ** rsc_info)
+{
+ return get_runnable_list(RA_PATH, rsc_info);
+}
+
+static char*
+get_resource_meta(const char* rsc_type, const char* provider)
+{
+ GString * meta_data;
+
+ meta_data = g_string_new("");
+ g_string_sprintf( meta_data, meta_data_template, rsc_type
+ , rsc_type, rsc_type);
+ return meta_data->str;
+}
+static int
+get_provider_list(const char* ra_type, GList ** providers)
+{
+ if ( providers == NULL ) {
+ cl_log(LOG_ERR, "%s:%d: Parameter error: providers==NULL"
+ , __FUNCTION__, __LINE__);
+ return -2;
+ }
+
+ if ( *providers != NULL ) {
+ cl_log(LOG_ERR, "%s:%d: Parameter error: *providers==NULL."
+ "This will cause memory leak."
+ , __FUNCTION__, __LINE__);
+ }
+
+ /* Now temporarily make it fixed */
+ *providers = g_list_append(*providers, g_strdup("heartbeat"));
+
+ return g_list_length(*providers);
+}
diff --git a/lib/plugins/lrm/raexeclsb.c b/lib/plugins/lrm/raexeclsb.c
new file mode 100644
index 0000000..46d7546
--- /dev/null
+++ b/lib/plugins/lrm/raexeclsb.c
@@ -0,0 +1,609 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * File: raexeclsb.c
+ * Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+ * Copyright (c) 2004 International Business Machines
+ *
+ * This code implements the Resource Agent Plugin Module for LSB style.
+ * It's a part of Local Resource Manager. Currently it's used by lrmd only.
+ */
+/*
+ * Todo
+ * 1) Use flex&bison to make the analysis functions for lsb compliant comment?
+ * 2) Support multiple paths which contain lsb compliant RAs.
+ * 3) Optional and additional actions analysis?
+ */
+
+#include <lha_internal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <dirent.h>
+#include <libgen.h> /* Add it for compiling on OSX */
+#include <glib.h>
+#include <clplumbing/cl_log.h>
+#include <pils/plugin.h>
+#include <lrm/raexec.h>
+#include <libgen.h>
+
+#include <libxml/entities.h>
+
+#define PIL_PLUGINTYPE RA_EXEC_TYPE
+#define PIL_PLUGIN lsb
+#define PIL_PLUGINTYPE_S "RAExec"
+#define PIL_PLUGIN_S "lsb"
+#define PIL_PLUGINLICENSE LICENSE_PUBDOM
+#define PIL_PLUGINLICENSEURL URL_PUBDOM
+
+
+/* meta-data template for lsb scripts */
+/* Note: As for optional actions -- extracted from lsb standard.
+ * The reload and the try-restart options are optional. Other init script
+ * actions may be defined by the init script.
+ */
+#define meta_data_template \
+"<?xml version=\"1.0\"?>\n"\
+"<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"\
+"<resource-agent name=\"%s\" version=\"0.1\">\n"\
+" <version>1.0</version>\n"\
+" <longdesc lang=\"en\">\n"\
+" %s"\
+" </longdesc>\n"\
+" <shortdesc lang=\"en\">%s</shortdesc>\n"\
+" <parameters>\n"\
+" </parameters>\n"\
+" <actions>\n"\
+" <action name=\"start\" timeout=\"15\" />\n"\
+" <action name=\"stop\" timeout=\"15\" />\n"\
+" <action name=\"status\" timeout=\"15\" />\n"\
+" <action name=\"restart\" timeout=\"15\" />\n"\
+" <action name=\"force-reload\" timeout=\"15\" />\n"\
+" <action name=\"monitor\" timeout=\"15\" interval=\"15\" />\n"\
+" <action name=\"meta-data\" timeout=\"5\" />\n"\
+" </actions>\n"\
+" <special tag=\"LSB\">\n"\
+" <Provides>%s</Provides>\n"\
+" <Required-Start>%s</Required-Start>\n"\
+" <Required-Stop>%s</Required-Stop>\n"\
+" <Should-Start>%s</Should-Start>\n"\
+" <Should-Stop>%s</Should-Stop>\n"\
+" <Default-Start>%s</Default-Start>\n"\
+" <Default-Stop>%s</Default-Stop>\n"\
+" </special>\n"\
+"</resource-agent>\n"
+
+/* The keywords for lsb-compliant comment */
+#define LSB_INITSCRIPT_INFOBEGIN_TAG "### BEGIN INIT INFO"
+#define LSB_INITSCRIPT_INFOEND_TAG "### END INIT INFO"
+#define PROVIDES "# Provides:"
+#define REQ_START "# Required-Start:"
+#define REQ_STOP "# Required-Stop:"
+#define SHLD_START "# Should-Start:"
+#define SHLD_STOP "# Should-Stop:"
+#define DFLT_START "# Default-Start:"
+#define DFLT_STOP "# Default-Stop:"
+#define SHORT_DSCR "# Short-Description:"
+#define DESCRIPTION "# Description:"
+
+#define ZAPXMLOBJ(m) \
+ if ( (m) != NULL ) { \
+ xmlFree(m); \
+ (m) = NULL; \
+ }
+
+#define RALSB_GET_VALUE(ptr, keyword) \
+ if ( (ptr == NULL) & (0 == strncasecmp(buffer, keyword, strlen(keyword))) ) { \
+ (ptr) = (char *)xmlEncodeEntitiesReentrant(NULL,BAD_CAST buffer+strlen(keyword)); \
+ continue; \
+ }
+/*
+ * Are there multiple paths? Now according to LSB init scripts, the answer
+ * is 'no', but should be 'yes' for lsb none-init scripts?
+ */
+static const char * RA_PATH = LSB_RA_DIR;
+/* Map to the return code of the 'monitor' operation defined in the OCF RA
+ * specification.
+ */
+static const int status_op_exitcode_map[] = {
+ EXECRA_OK, /* LSB_STATUS_OK */
+ EXECRA_NOT_RUNNING, /* LSB_STATUS_VAR_PID */
+ EXECRA_NOT_RUNNING, /* LSB_STATUS_VAR_LOCK */
+ EXECRA_NOT_RUNNING, /* LSB_STATUS_STOPPED */
+ EXECRA_UNKNOWN_ERROR /* LSB_STATUS_UNKNOWN */
+};
+
+/* The begin of exported function list */
+static int execra(const char * rsc_id,
+ const char * rsc_type,
+ const char * provider,
+ const char * op_type,
+ const int timeout,
+ GHashTable * params);
+
+static uniform_ret_execra_t map_ra_retvalue(int ret_execra
+ , const char * op_type, const char * std_output);
+static char* get_resource_meta(const char* rsc_type, const char* provider);
+static int get_resource_list(GList ** rsc_info);
+static int get_provider_list(const char* ra_type, GList ** providers);
+
+/* The end of exported function list */
+
+/* The begin of internal used function & data list */
+#define MAX_PARAMETER_NUM 40
+
+const int MAX_LENGTH_OF_RSCNAME = 40,
+ MAX_LENGTH_OF_OPNAME = 40;
+
+typedef char * RA_ARGV[MAX_PARAMETER_NUM];
+
+static int prepare_cmd_parameters(const char * rsc_type, const char * op_type,
+ GHashTable * params, RA_ARGV params_argv);
+/* The end of internal function & data list */
+
+/* Rource agent execution plugin operations */
+static struct RAExecOps raops =
+{ execra,
+ map_ra_retvalue,
+ get_resource_list,
+ get_provider_list,
+ get_resource_meta
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static void* OurImports;
+static void* interfprivate;
+
+/*
+ * Our plugin initialization and registration function
+ * It gets called when the plugin gets loaded.
+ */
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interfaces */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S, PIL_PLUGIN_S,
+ &raops, NULL, &OurInterface, &OurImports,
+ interfprivate);
+}
+
+/*
+ * Real work starts here ;-)
+ */
+
+static int
+execra( const char * rsc_id, const char * rsc_type, const char * provider,
+ const char * op_type, const int timeout, GHashTable * params)
+{
+ RA_ARGV params_argv;
+ char ra_pathname[RA_MAX_NAME_LENGTH];
+ GString * debug_info;
+ char * inherit_debuglevel = NULL;
+ char * optype_tmp = NULL;
+ int index_tmp = 0;
+ int save_errno;
+
+ /* Specially handle the operation "metameta-data". To build up its
+ * output from templet, dummy data and its comment head.
+ */
+ if ( 0 == STRNCMP_CONST(op_type, "meta-data")) {
+ printf("%s", get_resource_meta(rsc_type, provider));
+ exit(0);
+ }
+
+ /* To simulate the 'monitor' operation with 'status'.
+ * Now suppose there is no 'monitor' operation for LSB scripts.
+ */
+ if (0 == STRNCMP_CONST(op_type, "monitor")) {
+ optype_tmp = g_strdup("status");
+ } else {
+ optype_tmp = g_strdup(op_type);
+ }
+
+ /* Prepare the call parameter */
+ if ( prepare_cmd_parameters(rsc_type, optype_tmp, params, params_argv)
+ != 0) {
+ cl_log(LOG_ERR, "lsb RA: Error of preparing parameters");
+ g_free(optype_tmp);
+ return -1;
+ }
+ g_free(optype_tmp);
+ get_ra_pathname(RA_PATH, rsc_type, NULL, ra_pathname);
+
+ /* let this log show only high loglevel. */
+ inherit_debuglevel = getenv(HADEBUGVAL);
+ if ((inherit_debuglevel != NULL) && (atoi(inherit_debuglevel) > 1)) {
+ debug_info = g_string_new("");
+ do {
+ g_string_append(debug_info, params_argv[index_tmp]);
+ g_string_append(debug_info, " ");
+ } while (params_argv[++index_tmp] != NULL);
+ debug_info->str[debug_info->len-1] = '\0';
+
+ cl_log(LOG_DEBUG, "RA instance %s executing: lsb::%s"
+ , rsc_id, debug_info->str);
+
+ g_string_free(debug_info, TRUE);
+ }
+
+ closefiles(); /* don't leak open files */
+ execv(ra_pathname, params_argv);
+ /* oops, exec failed */
+ save_errno = errno; /* cl_perror may change errno */
+ cl_perror("(%s:%s:%d) execv failed for %s"
+ , __FILE__, __FUNCTION__, __LINE__, ra_pathname);
+ errno = save_errno;
+ exit(get_failed_exec_rc());
+}
+
+static uniform_ret_execra_t
+map_ra_retvalue(int ret_execra, const char * op_type, const char * std_output)
+{
+ /* Except op_type equals 'status', the UNIFORM_RET_EXECRA is compatible
+ * with the LSB standard.
+ */
+ if (ret_execra < 0) {
+ return EXECRA_UNKNOWN_ERROR;
+ }
+
+ if(ret_execra == EXECRA_NOT_INSTALLED) {
+ return ret_execra;
+ }
+
+ if ( 0 == STRNCMP_CONST(op_type, "status")
+ || 0 == STRNCMP_CONST(op_type, "monitor")) {
+ if (ret_execra < DIMOF(status_op_exitcode_map)) {
+ ret_execra = status_op_exitcode_map[ret_execra];
+ }
+ }
+ return ret_execra;
+}
+
+static int
+get_resource_list(GList ** rsc_info)
+{
+ char ra_pathname[RA_MAX_NAME_LENGTH];
+ FILE * fp;
+ gboolean next_continue, found_begin_tag, is_lsb_script;
+ int rc = 0;
+ GList *cur, *tmp;
+ const size_t BUFLEN = 80;
+ char buffer[BUFLEN];
+
+ if ((rc = get_runnable_list(RA_PATH, rsc_info)) <= 0) {
+ return rc;
+ }
+
+ /* Use the following comment line as the filter patterns to choose
+ * the real LSB-compliant scripts.
+ * "### BEGIN INIT INFO" and "### END INIT INFO"
+ */
+ cur = g_list_first(*rsc_info);
+ while ( cur != NULL ) {
+ get_ra_pathname(RA_PATH, cur->data, NULL, ra_pathname);
+ if ( (fp = fopen(ra_pathname, "r")) == NULL ) {
+ tmp = g_list_next(cur);
+ *rsc_info = g_list_remove(*rsc_info, cur->data);
+ if (cur->data)
+ g_free(cur->data);
+ cur = tmp;
+ continue;
+ }
+ is_lsb_script = FALSE;
+ next_continue = FALSE;
+ found_begin_tag = FALSE;
+ while (NULL != fgets(buffer, BUFLEN, fp)) {
+ /* Handle the lines over BUFLEN(80) columns, only
+ * the first part is compared.
+ */
+ if ( next_continue == TRUE ) {
+ continue;
+ }
+ if (strlen(buffer) == BUFLEN ) {
+ next_continue = TRUE;
+ } else {
+ next_continue = FALSE;
+ }
+ /* Shorten the search time */
+ if (buffer[0] != '#' && buffer[0] != ' '
+ && buffer[0] != '\n') {
+ break; /* donnot find */
+ }
+
+ if (found_begin_tag == TRUE && 0 == strncasecmp(buffer
+ , LSB_INITSCRIPT_INFOEND_TAG
+ , strlen(LSB_INITSCRIPT_INFOEND_TAG)) ) {
+ is_lsb_script = TRUE;
+ break;
+ }
+ if (found_begin_tag == FALSE && 0 == strncasecmp(buffer
+ , LSB_INITSCRIPT_INFOBEGIN_TAG
+ , strlen(LSB_INITSCRIPT_INFOBEGIN_TAG)) ) {
+ found_begin_tag = TRUE;
+ }
+ }
+ fclose(fp);
+ tmp = g_list_next(cur);
+
+/*
+ * Temporarily remove the filter to the initscript, or many initscripts on
+ * many distros, such as RHEL4 and fedora5, cannot be used by management GUI.
+ * Please refer to the bug
+ * http://www.osdl.org/developer_bugzilla/show_bug.cgi?id=1250
+ */
+
+#if 0
+ if ( is_lsb_script != TRUE ) {
+ *rsc_info = g_list_remove(*rsc_info, cur->data);
+ g_free(cur->data);
+ }
+#else
+ (void) is_lsb_script;
+#endif
+ cur = tmp;
+ }
+
+ return g_list_length(*rsc_info);
+}
+
+static int
+prepare_cmd_parameters(const char * rsc_type, const char * op_type,
+ GHashTable * params_ht, RA_ARGV params_argv)
+{
+ int tmp_len;
+ int ht_size = 0;
+#if 0
+ /* Reserve it for possible furture use */
+ int index;
+ void * value_tmp = NULL;
+ char buf_tmp[20];
+#endif
+
+ if (params_ht) {
+ ht_size = g_hash_table_size(params_ht);
+ }
+
+ /* Need 3 additonal spaces for accomodating:
+ * argv[0] = RA_file_name(RA_TYPE)
+ * argv[1] = operation
+ * a terminal NULL
+ */
+ if ( ht_size+3 > MAX_PARAMETER_NUM ) {
+ cl_log(LOG_ERR, "Too many parameters");
+ return -1;
+ }
+
+ tmp_len = strnlen(rsc_type, MAX_LENGTH_OF_RSCNAME);
+ params_argv[0] = g_strndup(rsc_type, tmp_len);
+ /* Add operation code as the first argument */
+ tmp_len = strnlen(op_type, MAX_LENGTH_OF_OPNAME);
+ params_argv[1] = g_strndup(op_type, tmp_len);
+
+ /*
+ * No actual arguments needed except op_type.
+ * Add the teminating NULL pointer.
+ */
+ params_argv[2] = NULL;
+ if ( (ht_size != 0) && (0 != STRNCMP_CONST(op_type, "status")) ) {
+ cl_log(LOG_WARNING, "For LSB init script, no additional "
+ "parameters are needed.");
+ }
+
+/* Actually comment the following code, but I still think it may be used
+ * in the future for LSB none-initial scripts, so reserver it.
+ */
+#if 0
+ /* Now suppose the parameter formate stored in Hashtabe is like
+ * key="1", value="-Wl,soname=test"
+ * Moreover, the key is supposed as a string transfered from an integer.
+ * It may be changed in the future.
+ */
+ for (index = 1; index <= ht_size; index++ ) {
+ snprintf(buf_tmp, sizeof(buf_tmp), "%d", index);
+ value_tmp = g_hash_table_lookup(params_ht, buf_tmp);
+ /* suppose the key is consecutive */
+ if ( value_tmp == NULL ) {
+ cl_log(LOG_ERR, "Parameter ordering error in"\
+ "prepare_cmd_parameters, raexeclsb.c");
+ return -1;
+ }
+ params_argv[index+1] = g_strdup((char *)value_tmp);
+ }
+#endif
+
+ return 0;
+}
+
+static char*
+get_resource_meta(const char* rsc_type, const char* provider)
+{
+ char ra_pathname[RA_MAX_NAME_LENGTH];
+ FILE * fp;
+ gboolean next_continue;
+ GString * meta_data;
+ const size_t BUFLEN = 132;
+ char buffer[BUFLEN];
+ char * provides = NULL,
+ * req_start = NULL,
+ * req_stop = NULL,
+ * shld_start = NULL,
+ * shld_stop = NULL,
+ * dflt_start = NULL,
+ * dflt_stop = NULL,
+ * s_dscrpt = NULL,
+ * xml_l_dscrpt = NULL;
+ GString * l_dscrpt = NULL;
+
+ /*
+ * Use the following tags to find the LSb-compliant comment block.
+ * "### BEGIN INIT INFO" and "### END INIT INFO"
+ */
+ get_ra_pathname(RA_PATH, rsc_type, NULL, ra_pathname);
+ if ( (fp = fopen(ra_pathname, "r")) == NULL ) {
+ cl_log(LOG_ERR, "Failed to open lsb RA %s. No meta-data gotten."
+ , rsc_type);
+ return NULL;
+ }
+ meta_data = g_string_new("");
+
+ next_continue = FALSE;
+
+/*
+ * Is not stick to the rule that the description should be located in the
+ * comment block between "### BEGIN INIT INFO" and "### END INIT INFO".
+ * Please refer to the bug
+ * http://www.osdl.org/developer_bugzilla/show_bug.cgi?id=1250
+ */
+#if 0
+ while (NULL != fgets(buffer, BUFLEN, fp)) {
+ /* Handle the lines over BUFLEN(80) columns, only
+ * the first part is compared.
+ */
+ if ( next_continue == TRUE ) {
+ continue;
+ }
+ if (strlen(buffer) == BUFLEN ) {
+ next_continue = TRUE;
+ } else {
+ next_continue = FALSE;
+ }
+
+ if ( 0 == strncasecmp(buffer , LSB_INITSCRIPT_INFOBEGIN_TAG
+ , strlen(LSB_INITSCRIPT_INFOBEGIN_TAG)) ) {
+ break;
+ }
+ }
+#else
+ (void) next_continue;
+#endif
+
+ /* Enter into the lsb-compliant comment block */
+ while ( NULL != fgets(buffer, BUFLEN, fp) ) {
+ /* Now suppose each of the following eight arguments contain
+ * only one line
+ */
+ RALSB_GET_VALUE(provides, PROVIDES)
+ RALSB_GET_VALUE(req_start, REQ_START)
+ RALSB_GET_VALUE(req_stop, REQ_STOP)
+ RALSB_GET_VALUE(shld_start, SHLD_START)
+ RALSB_GET_VALUE(shld_stop, SHLD_STOP)
+ RALSB_GET_VALUE(dflt_start, DFLT_START)
+ RALSB_GET_VALUE(dflt_stop, DFLT_STOP)
+ RALSB_GET_VALUE(s_dscrpt, SHORT_DSCR)
+
+ /* Long description may cross multiple lines */
+ if ( (l_dscrpt == NULL) && (0 == strncasecmp(buffer, DESCRIPTION
+ , strlen(DESCRIPTION))) ) {
+ l_dscrpt = g_string_new(buffer+strlen(DESCRIPTION));
+ /* Between # and keyword, more than one space, or a tab
+ * character, indicates the continuation line.
+ * Extracted from LSB init script standard
+ */
+ while ( NULL != fgets(buffer, BUFLEN, fp) ) {
+ if ( (0 == strncmp(buffer, "# ", 3))
+ || (0 == strncmp(buffer, "#\t", 2)) ) {
+ buffer[0] = ' ';
+ l_dscrpt = g_string_append(l_dscrpt
+ , buffer);
+ } else {
+ fputs(buffer, fp);
+ break; /* Long description ends */
+ }
+ }
+ continue;
+ }
+ if( l_dscrpt )
+ xml_l_dscrpt = (char *)xmlEncodeEntitiesReentrant(NULL,
+ BAD_CAST (l_dscrpt->str));
+
+ if ( 0 == strncasecmp(buffer, LSB_INITSCRIPT_INFOEND_TAG
+ , strlen(LSB_INITSCRIPT_INFOEND_TAG)) ) {
+ /* Get to the out border of LSB comment block */
+ break;
+ }
+
+ if ( buffer[0] != '#' ) {
+ break; /* Out of comment block in the beginning */
+ }
+ }
+ fclose(fp);
+
+ g_string_sprintf( meta_data, meta_data_template, rsc_type
+ , (xml_l_dscrpt==NULL)? rsc_type : xml_l_dscrpt
+ , (s_dscrpt==NULL)? rsc_type : s_dscrpt
+ , (provides==NULL)? "" : provides
+ , (req_start==NULL)? "" : req_start
+ , (req_stop==NULL)? "" : req_stop
+ , (shld_start==NULL)? "" : shld_start
+ , (shld_stop==NULL)? "" : shld_stop
+ , (dflt_start==NULL)? "" : dflt_start
+ , (dflt_stop==NULL)? "" : dflt_stop );
+
+ ZAPXMLOBJ(xml_l_dscrpt);
+ ZAPXMLOBJ(s_dscrpt);
+ ZAPXMLOBJ(provides);
+ ZAPXMLOBJ(req_start);
+ ZAPXMLOBJ(req_stop);
+ ZAPXMLOBJ(shld_start);
+ ZAPXMLOBJ(shld_stop);
+ ZAPXMLOBJ(dflt_start);
+ ZAPXMLOBJ(dflt_stop);
+
+ if( l_dscrpt )
+ g_string_free(l_dscrpt, TRUE);
+ return meta_data->str;
+}
+
+static int
+get_provider_list(const char* ra_type, GList ** providers)
+{
+ if ( providers == NULL ) {
+ cl_log(LOG_ERR, "%s:%d: Parameter error: providers==NULL"
+ , __FUNCTION__, __LINE__);
+ return -2;
+ }
+
+ if ( *providers != NULL ) {
+ cl_log(LOG_ERR, "%s:%d: Parameter error: *providers==NULL."
+ "This will cause memory leak."
+ , __FUNCTION__, __LINE__);
+ }
+
+ /* Now temporarily make it fixed */
+ *providers = g_list_append(*providers, g_strdup("heartbeat"));
+
+ return g_list_length(*providers);
+}
diff --git a/lib/plugins/lrm/raexecocf.c b/lib/plugins/lrm/raexecocf.c
new file mode 100644
index 0000000..f7cd7ed
--- /dev/null
+++ b/lib/plugins/lrm/raexecocf.c
@@ -0,0 +1,496 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * File: raexecocf.c
+ * Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+ * Copyright (c) 2004 International Business Machines
+ *
+ * This code implements the Resource Agent Plugin Module for LSB style.
+ * It's a part of Local Resource Manager. Currently it's used by lrmd only.
+ */
+
+#include <lha_internal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <libgen.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <glib.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/realtime.h>
+#include <pils/plugin.h>
+#include <dirent.h>
+#include <libgen.h> /* Add it for compiling on OSX */
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#include <lrm/raexec.h>
+
+# define PIL_PLUGINTYPE RA_EXEC_TYPE
+# define PIL_PLUGINTYPE_S "RAExec"
+# define PIL_PLUGINLICENSE LICENSE_PUBDOM
+# define PIL_PLUGINLICENSEURL URL_PUBDOM
+
+# define PIL_PLUGIN ocf
+# define PIL_PLUGIN_S "ocf"
+/*
+ * Are there multiple paths? Now according to OCF spec, the answer is 'no'.
+ * But actually or for future?
+ */
+static const char * RA_PATH = OCF_RA_DIR;
+
+/* The begin of exported function list */
+static int execra(const char * rsc_id,
+ const char * rsc_type,
+ const char * provider,
+ const char * op_type,
+ const int timeout,
+ GHashTable * params);
+static uniform_ret_execra_t map_ra_retvalue(int ret_execra,
+ const char * op_type, const char * std_output);
+static int get_resource_list(GList ** rsc_info);
+static char* get_resource_meta(const char* rsc_type, const char* provider);
+static int get_provider_list(const char* ra_type, GList ** providers);
+
+/* The end of exported function list */
+
+/* The begin of internal used function & data list */
+static void add_OCF_prefix(GHashTable * params, GHashTable * new_params);
+static void add_OCF_env_vars(GHashTable * env, const char * rsc_id,
+ const char * rsc_type, const char * provider);
+static void add_prefix_foreach(gpointer key, gpointer value,
+ gpointer user_data);
+
+static void hash_to_str(GHashTable * , GString *);
+static void hash_to_str_foreach(gpointer key, gpointer value,
+ gpointer user_data);
+
+static int raexec_setenv(GHashTable * env_params);
+static void set_env(gpointer key, gpointer value, gpointer user_data);
+
+static gboolean let_remove_eachitem(gpointer key, gpointer value,
+ gpointer user_data);
+static int get_providers(const char* class_path, const char* op_type,
+ GList ** providers);
+static void merge_string_list(GList** old, GList* new);
+static gint compare_str(gconstpointer a, gconstpointer b);
+
+/* The end of internal function & data list */
+
+/* Rource agent execution plugin operations */
+static struct RAExecOps raops =
+{ execra,
+ map_ra_retvalue,
+ get_resource_list,
+ get_provider_list,
+ get_resource_meta
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static void* OurImports;
+static void* interfprivate;
+
+/*
+ * Our plugin initialization and registration function
+ * It gets called when the plugin gets loaded.
+ */
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interfaces */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S, PIL_PLUGIN_S,
+ &raops, NULL, &OurInterface, &OurImports,
+ interfprivate);
+}
+
+/*
+ * The function to execute a RA.
+ */
+static int
+execra(const char * rsc_id, const char * rsc_type, const char * provider,
+ const char * op_type, const int timeout, GHashTable * params)
+{
+ char ra_pathname[RA_MAX_NAME_LENGTH];
+ GHashTable * tmp_for_setenv;
+ GString * params_gstring;
+ char * inherit_debuglevel = NULL;
+ int save_errno;
+
+ get_ra_pathname(RA_PATH, rsc_type, provider, ra_pathname);
+
+ /* Setup environment correctly */
+ tmp_for_setenv = g_hash_table_new(g_str_hash, g_str_equal);
+ add_OCF_prefix(params, tmp_for_setenv);
+ add_OCF_env_vars(tmp_for_setenv, rsc_id, rsc_type, provider);
+ raexec_setenv(tmp_for_setenv);
+ g_hash_table_foreach_remove(tmp_for_setenv, let_remove_eachitem, NULL);
+ g_hash_table_destroy(tmp_for_setenv);
+
+ /* let this log show only high loglevel. */
+ inherit_debuglevel = getenv(HADEBUGVAL);
+ if ((inherit_debuglevel != NULL) && (atoi(inherit_debuglevel) > 1)) {
+ params_gstring = g_string_new("");
+ hash_to_str(params, params_gstring);
+ cl_log(LOG_DEBUG, "RA instance %s executing: OCF::%s %s. Parameters: "
+ "{%s}", rsc_id, rsc_type, op_type, params_gstring->str);
+ g_string_free(params_gstring, TRUE);
+ }
+
+ closefiles(); /* don't leak open files */
+ /* execute the RA */
+ execl(ra_pathname, ra_pathname, op_type, (const char *)NULL);
+ /* oops, exec failed */
+ save_errno = errno; /* cl_perror may change errno */
+ cl_perror("(%s:%s:%d) execl failed for %s"
+ , __FILE__, __FUNCTION__, __LINE__, ra_pathname);
+ errno = save_errno;
+ exit(get_failed_exec_rc());
+}
+
+static uniform_ret_execra_t
+map_ra_retvalue(int ret_execra, const char * op_type, const char * std_output)
+{
+ /* Because the UNIFORM_RET_EXECRA is compatible with OCF standard,
+ * no actual mapping except validating, which ensure the return code
+ * will be in the range 0 to 7. Too strict?
+ */
+ if (ret_execra < 0 || ret_execra > 9) {
+ cl_log(LOG_WARNING, "mapped the invalid return code %d."
+ , ret_execra);
+ ret_execra = EXECRA_UNKNOWN_ERROR;
+ }
+ return ret_execra;
+}
+
+static gint
+compare_str(gconstpointer a, gconstpointer b)
+{
+ return strncmp(a,b,RA_MAX_NAME_LENGTH);
+}
+
+static int
+get_resource_list(GList ** rsc_info)
+{
+ struct dirent **namelist;
+ GList* item;
+ int file_num;
+ char subdir[FILENAME_MAX+1];
+
+ if ( rsc_info == NULL ) {
+ cl_log(LOG_ERR, "Parameter error: get_resource_list");
+ return -2;
+ }
+
+ if ( *rsc_info != NULL ) {
+ cl_log(LOG_ERR, "Parameter error: get_resource_list."\
+ "will cause memory leak.");
+ *rsc_info = NULL;
+ }
+ file_num = scandir(RA_PATH, &namelist, NULL, alphasort);
+ if (file_num < 0) {
+ return -2;
+ }
+ while (file_num--) {
+ GList* ra_subdir = NULL;
+ struct stat prop;
+ if ('.' == namelist[file_num]->d_name[0]) {
+ free(namelist[file_num]);
+ continue;
+ }
+
+ snprintf(subdir,FILENAME_MAX,"%s/%s",
+ RA_PATH, namelist[file_num]->d_name);
+
+ if (stat(subdir, &prop) == -1) {
+ cl_perror("%s:%s:%d: stat failed for %s"
+ , __FILE__, __FUNCTION__, __LINE__, subdir);
+ free(namelist[file_num]);
+ continue;
+ } else if (!S_ISDIR(prop.st_mode)) {
+ free(namelist[file_num]);
+ continue;
+ }
+
+ get_runnable_list(subdir,&ra_subdir);
+
+ merge_string_list(rsc_info,ra_subdir);
+
+ while (NULL != (item = g_list_first(ra_subdir))) {
+ ra_subdir = g_list_remove_link(ra_subdir, item);
+ g_free(item->data);
+ g_list_free_1(item);
+ }
+
+ free(namelist[file_num]);
+ }
+ free(namelist);
+
+ return 0;
+}
+
+static void
+merge_string_list(GList** old, GList* new)
+{
+ GList* item = NULL;
+ char* newitem;
+ for( item=g_list_first(new); NULL!=item; item=g_list_next(item)){
+ if (!g_list_find_custom(*old, item->data,compare_str)){
+ newitem = g_strndup(item->data,RA_MAX_NAME_LENGTH);
+ *old = g_list_append(*old, newitem);
+ }
+ }
+}
+
+static int
+get_provider_list(const char* ra_type, GList ** providers)
+{
+ int ret;
+ ret = get_providers(RA_PATH, ra_type, providers);
+ if (0>ret) {
+ cl_log(LOG_ERR, "scandir failed in OCF RA plugin");
+ }
+ return ret;
+}
+
+static char*
+get_resource_meta(const char* rsc_type, const char* provider)
+{
+ const int BUFF_LEN=4096;
+ int read_len = 0;
+ char buff[BUFF_LEN];
+ char* data = NULL;
+ GString* g_str_tmp = NULL;
+ char ra_pathname[RA_MAX_NAME_LENGTH];
+ FILE* file = NULL;
+ GHashTable * tmp_for_setenv;
+ struct timespec short_sleep = {0,200000000L}; /*20ms*/
+
+ get_ra_pathname(RA_PATH, rsc_type, provider, ra_pathname);
+
+ strncat(ra_pathname, " meta-data",RA_MAX_NAME_LENGTH-strlen(ra_pathname)-1);
+ tmp_for_setenv = g_hash_table_new(g_str_hash, g_str_equal);
+ add_OCF_env_vars(tmp_for_setenv, "DUMMY_INSTANCE", rsc_type, provider);
+ raexec_setenv(tmp_for_setenv);
+ g_hash_table_foreach_remove(tmp_for_setenv, let_remove_eachitem, NULL);
+ g_hash_table_destroy(tmp_for_setenv);
+
+ file = popen(ra_pathname, "r");
+ if (NULL==file) {
+ cl_log(LOG_ERR, "%s: popen failed: %s", __FUNCTION__, strerror(errno));
+ return NULL;
+ }
+
+ g_str_tmp = g_string_new("");
+ while(!feof(file)) {
+ read_len = fread(buff, 1, BUFF_LEN - 1, file);
+ if (0<read_len) {
+ *(buff+read_len) = '\0';
+ g_string_append(g_str_tmp, buff);
+ }
+ else {
+ nanosleep(&short_sleep,NULL);
+ }
+ }
+ if( pclose(file) ) {
+ cl_log(LOG_ERR, "%s: pclose failed: %s", __FUNCTION__, strerror(errno));
+ }
+ if (0 == g_str_tmp->len) {
+ g_string_free(g_str_tmp, TRUE);
+ return NULL;
+ }
+ data = (char*)g_new(char, g_str_tmp->len+1);
+ data[0] = data[g_str_tmp->len] = 0;
+ strncpy(data, g_str_tmp->str, g_str_tmp->len);
+
+ g_string_free(g_str_tmp, TRUE);
+
+ return data;
+}
+
+static void
+add_OCF_prefix(GHashTable * env_params, GHashTable * new_env_params)
+{
+ if (env_params) {
+ g_hash_table_foreach(env_params, add_prefix_foreach,
+ new_env_params);
+ }
+}
+
+static void
+add_prefix_foreach(gpointer key, gpointer value, gpointer user_data)
+{
+ const int MAX_LENGTH_OF_ENV = 128;
+ int prefix = STRLEN_CONST("OCF_RESKEY_");
+ GHashTable * new_hashtable = (GHashTable *) user_data;
+ char * newkey;
+ int keylen = strnlen((char*)key, MAX_LENGTH_OF_ENV-prefix)+prefix+1;
+
+ newkey = g_new(gchar, keylen);
+ strncpy(newkey, "OCF_RESKEY_", keylen);
+ strncat(newkey, key, keylen-strlen(newkey)-1);
+ g_hash_table_insert(new_hashtable, (gpointer)newkey, g_strdup(value));
+}
+
+static void
+hash_to_str(GHashTable * params , GString * str)
+{
+ if (params) {
+ g_hash_table_foreach(params, hash_to_str_foreach, str);
+ }
+}
+
+static void
+hash_to_str_foreach(gpointer key, gpointer value, gpointer user_data)
+{
+ char buffer_tmp[60];
+ GString * str = (GString *)user_data;
+
+ snprintf(buffer_tmp, 60, "%s=%s ", (char *)key, (char *)value);
+ str = g_string_append(str, buffer_tmp);
+}
+
+static gboolean
+let_remove_eachitem(gpointer key, gpointer value, gpointer user_data)
+{
+ g_free(key);
+ g_free(value);
+ return TRUE;
+}
+
+static int
+raexec_setenv(GHashTable * env_params)
+{
+ if (env_params) {
+ g_hash_table_foreach(env_params, set_env, NULL);
+ }
+ return 0;
+}
+
+static void
+set_env(gpointer key, gpointer value, gpointer user_data)
+{
+ if (setenv(key, value, 1) != 0) {
+ cl_log(LOG_ERR, "setenv failed in raexecocf.");
+ }
+}
+
+static int
+get_providers(const char* class_path, const char* ra_type, GList ** providers)
+{
+ struct dirent **namelist;
+ int file_num;
+
+ if ( providers == NULL ) {
+ cl_log(LOG_ERR, "Parameter error: get_providers");
+ return -2;
+ }
+
+ if ( *providers != NULL ) {
+ cl_log(LOG_ERR, "Parameter error: get_providers."\
+ "will cause memory leak.");
+ *providers = NULL;
+ }
+
+ file_num = scandir(class_path, &namelist, 0, alphasort);
+ if (file_num < 0) {
+ return -2;
+ }else{
+ char tmp_buffer[FILENAME_MAX+1];
+ struct stat prop;
+
+ while (file_num--) {
+ if ('.' == namelist[file_num]->d_name[0]) {
+ free(namelist[file_num]);
+ continue;
+ }
+ snprintf(tmp_buffer,FILENAME_MAX,"%s/%s",
+ class_path, namelist[file_num]->d_name);
+ stat(tmp_buffer, &prop);
+ if (!S_ISDIR(prop.st_mode)) {
+ free(namelist[file_num]);
+ continue;
+ }
+
+ snprintf(tmp_buffer,FILENAME_MAX,"%s/%s/%s",
+ class_path, namelist[file_num]->d_name, ra_type);
+
+ if ( filtered(tmp_buffer) == TRUE ) {
+ *providers = g_list_append(*providers,
+ g_strdup(namelist[file_num]->d_name));
+ }
+ free(namelist[file_num]);
+ }
+ free(namelist);
+ }
+ return g_list_length(*providers);
+}
+
+static void
+add_OCF_env_vars(GHashTable * env, const char * rsc_id,
+ const char * rsc_type, const char * provider)
+{
+ if ( env == NULL ) {
+ cl_log(LOG_WARNING, "env should not be a NULL pointer.");
+ return;
+ }
+
+ g_hash_table_insert(env, g_strdup("OCF_RA_VERSION_MAJOR"),
+ g_strdup("1"));
+ g_hash_table_insert(env, g_strdup("OCF_RA_VERSION_MINOR"),
+ g_strdup("0"));
+ g_hash_table_insert(env, g_strdup("OCF_ROOT"),
+ g_strdup(OCF_ROOT_DIR));
+
+ if ( rsc_id != NULL ) {
+ g_hash_table_insert(env, g_strdup("OCF_RESOURCE_INSTANCE"),
+ g_strdup(rsc_id));
+ }
+
+ /* Currently the rsc_type=="the filename of the RA script/executable",
+ * It seems always correct even in the furture. ;-)
+ */
+ if ( rsc_type != NULL ) {
+ g_hash_table_insert(env, g_strdup("OCF_RESOURCE_TYPE"),
+ g_strdup(rsc_type));
+ }
+
+ /* Notes: this is not added to specification yet. Sept 10,2004 */
+ if ( provider != NULL ) {
+ g_hash_table_insert(env, g_strdup("OCF_RESOURCE_PROVIDER"),
+ g_strdup(provider));
+ }
+}
+
diff --git a/lib/plugins/lrm/raexecupstart.c b/lib/plugins/lrm/raexecupstart.c
new file mode 100644
index 0000000..baa0278
--- /dev/null
+++ b/lib/plugins/lrm/raexecupstart.c
@@ -0,0 +1,222 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * File: raexecupstart.c
+ * Copyright (C) 2010 Senko Rasic <senko.rasic@dobarkod.hr>
+ * Copyright (c) 2010 Ante Karamatic <ivoks@init.hr>
+ *
+ * Heavily based on raexeclsb.c and raexechb.c:
+ * Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+ * Copyright (c) 2004 International Business Machines
+ *
+ * This code implements the Resource Agent Plugin Module for Upstart.
+ * It's a part of Local Resource Manager. Currently it's used by lrmd only.
+ */
+
+#define PIL_PLUGINTYPE RA_EXEC_TYPE
+#define PIL_PLUGIN upstart
+#define PIL_PLUGINTYPE_S "RAExec"
+#define PIL_PLUGIN_S "upstart"
+#define PIL_PLUGINLICENSE LICENSE_PUBDOM
+#define PIL_PLUGINLICENSEURL URL_PUBDOM
+
+#include <lha_internal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <dirent.h>
+#include <libgen.h> /* Add it for compiling on OSX */
+#include <glib.h>
+#include <clplumbing/cl_log.h>
+#include <pils/plugin.h>
+#include <lrm/raexec.h>
+#include <libgen.h>
+
+#include <glib-object.h>
+
+#include <libxml/entities.h>
+
+#include "upstart-dbus.h"
+
+#define meta_data_template \
+"<?xml version=\"1.0\"?>\n"\
+"<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"\
+"<resource-agent name=\"%s\" version=\"0.1\">\n"\
+" <version>1.0</version>\n"\
+" <longdesc lang=\"en\">\n"\
+" %s"\
+" </longdesc>\n"\
+" <shortdesc lang=\"en\">%s</shortdesc>\n"\
+" <parameters>\n"\
+" </parameters>\n"\
+" <actions>\n"\
+" <action name=\"start\" timeout=\"15\" />\n"\
+" <action name=\"stop\" timeout=\"15\" />\n"\
+" <action name=\"status\" timeout=\"15\" />\n"\
+" <action name=\"restart\" timeout=\"15\" />\n"\
+" <action name=\"monitor\" timeout=\"15\" interval=\"15\" start-delay=\"15\" />\n"\
+" <action name=\"meta-data\" timeout=\"5\" />\n"\
+" </actions>\n"\
+" <special tag=\"upstart\">\n"\
+" </special>\n"\
+"</resource-agent>\n"
+
+/* The begin of exported function list */
+static int execra(const char * rsc_id,
+ const char * rsc_type,
+ const char * provider,
+ const char * op_type,
+ const int timeout,
+ GHashTable * params);
+
+static uniform_ret_execra_t map_ra_retvalue(int ret_execra
+ , const char * op_type, const char * std_output);
+static char* get_resource_meta(const char* rsc_type, const char* provider);
+static int get_resource_list(GList ** rsc_info);
+static int get_provider_list(const char* ra_type, GList ** providers);
+
+/* The end of exported function list */
+
+/* The begin of internal used function & data list */
+#define MAX_PARAMETER_NUM 40
+
+const int MAX_LENGTH_OF_RSCNAME = 40,
+ MAX_LENGTH_OF_OPNAME = 40;
+
+typedef char * RA_ARGV[MAX_PARAMETER_NUM];
+
+/* The end of internal function & data list */
+
+/* Rource agent execution plugin operations */
+static struct RAExecOps raops =
+{ execra,
+ map_ra_retvalue,
+ get_resource_list,
+ get_provider_list,
+ get_resource_meta
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static void* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports)
+{
+ PluginImports = imports;
+ OurPlugin = us;
+
+ imports->register_plugin(us, &OurPIExports);
+
+ g_type_init ();
+
+ return imports->register_interface(us, PIL_PLUGINTYPE_S, PIL_PLUGIN_S,
+ &raops, NULL, &OurInterface, &OurImports,
+ interfprivate);
+}
+
+static int
+execra( const char * rsc_id, const char * rsc_type, const char * provider,
+ const char * op_type, const int timeout, GHashTable * params)
+{
+ UpstartJobCommand cmd;
+
+ if (!g_strcmp0(op_type, "meta-data")) {
+ printf("%s", get_resource_meta(rsc_type, provider));
+ exit(EXECRA_OK);
+ } else if (!g_strcmp0(op_type, "monitor") || !g_strcmp0(op_type, "status")) {
+ gboolean running = upstart_job_is_running (rsc_type);
+ printf("%s", running ? "running" : "stopped");
+
+ if (running)
+ exit(EXECRA_OK);
+ else
+ exit(EXECRA_NOT_RUNNING);
+ } else if (!g_strcmp0(op_type, "start")) {
+ cmd = UPSTART_JOB_START;
+ } else if (!g_strcmp0(op_type, "stop")) {
+ cmd = UPSTART_JOB_STOP;
+ } else if (!g_strcmp0(op_type, "restart")) {
+ cmd = UPSTART_JOB_RESTART;
+ } else {
+ exit(EXECRA_UNIMPLEMENT_FEATURE);
+ }
+
+ /* It'd be better if it returned GError, so we can distinguish
+ * between failure modes (can't contact upstart, no such job,
+ * or failure to do action. */
+ if (upstart_job_do(rsc_type, cmd, timeout)) {
+ exit(EXECRA_OK);
+ } else {
+ exit(EXECRA_NO_RA);
+ }
+}
+
+static uniform_ret_execra_t
+map_ra_retvalue(int ret_execra, const char * op_type, const char * std_output)
+{
+ /* no need to map anything, execra() returns correct exit code */
+ return ret_execra;
+}
+
+static int
+get_resource_list(GList ** rsc_info)
+{
+ gchar **jobs;
+ gint i;
+ *rsc_info = NULL;
+
+ jobs = upstart_get_all_jobs();
+
+ if (!jobs)
+ return 0;
+
+ for (i = 0; jobs[i] != NULL; i++) {
+ *rsc_info = g_list_prepend(*rsc_info, jobs[i]);
+ }
+
+ /* free the array, but not the strings */
+ g_free(jobs);
+
+ *rsc_info = g_list_reverse(*rsc_info);
+ return g_list_length(*rsc_info);
+}
+
+static char *
+get_resource_meta (const gchar *rsc_type, const gchar *provider)
+{
+ return g_strdup_printf(meta_data_template, rsc_type,
+ rsc_type, rsc_type);
+}
+
+static int
+get_provider_list (const gchar *ra_type, GList **providers)
+{
+ *providers = g_list_prepend(*providers, g_strdup("upstart"));
+ return g_list_length(*providers);
+}
+
diff --git a/lib/plugins/lrm/upstart-dbus.c b/lib/plugins/lrm/upstart-dbus.c
new file mode 100644
index 0000000..b994d87
--- /dev/null
+++ b/lib/plugins/lrm/upstart-dbus.c
@@ -0,0 +1,406 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * File: upstart-dbus.c
+ * Copyright (C) 2010 Senko Rasic <senko.rasic@dobarkod.hr>
+ * Copyright (c) 2010 Ante Karamatic <ivoks@init.hr>
+ *
+ *
+ * Each exported function is standalone, and creates a new connection to
+ * the upstart daemon. This is because lrmd plugins fork off for exec,
+ * and if we try and share the connection, the whole thing blocks
+ * indefinitely.
+ */
+
+#include "upstart-dbus.h"
+
+#include <glib.h>
+#include <dbus/dbus-glib.h>
+
+#include <dbus/dbus.h>
+
+#include "dbus/Upstart.h"
+#include "dbus/Upstart_Job.h"
+#include "dbus/Upstart_Instance.h"
+
+#include <stdio.h>
+
+#define SYSTEM_BUS_ADDRESS "unix:path=/var/run/dbus/system_bus_socket"
+#define UPSTART_BUS_ADDRESS "unix:abstract=/com/ubuntu/upstart"
+#define UPSTART_SERVICE_NAME "com.ubuntu.Upstart"
+#define UPSTART_MANAGER_PATH "/com/ubuntu/Upstart"
+#define UPSTART_IFACE "com.ubuntu.Upstart0_6"
+#define UPSTART_JOB_IFACE UPSTART_IFACE ".Job"
+#define UPSTART_INSTANCE_IFACE UPSTART_IFACE ".Instance"
+#define UPSTART_ERROR_ALREADY_STARTED UPSTART_IFACE ".Error.AlreadyStarted"
+#define UPSTART_ERROR_UNKNOWN_INSTANCE UPSTART_IFACE ".Error.UnknownInstance"
+
+static DBusGConnection *
+get_connection(void)
+{
+ GError *error = NULL;
+ DBusGConnection *conn;
+
+ conn = dbus_g_bus_get_private(DBUS_BUS_SYSTEM, NULL, &error);
+
+ if (error)
+ {
+ g_error_free(error);
+ error = NULL;
+
+ conn = dbus_g_connection_open("unix:abstract=/com/ubuntu/upstart",
+ &error);
+
+ if (error)
+ {
+ g_warning("Can't connect to either system or Upstart "
+ "DBus bus.");
+ g_error_free(error);
+
+ return NULL;
+ }
+ }
+
+ return conn;
+}
+
+static DBusGProxy *
+new_proxy(DBusGConnection *conn, const gchar *object_path,
+ const gchar *iface)
+{
+ return dbus_g_proxy_new_for_name(conn,
+ UPSTART_SERVICE_NAME,
+ object_path,
+ iface);
+}
+
+static GHashTable *
+get_object_properties(DBusGProxy *obj, const gchar *iface)
+{
+ GError *error = NULL;
+ DBusGProxy *proxy;
+ GHashTable *asv;
+ GHashTable *retval;
+ GHashTableIter iter;
+ gpointer k, v;
+
+ proxy = dbus_g_proxy_new_from_proxy(obj,
+ DBUS_INTERFACE_PROPERTIES, NULL);
+
+ dbus_g_proxy_call(proxy, "GetAll", &error, G_TYPE_STRING,
+ iface, G_TYPE_INVALID,
+ dbus_g_type_get_map("GHashTable",
+ G_TYPE_STRING,
+ G_TYPE_VALUE),
+ &asv, G_TYPE_INVALID);
+
+ if (error) {
+ g_warning("Error getting %s properties: %s", iface, error->message);
+ g_error_free(error);
+ g_object_unref(proxy);
+ return NULL;
+ }
+
+ retval = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, g_free);
+
+ g_hash_table_iter_init(&iter, asv);
+ while (g_hash_table_iter_next(&iter, &k, &v)) {
+ gchar *key = k;
+ GValue *val = v;
+
+ /* all known properties are strings */
+ if (G_VALUE_TYPE(val) == G_TYPE_STRING) {
+ g_hash_table_insert(retval, g_strdup(key),
+ g_value_dup_string(val));
+ }
+ }
+
+ g_hash_table_destroy(asv);
+
+ return retval;
+}
+
+gchar **
+upstart_get_all_jobs(void)
+{
+ DBusGConnection *conn;
+ DBusGProxy *manager;
+ GError *error = NULL;
+ GPtrArray *array;
+ gchar **retval = NULL;
+ gint i, j;
+
+ conn = get_connection();
+ if (!conn)
+ return NULL;
+
+ manager = new_proxy(conn, UPSTART_MANAGER_PATH, UPSTART_IFACE);
+
+ dbus_g_proxy_call(manager, "GetAllJobs", &error, G_TYPE_INVALID,
+ dbus_g_type_get_collection("GPtrArray", DBUS_TYPE_G_OBJECT_PATH),
+ &array, G_TYPE_INVALID);
+
+ if (error)
+ {
+ g_warning("Can't call GetAllJobs: %s", error->message);
+ g_error_free(error);
+ g_object_unref(manager);
+ dbus_g_connection_unref(conn);
+ return NULL;
+ }
+
+ retval = g_new0(gchar *, array->len + 1);
+
+ for (i = 0, j = 0; i < array->len; i++)
+ {
+ DBusGProxy *job;
+
+ job = new_proxy(conn, g_ptr_array_index(array, i),
+ UPSTART_JOB_IFACE);
+
+ if (job) {
+ GHashTable *props = get_object_properties(job,
+ UPSTART_JOB_IFACE);
+
+ if (props) {
+ gchar *name = g_hash_table_lookup(props,
+ "name");
+
+ if (name)
+ retval[j++] = g_strdup(name);
+
+ g_hash_table_destroy(props);
+ }
+
+ g_object_unref(job);
+ }
+ }
+
+ g_ptr_array_free(array, TRUE);
+
+ g_object_unref(manager);
+ dbus_g_connection_unref(conn);
+
+ return retval;
+}
+
+static DBusGProxy *
+upstart_get_job_by_name(DBusGConnection *conn, DBusGProxy *manager,
+ const gchar *name)
+{
+ GError *error = NULL;
+ gchar *object_path;
+ DBusGProxy *retval;
+
+ dbus_g_proxy_call(manager, "GetJobByName", &error, G_TYPE_STRING,
+ name, G_TYPE_INVALID, DBUS_TYPE_G_OBJECT_PATH, &object_path,
+ G_TYPE_INVALID);
+
+ if (error)
+ {
+ g_warning("Error calling GetJobByName: %s", error->message);
+ g_error_free(error);
+ return NULL;
+ }
+
+ retval = new_proxy(conn, object_path, UPSTART_JOB_IFACE);
+
+ g_free(object_path);
+
+ return retval;
+}
+
+static gchar **
+get_job_instances(DBusGProxy *job)
+{
+ GError *error = NULL;
+ GPtrArray *array;
+ gchar **retval;
+ gint i;
+
+ dbus_g_proxy_call(job, "GetAllInstances", &error, G_TYPE_INVALID,
+ dbus_g_type_get_collection("GPtrArray", DBUS_TYPE_G_OBJECT_PATH),
+ &array, G_TYPE_INVALID);
+
+ if (error)
+ {
+ g_warning("Can't call GetAllInstances: %s", error->message);
+ g_error_free(error);
+ return NULL;
+ }
+
+ retval = g_new0(gchar *, array->len + 1);
+
+ for (i = 0; i < array->len; i++)
+ {
+ retval[i] = g_ptr_array_index(array, i);
+ }
+
+ g_ptr_array_free(array, TRUE);
+
+ return retval;
+}
+
+static DBusGProxy *
+get_first_instance(DBusGConnection *conn, DBusGProxy *job)
+{
+ gchar **instances;
+ DBusGProxy *instance = NULL;
+
+ instances = get_job_instances(job);
+
+ if (!instances)
+ return NULL;
+
+ if (*instances)
+ {
+ instance = new_proxy(conn, instances[0],
+ UPSTART_INSTANCE_IFACE);
+ }
+
+ g_strfreev(instances);
+ return instance;
+}
+
+gboolean
+upstart_job_is_running(const gchar *name)
+{
+ DBusGConnection *conn;
+ DBusGProxy *manager;
+ DBusGProxy *job;
+ gboolean retval = FALSE;
+
+ conn = get_connection();
+ if (!conn)
+ return FALSE;
+
+ manager = new_proxy(conn, UPSTART_MANAGER_PATH, UPSTART_IFACE);
+
+ job = upstart_get_job_by_name(conn, manager, name);
+ if (job) {
+ DBusGProxy *instance = get_first_instance(conn, job);
+
+ if (instance) {
+ GHashTable *props = get_object_properties(instance,
+ UPSTART_INSTANCE_IFACE);
+
+ if (props) {
+ const gchar *state = g_hash_table_lookup(props,
+ "state");
+
+ retval = !g_strcmp0(state, "running");
+
+ g_hash_table_destroy(props);
+ }
+
+ g_object_unref(instance);
+ }
+
+ g_object_unref(job);
+ }
+
+ g_object_unref(manager);
+ dbus_g_connection_unref(conn);
+
+ return retval;
+}
+
+gboolean
+upstart_job_do(const gchar *name, UpstartJobCommand cmd, const int timeout)
+{
+ DBusGConnection *conn;
+ DBusGProxy *manager;
+ DBusGProxy *job;
+ gboolean retval;
+
+ conn = get_connection();
+ if (!conn)
+ return FALSE;
+
+ manager = new_proxy(conn, UPSTART_MANAGER_PATH, UPSTART_IFACE);
+
+ job = upstart_get_job_by_name(conn, manager, name);
+ if (job) {
+ GError *error = NULL;
+ const gchar *cmd_name = NULL;
+ gchar *instance_path = NULL;
+ gchar *no_args[] = { NULL };
+
+ switch (cmd) {
+ case UPSTART_JOB_START:
+ cmd_name = "Start";
+ dbus_g_proxy_call_with_timeout (job, cmd_name,
+ timeout, &error,
+ G_TYPE_STRV, no_args,
+ G_TYPE_BOOLEAN, TRUE,
+ G_TYPE_INVALID,
+ DBUS_TYPE_G_OBJECT_PATH, &instance_path,
+ G_TYPE_INVALID);
+ g_free (instance_path);
+ break;
+ case UPSTART_JOB_STOP:
+ cmd_name = "Stop";
+ dbus_g_proxy_call_with_timeout(job, cmd_name,
+ timeout, &error,
+ G_TYPE_STRV, no_args,
+ G_TYPE_BOOLEAN, TRUE,
+ G_TYPE_INVALID,
+ G_TYPE_INVALID);
+ break;
+ case UPSTART_JOB_RESTART:
+ cmd_name = "Restart";
+ dbus_g_proxy_call_with_timeout (job, cmd_name,
+ timeout, &error,
+ G_TYPE_STRV, no_args,
+ G_TYPE_BOOLEAN, TRUE,
+ G_TYPE_INVALID,
+ DBUS_TYPE_G_OBJECT_PATH, &instance_path,
+ G_TYPE_INVALID);
+ g_free (instance_path);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ if (error) {
+ g_warning("Could not issue %s: %s", cmd_name,
+ error->message);
+
+ /* ignore "already started" or "not running" errors */
+ if (dbus_g_error_has_name(error,
+ UPSTART_ERROR_ALREADY_STARTED) ||
+ dbus_g_error_has_name(error,
+ UPSTART_ERROR_UNKNOWN_INSTANCE)) {
+ retval = TRUE;
+ } else {
+ retval = FALSE;
+ }
+ g_error_free(error);
+ } else {
+ retval = TRUE;
+ }
+
+ g_object_unref(job);
+ } else {
+ retval = FALSE;
+ }
+
+ g_object_unref(manager);
+ dbus_g_connection_unref(conn);
+ return retval;
+}
+
+
diff --git a/lib/plugins/lrm/upstart-dbus.h b/lib/plugins/lrm/upstart-dbus.h
new file mode 100644
index 0000000..bc72c95
--- /dev/null
+++ b/lib/plugins/lrm/upstart-dbus.h
@@ -0,0 +1,36 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * File: upstart-dbus.c
+ * Copyright (C) 2010 Senko Rasic <senko.rasic@dobarkod.hr>
+ * Copyright (c) 2010 Ante Karamatic <ivoks@init.hr>
+ */
+#ifndef _UPSTART_DBUS_H_
+#define _UPSTART_DBUS_H_
+
+#include <glib.h>
+
+typedef enum {
+ UPSTART_JOB_START,
+ UPSTART_JOB_STOP,
+ UPSTART_JOB_RESTART
+} UpstartJobCommand;
+
+G_GNUC_INTERNAL gchar **upstart_get_all_jobs(void);
+G_GNUC_INTERNAL gboolean upstart_job_do(const gchar *name, UpstartJobCommand cmd, const int timeout);
+G_GNUC_INTERNAL gboolean upstart_job_is_running (const gchar *name);
+
+#endif /* _UPSTART_DBUS_H_ */
+
diff --git a/lib/plugins/stonith/Makefile.am b/lib/plugins/stonith/Makefile.am
new file mode 100644
index 0000000..01f2f4a
--- /dev/null
+++ b/lib/plugins/stonith/Makefile.am
@@ -0,0 +1,216 @@
+#
+# stonith: STONITH plugins for Linux-HA
+#
+# Copyright (C) 2001 Alan Robertson
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+SUBDIRS = external
+
+INCFILES = stonith_expect_helpers.h \
+ stonith_plugin_common.h \
+ stonith_signal.h \
+ stonith_config_xml.h
+
+idir=$(includedir)/stonith
+
+i_HEADERS = $(INCFILES)
+
+INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \
+ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \
+ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl
+
+
+AM_CFLAGS = @NON_FATAL_CFLAGS@
+
+#
+# We need "vacmclient_api.h" and -lvacmclient to make the VACM
+# plugin work.
+#
+if USE_VACM
+vacm_LIB = vacm.la
+else
+vacm_LIB =
+endif
+
+#
+# We need <ucd-snmp/asn1.h>, <ucd-snmp/snmp_api.h>, <ucd-snmp/snmp.h>
+# <ucd-snmp/snmp_client.h>, <ucd-snmp/mib.h>, -lsnmp and -lcrypto
+# for the apcmastersnmp plugin to work
+#
+
+if USE_APC_SNMP
+apcmastersnmp_LIB = apcmastersnmp.la wti_mpc.la
+else
+apcmastersnmp_LIB =
+endif
+if IPMILAN_BUILD
+OPENIPMI_LIB = -lOpenIPMI -lOpenIPMIposix -lOpenIPMIutils
+libipmilan_LIB = libipmilan.la
+ipmilan_LIB = ipmilan.la
+ipmilan_TEST = ipmilantest
+else
+OPENIPMI_LIB =
+libipmilan_LIB =
+ipmilan_LIB =
+endif
+#
+# We need <curl/curl.h>, <libxml/xmlmemory.h>,
+# <libxml/parser.h>, <libxml/xpath.h>,
+# -lcurl and -lxml2 for the drac3 plugin to work
+#
+if USE_DRAC3
+drac3_LIB = drac3.la
+else
+drac3_LIB =
+endif
+
+#
+# We need OpenHPI to make the IBM BladeCenter plugin work.
+#
+if USE_OPENHPI
+bladehpi_LIB = bladehpi.la
+else
+bladehpi_LIB =
+endif
+
+noinst_PROGRAMS = $(ipmilan_TEST)
+ipmilantest_SOURCES = ipmilan_test.c
+ipmilantest_LDADD = $(libipmilan_LIB) \
+ $(top_builddir)/lib/pils/libpils.la \
+ $(top_builddir)/lib/stonith/libstonith.la \
+ $(OPENIPMI_LIB)
+
+## libraries
+
+plugindir = $(stonith_plugindir)/stonith2
+
+plugin_LTLIBRARIES = apcmaster.la \
+ $(apcmastersnmp_LIB) \
+ apcsmart.la \
+ baytech.la \
+ $(bladehpi_LIB) \
+ cyclades.la \
+ $(drac3_LIB) \
+ external.la \
+ rhcs.la \
+ ibmhmc.la \
+ $(ipmilan_LIB) \
+ meatware.la \
+ null.la \
+ nw_rpc100s.la \
+ rcd_serial.la \
+ rps10.la \
+ ssh.la \
+ suicide.la \
+ $(vacm_LIB) \
+ wti_nps.la
+noinst_LTLIBRARIES = $(libipmilan_LIB)
+
+apcmaster_la_SOURCES = apcmaster.c $(INCFILES)
+apcmaster_la_LDFLAGS = -export-dynamic -module -avoid-version
+apcmaster_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+apcmastersnmp_la_SOURCES= apcmastersnmp.c $(INCFILES)
+apcmastersnmp_la_LDFLAGS= -export-dynamic -module -avoid-version @SNMPLIB@ \
+ @CRYPTOLIB@
+apcmastersnmp_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+apcsmart_la_SOURCES = apcsmart.c $(INCFILES)
+apcsmart_la_LDFLAGS = -export-dynamic -module -avoid-version
+apcsmart_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+baytech_la_SOURCES = baytech.c $(INCFILES)
+baytech_la_LDFLAGS = -export-dynamic -module -avoid-version
+baytech_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+bladehpi_la_SOURCES = bladehpi.c $(INCFILES)
+bladehpi_la_LDFLAGS = -export-dynamic -module -avoid-version
+bladehpi_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB) -lopenhpi
+
+cyclades_la_SOURCES = cyclades.c $(INCFILES)
+cyclades_la_LDFLAGS = -export-dynamic -module -avoid-version
+cyclades_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+drac3_la_SOURCES = drac3.c drac3_command.c drac3_command.h drac3_hash.c drac3_hash.h $(INCFILES)
+drac3_la_LDFLAGS = -export-dynamic -module -avoid-version
+drac3_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la -lcurl -lxml2 $(GLIBLIB)
+
+external_la_SOURCES = external.c $(INCFILES)
+external_la_LDFLAGS = -export-dynamic -module -avoid-version
+external_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la
+
+rhcs_la_SOURCES = rhcs.c $(INCFILES)
+rhcs_la_LDFLAGS = -export-dynamic -module -avoid-version
+rhcs_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la
+
+ibmhmc_la_SOURCES = ibmhmc.c $(INCFILES)
+ibmhmc_la_LDFLAGS = -export-dynamic -module -avoid-version
+ibmhmc_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la $(GLIBLIB)
+
+ipmilan_la_SOURCES = ipmilan.c ipmilan.h ipmilan_command.c $(INCFILES)
+ipmilan_la_LDFLAGS = -export-dynamic -module -avoid-version
+ipmilan_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la $(OPENIPMI_LIB) $(GLIBLIB)
+
+libipmilan_la_SOURCES = ipmilan.c ipmilan.h ipmilan_command.c $(INCFILES)
+libipmilan_la_LDFLAGS = -version-info 1:0:0
+libipmilan_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la $(OPENIPMI_LIB) $(GLIBLIB)
+
+meatware_la_SOURCES = meatware.c $(INCFILES)
+meatware_la_LDFLAGS = -export-dynamic -module -avoid-version
+meatware_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+null_la_SOURCES = null.c $(INCFILES)
+null_la_LDFLAGS = -export-dynamic -module -avoid-version
+null_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+nw_rpc100s_la_SOURCES = nw_rpc100s.c $(INCFILES)
+nw_rpc100s_la_LDFLAGS = -export-dynamic -module -avoid-version
+nw_rpc100s_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la $(GLIBLIB)
+
+rcd_serial_la_SOURCES = rcd_serial.c $(INCFILES)
+rcd_serial_la_LDFLAGS = -export-dynamic -module -avoid-version
+rcd_serial_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+rps10_la_SOURCES = rps10.c $(INCFILES)
+rps10_la_LDFLAGS = -export-dynamic -module -avoid-version
+rps10_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+ssh_la_SOURCES = ssh.c $(INCFILES)
+ssh_la_LDFLAGS = -export-dynamic -module -avoid-version
+ssh_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la
+
+vacm_la_SOURCES = vacm.c $(INCFILES)
+vacm_la_LDFLAGS = -export-dynamic -module -avoid-version
+vacm_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la
+
+wti_nps_la_SOURCES = wti_nps.c $(INCFILES)
+wti_nps_la_LDFLAGS = -export-dynamic -module -avoid-version
+wti_nps_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la $(GLIBLIB)
+
+wti_mpc_la_SOURCES= wti_mpc.c $(INCFILES)
+wti_mpc_la_LDFLAGS= -export-dynamic -module -avoid-version @SNMPLIB@ \
+ @CRYPTOLIB@
+wti_mpc_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+suicide_la_SOURCES = suicide.c $(INCFILES)
+suicide_la_LDFLAGS = -export-dynamic -module -avoid-version
+suicide_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la
+
+stonithscriptdir = $(stonith_plugindir)/stonith2
+
+stonithscript_SCRIPTS = ribcl.py
diff --git a/lib/plugins/stonith/apcmaster.c b/lib/plugins/stonith/apcmaster.c
new file mode 100644
index 0000000..09a56d3
--- /dev/null
+++ b/lib/plugins/stonith/apcmaster.c
@@ -0,0 +1,822 @@
+/*
+*
+* Copyright 2001 Mission Critical Linux, Inc.
+*
+* All Rights Reserved.
+*/
+/*
+ * Stonith module for APC Master Switch (AP9211)
+ *
+ * Copyright (c) 2001 Mission Critical Linux, Inc.
+ * author: mike ledoux <mwl@mclinux.com>
+ * author: Todd Wheeling <wheeling@mclinux.com>
+ * mangled by Sun Jiang Dong, <sunjd@cn.ibm.com>, IBM, 2005
+ *
+ * Based strongly on original code from baytech.c by Alan Robertson.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* Observations/Notes
+ *
+ * 1. The APC MasterSwitch, unlike the BayTech network power switch,
+ * accepts only one (telnet) connection/session at a time. When one
+ * session is active, any subsequent attempt to connect to the MasterSwitch
+ * will result in a connection refused/closed failure. In a cluster
+ * environment or other environment utilizing polling/monitoring of the
+ * MasterSwitch (from multiple nodes), this can clearly cause problems.
+ * Obviously the more nodes and the shorter the polling interval, the more
+ * frequently such errors/collisions may occur.
+ *
+ * 2. We observed that on busy networks where there may be high occurances
+ * of broadcasts, the MasterSwitch became unresponsive. In some
+ * configurations this necessitated placing the power switch onto a
+ * private subnet.
+ */
+
+#include <lha_internal.h>
+
+#define DEVICE "APC MasterSwitch"
+
+#define DOESNT_USE_STONITHKILLCOMM 1
+
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN apcmaster
+#define PIL_PLUGIN_S "apcmaster"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+#include "stonith_signal.h"
+
+static StonithPlugin * apcmaster_new(const char *);
+static void apcmaster_destroy(StonithPlugin *);
+static const char * const * apcmaster_get_confignames(StonithPlugin *);
+static int apcmaster_set_config(StonithPlugin *, StonithNVpair *);
+static const char * apcmaster_getinfo(StonithPlugin * s, int InfoType);
+static int apcmaster_status(StonithPlugin * );
+static int apcmaster_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** apcmaster_hostlist(StonithPlugin *);
+
+static struct stonith_ops apcmasterOps ={
+ apcmaster_new, /* Create new STONITH object */
+ apcmaster_destroy, /* Destroy STONITH object */
+ apcmaster_getinfo, /* Return STONITH info string */
+ apcmaster_get_confignames, /* Get configuration parameters */
+ apcmaster_set_config, /* Set configuration */
+ apcmaster_status, /* Return STONITH device status */
+ apcmaster_reset_req, /* Request a reset */
+ apcmaster_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+#include "stonith_expect_helpers.h"
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &apcmasterOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * I have an AP9211. This code has been tested with this switch.
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ pid_t pid;
+ int rdfd;
+ int wrfd;
+ char * device;
+ char * user;
+ char * passwd;
+};
+
+static const char * pluginid = "APCMS-Stonith";
+static const char * NOTpluginID = "APCMS device has been destroyed";
+
+/*
+ * Different expect strings that we get from the APC MasterSwitch
+ */
+
+#define APCMSSTR "American Power Conversion"
+
+static struct Etoken EscapeChar[] = { {"Escape character is '^]'.", 0, 0}
+ , {NULL,0,0}};
+static struct Etoken login[] = { {"User Name :", 0, 0}, {NULL,0,0}};
+static struct Etoken password[] = { {"Password :", 0, 0} ,{NULL,0,0}};
+static struct Etoken Prompt[] = { {"> ", 0, 0} ,{NULL,0,0}};
+static struct Etoken LoginOK[] = { {APCMSSTR, 0, 0}
+ , {"User Name :", 1, 0} ,{NULL,0,0}};
+static struct Etoken Separator[] = { {"-----", 0, 0} ,{NULL,0,0}};
+
+/* We may get a notice about rebooting, or a request for confirmation */
+static struct Etoken Processing[] = { {"Press <ENTER> to continue", 0, 0}
+ , {"Enter 'YES' to continue", 1, 0}
+ , {NULL,0,0}};
+
+#include "stonith_config_xml.h"
+
+static const char *apcmasterXML =
+ XML_PARAMETERS_BEGIN
+ XML_IPADDR_PARM
+ XML_LOGIN_PARM
+ XML_PASSWD_PARM
+ XML_PARAMETERS_END;
+
+static int MS_connect_device(struct pluginDevice * ms);
+static int MSLogin(struct pluginDevice * ms);
+static int MSRobustLogin(struct pluginDevice * ms);
+static int MSNametoOutlet(struct pluginDevice*, const char * name);
+static int MSReset(struct pluginDevice*, int outletNum, const char * host);
+static int MSLogout(struct pluginDevice * ms);
+
+#if defined(ST_POWERON) && defined(ST_POWEROFF)
+static int apcmaster_onoff(struct pluginDevice*, int outletnum, const char * unitid
+, int request);
+#endif
+
+/* Login to the APC Master Switch */
+
+static int
+MSLogin(struct pluginDevice * ms)
+{
+ EXPECT(ms->rdfd, EscapeChar, 10);
+
+ /*
+ * We should be looking at something like this:
+ * User Name :
+ */
+ EXPECT(ms->rdfd, login, 10);
+ SEND(ms->wrfd, ms->user);
+ SEND(ms->wrfd, "\r");
+
+ /* Expect "Password :" */
+ EXPECT(ms->rdfd, password, 10);
+ SEND(ms->wrfd, ms->passwd);
+ SEND(ms->wrfd, "\r");
+
+ switch (StonithLookFor(ms->rdfd, LoginOK, 30)) {
+
+ case 0: /* Good! */
+ LOG(PIL_INFO, "Successful login to %s.", ms->idinfo);
+ break;
+
+ case 1: /* Uh-oh - bad password */
+ LOG(PIL_CRIT, "Invalid password for %s.", ms->idinfo);
+ return(S_ACCESS);
+
+ default:
+ return(errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS);
+ }
+
+ return(S_OK);
+}
+
+/* Attempt to login up to 20 times... */
+
+static int
+MSRobustLogin(struct pluginDevice * ms)
+{
+ int rc = S_OOPS;
+ int j = 0;
+
+ for ( ; ; ) {
+ if (MS_connect_device(ms) == S_OK) {
+ rc = MSLogin(ms);
+ if( rc == S_OK ) {
+ break;
+ }
+ }
+ if ((++j) == 20) {
+ break;
+ } else {
+ sleep(1);
+ }
+ }
+
+ return rc;
+}
+
+/* Log out of the APC Master Switch */
+
+static
+int MSLogout(struct pluginDevice* ms)
+{
+ int rc;
+
+ /* Make sure we're in the right menu... */
+ /*SEND(ms->wrfd, "\033\033\033\033\033\033\033"); */
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+
+ /* Expect "> " */
+ rc = StonithLookFor(ms->rdfd, Prompt, 5);
+
+ /* "4" is logout */
+ SEND(ms->wrfd, "4\r");
+
+ close(ms->wrfd);
+ close(ms->rdfd);
+ ms->wrfd = ms->rdfd = -1;
+
+ return(rc >= 0 ? S_OK : (errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS));
+}
+/* Reset (power-cycle) the given outlets */
+static int
+MSReset(struct pluginDevice* ms, int outletNum, const char *host)
+{
+ char unum[32];
+
+ /* Make sure we're in the top level menu */
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+
+ /* Expect ">" */
+ EXPECT(ms->rdfd, Prompt, 5);
+
+ /* Request menu 1 (Device Control) */
+ SEND(ms->wrfd, "1\r");
+
+ /* Select requested outlet */
+ EXPECT(ms->rdfd, Prompt, 5);
+ snprintf(unum, sizeof(unum), "%i\r", outletNum);
+ SEND(ms->wrfd, unum);
+
+ /* Select menu 1 (Control Outlet) */
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "1\r");
+
+ /* Select menu 3 (Immediate Reboot) */
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "3\r");
+
+ /* Expect "Press <ENTER> " or "Enter 'YES'" (if confirmation turned on) */
+ retry:
+ switch (StonithLookFor(ms->rdfd, Processing, 5)) {
+ case 0: /* Got "Press <ENTER>" Do so */
+ SEND(ms->wrfd, "\r");
+ break;
+
+ case 1: /* Got that annoying command confirmation :-( */
+ SEND(ms->wrfd, "YES\r");
+ goto retry;
+
+ default:
+ return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS);
+ }
+
+
+ LOG(PIL_INFO, "Host being rebooted: %s", host);
+
+ /* Expect ">" */
+ if (StonithLookFor(ms->rdfd, Prompt, 10) < 0) {
+ return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS);
+ }
+
+ /* All Right! Power is back on. Life is Good! */
+
+ LOG(PIL_INFO, "Power restored to host: %s", host);
+
+ /* Return to top level menu */
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+
+ return(S_OK);
+}
+
+#if defined(ST_POWERON) && defined(ST_POWEROFF)
+static int
+apcmaster_onoff(struct pluginDevice* ms, int outletNum, const char * unitid, int req)
+{
+ char unum[32];
+
+ const char * onoff = (req == ST_POWERON ? "1\r" : "2\r");
+ int rc;
+
+ if ((rc = MSRobustLogin(ms) != S_OK)) {
+ LOG(PIL_CRIT, "Cannot log into %s.", ms->idinfo);
+ return(rc);
+ }
+
+ /* Make sure we're in the top level menu */
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+
+ /* Expect ">" */
+ EXPECT(ms->rdfd, Prompt, 5);
+
+ /* Request menu 1 (Device Control) */
+ SEND(ms->wrfd, "1\r");
+
+ /* Select requested outlet */
+ snprintf(unum, sizeof(unum), "%d\r", outletNum);
+ SEND(ms->wrfd, unum);
+
+ /* Select menu 1 (Control Outlet) */
+ SEND(ms->wrfd, "1\r");
+
+ /* Send ON/OFF command for given outlet */
+ SEND(ms->wrfd, onoff);
+
+ /* Expect "Press <ENTER> " or "Enter 'YES'" (if confirmation turned on) */
+ retry:
+ switch (StonithLookFor(ms->rdfd, Processing, 5)) {
+ case 0: /* Got "Press <ENTER>" Do so */
+ SEND(ms->wrfd, "\r");
+ break;
+
+ case 1: /* Got that annoying command confirmation :-( */
+ SEND(ms->wrfd, "YES\r");
+ goto retry;
+
+ default:
+ return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS);
+ }
+
+ EXPECT(ms->rdfd, Prompt, 10);
+
+ /* All Right! Command done. Life is Good! */
+ LOG(PIL_INFO, "Power to MS outlet(s) %d turned %s", outletNum, onoff);
+ /* Pop back to main menu */
+ SEND(ms->wrfd, "\033\033\033\033\033\033\033\r");
+ return(S_OK);
+}
+#endif /* defined(ST_POWERON) && defined(ST_POWEROFF) */
+
+/*
+ * Map the given host name into an (AC) Outlet number on the power strip
+ */
+
+static int
+MSNametoOutlet(struct pluginDevice* ms, const char * name)
+{
+ char NameMapping[128];
+ int sockno;
+ char sockname[32];
+ int times = 0;
+ int ret = -1;
+
+ /* Verify that we're in the top-level menu */
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+
+ /* Expect ">" */
+ EXPECT(ms->rdfd, Prompt, 5);
+
+ /* Request menu 1 (Device Control) */
+ SEND(ms->wrfd, "1\r");
+
+ /* Expect: "-----" so we can skip over it... */
+ EXPECT(ms->rdfd, Separator, 5);
+ EXPECT(ms->rdfd, CRNL, 5);
+ EXPECT(ms->rdfd, CRNL, 5);
+
+ /* Looks Good! Parse the status output */
+
+ do {
+ times++;
+ NameMapping[0] = EOS;
+ SNARF(ms->rdfd, NameMapping, 5);
+ if (sscanf(NameMapping
+ , "%d- %23c",&sockno, sockname) == 2) {
+
+ char * last = sockname+23;
+ *last = EOS;
+ --last;
+
+ /* Strip off trailing blanks */
+ for(; last > sockname; --last) {
+ if (*last == ' ') {
+ *last = EOS;
+ }else{
+ break;
+ }
+ }
+ if (strcasecmp(name, sockname) == 0) {
+ ret = sockno;
+ }
+ }
+ } while (strlen(NameMapping) > 2 && times < 8);
+
+ /* Pop back out to the top level menu */
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ return(ret);
+}
+
+static int
+apcmaster_status(StonithPlugin *s)
+{
+ struct pluginDevice* ms;
+ int rc;
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ ms = (struct pluginDevice*) s;
+
+ if ((rc = MSRobustLogin(ms) != S_OK)) {
+ LOG(PIL_CRIT, "Cannot log into %s.", ms->idinfo);
+ return(rc);
+ }
+
+ /* Expect ">" */
+ SEND(ms->wrfd, "\033\r");
+ EXPECT(ms->rdfd, Prompt, 5);
+
+ return(MSLogout(ms));
+}
+
+/*
+ * Return the list of hosts (outlet names) for the devices on this MS unit
+ */
+
+static char **
+apcmaster_hostlist(StonithPlugin *s)
+{
+ char NameMapping[128];
+ char* NameList[64];
+ unsigned int numnames = 0;
+ char ** ret = NULL;
+ struct pluginDevice* ms;
+ unsigned int i;
+
+ ERRIFNOTCONFIGED(s,NULL);
+
+ ms = (struct pluginDevice*) s;
+
+ if (MSRobustLogin(ms) != S_OK) {
+ LOG(PIL_CRIT, "Cannot log into %s.", ms->idinfo);
+ return(NULL);
+ }
+
+ /* Expect ">" */
+ NULLEXPECT(ms->rdfd, Prompt, 10);
+
+ /* Request menu 1 (Device Control) */
+ SEND(ms->wrfd, "1\r");
+
+ /* Expect: "-----" so we can skip over it... */
+ NULLEXPECT(ms->rdfd, Separator, 5);
+ NULLEXPECT(ms->rdfd, CRNL, 5);
+ NULLEXPECT(ms->rdfd, CRNL, 5);
+
+ /* Looks Good! Parse the status output */
+ do {
+ int sockno;
+ char sockname[64];
+ NameMapping[0] = EOS;
+ NULLSNARF(ms->rdfd, NameMapping, 5);
+ if (sscanf(NameMapping
+ , "%d- %23c",&sockno, sockname) == 2) {
+
+ char * last = sockname+23;
+ char * nm;
+ *last = EOS;
+ --last;
+
+ /* Strip off trailing blanks */
+ for(; last > sockname; --last) {
+ if (*last == ' ') {
+ *last = EOS;
+ }else{
+ break;
+ }
+ }
+ if (numnames >= DIMOF(NameList)-1) {
+ break;
+ }
+ if ((nm = (char*)STRDUP(sockname)) == NULL) {
+ goto out_of_memory;
+ }
+ strdown(nm);
+ NameList[numnames] = nm;
+ ++numnames;
+ NameList[numnames] = NULL;
+ }
+ } while (strlen(NameMapping) > 2);
+
+ /* Pop back out to the top level menu */
+ SEND(ms->wrfd, "\033");
+ NULLEXPECT(ms->rdfd, Prompt, 10);
+ SEND(ms->wrfd, "\033");
+ NULLEXPECT(ms->rdfd, Prompt, 10);
+ SEND(ms->wrfd, "\033");
+ NULLEXPECT(ms->rdfd, Prompt, 10);
+ SEND(ms->wrfd, "\033");
+ NULLEXPECT(ms->rdfd, Prompt, 10);
+
+
+ if (numnames >= 1) {
+ ret = (char **)MALLOC((numnames+1)*sizeof(char*));
+ if (ret == NULL) {
+ goto out_of_memory;
+ }else{
+ memcpy(ret, NameList, (numnames+1)*sizeof(char*));
+ }
+ }
+ (void)MSLogout(ms);
+ return(ret);
+
+out_of_memory:
+ LOG(PIL_CRIT, "out of memory");
+ for (i=0; i<numnames; i++) {
+ FREE(NameList[i]);
+ }
+ return(NULL);
+}
+
+/*
+ * Connect to the given MS device. We should add serial support here
+ * eventually...
+ */
+static int
+MS_connect_device(struct pluginDevice * ms)
+{
+ int fd = OurImports->OpenStreamSocket(ms->device
+ , TELNET_PORT, TELNET_SERVICE);
+
+ if (fd < 0) {
+ return(S_OOPS);
+ }
+ ms->rdfd = ms->wrfd = fd;
+ return(S_OK);
+}
+
+/*
+ * Reset the given host on this StonithPlugin device.
+ */
+static int
+apcmaster_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ int rc = 0;
+ int lorc = 0;
+ struct pluginDevice* ms;
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ ms = (struct pluginDevice*) s;
+
+ if ((rc = MSRobustLogin(ms)) != S_OK) {
+ LOG(PIL_CRIT, "Cannot log into %s.", ms->idinfo);
+ return(rc);
+ }else{
+ int noutlet;
+ noutlet = MSNametoOutlet(ms, host);
+ if (noutlet < 1) {
+ LOG(PIL_WARN, "%s doesn't control host [%s]"
+ , ms->device, host);
+ return(S_BADHOST);
+ }
+ switch(request) {
+
+#if defined(ST_POWERON) && defined(ST_POWEROFF)
+ case ST_POWERON:
+ rc = apcmaster_onoff(ms, noutlet, host, request);
+ break;
+ case ST_POWEROFF:
+ rc = apcmaster_onoff(ms, noutlet, host, request);
+ break;
+#endif
+ case ST_GENERIC_RESET:
+ rc = MSReset(ms, noutlet, host);
+ break;
+ default:
+ rc = S_INVAL;
+ break;
+ }
+ }
+
+ lorc = MSLogout(ms);
+ return(rc != S_OK ? rc : lorc);
+}
+
+/*
+ * Get the configuration parameters names
+ */
+static const char * const *
+apcmaster_get_confignames(StonithPlugin * s)
+{
+ static const char * ret[] = {ST_IPADDR, ST_LOGIN, ST_PASSWD, NULL};
+ return ret;
+}
+
+/*
+ * Set the configuration parameters
+ */
+static int
+apcmaster_set_config(StonithPlugin * s, StonithNVpair * list)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ int rc;
+ StonithNamesToGet namestocopy [] =
+ { {ST_IPADDR, NULL}
+ , {ST_LOGIN, NULL}
+ , {ST_PASSWD, NULL}
+ , {NULL, NULL}
+ };
+
+ ERRIFWRONGDEV(s, S_OOPS);
+ if (sd->sp.isconfigured) {
+ return S_OOPS;
+ }
+
+ if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ sd->device = namestocopy[0].s_value;
+ sd->user = namestocopy[1].s_value;
+ sd->passwd = namestocopy[2].s_value;
+
+ return(S_OK);
+}
+
+static const char *
+apcmaster_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* ms;
+ const char * ret;
+
+ ERRIFWRONGDEV(s,NULL);
+
+ /*
+ * We look in the ST_TEXTDOMAIN catalog for our messages
+ */
+ ms = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = ms->idinfo;
+ break;
+
+ case ST_DEVICENAME: /* Which particular device? */
+ ret = ms->device;
+ break;
+
+ case ST_DEVICEDESCR:
+ ret = "APC MasterSwitch (via telnet)\n"
+ "NOTE: The APC MasterSwitch accepts only one (telnet)\n"
+ "connection/session a time. When one session is active,\n"
+ "subsequent attempts to connect to the MasterSwitch"
+ " will fail.";
+ break;
+
+ case ST_DEVICEURL:
+ ret = "http://www.apc.com/";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = apcmasterXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * APC MasterSwitch StonithPlugin destructor...
+ */
+static void
+apcmaster_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* ms;
+
+ VOIDERRIFWRONGDEV(s);
+
+ ms = (struct pluginDevice *)s;
+
+ ms->pluginid = NOTpluginID;
+ if (ms->rdfd >= 0) {
+ close(ms->rdfd);
+ ms->rdfd = -1;
+ }
+ if (ms->wrfd >= 0) {
+ close(ms->wrfd);
+ ms->wrfd = -1;
+ }
+ if (ms->device != NULL) {
+ FREE(ms->device);
+ ms->device = NULL;
+ }
+ if (ms->user != NULL) {
+ FREE(ms->user);
+ ms->user = NULL;
+ }
+ if (ms->passwd != NULL) {
+ FREE(ms->passwd);
+ ms->passwd = NULL;
+ }
+ FREE(ms);
+}
+
+/* Create a new APC Master Switch StonithPlugin device. */
+
+static StonithPlugin *
+apcmaster_new(const char *subplugin)
+{
+ struct pluginDevice* ms = ST_MALLOCT(struct pluginDevice);
+
+ if (ms == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(ms, 0, sizeof(*ms));
+ ms->pluginid = pluginid;
+ ms->pid = -1;
+ ms->rdfd = -1;
+ ms->wrfd = -1;
+ ms->user = NULL;
+ ms->device = NULL;
+ ms->passwd = NULL;
+ ms->idinfo = DEVICE;
+ ms->sp.s_ops = &apcmasterOps;
+
+ return(&(ms->sp));
+}
diff --git a/lib/plugins/stonith/apcmastersnmp.c b/lib/plugins/stonith/apcmastersnmp.c
new file mode 100644
index 0000000..a9eeaeb
--- /dev/null
+++ b/lib/plugins/stonith/apcmastersnmp.c
@@ -0,0 +1,890 @@
+/*
+ * Stonith module for APC Masterswitch (SNMP)
+ * Copyright (c) 2001 Andreas Piesk <a.piesk@gmx.net>
+ * Mangled by Sun Jiang Dong <sunjd@cn.ibm.com>, IBM, 2005
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.*
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <lha_internal.h>
+
+/* device ID */
+#define DEVICE "APC MasterSwitch (SNMP)"
+
+#include "stonith_plugin_common.h"
+#undef FREE /* defined by snmp stuff */
+
+#ifdef PACKAGE_BUGREPORT
+#undef PACKAGE_BUGREPORT
+#endif
+#ifdef PACKAGE_NAME
+#undef PACKAGE_NAME
+#endif
+#ifdef PACKAGE_STRING
+#undef PACKAGE_STRING
+#endif
+#ifdef PACKAGE_TARNAME
+#undef PACKAGE_TARNAME
+#endif
+#ifdef PACKAGE_VERSION
+#undef PACKAGE_VERSION
+#endif
+
+#ifdef HAVE_NET_SNMP_NET_SNMP_CONFIG_H
+# include <net-snmp/net-snmp-config.h>
+# include <net-snmp/net-snmp-includes.h>
+# include <net-snmp/agent/net-snmp-agent-includes.h>
+# define INIT_AGENT() init_master_agent()
+#else
+# include <ucd-snmp/ucd-snmp-config.h>
+# include <ucd-snmp/ucd-snmp-includes.h>
+# include <ucd-snmp/ucd-snmp-agent-includes.h>
+# ifndef NETSNMP_DS_APPLICATION_ID
+# define NETSNMP_DS_APPLICATION_ID DS_APPLICATION_ID
+# endif
+# ifndef NETSNMP_DS_AGENT_ROLE
+# define NETSNMP_DS_AGENT_ROLE DS_AGENT_ROLE
+# endif
+# define netsnmp_ds_set_boolean ds_set_boolean
+# define INIT_AGENT() init_master_agent(161, NULL, NULL)
+#endif
+
+#define PIL_PLUGIN apcmastersnmp
+#define PIL_PLUGIN_S "apcmastersnmp"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+#define DEBUGCALL \
+ if (Debug) { \
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); \
+ }
+
+static StonithPlugin * apcmastersnmp_new(const char *);
+static void apcmastersnmp_destroy(StonithPlugin *);
+static const char * const * apcmastersnmp_get_confignames(StonithPlugin *);
+static int apcmastersnmp_set_config(StonithPlugin *, StonithNVpair *);
+static const char * apcmastersnmp_getinfo(StonithPlugin * s, int InfoType);
+static int apcmastersnmp_status(StonithPlugin * );
+static int apcmastersnmp_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** apcmastersnmp_hostlist(StonithPlugin *);
+
+static struct stonith_ops apcmastersnmpOps ={
+ apcmastersnmp_new, /* Create new STONITH object */
+ apcmastersnmp_destroy, /* Destroy STONITH object */
+ apcmastersnmp_getinfo, /* Return STONITH info string */
+ apcmastersnmp_get_confignames, /* Get configuration parameters */
+ apcmastersnmp_set_config, /* Set configuration */
+ apcmastersnmp_status, /* Return STONITH device status */
+ apcmastersnmp_reset_req, /* Request a reset */
+ apcmastersnmp_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+ DEBUGCALL;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &apcmastersnmpOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * APCMaster tested with APC Masterswitch 9212
+ */
+
+/* outlet commands / status codes */
+#define OUTLET_ON 1
+#define OUTLET_OFF 2
+#define OUTLET_REBOOT 3
+#define OUTLET_NO_CMD_PEND 2
+
+/* oids */
+#define OID_IDENT ".1.3.6.1.4.1.318.1.1.12.1.5.0"
+#define OID_NUM_OUTLETS ".1.3.6.1.4.1.318.1.1.12.1.8.0"
+#define OID_OUTLET_NAMES ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.2.%i"
+#define OID_OUTLET_STATE ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.%i"
+#define OID_OUTLET_COMMAND_PENDING ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.5.%i"
+#define OID_OUTLET_REBOOT_DURATION ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.6.%i"
+
+/*
+ snmpset -c private -v1 172.16.0.32:161
+ ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.1" i 1
+ The last octet in the OID is the plug number. The value can
+ be 1 thru 8 because there are 8 power plugs on this device.
+ The integer that can be set is as follows: 1=on, 2=off, and
+ 3=reset
+*/
+
+/* own defines */
+#define MAX_STRING 128
+#define ST_PORT "port"
+
+/* structur of stonith object */
+struct pluginDevice {
+ StonithPlugin sp; /* StonithPlugin object */
+ const char* pluginid; /* id of object */
+ const char* idinfo; /* type of device */
+ struct snmp_session* sptr; /* != NULL->session created */
+ char * hostname; /* masterswitch's hostname */
+ /* or ip addr */
+ int port; /* snmp port */
+ char * community; /* snmp community (r/w) */
+ int num_outlets; /* number of outlets */
+};
+
+/* for checking hardware (issue a warning if mismatch) */
+static const char* APC_tested_ident[] = {"AP9606","AP7920","AP7921","AP7900","AP_other_well_tested"};
+
+/* constant strings */
+static const char *pluginid = "APCMS-SNMP-Stonith";
+static const char *NOTpluginID = "APCMS SNMP device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+#define XML_PORT_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_PORT \
+ XML_PARM_SHORTDESC_END
+
+#define XML_PORT_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The port number on which the SNMP server is running on the STONITH device" \
+ XML_PARM_LONGDESC_END
+
+#define XML_PORT_PARM \
+ XML_PARAMETER_BEGIN(ST_PORT, "string", "1", "0") \
+ XML_PORT_SHORTDESC \
+ XML_PORT_LONGDESC \
+ XML_PARAMETER_END
+
+static const char *apcmastersnmpXML =
+ XML_PARAMETERS_BEGIN
+ XML_IPADDR_PARM
+ XML_PORT_PARM
+ XML_COMMUNITY_PARM
+ XML_PARAMETERS_END;
+
+/*
+ * own prototypes
+ */
+
+static void APC_error(struct snmp_session *sptr, const char *fn
+, const char *msg);
+static struct snmp_session *APC_open(char *hostname, int port
+, char *community);
+static void *APC_read(struct snmp_session *sptr, const char *objname
+, int type);
+static int APC_write(struct snmp_session *sptr, const char *objname
+, char type, char *value);
+
+static void
+APC_error(struct snmp_session *sptr, const char *fn, const char *msg)
+{
+ int snmperr = 0;
+ int cliberr = 0;
+ char *errstr;
+
+ snmp_error(sptr, &cliberr, &snmperr, &errstr);
+ LOG(PIL_CRIT
+ , "%s: %s (cliberr: %i / snmperr: %i / error: %s)."
+ , fn, msg, cliberr, snmperr, errstr);
+ free(errstr);
+}
+
+
+/*
+ * creates a snmp session
+ */
+static struct snmp_session *
+APC_open(char *hostname, int port, char *community)
+{
+ static struct snmp_session session;
+ struct snmp_session *sptr;
+
+ DEBUGCALL;
+
+ /* create session */
+ snmp_sess_init(&session);
+
+ /* fill session */
+ session.peername = hostname;
+ session.version = SNMP_VERSION_1;
+ session.remote_port = port;
+ session.community = (u_char *)community;
+ session.community_len = strlen(community);
+ session.retries = 5;
+ session.timeout = 1000000;
+
+ /* open session */
+ sptr = snmp_open(&session);
+
+ if (sptr == NULL) {
+ APC_error(&session, __FUNCTION__, "cannot open snmp session");
+ }
+
+ /* return pointer to opened session */
+ return (sptr);
+}
+
+/*
+ * parse config
+ */
+
+/*
+ * read value of given oid and return it as string
+ */
+static void *
+APC_read(struct snmp_session *sptr, const char *objname, int type)
+{
+ oid name[MAX_OID_LEN];
+ size_t namelen = MAX_OID_LEN;
+ struct variable_list *vars;
+ struct snmp_pdu *pdu;
+ struct snmp_pdu *resp;
+ static char response_str[MAX_STRING];
+ static int response_int;
+
+ DEBUGCALL;
+
+ /* convert objname into oid; return NULL if invalid */
+ if (!read_objid(objname, name, &namelen)) {
+ LOG(PIL_CRIT, "%s: cannot convert %s to oid.", __FUNCTION__, objname);
+ return (NULL);
+ }
+
+ /* create pdu */
+ if ((pdu = snmp_pdu_create(SNMP_MSG_GET)) != NULL) {
+
+ /* get-request have no values */
+ snmp_add_null_var(pdu, name, namelen);
+
+ /* send pdu and get response; return NULL if error */
+ if (snmp_synch_response(sptr, pdu, &resp) == SNMPERR_SUCCESS) {
+
+ /* request succeed, got valid response ? */
+ if (resp->errstat == SNMP_ERR_NOERROR) {
+
+ /* go through the returned vars */
+ for (vars = resp->variables; vars;
+ vars = vars->next_variable) {
+
+ /* return response as string */
+ if ((vars->type == type) && (type == ASN_OCTET_STR)) {
+ memset(response_str, 0, MAX_STRING);
+ strncpy(response_str, (char *)vars->val.string,
+ MIN(vars->val_len, MAX_STRING));
+ snmp_free_pdu(resp);
+ return ((void *) response_str);
+ }
+ /* return response as integer */
+ if ((vars->type == type) && (type == ASN_INTEGER)) {
+ response_int = *vars->val.integer;
+ snmp_free_pdu(resp);
+ return ((void *) &response_int);
+ }
+ }
+ }else{
+ LOG(PIL_CRIT, "%s: error in response packet, reason %ld [%s]."
+ , __FUNCTION__, resp->errstat, snmp_errstring(resp->errstat));
+ }
+ }else{
+ APC_error(sptr, __FUNCTION__, "error sending/receiving pdu");
+ }
+ /* free repsonse pdu (necessary?) */
+ snmp_free_pdu(resp);
+ }else{
+ APC_error(sptr, __FUNCTION__, "cannot create pdu");
+ }
+ /* error: return nothing */
+ return (NULL);
+}
+
+/*
+ * write value of given oid
+ */
+static int
+APC_write(struct snmp_session *sptr, const char *objname, char type,
+ char *value)
+{
+ oid name[MAX_OID_LEN];
+ size_t namelen = MAX_OID_LEN;
+ struct snmp_pdu *pdu;
+ struct snmp_pdu *resp;
+
+ DEBUGCALL;
+
+ /* convert objname into oid; return FALSE if invalid */
+ if (!read_objid(objname, name, &namelen)) {
+ LOG(PIL_CRIT, "%s: cannot convert %s to oid.", __FUNCTION__, objname);
+ return (FALSE);
+ }
+
+ /* create pdu */
+ if ((pdu = snmp_pdu_create(SNMP_MSG_SET)) != NULL) {
+
+ /* add to be written value to pdu */
+ snmp_add_var(pdu, name, namelen, type, value);
+
+ /* send pdu and get response; return NULL if error */
+ if (snmp_synch_response(sptr, pdu, &resp) == STAT_SUCCESS) {
+
+ /* go through the returned vars */
+ if (resp->errstat == SNMP_ERR_NOERROR) {
+
+ /* request successful done */
+ snmp_free_pdu(resp);
+ return (TRUE);
+
+ }else{
+ LOG(PIL_CRIT, "%s: error in response packet, reason %ld [%s]."
+ , __FUNCTION__, resp->errstat, snmp_errstring(resp->errstat));
+ }
+ }else{
+ APC_error(sptr, __FUNCTION__, "error sending/receiving pdu");
+ }
+ /* free pdu (again: necessary?) */
+ snmp_free_pdu(resp);
+ }else{
+ APC_error(sptr, __FUNCTION__, "cannot create pdu");
+ }
+ /* error */
+ return (FALSE);
+}
+
+/*
+ * return the status for this device
+ */
+
+static int
+apcmastersnmp_status(StonithPlugin * s)
+{
+ struct pluginDevice *ad;
+ char *ident;
+ int i;
+
+ DEBUGCALL;
+
+ ERRIFNOTCONFIGED(s, S_OOPS);
+
+ ad = (struct pluginDevice *) s;
+
+ if ((ident = APC_read(ad->sptr, OID_IDENT, ASN_OCTET_STR)) == NULL) {
+ LOG(PIL_CRIT, "%s: cannot read ident.", __FUNCTION__);
+ return (S_ACCESS);
+ }
+
+ /* issue a warning if ident mismatches */
+ for(i=DIMOF(APC_tested_ident) -1; i >=0 ; i--) {
+ if (strcmp(ident, APC_tested_ident[i]) == 0) {
+ break;
+ }
+ }
+
+ if (i<0) {
+ LOG(PIL_WARN
+ , "%s: module not tested with this hardware '%s'."
+ , __FUNCTION__, ident);
+ }
+ /* status ok */
+ return (S_OK);
+}
+
+/*
+ * return the list of hosts configured for this device
+ */
+
+static char **
+apcmastersnmp_hostlist(StonithPlugin * s)
+{
+ char **hl;
+ struct pluginDevice *ad;
+ int j, h, num_outlets;
+ char *outlet_name;
+ char objname[MAX_STRING];
+
+ DEBUGCALL;
+
+ ERRIFNOTCONFIGED(s, NULL);
+
+ ad = (struct pluginDevice *) s;
+
+ /* allocate memory for array of up to NUM_OUTLETS strings */
+ if ((hl = (char **)MALLOC((ad->num_outlets+1) * sizeof(char *))) == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory.", __FUNCTION__);
+ return (NULL);
+ }
+ /* clear hostlist array */
+ memset(hl, 0, (ad->num_outlets + 1) * sizeof(char *));
+ num_outlets = 0;
+
+ /* read NUM_OUTLETS values and put them into hostlist array */
+ for (j = 0; j < ad->num_outlets; ++j) {
+
+ /* prepare objname */
+ snprintf(objname, MAX_STRING, OID_OUTLET_NAMES, j + 1);
+
+ /* read outlet name */
+ if ((outlet_name = APC_read(ad->sptr, objname, ASN_OCTET_STR)) ==
+ NULL) {
+ LOG(PIL_CRIT, "%s: cannot read name for outlet %d."
+ , __FUNCTION__, j+1);
+ stonith_free_hostlist(hl);
+ hl = NULL;
+ return (hl);
+ }
+
+ /* Check whether the host is already listed */
+ for (h = 0; h < num_outlets; ++h) {
+ if (strcasecmp(hl[h],outlet_name) == 0)
+ break;
+ }
+
+ if (h >= num_outlets) {
+ /* put outletname in hostlist */
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: added %s to hostlist."
+ , __FUNCTION__, outlet_name);
+ }
+
+ if ((hl[num_outlets] = STRDUP(outlet_name)) == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory.", __FUNCTION__);
+ stonith_free_hostlist(hl);
+ hl = NULL;
+ return (hl);
+ }
+ strdown(hl[num_outlets]);
+ num_outlets++;
+ }
+ }
+
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: %d unique hosts connected to %d outlets."
+ , __FUNCTION__, num_outlets, j);
+ }
+ /* return list */
+ return (hl);
+}
+
+/*
+ * reset the host
+ */
+
+static int
+apcmastersnmp_reset_req(StonithPlugin * s, int request, const char *host)
+{
+ struct pluginDevice *ad;
+ char objname[MAX_STRING];
+ char value[MAX_STRING];
+ char *outlet_name;
+ int req_oid = OUTLET_REBOOT;
+ int expect_state = OUTLET_ON;
+ int i, h, num_outlets, outlet, reboot_duration, *state, bad_outlets;
+ int outlets[8]; /* Assume that one node is connected to a
+ maximum of 8 outlets */
+
+ DEBUGCALL;
+
+ ERRIFNOTCONFIGED(s, S_OOPS);
+
+ ad = (struct pluginDevice *) s;
+
+ num_outlets = 0;
+ reboot_duration = 0;
+ bad_outlets = 0;
+
+ /* read max. as->num_outlets values */
+ for (outlet = 1; outlet <= ad->num_outlets; outlet++) {
+
+ /* prepare objname */
+ snprintf(objname, MAX_STRING, OID_OUTLET_NAMES, outlet);
+
+ /* read outlet name */
+ if ((outlet_name = APC_read(ad->sptr, objname, ASN_OCTET_STR))
+ == NULL) {
+ LOG(PIL_CRIT, "%s: cannot read name for outlet %d."
+ , __FUNCTION__, outlet);
+ return (S_ACCESS);
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: found outlet: %s.", __FUNCTION__, outlet_name);
+ }
+
+ /* found one */
+ if (strcasecmp(outlet_name, host) == 0) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: found %s at outlet %d."
+ , __FUNCTION__, host, outlet);
+ }
+ /* Check that the outlet is not administratively down */
+
+ /* prepare objname */
+ snprintf(objname, MAX_STRING, OID_OUTLET_STATE, outlet);
+
+ /* get outlet's state */
+ if ((state = APC_read(ad->sptr, objname, ASN_INTEGER)) == NULL) {
+ LOG(PIL_CRIT
+ , "%s: cannot read state for outlet %d."
+ , __FUNCTION__, outlet);
+ return (S_ACCESS);
+ }
+
+ /* prepare oid */
+ snprintf(objname, MAX_STRING, OID_OUTLET_REBOOT_DURATION
+ , outlet);
+
+ /* read reboot duration of the port */
+ if ((state = APC_read(ad->sptr, objname, ASN_INTEGER))
+ == NULL) {
+ LOG(PIL_CRIT
+ , "%s: cannot read reboot duration for outlet %d."
+ , __FUNCTION__, outlet);
+ return (S_ACCESS);
+ }
+ if (num_outlets == 0) {
+ /* save the inital value of the first port */
+ reboot_duration = *state;
+ } else if (reboot_duration != *state) {
+ LOG(PIL_WARN, "%s: outlet %d has a different reboot duration!"
+ , __FUNCTION__, outlet);
+ if (reboot_duration < *state)
+ reboot_duration = *state;
+ }
+
+ /* Ok, add it to the list of outlets to control */
+ outlets[num_outlets]=outlet;
+ num_outlets++;
+ }
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: outlet: %i.", __FUNCTION__, outlet);
+ }
+
+ /* host not found in outlet names */
+ if (num_outlets < 1) {
+ LOG(PIL_CRIT, "%s: no active outlet for '%s'.", __FUNCTION__, host);
+ return (S_BADHOST);
+ }
+
+
+ /* choose the OID for the stonith request */
+ switch (request) {
+ case ST_POWERON:
+ req_oid = OUTLET_ON;
+ expect_state = OUTLET_ON;
+ break;
+ case ST_POWEROFF:
+ req_oid = OUTLET_OFF;
+ expect_state = OUTLET_OFF;
+ break;
+ case ST_GENERIC_RESET:
+ req_oid = OUTLET_REBOOT;
+ expect_state = OUTLET_ON;
+ break;
+ default: break;
+ }
+
+ /* Turn them all off */
+
+ for (outlet=outlets[0], i=0 ; i < num_outlets; i++, outlet = outlets[i]) {
+ /* prepare objname */
+ snprintf(objname, MAX_STRING, OID_OUTLET_COMMAND_PENDING, outlet);
+
+ /* are there pending commands ? */
+ if ((state = APC_read(ad->sptr, objname, ASN_INTEGER)) == NULL) {
+ LOG(PIL_CRIT, "%s: cannot read pending commands for outlet %d."
+ , __FUNCTION__, outlet);
+ return (S_ACCESS);
+ }
+
+ if (*state != OUTLET_NO_CMD_PEND) {
+ LOG(PIL_CRIT, "%s: command pending.", __FUNCTION__);
+ return (S_RESETFAIL);
+ }
+
+ /* prepare objnames */
+ snprintf(objname, MAX_STRING, OID_OUTLET_STATE, outlet);
+ snprintf(value, MAX_STRING, "%i", req_oid);
+
+ /* send reboot cmd */
+ if (!APC_write(ad->sptr, objname, 'i', value)) {
+ LOG(PIL_CRIT
+ , "%s: cannot send reboot command for outlet %d."
+ , __FUNCTION__, outlet);
+ return (S_ACCESS);
+ }
+ }
+
+ /* wait max. 2*reboot_duration for all outlets to go back on */
+ for (i = 0; i < reboot_duration << 1; i++) {
+
+ sleep(1);
+
+ bad_outlets = 0;
+ for (outlet=outlets[0], h=0 ; h < num_outlets; h++,
+ outlet = outlets[h]) {
+
+ /* prepare objname of the first outlet */
+ snprintf(objname, MAX_STRING, OID_OUTLET_STATE, outlet);
+ /* get outlet's state */
+
+ if ((state = APC_read(ad->sptr, objname, ASN_INTEGER))
+ == NULL) {
+ LOG(PIL_CRIT
+ , "%s: cannot read state for outlet %d."
+ , __FUNCTION__, outlet);
+ return (S_ACCESS);
+ }
+
+ if (*state != expect_state)
+ bad_outlets++;
+ }
+
+ if (bad_outlets == 0)
+ return (S_OK);
+ }
+
+ if (bad_outlets == num_outlets) {
+ /* reset failed */
+ LOG(PIL_CRIT, "%s: stonith operation for '%s' failed."
+ , __FUNCTION__, host);
+ return (S_RESETFAIL);
+ } else {
+ /* Not all outlets back on, but at least one; implies node was */
+ /* rebooted correctly */
+ LOG(PIL_WARN,"%s: Not all outlets in the expected state!"
+ , __FUNCTION__);
+ return (S_OK);
+ }
+}
+
+/*
+ * Get the configuration parameter names.
+ */
+
+static const char * const *
+apcmastersnmp_get_confignames(StonithPlugin * s)
+{
+ static const char * ret[] = {ST_IPADDR, ST_PORT, ST_COMMUNITY, NULL};
+ return ret;
+}
+
+/*
+ * Set the configuration parameters.
+ */
+
+static int
+apcmastersnmp_set_config(StonithPlugin * s, StonithNVpair * list)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ int rc;
+ int * i;
+ StonithNamesToGet namestocopy [] =
+ { {ST_IPADDR, NULL}
+ , {ST_PORT, NULL}
+ , {ST_COMMUNITY, NULL}
+ , {NULL, NULL}
+ };
+
+ DEBUGCALL;
+ ERRIFWRONGDEV(s,S_INVAL);
+ if (sd->sp.isconfigured) {
+ return S_OOPS;
+ }
+
+ if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ sd->hostname = namestocopy[0].s_value;
+ sd->port = atoi(namestocopy[1].s_value);
+ PluginImports->mfree(namestocopy[1].s_value);
+ sd->community = namestocopy[2].s_value;
+
+ /* try to resolve the hostname/ip-address */
+ if (gethostbyname(sd->hostname) != NULL) {
+ /* init snmp library */
+ init_snmp("apcmastersnmp");
+
+ /* now try to get a snmp session */
+ if ((sd->sptr = APC_open(sd->hostname, sd->port, sd->community)) != NULL) {
+
+ /* ok, get the number of outlets from the masterswitch */
+ if ((i = APC_read(sd->sptr, OID_NUM_OUTLETS, ASN_INTEGER))
+ == NULL) {
+ LOG(PIL_CRIT
+ , "%s: cannot read number of outlets."
+ , __FUNCTION__);
+ return (S_ACCESS);
+ }
+ /* store the number of outlets */
+ sd->num_outlets = *i;
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: number of outlets: %i."
+ , __FUNCTION__, sd->num_outlets );
+ }
+
+ /* Everything went well */
+ return (S_OK);
+ }else{
+ LOG(PIL_CRIT, "%s: cannot create snmp session."
+ , __FUNCTION__);
+ }
+ }else{
+ LOG(PIL_CRIT, "%s: cannot resolve hostname '%s', h_errno %d."
+ , __FUNCTION__, sd->hostname, h_errno);
+ }
+
+ /* not a valid config */
+ return (S_BADCONFIG);
+}
+
+/*
+ * get info about the stonith device
+ */
+
+static const char *
+apcmastersnmp_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice *ad;
+ const char *ret = NULL;
+
+ DEBUGCALL;
+
+ ERRIFWRONGDEV(s, NULL);
+
+ ad = (struct pluginDevice *) s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = ad->idinfo;
+ break;
+
+ case ST_DEVICENAME:
+ ret = ad->hostname;
+ break;
+
+ case ST_DEVICEDESCR:
+ ret = "APC MasterSwitch (via SNMP)\n"
+ "The APC MasterSwitch can accept multiple simultaneous SNMP clients";
+ break;
+
+ case ST_DEVICEURL:
+ ret = "http://www.apc.com/";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = apcmastersnmpXML;
+ break;
+
+ }
+ return ret;
+}
+
+
+/*
+ * APC StonithPlugin destructor...
+ */
+
+static void
+apcmastersnmp_destroy(StonithPlugin * s)
+{
+ struct pluginDevice *ad;
+
+ DEBUGCALL;
+
+ VOIDERRIFWRONGDEV(s);
+
+ ad = (struct pluginDevice *) s;
+
+ ad->pluginid = NOTpluginID;
+
+ /* release snmp session */
+ if (ad->sptr != NULL) {
+ snmp_close(ad->sptr);
+ ad->sptr = NULL;
+ }
+
+ /* reset defaults */
+ if (ad->hostname != NULL) {
+ PluginImports->mfree(ad->hostname);
+ ad->hostname = NULL;
+ }
+ if (ad->community != NULL) {
+ PluginImports->mfree(ad->community);
+ ad->community = NULL;
+ }
+ ad->num_outlets = 0;
+
+ PluginImports->mfree(ad);
+}
+
+/*
+ * Create a new APC StonithPlugin device. Too bad this function can't be
+ * static
+ */
+
+static StonithPlugin *
+apcmastersnmp_new(const char *subplugin)
+{
+ struct pluginDevice *ad = ST_MALLOCT(struct pluginDevice);
+
+ DEBUGCALL;
+
+ /* no memory for stonith-object */
+ if (ad == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory.", __FUNCTION__);
+ return (NULL);
+ }
+
+ /* clear stonith-object */
+ memset(ad, 0, sizeof(*ad));
+
+ /* set defaults */
+ ad->pluginid = pluginid;
+ ad->sptr = NULL;
+ ad->hostname = NULL;
+ ad->community = NULL;
+ ad->idinfo = DEVICE;
+ ad->sp.s_ops = &apcmastersnmpOps;
+
+ /* return the object */
+ return (&(ad->sp));
+}
diff --git a/lib/plugins/stonith/apcmastersnmp.cfg.example b/lib/plugins/stonith/apcmastersnmp.cfg.example
new file mode 100644
index 0000000..76fea08
--- /dev/null
+++ b/lib/plugins/stonith/apcmastersnmp.cfg.example
@@ -0,0 +1,39 @@
+#
+# this is an example config for the stonith module apcmastersnmp
+#
+# 1. what does the fields on the line mean ?
+#
+# all parameters must be given on a single line. blank lines and lines
+# starting with '#' are ignored. only the first not ignored line will
+# be processed. all subsequent lines will be ignored. the different
+# fields must be seperated by white-spaces (blanks and/or tabs).
+#
+# the first field is the either the hostname or the ip address. the
+# hostname must be resolvable. the second fields specifies the snmp port
+# the masterswitch is listening. for snmp the default is 161. the last
+# field contains the so called 'community' string. this must be the same
+# as the one in the masterswitch configuration.
+#
+#
+# 2. how must the masterswitch be configured ?
+#
+# as said above, the community string must be set to the same value entered
+# in this config. the different outlets must be named after the connected
+# hosts. that means, the outlet names must be the same as the node names
+# in /etc/ha.d/ha.cf. the reset values should be set to reasonable values.
+#
+# the module DON'T configure the module in any way!
+#
+#
+# 3. how does the module work ?
+#
+# in case of a stonith the module receives the nodename of the host, which
+# should be reset. the module looks up this nodename in the list of outlet
+# names. that's why the names must be identical (see 2.). if it finds the
+# name, it'll reset the appropriate outlet using the configured values
+# (eg. delay, duration). then the module waits for the outlet to coming
+# up. if it comes up, a successful stonith will be reported back. otherwise
+# the stonith failed and a failure code will be returned.
+#
+
+192.168.1.110 161 private
diff --git a/lib/plugins/stonith/apcsmart.c b/lib/plugins/stonith/apcsmart.c
new file mode 100644
index 0000000..18d1612
--- /dev/null
+++ b/lib/plugins/stonith/apcsmart.c
@@ -0,0 +1,1028 @@
+/*
+ * Stonith module for APCSmart Stonith device
+ * Copyright (c) 2000 Andreas Piesk <a.piesk@gmx.net>
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.*
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Original version of this UPS code was taken from:
+ * 'Network UPS Tools' by Russell Kroll <rkroll@exploits.org>
+ * homepage: http://www.networkupstools.org/
+ *
+ * Significantly mangled by Alan Robertson <alanr@unix.sh>
+ */
+
+#include <lha_internal.h>
+
+#define DEVICE "APCSmart"
+
+#include "stonith_plugin_common.h"
+
+/*
+ * APCSmart (tested with old 900XLI, APC SmartUPS 700 and SmartUPS-1000)
+ *
+ * The reset is a combined reset: "S" and "@000"
+ * The "S" command tells the ups that if it is on-battery, it should
+ * remain offline until the power is back.
+ * If that command is not accepted, the "@000" command will be sent
+ * to tell the ups to turn off and back on right away.
+ * In both cases, if the UPS supports a 20 second shutdown grace
+ * period (such as on the 900XLI), the shutdown will delay that long,
+ * otherwise the shutdown will happen immediately (the code searches
+ * for the smallest possible delay).
+ */
+
+#define CFG_FILE "/etc/ha.d/apcsmart.cfg"
+
+#define MAX_DEVICES 1
+
+#define SERIAL_TIMEOUT 3 /* timeout in sec */
+#define SEND_DELAY 50000 /* in microseconds */
+#define ENDCHAR 10 /* use LF */
+#define MAX_STRING 512
+#define MAX_DELAY_STRING 16
+#define SWITCH_TO_NEXT_VAL "-" /* APC cmd for cycling through
+ * the values
+ */
+
+#define CMD_SMART_MODE "Y"
+#define RSP_SMART_MODE "SM"
+#define CMD_GET_STATUS "Q"
+#define RSP_GET_STATUS NULL
+#define CMD_RESET "S" /* turn off & stay off if on battery */
+#define CMD_RESET2 "@000" /* turn off & immediately turn on */
+#define RSP_RESET "*" /* RESET response from older models */
+#define RSP_RESET2 "OK" /* RESET response from newer models */
+#define RSP_NA "NA"
+#define CMD_READREG1 "~"
+#define CMD_OFF "Z"
+#define CMD_ON "\016" /* (control-n) */
+#define CMD_SHUTDOWN_DELAY "p"
+#define CMD_WAKEUP_DELAY "r"
+
+#define CR 13
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid; /* of object */
+ const char * idinfo; /* type of device */
+ char ** hostlist; /* served by the device (only 1) */
+ int hostcount;/* of hosts (1) */
+ char * upsdev; /* */
+ int upsfd; /* for serial port */
+ int retries;
+ char shutdown_delay[MAX_DELAY_STRING];
+ char old_shutdown_delay[MAX_DELAY_STRING];
+ char wakeup_delay[MAX_DELAY_STRING];
+ char old_wakeup_delay[MAX_DELAY_STRING];
+};
+
+/* saving old settings */
+/* FIXME! These should be part of pluginDevice struct above */
+static struct termios old_tio;
+
+static int f_serialtimeout; /* flag for timeout */
+static const char *pluginid = "APCSmart-Stonith";
+static const char *NOTpluginID = "APCSmart device has been destroyed";
+
+/*
+ * stonith prototypes
+ */
+
+#define PIL_PLUGIN apcsmart
+#define PIL_PLUGIN_S "apcsmart"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+#include "stonith_signal.h"
+
+static StonithPlugin * apcsmart_new(const char *);
+static void apcsmart_destroy(StonithPlugin *);
+static const char * const * apcsmart_get_confignames(StonithPlugin*);
+static int apcsmart_set_config(StonithPlugin *, StonithNVpair*);
+static const char * apcsmart_get_info(StonithPlugin * s, int InfoType);
+static int apcsmart_status(StonithPlugin * );
+static int apcsmart_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** apcsmart_hostlist(StonithPlugin *);
+
+static struct stonith_ops apcsmartOps ={
+ apcsmart_new, /* Create new STONITH object */
+ apcsmart_destroy, /* Destroy STONITH object */
+ apcsmart_get_info, /* Return STONITH info string */
+ apcsmart_get_confignames, /* Return STONITH info string */
+ apcsmart_set_config, /* Get configuration from NVpairs */
+ apcsmart_status, /* Return STONITH device status */
+ apcsmart_reset_req, /* Request a reset */
+ apcsmart_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &apcsmartOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+#include "stonith_config_xml.h"
+
+static const char *apcsmartXML =
+ XML_PARAMETERS_BEGIN
+ XML_TTYDEV_PARM
+ XML_HOSTLIST_PARM
+ XML_PARAMETERS_END;
+
+/*
+ * own prototypes
+ */
+
+int APC_open_serialport(const char *port, speed_t speed);
+void APC_close_serialport(const char *port, int upsfd);
+void APC_sh_serial_timeout(int sig);
+int APC_send_cmd(int upsfd, const char *cmd);
+int APC_recv_rsp(int upsfd, char *rsp);
+int APC_enter_smartmode(int upsfd);
+int APC_set_ups_var(int upsfd, const char *cmd, char *newval);
+int APC_get_smallest_delay(int upsfd, const char *cmd, char *smdelay);
+int APC_init( struct pluginDevice *ad );
+void APC_deinit( struct pluginDevice *ad );
+
+/*
+ * Signal handler for serial port timeouts
+ */
+
+void
+APC_sh_serial_timeout(int sig)
+{
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ STONITH_IGNORE_SIG(SIGALRM);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: serial port timed out.", __FUNCTION__);
+ }
+
+ f_serialtimeout = TRUE;
+
+ return;
+}
+
+/*
+ * Open serial port and set it to b2400
+ */
+
+int
+APC_open_serialport(const char *port, speed_t speed)
+{
+ struct termios tio;
+ int fd;
+ int rc;
+ int errno_save;
+ int fflags;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ if ((rc = OurImports->TtyLock(port)) < 0) {
+ LOG(PIL_CRIT, "%s: Could not lock tty %s [rc=%d]."
+ , __FUNCTION__, port, rc);
+ return -1;
+ }
+
+ STONITH_SIGNAL(SIGALRM, APC_sh_serial_timeout);
+ alarm(SERIAL_TIMEOUT);
+ f_serialtimeout = FALSE;
+
+ fd = open(port, O_RDWR | O_NOCTTY | O_NONBLOCK | O_EXCL);
+ errno_save = errno;
+
+ alarm(0);
+ STONITH_IGNORE_SIG(SIGALRM);
+
+ if (fd < 0) {
+ LOG(PIL_CRIT, "%s: Open of %s %s [%s].", __FUNCTION__
+ , port
+ , f_serialtimeout ? "timed out" : "failed"
+ , strerror(errno_save));
+ OurImports->TtyUnlock(port);
+ return -1;
+ }
+
+ if ((fflags = fcntl(fd, F_GETFL)) < 0
+ || fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
+ LOG(PIL_CRIT, "%s: Setting flags on %s failed [%s]."
+ , __FUNCTION__
+ , port
+ , strerror(errno_save));
+ close(fd);
+ OurImports->TtyUnlock(port);
+ return -1;
+ }
+
+ if (tcgetattr(fd, &old_tio) < 0) {
+ LOG(PIL_CRIT, "%s: tcgetattr of %s failed [%s].", __FUNCTION__
+ , port
+ , strerror(errno));
+ close(fd);
+ OurImports->TtyUnlock(port);
+ return -1;
+ }
+
+ memcpy(&tio, &old_tio, sizeof(struct termios));
+ tio.c_cflag = CS8 | CLOCAL | CREAD;
+ tio.c_iflag = IGNPAR;
+ tio.c_oflag = 0;
+ tio.c_lflag = 0;
+ tio.c_cc[VMIN] = 1;
+ tio.c_cc[VTIME] = 0;
+
+ cfsetispeed(&tio, speed);
+ cfsetospeed(&tio, speed);
+
+ tcflush(fd, TCIOFLUSH);
+ tcsetattr(fd, TCSANOW, &tio);
+
+ return (fd);
+}
+
+/*
+ * Close serial port and restore old settings
+ */
+
+void
+APC_close_serialport(const char *port, int upsfd)
+{
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+ if (upsfd < 0) {
+ return;
+ }
+
+ tcflush(upsfd, TCIFLUSH);
+ tcsetattr(upsfd, TCSANOW, &old_tio);
+ close(upsfd);
+ if (port != NULL) {
+ OurImports->TtyUnlock(port);
+ }
+}
+
+/*
+ * Send a command to the ups
+ */
+
+int
+APC_send_cmd(int upsfd, const char *cmd)
+{
+ int i;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s(\"%s\")", __FUNCTION__, cmd);
+ }
+
+ tcflush(upsfd, TCIFLUSH);
+ for (i = strlen(cmd); i > 0; i--) {
+ if (write(upsfd, cmd++, 1) != 1) {
+ return (S_ACCESS);
+ }
+
+ usleep(SEND_DELAY);
+ }
+ return (S_OK);
+}
+
+/*
+ * Get the response from the ups
+ */
+
+int
+APC_recv_rsp(int upsfd, char *rsp)
+{
+ char *p = rsp;
+ char inp;
+ int num = 0;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ *p = '\0';
+
+ STONITH_SIGNAL(SIGALRM, APC_sh_serial_timeout);
+
+ alarm(SERIAL_TIMEOUT);
+
+ while (num < MAX_STRING) {
+
+ if (read(upsfd, &inp, 1) == 1) {
+
+ /* shutdown sends only a '*' without LF */
+ if ((inp == '*') && (num == 0)) {
+ *p++ = inp;
+ num++;
+ inp = ENDCHAR;
+ }
+
+ if (inp == ENDCHAR) {
+ alarm(0);
+ STONITH_IGNORE_SIG(SIGALRM);
+
+ *p = '\0';
+ if (Debug) {
+ LOG(PIL_DEBUG, "return(\"%s\")/*%s*/;"
+ , rsp, __FUNCTION__);
+ }
+ return (S_OK);
+ }
+
+ if (inp != CR) {
+ *p++ = inp;
+ num++;
+ }
+ }else{
+ alarm(0);
+ STONITH_IGNORE_SIG(SIGALRM);
+ *p = '\0';
+ LOG(PIL_DEBUG, "%s: %s.", __FUNCTION__,
+ f_serialtimeout ? "timeout" :
+ "can't access device" );
+ return (f_serialtimeout ? S_TIMEOUT : S_ACCESS);
+ }
+ }
+ return (S_ACCESS);
+}
+
+/*
+ * Enter smart mode
+ */
+
+int
+APC_enter_smartmode(int upsfd)
+{
+ int rc;
+ char resp[MAX_STRING];
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ strcpy(resp, RSP_SMART_MODE);
+
+ if (((rc = APC_send_cmd(upsfd, CMD_SMART_MODE)) == S_OK)
+ && ((rc = APC_recv_rsp(upsfd, resp)) == S_OK)
+ && (strcmp(RSP_SMART_MODE, resp) == 0)) {
+ return (S_OK);
+ }
+
+ return (S_ACCESS);
+}
+
+/*
+ * Set a value in the hardware using the <cmdchar> '-' (repeat) approach
+ */
+
+int
+APC_set_ups_var(int upsfd, const char *cmd, char *newval)
+{
+ char resp[MAX_STRING];
+ char orig[MAX_STRING];
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ if (((rc = APC_enter_smartmode(upsfd)) != S_OK)
+ || ((rc = APC_send_cmd(upsfd, cmd)) != S_OK)
+ || ((rc = APC_recv_rsp(upsfd, orig)) != S_OK)) {
+ return (rc);
+ }
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: var '%s' original val %s"
+ , __FUNCTION__, cmd, orig);
+ }
+
+ if (strcmp(orig, newval) == 0) {
+ return (S_OK); /* already set */
+ }
+
+ *resp = '\0';
+
+ while (strcmp(resp, orig) != 0) {
+ if (((rc = APC_send_cmd(upsfd, SWITCH_TO_NEXT_VAL)) != S_OK)
+ || ((rc = APC_recv_rsp(upsfd, resp)) != S_OK)) {
+ return (rc);
+ }
+
+ if (((rc = APC_enter_smartmode(upsfd)) != S_OK)
+ || ((rc = APC_send_cmd(upsfd, cmd)) != S_OK)
+ || ((rc = APC_recv_rsp(upsfd, resp)) != S_OK)) {
+ return (rc);
+ }
+
+ if (strcmp(resp, newval) == 0) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: var '%s' set to %s"
+ , __FUNCTION__, cmd, newval);
+ }
+
+ strcpy(newval, orig); /* return the old value */
+ return (S_OK); /* got it */
+ }
+ }
+
+ LOG(PIL_CRIT, "%s(): Could not set variable '%s' to %s!"
+ , __FUNCTION__, cmd, newval);
+ LOG(PIL_CRIT, "%s(): This UPS may not support STONITH :-("
+ , __FUNCTION__);
+
+ return (S_OOPS);
+}
+
+/*
+ * Query the smallest delay supported by the hardware using the
+ * <cmdchar> '-' (repeat) approach and looping through all possible values,
+ * saving the smallest
+ */
+
+int
+APC_get_smallest_delay(int upsfd, const char *cmd, char *smdelay)
+{
+ char resp[MAX_DELAY_STRING];
+ char orig[MAX_DELAY_STRING];
+ int delay, smallest;
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ if (((rc = APC_enter_smartmode(upsfd)) != S_OK)
+ || ((rc = APC_send_cmd(upsfd, cmd)) != S_OK)
+ || ((rc = APC_recv_rsp(upsfd, orig)) != S_OK)) {
+ return (rc);
+ }
+
+ smallest = atoi(orig);
+ strcpy(smdelay, orig);
+
+ *resp = '\0';
+
+ /* search for smallest delay; need to loop through all possible
+ * values so that we leave delay the way we found it */
+ while (strcmp(resp, orig) != 0) {
+ if (((rc = APC_send_cmd(upsfd, SWITCH_TO_NEXT_VAL)) != S_OK)
+ || ((rc = APC_recv_rsp(upsfd, resp)) != S_OK)) {
+ return (rc);
+ }
+
+ if (((rc = APC_enter_smartmode(upsfd)) != S_OK)
+ || ((rc = APC_send_cmd(upsfd, cmd)) != S_OK)
+ || ((rc = APC_recv_rsp(upsfd, resp)) != S_OK)) {
+ return (rc);
+ }
+
+ if ((delay = atoi(resp)) < smallest) {
+ smallest = delay;
+ strcpy(smdelay, resp);
+ }
+ }
+
+ return (S_OK);
+}
+
+/*
+ * Initialize the ups
+ */
+
+int
+APC_init(struct pluginDevice *ad)
+{
+ int upsfd;
+ char value[MAX_DELAY_STRING];
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ /* if ad->upsfd != -1 device has already been configured. */
+ /* Just enter smart mode again because otherwise a SmartUPS-1000 */
+ /* has been observed to sometimes not respond. */
+ if(ad->upsfd >= 0) {
+ if(APC_enter_smartmode(ad->upsfd) != S_OK) {
+ return(S_OOPS);
+ }
+ return S_OK;
+ }
+
+ /* open serial port and store the fd in ad->upsfd */
+ if ((upsfd = APC_open_serialport(ad->upsdev, B2400)) == -1) {
+ return S_OOPS;
+ }
+
+ /* switch into smart mode */
+ if (APC_enter_smartmode(upsfd) != S_OK) {
+ APC_close_serialport(ad->upsdev, upsfd);
+ ad->upsfd = -1;
+ return S_OOPS;
+ }
+
+ /* get the smallest possible delays for this particular hardware */
+ if (APC_get_smallest_delay(upsfd, CMD_SHUTDOWN_DELAY
+ , ad->shutdown_delay) != S_OK
+ || APC_get_smallest_delay(upsfd, CMD_WAKEUP_DELAY
+ , ad->wakeup_delay) != S_OK) {
+ LOG(PIL_CRIT, "%s: couldn't retrieve smallest delay from UPS"
+ , __FUNCTION__);
+ APC_close_serialport(ad->upsdev, upsfd);
+ ad->upsfd = -1;
+ return S_OOPS;
+ }
+
+ /* get the old settings and store them */
+ strcpy(value, ad->shutdown_delay);
+ if (APC_set_ups_var(upsfd, CMD_SHUTDOWN_DELAY, value) != S_OK) {
+ LOG(PIL_CRIT, "%s: couldn't set shutdown delay to %s"
+ , __FUNCTION__, ad->shutdown_delay);
+ APC_close_serialport(ad->upsdev, upsfd);
+ ad->upsfd = -1;
+ return S_OOPS;
+ }
+ strcpy(ad->old_shutdown_delay, value);
+ strcpy(value, ad->wakeup_delay);
+ if (APC_set_ups_var(upsfd, CMD_WAKEUP_DELAY, value) != S_OK) {
+ LOG(PIL_CRIT, "%s: couldn't set wakeup delay to %s"
+ , __FUNCTION__, ad->wakeup_delay);
+ APC_close_serialport(ad->upsdev, upsfd);
+ ad->upsfd = -1;
+ return S_OOPS;
+ }
+ strcpy(ad->old_wakeup_delay, value);
+
+ ad->upsfd = upsfd;
+ return S_OK;
+}
+
+/*
+ * Restore original settings and close the port
+ */
+
+void
+APC_deinit(struct pluginDevice *ad)
+{
+ APC_enter_smartmode( ad->upsfd );
+
+ APC_set_ups_var(ad->upsfd, CMD_SHUTDOWN_DELAY, ad->old_shutdown_delay);
+ APC_set_ups_var(ad->upsfd, CMD_WAKEUP_DELAY, ad->old_wakeup_delay);
+
+ /* close serial port */
+ if (ad->upsfd >= 0) {
+ APC_close_serialport(ad->upsdev, ad->upsfd);
+ ad->upsfd = -1;
+ }
+}
+static const char * const *
+apcsmart_get_confignames(StonithPlugin* sp)
+{
+ static const char * names[] = {ST_TTYDEV, ST_HOSTLIST, NULL};
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+ return names;
+}
+
+/*
+ * Stash away the config info we've been given...
+ */
+
+static int
+apcsmart_set_config(StonithPlugin * s, StonithNVpair* list)
+{
+ struct pluginDevice * ad = (struct pluginDevice*)s;
+ StonithNamesToGet namestocopy [] =
+ { {ST_TTYDEV, NULL}
+ , {ST_HOSTLIST, NULL}
+ , {NULL, NULL}
+ };
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+ ERRIFWRONGDEV(s, S_OOPS);
+
+ if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ ad->upsdev = namestocopy[0].s_value;
+ ad->hostlist = OurImports->StringToHostList(namestocopy[1].s_value);
+ FREE(namestocopy[1].s_value);
+
+ if (ad->hostlist == NULL) {
+ LOG(PIL_CRIT,"StringToHostList() failed");
+ return S_OOPS;
+ }
+ for (ad->hostcount = 0; ad->hostlist[ad->hostcount]
+ ; ad->hostcount++) {
+ strdown(ad->hostlist[ad->hostcount]);
+ }
+ if (access(ad->upsdev, R_OK|W_OK|F_OK) < 0) {
+ LOG(PIL_CRIT,"Cannot access tty [%s]", ad->upsdev);
+ return S_BADCONFIG;
+ }
+
+ return ad->hostcount ? S_OK : S_BADCONFIG;
+}
+
+/*
+ * return the status for this device
+ */
+
+static int
+apcsmart_status(StonithPlugin * s)
+{
+ struct pluginDevice *ad = (struct pluginDevice *) s;
+ char resp[MAX_STRING];
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+
+ /* get status */
+ if (((rc = APC_init( ad )) == S_OK)
+ && ((rc = APC_send_cmd(ad->upsfd, CMD_GET_STATUS)) == S_OK)
+ && ((rc = APC_recv_rsp(ad->upsfd, resp)) == S_OK)) {
+ return (S_OK); /* everything ok. */
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: failed, rc=%d.", __FUNCTION__, rc);
+ }
+ return (rc);
+}
+
+
+/*
+ * return the list of hosts configured for this device
+ */
+
+static char **
+apcsmart_hostlist(StonithPlugin * s)
+{
+ struct pluginDevice *ad = (struct pluginDevice *) s;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+ ERRIFNOTCONFIGED(s,NULL);
+
+ return OurImports->CopyHostList((const char **)(void*)ad->hostlist);
+}
+
+static gboolean
+apcsmart_RegisterBitsSet(struct pluginDevice * ad, int nreg, unsigned bits
+, gboolean* waserr)
+{
+ const char* reqregs[4] = {"?", "~", "'", "8"};
+ unsigned regval;
+ char resp[MAX_STRING];
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+
+ if (APC_enter_smartmode(ad->upsfd) != S_OK
+ || APC_send_cmd(ad->upsfd, reqregs[nreg]) != S_OK
+ || APC_recv_rsp(ad->upsfd, resp) != S_OK
+ || (sscanf(resp, "%02x", &regval) != 1)) {
+ if (waserr){
+ *waserr = TRUE;
+ }
+ return FALSE;
+ }
+ if (waserr){
+ *waserr = FALSE;
+ }
+ return ((regval & bits) == bits);
+}
+
+#define apcsmart_IsPoweredOff(ad, err) apcsmart_RegisterBitsSet(ad,1,0x40,err)
+#define apcsmart_ResetHappening(ad,err) apcsmart_RegisterBitsSet(ad,3,0x08,err)
+
+
+static int
+apcsmart_ReqOnOff(struct pluginDevice * ad, int request)
+{
+ const char * cmdstr;
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+ cmdstr = (request == ST_POWEROFF ? CMD_OFF : CMD_ON);
+ /* enter smartmode, send on/off command */
+ if ((rc =APC_enter_smartmode(ad->upsfd)) != S_OK
+ || (rc = APC_send_cmd(ad->upsfd, cmdstr)) != S_OK) {
+ return rc;
+ }
+ sleep(2);
+ if ((rc = APC_send_cmd(ad->upsfd, cmdstr)) == S_OK) {
+ gboolean ison;
+ gboolean waserr;
+ sleep(1);
+ ison = !apcsmart_IsPoweredOff(ad, &waserr);
+ if (waserr) {
+ return S_RESETFAIL;
+ }
+ if (request == ST_POWEROFF) {
+ return ison ? S_RESETFAIL : S_OK;
+ }else{
+ return ison ? S_OK : S_RESETFAIL;
+ }
+ }
+ return rc;
+}
+
+/*
+ * reset the host
+ */
+
+static int
+apcsmart_ReqGenericReset(struct pluginDevice *ad)
+{
+ char resp[MAX_STRING];
+ int rc = S_RESETFAIL;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ /* send reset command(s) */
+ if (((rc = APC_init(ad)) == S_OK)
+ && ((rc = APC_send_cmd(ad->upsfd, CMD_RESET)) == S_OK)) {
+ if (((rc = APC_recv_rsp(ad->upsfd, resp)) == S_OK)
+ && (strcmp(resp, RSP_RESET) == 0
+ || strcmp(resp, RSP_RESET2) == 0)) {
+ /* first kind of reset command was accepted */
+ } else if (((rc = APC_send_cmd(ad->upsfd, CMD_RESET2)) == S_OK)
+ && ((rc = APC_recv_rsp(ad->upsfd, resp)) == S_OK)
+ && (strcmp(resp, RSP_RESET) == 0
+ || strcmp(resp, RSP_RESET2) == 0)) {
+ /* second kind of command was accepted */
+ } else {
+ if (Debug) {
+ LOG(PIL_DEBUG, "APC: neither reset command "
+ "was accepted");
+ }
+ rc = S_RESETFAIL;
+ }
+ }
+ if (rc == S_OK) {
+ /* we wait grace period + up to 10 seconds after shutdown */
+ int maxdelay = atoi(ad->shutdown_delay)+10;
+ int j;
+
+ for (j=0; j < maxdelay; ++j) {
+ gboolean err;
+ if (apcsmart_ResetHappening(ad, &err)) {
+ return err ? S_RESETFAIL : S_OK;
+ }
+ sleep(1);
+ }
+ LOG(PIL_CRIT, "%s: timed out waiting for reset to end."
+ , __FUNCTION__);
+ return S_RESETFAIL;
+
+ }else{
+ if (strcmp(resp, RSP_NA) == 0){
+ gboolean iserr;
+ /* This means it's currently powered off */
+ /* or busy on a previous command... */
+ if (apcsmart_IsPoweredOff(ad, &iserr)) {
+ if (iserr) {
+ LOG(PIL_DEBUG, "%s: power off "
+ "detection failed.", __FUNCTION__);
+ return S_RESETFAIL;
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "APC: was powered off, "
+ "powering back on.");
+ }
+ return apcsmart_ReqOnOff(ad, ST_POWERON);
+ }
+ }
+ }
+ strcpy(resp, "?");
+
+ /* reset failed */
+
+ return S_RESETFAIL;
+}
+
+static int
+apcsmart_reset_req(StonithPlugin * s, int request, const char *host)
+{
+ char ** hl;
+ int b_found=FALSE;
+ struct pluginDevice * ad = (struct pluginDevice *) s;
+ int rc;
+
+ ERRIFNOTCONFIGED(s, S_OOPS);
+
+ if (host == NULL) {
+ LOG(PIL_CRIT, "%s: invalid hostname argument.", __FUNCTION__);
+ return (S_INVAL);
+ }
+
+ /* look through the hostlist */
+ hl = ad->hostlist;
+
+ while (*hl && !b_found ) {
+ if( strcasecmp( *hl, host ) == 0 ) {
+ b_found = TRUE;
+ break;
+ }else{
+ ++hl;
+ }
+ }
+
+ /* host not found in hostlist */
+ if( !b_found ) {
+ LOG(PIL_CRIT, "%s: host '%s' not in hostlist."
+ , __FUNCTION__, host);
+ return S_BADHOST;
+ }
+ if ((rc = APC_init(ad)) != S_OK) {
+ return rc;
+ }
+
+ if (request == ST_POWERON || request == ST_POWEROFF) {
+ return apcsmart_ReqOnOff(ad, request);
+ }
+ return apcsmart_ReqGenericReset(ad);
+}
+
+
+/*
+ * get info about the stonith device
+ */
+
+static const char *
+apcsmart_get_info(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice *ad = (struct pluginDevice *) s;
+ const char *ret;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,NULL);
+
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = ad->idinfo;
+ break;
+
+ case ST_DEVICENAME:
+ ret = ad->upsdev;
+ break;
+
+ case ST_DEVICEDESCR:
+ ret = "APC Smart UPS\n"
+ " (via serial port - NOT USB!). \n"
+ " Works with higher-end APC UPSes, like\n"
+ " Back-UPS Pro, Smart-UPS, Matrix-UPS, etc.\n"
+ " (Smart-UPS may have to be >= Smart-UPS 700?).\n"
+ " See http://www.networkupstools.org/protocols/apcsmart.html\n"
+ " for protocol compatibility details.";
+ break;
+
+ case ST_DEVICEURL:
+ ret = "http://www.apc.com/";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = apcsmartXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+ return (ret);
+}
+
+/*
+ * APC Stonith destructor...
+ */
+
+static void
+apcsmart_destroy(StonithPlugin * s)
+{
+ struct pluginDevice *ad = (struct pluginDevice *) s;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+ VOIDERRIFWRONGDEV(s);
+
+ if (ad->upsfd >= 0 && ad->upsdev) {
+ APC_deinit( ad );
+ }
+
+ ad->pluginid = NOTpluginID;
+
+ if (ad->hostlist) {
+ stonith_free_hostlist(ad->hostlist);
+ ad->hostlist = NULL;
+ }
+ if (ad->upsdev != NULL) {
+ FREE(ad->upsdev);
+ ad->upsdev = NULL;
+ }
+
+ ad->hostcount = -1;
+ ad->upsfd = -1;
+
+ FREE(ad);
+
+}
+
+/*
+ * Create a new APC Stonith device. Too bad this function can't be
+ * static
+ */
+
+static StonithPlugin *
+apcsmart_new(const char *subplugin)
+{
+ struct pluginDevice *ad = ST_MALLOCT(struct pluginDevice);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+ if (ad == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory.", __FUNCTION__);
+ return (NULL);
+ }
+
+ memset(ad, 0, sizeof(*ad));
+
+ ad->pluginid = pluginid;
+ ad->hostlist = NULL;
+ ad->hostcount = -1;
+ ad->upsfd = -1;
+ ad->idinfo = DEVICE;
+ ad->sp.s_ops = &apcsmartOps;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: returning successfully.", __FUNCTION__);
+ }
+ return &(ad->sp);
+}
diff --git a/lib/plugins/stonith/apcsmart.cfg.example b/lib/plugins/stonith/apcsmart.cfg.example
new file mode 100644
index 0000000..278f925
--- /dev/null
+++ b/lib/plugins/stonith/apcsmart.cfg.example
@@ -0,0 +1 @@
+/dev/ups hostname
diff --git a/lib/plugins/stonith/baytech.c b/lib/plugins/stonith/baytech.c
new file mode 100644
index 0000000..33093ad
--- /dev/null
+++ b/lib/plugins/stonith/baytech.c
@@ -0,0 +1,924 @@
+/*
+ * Stonith module for BayTech Remote Power Controllers (RPC-x devices)
+ *
+ * Copyright (c) 2000 Alan Robertson <alanr@unix.sh>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <lha_internal.h>
+#define DEVICE "BayTech power switch"
+
+#define DOESNT_USE_STONITHKILLCOMM 1
+
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN baytech
+#define PIL_PLUGIN_S "baytech"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+#include "stonith_signal.h"
+
+static StonithPlugin * baytech_new(const char *);
+static void baytech_destroy(StonithPlugin *);
+static int baytech_set_config(StonithPlugin *, StonithNVpair *);
+static const char * const * baytech_get_confignames(StonithPlugin * s);
+static const char * baytech_get_info(StonithPlugin * s, int InfoType);
+static int baytech_status(StonithPlugin *);
+static int baytech_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** baytech_hostlist(StonithPlugin *);
+
+static struct stonith_ops baytechOps ={
+ baytech_new, /* Create new STONITH object */
+ baytech_destroy, /* Destroy STONITH object */
+ baytech_get_info, /* Return STONITH info string */
+ baytech_get_confignames, /* Return STONITH config vars */
+ baytech_set_config, /* set configuration from vars */
+ baytech_status, /* Return STONITH device status */
+ baytech_reset_req, /* Request a reset */
+ baytech_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+#include "stonith_expect_helpers.h"
+
+#define MAXOUTLET 32
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &baytechOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * I have an RPC-5. This code has been tested with this switch.
+ *
+ * The BayTech switches are quite nice, but the dialogues are a bit of a
+ * pain for mechanical parsing.
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ char * idinfo;
+ char * unitid;
+ const struct BayTechModelInfo* modelinfo;
+ pid_t pid;
+ int rdfd;
+ int wrfd;
+ char * device;
+ char * user;
+ char * passwd;
+};
+
+struct BayTechModelInfo {
+ const char * type; /* Baytech model info */
+ size_t socklen; /* Length of socket name string */
+ struct Etoken * expect; /* Expect string before outlet list */
+};
+
+static int parse_socket_line(struct pluginDevice*,const char *
+, int *, char *);
+
+static const char * pluginid = "BayTech-Stonith";
+static const char * NOTpluginID = "BayTech device has been destroyed";
+
+/*
+ * Different expect strings that we get from the Baytech
+ * Remote Power Controllers...
+ */
+
+#define BAYTECHASSOC "Bay Technical Associates"
+
+static struct Etoken BayTechAssoc[] = { {BAYTECHASSOC, 0, 0}, {NULL,0,0}};
+static struct Etoken UnitId[] = { {"Unit ID: ", 0, 0}, {NULL,0,0}};
+static struct Etoken login[] = { {"username>", 0, 0} ,{NULL,0,0}};
+static struct Etoken password[] = { {"password>", 0, 0}
+ , {"username>", 0, 0} ,{NULL,0,0}};
+static struct Etoken Selection[] = { {"election>", 0, 0} ,{NULL,0,0}};
+static struct Etoken RPC[] = { {"RPC", 0, 0} ,{NULL,0,0}};
+static struct Etoken LoginOK[] = { {"RPC", 0, 0}, {"Invalid password", 1, 0}
+ , {NULL,0,0}};
+static struct Etoken GTSign[] = { {">", 0, 0} ,{NULL,0,0}};
+static struct Etoken Menu[] = { {"Menu:", 0, 0} ,{NULL,0,0}};
+static struct Etoken Temp[] = { {"emperature: ", 0, 0}
+ , {NULL,0,0}};
+static struct Etoken Break[] = { {"Status", 0, 0}
+ , {NULL,0,0}};
+static struct Etoken PowerApplied[] = { {"ower applied to outlet", 0, 0}
+ , {NULL,0,0}};
+
+/* We may get a notice about rebooting, or a request for confirmation */
+static struct Etoken Rebooting[] = { {"ebooting selected outlet", 0, 0}
+ , {"(Y/N)>", 1, 0}
+ , {"already off.", 2, 0}
+ , {NULL,0,0}};
+
+static struct Etoken TurningOnOff[] = { {"RPC", 0, 0}
+ , {"(Y/N)>", 1, 0}
+ , {"already ", 2, 0}
+ , {NULL,0,0}};
+
+
+static struct BayTechModelInfo ModelInfo [] = {
+ {"BayTech RPC-5", 18, Temp},/* This first model will be the default */
+ {"BayTech RPC-3", 10, Break},
+ {"BayTech RPC-3A", 10, Break},
+ {NULL, 0, NULL},
+};
+
+#include "stonith_config_xml.h"
+
+static const char *baytechXML =
+ XML_PARAMETERS_BEGIN
+ XML_IPADDR_PARM
+ XML_LOGIN_PARM
+ XML_PASSWD_PARM
+ XML_PARAMETERS_END;
+
+static int RPC_connect_device(struct pluginDevice * bt);
+static int RPCLogin(struct pluginDevice * bt);
+static int RPCRobustLogin(struct pluginDevice * bt);
+static int RPCNametoOutletList(struct pluginDevice*, const char * name
+, int outletlist[]);
+static int RPCReset(struct pluginDevice*, int unitnum, const char * rebootid);
+static int RPCLogout(struct pluginDevice * bt);
+
+
+static int RPC_onoff(struct pluginDevice*, int unitnum, const char * unitid
+, int request);
+
+/* Login to the Baytech Remote Power Controller (RPC) */
+
+static int
+RPCLogin(struct pluginDevice * bt)
+{
+ char IDinfo[128];
+ static char IDbuf[128];
+ char * idptr = IDinfo;
+ char * delim;
+ int j;
+
+ EXPECT(bt->rdfd, RPC, 10);
+
+ /* Look for the unit type info */
+ if (EXPECT_TOK(bt->rdfd, BayTechAssoc, 2, IDinfo
+ , sizeof(IDinfo), Debug) < 0) {
+ LOG(PIL_CRIT, "No initial response from %s.", bt->idinfo);
+ return(errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS);
+ }
+ idptr += strspn(idptr, WHITESPACE);
+ /*
+ * We should be looking at something like this:
+ * RPC-5 Telnet Host
+ * Revision F 4.22, (C) 1999
+ * Bay Technical Associates
+ */
+
+ /* Truncate the result after the RPC-5 part */
+ if ((delim = strchr(idptr, ' ')) != NULL) {
+ *delim = EOS;
+ }
+ snprintf(IDbuf, sizeof(IDbuf), "BayTech RPC%s", idptr);
+ REPLSTR(bt->idinfo, IDbuf);
+ if (bt->idinfo == NULL) {
+ return(S_OOPS);
+ }
+
+ bt->modelinfo = &ModelInfo[0];
+
+ for (j=0; ModelInfo[j].type != NULL; ++j) {
+ /*
+ * TIMXXX -
+ * Look at device ID as this really describes the model.
+ */
+ if (strcasecmp(ModelInfo[j].type, IDbuf) == 0) {
+ bt->modelinfo = &ModelInfo[j];
+ break;
+ }
+ }
+
+ /* Look for the unit id info */
+ EXPECT(bt->rdfd, UnitId, 10);
+ SNARF(bt->rdfd, IDbuf, 2);
+ delim = IDbuf + strcspn(IDbuf, WHITESPACE);
+ *delim = EOS;
+ REPLSTR(bt->unitid, IDbuf);
+ if (bt->unitid == NULL) {
+ return(S_OOPS);
+ }
+
+ /* Expect "username>" */
+ EXPECT(bt->rdfd, login, 2);
+
+ SEND(bt->wrfd, bt->user);
+ SEND(bt->wrfd, "\r");
+
+ /* Expect "password>" */
+
+ switch (StonithLookFor(bt->rdfd, password, 5)) {
+ case 0: /* Good! */
+ break;
+
+ case 1: /* OOPS! got another username prompt */
+ LOG(PIL_CRIT, "Invalid username for %s.", bt->idinfo);
+ return(S_ACCESS);
+
+ default:
+ return(errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS);
+ }
+
+ SEND(bt->wrfd, bt->passwd);
+ SEND(bt->wrfd, "\r");
+
+ /* Expect "RPC-x Menu" */
+
+ switch (StonithLookFor(bt->rdfd, LoginOK, 5)) {
+
+ case 0: /* Good! */
+ break;
+
+ case 1: /* Uh-oh - bad password */
+ LOG(PIL_CRIT, "Invalid password for %s.", bt->idinfo);
+ return(S_ACCESS);
+
+ default:
+ return(errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS);
+ }
+ EXPECT(bt->rdfd, Menu, 2);
+
+ return(S_OK);
+}
+
+static int
+RPCRobustLogin(struct pluginDevice * bt)
+{
+ int rc=S_OOPS;
+ int j;
+
+ for (j=0; j < 20 && rc != S_OK; ++j) {
+
+
+ if (RPC_connect_device(bt) != S_OK) {
+ continue;
+ }
+
+ rc = RPCLogin(bt);
+ }
+ return rc;
+}
+
+/* Log out of the Baytech RPC */
+
+static int
+RPCLogout(struct pluginDevice* bt)
+{
+ int rc;
+
+ /* Make sure we're in the right menu... */
+ SEND(bt->wrfd, "\r");
+
+ /* Expect "Selection>" */
+ rc = StonithLookFor(bt->rdfd, Selection, 5);
+
+ /* Option 6 is Logout */
+ SEND(bt->wrfd, "6\r");
+
+ close(bt->wrfd);
+ close(bt->rdfd);
+ bt->wrfd = bt->rdfd = -1;
+ return(rc >= 0 ? S_OK : (errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS));
+}
+
+/* Reset (power-cycle) the given outlet number */
+static int
+RPCReset(struct pluginDevice* bt, int unitnum, const char * rebootid)
+{
+ char unum[32];
+
+
+ SEND(bt->wrfd, "\r");
+
+ /* Make sure we're in the top level menu */
+
+ /* Expect "RPC-x Menu" */
+ EXPECT(bt->rdfd, RPC, 5);
+ EXPECT(bt->rdfd, Menu, 5);
+
+ /* OK. Request sub-menu 1 (Outlet Control) */
+ SEND(bt->wrfd, "1\r");
+
+ /* Verify that we're in the sub-menu */
+
+ /* Expect: "RPC-x>" */
+ EXPECT(bt->rdfd, RPC, 5);
+ EXPECT(bt->rdfd, GTSign, 5);
+
+
+ /* Send REBOOT command for given outlet */
+ snprintf(unum, sizeof(unum), "REBOOT %d\r", unitnum);
+ SEND(bt->wrfd, unum);
+
+ /* Expect "ebooting "... or "(Y/N)" (if confirmation turned on) */
+
+ retry:
+ switch (StonithLookFor(bt->rdfd, Rebooting, 5)) {
+ case 0: /* Got "Rebooting" Do nothing */
+ break;
+
+ case 1: /* Got that annoying command confirmation :-( */
+ SEND(bt->wrfd, "Y\r");
+ goto retry;
+
+ case 2: /* Outlet is turned off */
+ LOG(PIL_CRIT, "Host is OFF: %s.", rebootid);
+ return(S_ISOFF);
+
+ default:
+ return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS);
+ }
+ LOG(PIL_INFO, "Host %s (outlet %d) being rebooted."
+ , rebootid, unitnum);
+
+ /* Expect "ower applied to outlet" */
+ if (StonithLookFor(bt->rdfd, PowerApplied, 30) < 0) {
+ return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS);
+ }
+
+ /* All Right! Power is back on. Life is Good! */
+
+ LOG(PIL_INFO, "Power restored to host %s (outlet %d)."
+ , rebootid, unitnum);
+
+ /* Expect: "RPC-x>" */
+ EXPECT(bt->rdfd, RPC,5);
+ EXPECT(bt->rdfd, GTSign, 5);
+
+ /* Pop back to main menu */
+ SEND(bt->wrfd, "MENU\r");
+ return(S_OK);
+}
+
+static int
+RPC_onoff(struct pluginDevice* bt, int unitnum, const char * unitid, int req)
+{
+ char unum[32];
+
+ const char * onoff = (req == ST_POWERON ? "on" : "off");
+ int rc;
+
+
+ if ((rc = RPCRobustLogin(bt) != S_OK)) {
+ LOG(PIL_CRIT, "Cannot log into %s."
+ , bt->idinfo ? bt->idinfo : DEVICE);
+ return(rc);
+ }
+ SEND(bt->wrfd, "\r");
+
+ /* Make sure we're in the top level menu */
+
+ /* Expect "RPC-x Menu" */
+ EXPECT(bt->rdfd, RPC, 5);
+ EXPECT(bt->rdfd, Menu, 5);
+
+ /* OK. Request sub-menu 1 (Outlet Control) */
+ SEND(bt->wrfd, "1\r");
+
+ /* Verify that we're in the sub-menu */
+
+ /* Expect: "RPC-x>" */
+ EXPECT(bt->rdfd, RPC, 5);
+ EXPECT(bt->rdfd, GTSign, 5);
+
+
+ /* Send ON/OFF command for given outlet */
+ snprintf(unum, sizeof(unum), "%s %d\r"
+ , onoff, unitnum);
+ SEND(bt->wrfd, unum);
+
+ /* Expect "RPC->x "... or "(Y/N)" (if confirmation turned on) */
+
+ if (StonithLookFor(bt->rdfd, TurningOnOff, 10) == 1) {
+ /* They've turned on that annoying command confirmation :-( */
+ SEND(bt->wrfd, "Y\r");
+ EXPECT(bt->rdfd, TurningOnOff, 10);
+ }
+
+ EXPECT(bt->rdfd, GTSign, 10);
+
+ /* All Right! Command done. Life is Good! */
+ LOG(PIL_INFO, "Power to host %s (outlet %d) turned %s."
+ , unitid, unitnum, onoff);
+ /* Pop back to main menu */
+ SEND(bt->wrfd, "MENU\r");
+ return(S_OK);
+}
+
+/*
+ * Map the given host name into an (AC) Outlet number on the power strip
+ */
+
+static int
+RPCNametoOutletList(struct pluginDevice* bt, const char * name
+, int outletlist[])
+{
+ char NameMapping[128];
+ int sockno;
+ char sockname[32];
+ int maxfound = 0;
+
+
+
+ /* Verify that we're in the top-level menu */
+ SEND(bt->wrfd, "\r");
+
+ /* Expect "RPC-x Menu" */
+ EXPECT(bt->rdfd, RPC, 5);
+ EXPECT(bt->rdfd, Menu, 5);
+
+
+ /* OK. Request sub-menu 1 (Outlet Control) */
+ SEND(bt->wrfd, "1\r");
+
+ /* Verify that we're in the sub-menu */
+
+ /* Expect: "RPC-x>" */
+ EXPECT(bt->rdfd, RPC, 5);
+ EXPECT(bt->rdfd, GTSign, 5);
+
+ /* The status command output contains mapping of hosts to outlets */
+ SEND(bt->wrfd, "STATUS\r");
+
+ /* Expect: "emperature:" so we can skip over it... */
+ EXPECT(bt->rdfd, bt->modelinfo->expect, 5);
+ EXPECT(bt->rdfd, CRNL, 5);
+
+ /* Looks Good! Parse the status output */
+
+ do {
+ char * last;
+ NameMapping[0] = EOS;
+ SNARF(bt->rdfd, NameMapping, 5);
+
+ if (!parse_socket_line(bt, NameMapping, &sockno, sockname)) {
+ continue;
+ }
+
+ last = sockname+bt->modelinfo->socklen;
+ *last = EOS;
+ --last;
+
+ /* Strip off trailing blanks */
+ for(; last > sockname; --last) {
+ if (*last == ' ') {
+ *last = EOS;
+ }else{
+ break;
+ }
+ }
+ if (strcasecmp(name, sockname) == 0) {
+ outletlist[maxfound] = sockno;
+ ++maxfound;
+ }
+ } while (strlen(NameMapping) > 2 && maxfound < MAXOUTLET);
+
+ /* Pop back out to the top level menu */
+ SEND(bt->wrfd, "MENU\r");
+ return(maxfound);
+}
+
+static int
+baytech_status(StonithPlugin *s)
+{
+ struct pluginDevice* bt;
+ int rc;
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ bt = (struct pluginDevice*) s;
+
+ if ((rc = RPCRobustLogin(bt) != S_OK)) {
+ LOG(PIL_CRIT, "Cannot log into %s."
+ , bt->idinfo ? bt->idinfo : DEVICE);
+ return(rc);
+ }
+
+ /* Verify that we're in the top-level menu */
+ SEND(bt->wrfd, "\r");
+
+ /* Expect "RPC-x Menu" */
+ EXPECT(bt->rdfd, RPC, 5);
+ EXPECT(bt->rdfd, Menu, 5);
+
+ return(RPCLogout(bt));
+}
+/*
+ * Return the list of hosts (outlet names) for the devices on this BayTech unit
+ */
+
+static char **
+baytech_hostlist(StonithPlugin *s)
+{
+ char NameMapping[128];
+ char* NameList[64];
+ unsigned int numnames = 0;
+ char ** ret = NULL;
+ struct pluginDevice* bt;
+ unsigned int i;
+
+ ERRIFNOTCONFIGED(s,NULL);
+
+ bt = (struct pluginDevice*) s;
+
+ if (RPCRobustLogin(bt) != S_OK) {
+ LOG(PIL_CRIT, "Cannot log into %s."
+ , bt->idinfo ? bt->idinfo : DEVICE);
+ return(NULL);
+ }
+
+ /* Verify that we're in the top-level menu */
+ SEND(bt->wrfd, "\r");
+
+ /* Expect "RPC-x Menu" */
+ NULLEXPECT(bt->rdfd, RPC, 5);
+ NULLEXPECT(bt->rdfd, Menu, 5);
+
+ /* OK. Request sub-menu 1 (Outlet Control) */
+ SEND(bt->wrfd, "1\r");
+
+ /* Verify that we're in the sub-menu */
+
+ /* Expect: "RPC-x>" */
+ NULLEXPECT(bt->rdfd, RPC, 5);
+ NULLEXPECT(bt->rdfd, GTSign, 5);
+
+ /* The status command output contains mapping of hosts to outlets */
+ SEND(bt->wrfd, "STATUS\r");
+
+ /* Expect: "emperature:" so we can skip over it... */
+ NULLEXPECT(bt->rdfd, bt->modelinfo->expect, 5);
+ NULLEXPECT(bt->rdfd, CRNL, 5);
+
+ /* Looks Good! Parse the status output */
+
+ do {
+ int sockno;
+ char sockname[64];
+ char * last;
+ char * nm;
+
+ NameMapping[0] = EOS;
+
+ NULLSNARF(bt->rdfd, NameMapping, 5);
+
+ if (!parse_socket_line(bt, NameMapping, &sockno, sockname)) {
+ continue;
+ }
+
+ last = sockname+bt->modelinfo->socklen;
+ *last = EOS;
+ --last;
+
+ /* Strip off trailing blanks */
+ for(; last > sockname; --last) {
+ if (*last == ' ') {
+ *last = EOS;
+ }else{
+ break;
+ }
+ }
+ if (numnames >= DIMOF(NameList)-1) {
+ break;
+ }
+ if ((nm = (char*)STRDUP(sockname)) == NULL) {
+ goto out_of_memory;
+ }
+ strdown(nm);
+ NameList[numnames] = nm;
+ ++numnames;
+ NameList[numnames] = NULL;
+ } while (strlen(NameMapping) > 2);
+
+ /* Pop back out to the top level menu */
+ SEND(bt->wrfd, "MENU\r");
+ if (numnames >= 1) {
+ ret = (char **)MALLOC((numnames+1)*sizeof(char*));
+ if (ret == NULL) {
+ goto out_of_memory;
+ }else{
+ memcpy(ret, NameList, (numnames+1)*sizeof(char*));
+ }
+ }
+ (void)RPCLogout(bt);
+ return(ret);
+
+out_of_memory:
+ LOG(PIL_CRIT, "out of memory");
+ for (i=0; i<numnames; i++) {
+ FREE(NameList[i]);
+ }
+ return(NULL);
+}
+
+/*
+ * Connect to the given BayTech device.
+ * We should add serial support here eventually...
+ */
+static int
+RPC_connect_device(struct pluginDevice * bt)
+{
+ int fd = OurImports->OpenStreamSocket(bt->device
+ , TELNET_PORT, TELNET_SERVICE);
+
+ if (fd < 0) {
+ return(S_OOPS);
+ }
+ bt->rdfd = bt->wrfd = fd;
+ return(S_OK);
+}
+
+/*
+ * Reset the given host on this Stonith device.
+ */
+static int
+baytech_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ int rc = S_OK;
+ int lorc = 0;
+ struct pluginDevice* bt;
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ bt = (struct pluginDevice*) s;
+
+ if ((rc = RPCRobustLogin(bt)) != S_OK) {
+ LOG(PIL_CRIT, "Cannot log into %s."
+ , bt->idinfo ? bt->idinfo : DEVICE);
+ }else{
+ int noutlets;
+ int outlets[MAXOUTLET];
+ int j;
+ noutlets = RPCNametoOutletList(bt, host, outlets);
+
+ if (noutlets < 1) {
+ LOG(PIL_CRIT, "%s %s doesn't control host [%s]"
+ , bt->idinfo, bt->unitid, host);
+ return(S_BADHOST);
+ }
+ switch(request) {
+
+ case ST_POWERON:
+ case ST_POWEROFF:
+ for (j=0; rc == S_OK && j < noutlets;++j) {
+ rc = RPC_onoff(bt, outlets[j], host, request);
+ }
+ break;
+ case ST_GENERIC_RESET:
+ /*
+ * Our strategy here:
+ * 1. Power off all outlets except the last one
+ * 2. reset the last outlet
+ * 3. power the other outlets back on
+ */
+
+ for (j=0; rc == S_OK && j < noutlets-1; ++j) {
+ rc = RPC_onoff(bt,outlets[j],host
+ , ST_POWEROFF);
+ }
+ if (rc == S_OK) {
+ rc = RPCReset(bt, outlets[j], host);
+ }
+ for (j=0; rc == S_OK && j < noutlets-1; ++j) {
+ rc = RPC_onoff(bt, outlets[j], host
+ , ST_POWERON);
+ }
+ break;
+ default:
+ rc = S_INVAL;
+ break;
+ }
+ }
+
+ lorc = RPCLogout(bt);
+
+ return(rc != S_OK ? rc : lorc);
+}
+
+static const char * const *
+baytech_get_confignames(StonithPlugin * s)
+{
+ static const char * ret[] = {ST_IPADDR, ST_LOGIN, ST_PASSWD, NULL};
+ return ret;
+}
+
+
+/*
+ * Parse the config information in the given string, and stash it away...
+ */
+static int
+baytech_set_config(StonithPlugin* s, StonithNVpair* list)
+{
+ struct pluginDevice* bt = (struct pluginDevice *)s;
+ int rc;
+ StonithNamesToGet namestocopy [] =
+ { {ST_IPADDR, NULL}
+ , {ST_LOGIN, NULL}
+ , {ST_PASSWD, NULL}
+ , {NULL, NULL}
+ };
+
+ ERRIFWRONGDEV(s, S_OOPS);
+ if (bt->sp.isconfigured) {
+ return S_OOPS;
+ }
+
+ if ((rc =OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ bt->device = namestocopy[0].s_value;
+ bt->user = namestocopy[1].s_value;
+ bt->passwd = namestocopy[2].s_value;
+
+ return(S_OK);
+}
+
+static const char *
+baytech_get_info(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* bt;
+ const char * ret;
+
+ ERRIFWRONGDEV(s,NULL);
+
+ bt = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+
+ case ST_DEVICEID: /* What type of device? */
+ ret = bt->idinfo;
+ break;
+
+ case ST_DEVICENAME: /* Which particular device? */
+ ret = bt->device;
+ break;
+
+ case ST_DEVICEDESCR: /* Description of dev type */
+ ret = "Bay Technical Associates (Baytech) RPC "
+ "series power switches (via telnet).\n"
+ "The RPC-5, RPC-3 and RPC-3A switches are well tested"
+ ".";
+ break;
+
+ case ST_DEVICEURL: /* Manufacturer's web site */
+ ret = "http://www.baytech.net/";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = baytechXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * Baytech Stonith destructor...
+ */
+static void
+baytech_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* bt;
+
+ VOIDERRIFWRONGDEV(s);
+
+ bt = (struct pluginDevice *)s;
+
+ bt->pluginid = NOTpluginID;
+ if (bt->rdfd >= 0) {
+ close(bt->rdfd);
+ bt->rdfd = -1;
+ }
+ if (bt->wrfd >= 0) {
+ close(bt->wrfd);
+ bt->wrfd = -1;
+ }
+ if (bt->device != NULL) {
+ FREE(bt->device);
+ bt->device = NULL;
+ }
+ if (bt->user != NULL) {
+ FREE(bt->user);
+ bt->user = NULL;
+ }
+ if (bt->passwd != NULL) {
+ FREE(bt->passwd);
+ bt->passwd = NULL;
+ }
+ if (bt->idinfo != NULL) {
+ FREE(bt->idinfo);
+ bt->idinfo = NULL;
+ }
+ if (bt->unitid != NULL) {
+ FREE(bt->unitid);
+ bt->unitid = NULL;
+ }
+ FREE(bt);
+}
+
+/* Create a new BayTech Stonith device. */
+
+static StonithPlugin *
+baytech_new(const char *subplugin)
+{
+ struct pluginDevice* bt = ST_MALLOCT(struct pluginDevice);
+
+ if (bt == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(bt, 0, sizeof(*bt));
+ bt->pluginid = pluginid;
+ bt->pid = -1;
+ bt->rdfd = -1;
+ bt->wrfd = -1;
+ REPLSTR(bt->idinfo, DEVICE);
+ if (bt->idinfo == NULL) {
+ FREE(bt);
+ return(NULL);
+ }
+ bt->modelinfo = &ModelInfo[0];
+ bt->sp.s_ops = &baytechOps;
+
+ return &(bt->sp); /* same as "bt" */
+}
+
+static int
+parse_socket_line(struct pluginDevice * bt, const char *NameMapping
+, int *sockno, char *sockname)
+{
+#if 0
+ char format[64];
+ snprintf(format, sizeof(format), "%%7d %%%dc"
+ , bt->modelinfo->socklen);
+ /* 7 digits, 7 blanks, then 'socklen' characters */
+ /* [0-6]: digits, NameMapping[13] begins the sockname */
+ /* NameMapping strlen must be >= socklen + 14 */
+
+ if (sscanf(NameMapping, format, sockno, sockname) != 2) {
+ return FALSE;
+ }
+#else
+# define OFFSET 14
+
+ if (sscanf(NameMapping, "%7d", sockno) != 1
+ || strlen(NameMapping) < OFFSET+bt->modelinfo->socklen) {
+ return FALSE;
+ }
+ strncpy(sockname, NameMapping+OFFSET, bt->modelinfo->socklen);
+ sockname[bt->modelinfo->socklen] = EOS;
+#endif
+ return TRUE;
+}
diff --git a/lib/plugins/stonith/bladehpi.c b/lib/plugins/stonith/bladehpi.c
new file mode 100644
index 0000000..ae9a4cf
--- /dev/null
+++ b/lib/plugins/stonith/bladehpi.c
@@ -0,0 +1,1101 @@
+/*
+ * Stonith module for BladeCenter via OpenHPI, an implementation of Service
+ * Availability Forum's Hardware Platfrom Interface
+ *
+ * Author: Dave Blaschke <debltc@us.ibm.com>
+ *
+ * Copyright (c) 2005 International Business Machines
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <lha_internal.h>
+
+#define DEVICE "IBM BladeCenter (OpenHPI)"
+
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN bladehpi
+#define PIL_PLUGIN_S "bladehpi"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+#include <openhpi/SaHpi.h>
+
+/* Maximum number of seconds to wait for host to power off */
+#define MAX_POWEROFF_WAIT 60
+
+/* entity_root, the one required plugin parameter */
+#define ST_ENTITYROOT "entity_root"
+
+/* String format of entity_root */
+#define SYSTEM_CHASSIS_FMT "{SYSTEM_CHASSIS,%d}"
+
+/* soft_reset, the one optional plugin parameter */
+#define ST_SOFTRESET "soft_reset"
+
+#define OPENHPIURL "http://www.openhpi.org/"
+
+/* OpenHPI resource types of interest to this plugin */
+#define OHRES_NONE 0
+#define OHRES_BLADECENT 1
+#define OHRES_MGMTMOD 2
+#define OHRES_BLADE 3
+
+/* IBMBC_WAIT_FOR_OFF - This constant has to do with the problem that
+ saHpiResourcePowerStateSet can return before the desired state has been
+ achieved by the blade. In the SAHPI_POWER_OFF case this is not good,
+ as whoever calls this plugin assumes that the power is actually off
+ when the plugin returns with a successful return code. Define this
+ constant to build code that loops in one second intervals after calling
+ saHpiResourcePowerStateSet(SAHPI_POWER_OFF) to make sure the power is
+ really off.
+#define IBMBC_WAIT_FOR_OFF */
+
+static StonithPlugin * bladehpi_new(const char *);
+static void bladehpi_destroy(StonithPlugin *);
+static const char * bladehpi_getinfo(StonithPlugin *, int);
+static const char * const * bladehpi_get_confignames(StonithPlugin *);
+static int bladehpi_status(StonithPlugin *);
+static int bladehpi_reset_req(StonithPlugin *, int, const char *);
+static char ** bladehpi_hostlist(StonithPlugin *);
+static int bladehpi_set_config(StonithPlugin *, StonithNVpair *);
+
+static struct stonith_ops bladehpiOps = {
+ bladehpi_new, /* Create new STONITH object */
+ bladehpi_destroy, /* Destroy STONITH object */
+ bladehpi_getinfo, /* Return STONITH info string */
+ bladehpi_get_confignames, /* Return configuration parameters */
+ bladehpi_set_config, /* Set configuration */
+ bladehpi_status, /* Return STONITH device status */
+ bladehpi_reset_req, /* Request a reset */
+ bladehpi_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+
+static const PILPluginImports * PluginImports;
+static PILPlugin * OurPlugin;
+static PILInterface * OurInterface;
+static StonithImports * OurImports;
+static void * interfprivate;
+
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin *us, const PILPluginImports *imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin *us, const PILPluginImports *imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us
+ , PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &bladehpiOps
+ , NULL /* close */
+ , &OurInterface
+ , (void *)&OurImports
+ , &interfprivate);
+}
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ char * idinfo;
+ char * device;
+ int softreset;
+ GList * hostlist;
+ SaHpiVersionT ohver; /* OpenHPI interface version */
+ SaHpiSessionIdT ohsession; /* session ID */
+ SaHpiUint32T ohrptcnt; /* RPT count for hostlist */
+ SaHpiResourceIdT ohdevid; /* device resource ID */
+ SaHpiResourceIdT ohsensid; /* sensor resource ID */
+ SaHpiSensorNumT ohsensnum; /* sensor number */
+};
+
+static int open_hpi_session(struct pluginDevice *dev);
+static void close_hpi_session(struct pluginDevice *dev);
+
+static const char *pluginid = "BladeCenterDevice-Stonith";
+static const char *NOTpluginID = "IBM BladeCenter device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+#define XML_ENTITYROOT_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_ENTITYROOT \
+ XML_PARM_SHORTDESC_END
+
+#define XML_ENTITYROOT_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The entity_root of the STONITH device from the OpenHPI config file" \
+ XML_PARM_LONGDESC_END
+
+#define XML_ENTITYROOT_PARM \
+ XML_PARAMETER_BEGIN(ST_ENTITYROOT, "string", "1", "0") \
+ XML_ENTITYROOT_SHORTDESC \
+ XML_ENTITYROOT_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_SOFTRESET_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_SOFTRESET \
+ XML_PARM_SHORTDESC_END
+
+#define XML_SOFTRESET_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "Soft reset indicator, true|1 if STONITH device should use soft reset (power cycle) to reset nodes, false|0 if device should use hard reset (power off, wait, power on); default is false" \
+ XML_PARM_LONGDESC_END
+
+#define XML_SOFTRESET_PARM \
+ XML_PARAMETER_BEGIN(ST_SOFTRESET, "string", "0", "0") \
+ XML_SOFTRESET_SHORTDESC \
+ XML_SOFTRESET_LONGDESC \
+ XML_PARAMETER_END
+
+static const char *bladehpiXML =
+ XML_PARAMETERS_BEGIN
+ XML_ENTITYROOT_PARM
+ XML_SOFTRESET_PARM
+ XML_PARAMETERS_END;
+
+static int get_resource_type(char *, SaHpiRptEntryT *);
+static int get_sensor_num(SaHpiSessionIdT, SaHpiResourceIdT);
+static int get_bladehpi_hostlist(struct pluginDevice *);
+static void free_bladehpi_hostlist(struct pluginDevice *);
+static int get_num_tokens(char *str);
+
+struct blade_info {
+ char * name; /* blade name */
+ SaHpiResourceIdT resourceId; /* blade resource ID */
+ SaHpiCapabilitiesT resourceCaps; /* blade capabilities */
+};
+
+
+static int
+bladehpi_status(StonithPlugin *s)
+{
+ struct pluginDevice * dev;
+ SaErrorT ohrc;
+ SaHpiDomainInfoT ohdi;
+ int rc = S_OK;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s, S_OOPS);
+
+ dev = (struct pluginDevice *)s;
+ rc = open_hpi_session(dev);
+ if( rc != S_OK )
+ return rc;
+
+ /* Refresh the hostlist only if RPTs updated */
+ ohrc = saHpiDomainInfoGet(dev->ohsession, &ohdi);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to get domain info in %s (%d)"
+ , __FUNCTION__, ohrc);
+ rc = S_BADCONFIG;
+ goto done;
+ }
+ if (dev->ohrptcnt != ohdi.RptUpdateCount) {
+ free_bladehpi_hostlist(dev);
+ if (get_bladehpi_hostlist(dev) != S_OK) {
+ LOG(PIL_CRIT, "Unable to obtain list of hosts in %s"
+ , __FUNCTION__);
+ rc = S_BADCONFIG;
+ goto done;
+ }
+ }
+
+ /* At this point, hostlist is up to date */
+ if (dev->ohsensid && dev->ohsensnum) {
+ /*
+ * For accurate status, need to make a call that goes out to
+ * BladeCenter MM because the calls made so far by this
+ * function (and perhaps get_bladehpi_hostlist) only retrieve
+ * information from memory cached by OpenHPI
+ */
+ ohrc = saHpiSensorReadingGet(dev->ohsession
+ , dev->ohsensid, dev->ohsensnum, NULL, NULL);
+ if (ohrc == SA_ERR_HPI_BUSY || ohrc == SA_ERR_HPI_NO_RESPONSE) {
+ LOG(PIL_CRIT, "Unable to connect to BladeCenter in %s"
+ , __FUNCTION__);
+ rc = S_OOPS;
+ goto done;
+ }
+ }
+
+done:
+ close_hpi_session(dev);
+ return (rc == S_OK) ? (dev->ohdevid ? S_OK : S_OOPS) : rc;
+}
+
+
+/*
+ * Return the list of hosts configured for this HMC device
+ */
+
+static char **
+bladehpi_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice * dev;
+ int numnames = 0, j;
+ char ** ret = NULL;
+ GList * node = NULL;
+ SaErrorT ohrc;
+ SaHpiDomainInfoT ohdi;
+ int rc = S_OK;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s, NULL);
+
+ dev = (struct pluginDevice *)s;
+ rc = open_hpi_session(dev);
+ if( rc != S_OK )
+ return NULL;
+
+ /* Refresh the hostlist only if RPTs updated */
+ ohrc = saHpiDomainInfoGet(dev->ohsession, &ohdi);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to get domain info in %s (%d)"
+ , __FUNCTION__, ohrc);
+ goto done;
+ }
+ if (dev->ohrptcnt != ohdi.RptUpdateCount) {
+ free_bladehpi_hostlist(dev);
+ if (get_bladehpi_hostlist(dev) != S_OK) {
+ LOG(PIL_CRIT, "Unable to obtain list of hosts in %s"
+ , __FUNCTION__);
+ goto done;
+ }
+ }
+
+ /* At this point, hostlist is up to date */
+ numnames = g_list_length(dev->hostlist);
+ if (numnames < 0) {
+ LOG(PIL_CRIT, "Unconfigured stonith object in %s"
+ , __FUNCTION__);
+ goto done;
+ }
+
+ ret = (char **)MALLOC((numnames+1) * sizeof(char *));
+ if (ret == NULL) {
+ LOG(PIL_CRIT, "Out of memory for malloc in %s", __FUNCTION__);
+ goto done;
+ }
+
+ memset(ret, 0, (numnames+1) * sizeof(char *));
+ for (node = g_list_first(dev->hostlist), j = 0
+ ; NULL != node
+ ; j++, node = g_list_next(node)) {
+ ret[j] = STRDUP(((struct blade_info *)node->data)->name);
+ if (ret[j] == NULL) {
+ LOG(PIL_CRIT, "Out of memory for strdup in %s"
+ , __FUNCTION__);
+ stonith_free_hostlist(ret);
+ ret = NULL;
+ goto done;
+ }
+ strdown(ret[j]);
+ }
+
+done:
+ close_hpi_session(dev);
+ return ret;
+}
+
+
+static const char * const *
+bladehpi_get_confignames(StonithPlugin *s)
+{
+ static const char * names[] = {ST_ENTITYROOT, NULL};
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
+ }
+
+ return names;
+}
+
+
+/*
+ * Reset the given host, and obey the request type.
+ */
+
+static int
+bladehpi_reset_req(StonithPlugin *s, int request, const char *host)
+{
+ GList * node = NULL;
+ struct pluginDevice * dev = NULL;
+ struct blade_info * bi = NULL;
+ SaHpiPowerStateT ohcurstate, ohnewstate;
+ SaHpiDomainInfoT ohdi;
+ SaErrorT ohrc;
+ int rc = S_OK;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called, request=%d, host=%s"
+ , __FUNCTION__, request, host);
+ }
+
+ ERRIFWRONGDEV(s, S_OOPS);
+
+ if (host == NULL) {
+ LOG(PIL_CRIT, "Invalid host argument to %s", __FUNCTION__);
+ rc = S_OOPS;
+ goto done;
+ }
+
+ dev = (struct pluginDevice *)s;
+ rc = open_hpi_session(dev);
+ if( rc != S_OK )
+ return rc;
+
+ ohrc = saHpiDomainInfoGet(dev->ohsession, &ohdi);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to get domain info in %s (%d)"
+ , __FUNCTION__, ohrc);
+ rc = S_BADCONFIG;
+ goto done;
+ }
+ if (dev->ohrptcnt != ohdi.RptUpdateCount) {
+ free_bladehpi_hostlist(dev);
+ if (get_bladehpi_hostlist(dev) != S_OK) {
+ LOG(PIL_CRIT, "Unable to obtain list of hosts in %s"
+ , __FUNCTION__);
+ rc = S_OOPS;
+ goto done;
+ }
+ }
+
+ for (node = g_list_first(dev->hostlist)
+ ; node != NULL
+ ; node = g_list_next(node)) {
+ bi = ((struct blade_info *)node->data);
+ if (Debug) {
+ LOG(PIL_DEBUG, "Found host %s in hostlist", bi->name);
+ }
+
+ if (!strcasecmp(bi->name, host)) {
+ break;
+ }
+ }
+
+ if (!node || !bi) {
+ LOG(PIL_CRIT
+ , "Host %s is not configured in this STONITH module, "
+ "please check your configuration information", host);
+ rc = S_OOPS;
+ goto done;
+ }
+
+ /* Make sure host has proper capabilities for get */
+ if (!(bi->resourceCaps & SAHPI_CAPABILITY_POWER)) {
+ LOG(PIL_CRIT
+ , "Host %s does not have power capability", host);
+ rc = S_OOPS;
+ goto done;
+ }
+
+ ohrc = saHpiResourcePowerStateGet(dev->ohsession, bi->resourceId
+ , &ohcurstate);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to get host %s power state (%d)"
+ , host, ohrc);
+ rc = S_OOPS;
+ goto done;
+ }
+
+ switch (request) {
+ case ST_POWERON:
+ if (ohcurstate == SAHPI_POWER_ON) {
+ LOG(PIL_INFO, "Host %s already on", host);
+ goto done;
+ }
+ ohnewstate = SAHPI_POWER_ON;
+
+ break;
+
+ case ST_POWEROFF:
+ if (ohcurstate == SAHPI_POWER_OFF) {
+ LOG(PIL_INFO, "Host %s already off", host);
+ goto done;
+ }
+ ohnewstate = SAHPI_POWER_OFF;
+
+ break;
+
+ case ST_GENERIC_RESET:
+ if (ohcurstate == SAHPI_POWER_OFF) {
+ ohnewstate = SAHPI_POWER_ON;
+ } else {
+ ohnewstate = SAHPI_POWER_CYCLE;
+ }
+
+ break;
+
+ default:
+ LOG(PIL_CRIT, "Invalid request argument to %s"
+ , __FUNCTION__);
+ rc = S_INVAL;
+ goto done;
+ }
+
+ if (!dev->softreset && (ohnewstate == SAHPI_POWER_CYCLE)) {
+ int maxwait;
+
+ ohrc = saHpiResourcePowerStateSet(dev->ohsession
+ , bi->resourceId, SAHPI_POWER_OFF);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to set host %s power state to"
+ " OFF (%d)", host, ohrc);
+ rc = S_OOPS;
+ goto done;
+ }
+
+ /*
+ * Must wait for power off here or subsequent power on request
+ * may take place while power is still on and thus ignored
+ */
+ maxwait = MAX_POWEROFF_WAIT;
+ do {
+ maxwait--;
+ sleep(1);
+ ohrc = saHpiResourcePowerStateGet(dev->ohsession
+ , bi->resourceId, &ohcurstate);
+ } while ((ohrc == SA_OK)
+ && (ohcurstate != SAHPI_POWER_OFF)
+ && (maxwait > 0));
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Waited %d seconds for power off"
+ , MAX_POWEROFF_WAIT - maxwait);
+ }
+
+ ohrc = saHpiResourcePowerStateSet(dev->ohsession
+ , bi->resourceId, SAHPI_POWER_ON);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to set host %s power state to"
+ " ON (%d)", host, ohrc);
+ rc = S_OOPS;
+ goto done;
+ }
+ } else {
+ /* Make sure host has proper capabilities to reset */
+ if ((ohnewstate == SAHPI_POWER_CYCLE) &&
+ (!(bi->resourceCaps & SAHPI_CAPABILITY_RESET))) {
+ LOG(PIL_CRIT
+ , "Host %s does not have reset capability"
+ , host);
+ rc = S_OOPS;
+ goto done;
+ }
+
+ if ((ohrc = saHpiResourcePowerStateSet(dev->ohsession
+ , bi->resourceId, ohnewstate)) != SA_OK) {
+ LOG(PIL_CRIT, "Unable to set host %s power state (%d)"
+ , host, ohrc);
+ rc = S_OOPS;
+ goto done;
+ }
+ }
+
+#ifdef IBMBC_WAIT_FOR_OFF
+ if (ohnewstate == SAHPI_POWER_OFF) {
+ int maxwait = MAX_POWEROFF_WAIT;
+
+ do {
+ maxwait--;
+ sleep(1);
+ ohrc = saHpiResourcePowerStateGet(dev->ohsession
+ , bi->resourceId, &ohcurstate);
+ } while ((ohrc == SA_OK)
+ && (ohcurstate != SAHPI_POWER_OFF)
+ && (maxwait > 0));
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Waited %d seconds for power off"
+ , MAX_POWEROFF_WAIT - maxwait);
+ }
+ }
+#endif
+
+ LOG(PIL_INFO, "Host %s %s %d.", host, __FUNCTION__, request);
+
+done:
+ close_hpi_session(dev);
+ return rc;
+}
+
+
+/*
+ * Parse the information in the given configuration file,
+ * and stash it away...
+ */
+
+static int
+bladehpi_set_config(StonithPlugin *s, StonithNVpair *list)
+{
+ struct pluginDevice * dev = NULL;
+ StonithNamesToGet namestocopy [] =
+ { {ST_ENTITYROOT, NULL}
+ , {NULL, NULL}
+ };
+ int rc, i;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s, S_OOPS);
+
+ dev = (struct pluginDevice *)s;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s conditionally compiled with:"
+#ifdef IBMBC_WAIT_FOR_OFF
+ " IBMBC_WAIT_FOR_OFF"
+#endif
+ , dev->pluginid);
+ }
+
+ if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s = %s", ST_ENTITYROOT
+ , namestocopy[0].s_value);
+ }
+
+ if (get_num_tokens(namestocopy[0].s_value) == 1) {
+ /* name=value pairs on command line, look for soft_reset */
+ const char *softreset =
+ OurImports->GetValue(list, ST_SOFTRESET);
+ if (softreset != NULL) {
+ if (!strcasecmp(softreset, "true") ||
+ !strcmp(softreset, "1")) {
+ dev->softreset = 1;
+ } else if (!strcasecmp(softreset, "false") ||
+ !strcmp(softreset, "0")) {
+ dev->softreset = 0;
+ } else {
+ LOG(PIL_CRIT, "Invalid %s %s, must be "
+ "true, 1, false or 0"
+ , ST_SOFTRESET, softreset);
+ FREE(namestocopy[0].s_value);
+ return S_OOPS;
+ }
+ }
+ } else {
+ /* -p or -F option with args "entity_root [soft_reset]..." */
+ char *pch = namestocopy[0].s_value;
+
+ /* skip over entity_root and null-terminate */
+ pch += strcspn(pch, WHITESPACE);
+ *pch = EOS;
+
+ /* skip over white-space up to next token */
+ pch++;
+ pch += strspn(pch, WHITESPACE);
+ if (!strcasecmp(pch, "true") || !strcmp(pch, "1")) {
+ dev->softreset = 1;
+ } else if (!strcasecmp(pch, "false") || !strcmp(pch, "0")) {
+ dev->softreset = 0;
+ } else {
+ LOG(PIL_CRIT, "Invalid %s %s, must be "
+ "true, 1, false or 0"
+ , ST_SOFTRESET, pch);
+ FREE(namestocopy[0].s_value);
+ return S_OOPS;
+ }
+ }
+
+ dev->device = STRDUP(namestocopy[0].s_value);
+ FREE(namestocopy[0].s_value);
+ if (dev->device == NULL) {
+ LOG(PIL_CRIT, "Out of memory for strdup in %s", __FUNCTION__);
+ return S_OOPS;
+ }
+
+ if (strcspn(dev->device, WHITESPACE) != strlen(dev->device) ||
+ sscanf(dev->device, SYSTEM_CHASSIS_FMT, &i) != 1 || i < 0) {
+ LOG(PIL_CRIT, "Invalid %s %s, must be of format %s"
+ , ST_ENTITYROOT, dev->device, SYSTEM_CHASSIS_FMT);
+ return S_BADCONFIG;
+ }
+
+ dev->ohver = saHpiVersionGet();
+ if (dev->ohver > SAHPI_INTERFACE_VERSION) {
+ LOG(PIL_CRIT, "Installed OpenHPI interface (%x) greater than "
+ "one used by plugin (%x), incompatibilites may exist"
+ , dev->ohver, SAHPI_INTERFACE_VERSION);
+ return S_BADCONFIG;
+ }
+ return S_OK;
+}
+
+static int
+open_hpi_session(struct pluginDevice *dev)
+{
+ SaErrorT ohrc;
+
+ ohrc = saHpiSessionOpen(SAHPI_UNSPECIFIED_DOMAIN_ID
+ , &dev->ohsession, NULL);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to open HPI session (%d)", ohrc);
+ return S_BADCONFIG;
+ }
+
+ ohrc = saHpiDiscover(dev->ohsession);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to discover resources (%d)", ohrc);
+ return S_BADCONFIG;
+ }
+
+ return S_OK;
+}
+static void
+close_hpi_session(struct pluginDevice *dev)
+{
+ if (dev && dev->ohsession) {
+ saHpiSessionClose(dev->ohsession);
+ dev->ohsession = 0;
+ }
+}
+
+static const char *
+bladehpi_getinfo(StonithPlugin *s, int reqtype)
+{
+ struct pluginDevice * dev;
+ const char * ret;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called, reqtype=%d"
+ , __FUNCTION__, reqtype);
+ }
+
+ ERRIFWRONGDEV(s, NULL);
+
+ dev = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = dev->idinfo;
+ break;
+
+ case ST_DEVICENAME:
+ ret = dev->device;
+ break;
+
+ case ST_DEVICEDESCR:
+ ret = "IBM BladeCenter via OpenHPI\n"
+ "Use for IBM xSeries systems managed by BladeCenter\n"
+ " Required parameter name " ST_ENTITYROOT " is "
+ "a string (no white-space) of\n"
+ "the format \""SYSTEM_CHASSIS_FMT"\" "
+ "which is entity_root of BladeCenter\n"
+ "from OpenHPI config file, where %d is a positive "
+ "integer\n"
+ " Optional parameter name " ST_SOFTRESET " is "
+ "true|1 if STONITH device should\n"
+ "use soft reset (power cycle) to reset nodes or "
+ "false|0 if device should\n"
+ "use hard reset (power off, wait, power on); "
+ "default is false";
+ break;
+
+ case ST_DEVICEURL:
+ ret = OPENHPIURL;
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = bladehpiXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+
+ return ret;
+}
+
+
+/*
+ * HMC Stonith destructor...
+ */
+
+static void
+bladehpi_destroy(StonithPlugin *s)
+{
+ struct pluginDevice * dev;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
+ }
+
+ VOIDERRIFWRONGDEV(s);
+
+ dev = (struct pluginDevice *)s;
+
+ dev->pluginid = NOTpluginID;
+ if (dev->device) {
+ FREE(dev->device);
+ dev->device = NULL;
+ }
+ if (dev->idinfo) {
+ FREE(dev->idinfo);
+ dev->idinfo = NULL;
+ }
+ free_bladehpi_hostlist(dev);
+
+ if (dev->ohsession) {
+ saHpiSessionClose(dev->ohsession);
+ dev->ohsession = 0;
+ }
+
+ FREE(dev);
+}
+
+
+static StonithPlugin *
+bladehpi_new(const char *subplugin)
+{
+ struct pluginDevice * dev = ST_MALLOCT(struct pluginDevice);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
+ }
+
+ if (dev == NULL) {
+ LOG(PIL_CRIT, "Out of memory in %s", __FUNCTION__);
+ return NULL;
+ }
+
+ memset(dev, 0, sizeof(*dev));
+
+ dev->pluginid = pluginid;
+ dev->device = NULL;
+ dev->hostlist = NULL;
+ REPLSTR(dev->idinfo, DEVICE);
+ if (dev->idinfo == NULL) {
+ FREE(dev);
+ return NULL;
+ }
+ dev->sp.s_ops = &bladehpiOps;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: returning successfully", __FUNCTION__);
+ }
+
+ return ((void *)dev);
+}
+
+
+static int
+get_resource_type(char *entityRoot, SaHpiRptEntryT *ohRPT)
+{
+ int i, rc = OHRES_NONE;
+ int foundBlade = 0, foundExp = 0, foundMgmt = 0;
+ int foundRoot = 0, foundOther = 0;
+ char rootName[64];
+ SaHpiEntityPathT * ohep = &ohRPT->ResourceEntity;
+
+ if (ohep == NULL || entityRoot == NULL) {
+ return 0;
+ }
+
+ /* First find root of entity path, which is last entity in entry */
+ for (i = 0; i < SAHPI_MAX_ENTITY_PATH; i++) {
+ if (ohep->Entry[i].EntityType == SAHPI_ENT_ROOT) {
+ break;
+ }
+ }
+
+ /* Then back up through entries looking for specific entity */
+ for (i--; i >= 0; i--) {
+ switch (ohep->Entry[i].EntityType) {
+ case SAHPI_ENT_SBC_BLADE:
+ foundBlade = 1;
+ break;
+
+ case SAHPI_ENT_SYS_EXPANSION_BOARD:
+ foundExp = 1;
+ break;
+
+ case SAHPI_ENT_SYS_MGMNT_MODULE:
+ if (ohep->Entry[i].EntityLocation == 0) {
+ foundMgmt = 1;
+ }
+ break;
+
+ case SAHPI_ENT_SYSTEM_CHASSIS:
+ snprintf(rootName, sizeof(rootName)
+ , SYSTEM_CHASSIS_FMT
+ , ohep->Entry[i].EntityLocation);
+ if (!strcmp(entityRoot, rootName)) {
+ foundRoot = 1;
+ }
+ break;
+
+ default:
+ foundOther = 1;
+ break;
+ }
+ }
+
+ /* We are only interested in specific entities on specific device */
+ if (foundRoot) {
+ if (foundMgmt && !(foundBlade||foundExp||foundOther)) {
+ rc = OHRES_MGMTMOD;
+ } else if (!(foundMgmt||foundBlade||foundExp||foundOther)) {
+ rc = OHRES_BLADECENT;
+ } else if (foundBlade && !foundExp) {
+ rc = OHRES_BLADE;
+ }
+ }
+
+ return rc;
+}
+
+
+static int
+get_sensor_num(SaHpiSessionIdT ohsession, SaHpiResourceIdT ohresid)
+{
+ SaErrorT ohrc = SA_OK;
+ SaHpiEntryIdT ohnextid;
+ SaHpiRdrT ohRDR;
+
+ ohnextid = SAHPI_FIRST_ENTRY;
+ do {
+ ohrc = saHpiRdrGet(ohsession, ohresid, ohnextid
+ , &ohnextid, &ohRDR);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to get RDR entry in %s (%d)"
+ , __FUNCTION__, ohrc);
+ } else if (ohRDR.RdrType == SAHPI_SENSOR_RDR) {
+ return ohRDR.RdrTypeUnion.SensorRec.Num;
+ }
+ } while (ohrc == SA_OK && ohnextid != SAHPI_LAST_ENTRY);
+
+ return 0;
+}
+
+
+/*
+ * Get RPT update count
+ * Loop through all RPT entries
+ * If entry is BladeCenter, save resource ID in dev->ohdevid
+ * If entry is MgmtMod and has sensor, save resource ID in dev->ohsensid
+ * and sensor number in dev->ohsensnum
+ * If entry is blade, save blade_info and add to dev->hostlist
+ * Get RPT update count
+ * If RPT update count changed since start of loop, repeat loop
+ * Save RPT update count in dev->ohrptcnt
+ *
+ * Note that not only does this function update hostlist, it also
+ * updates ohrptcnt, ohdevid, ohsensid and ohsensnum. However, with
+ * this logic it does not need to be called again until the RPT update
+ * count changes.
+ */
+
+static int
+get_bladehpi_hostlist(struct pluginDevice *dev)
+{
+ struct blade_info * bi;
+ SaErrorT ohrc;
+ SaHpiEntryIdT ohnextid;
+ SaHpiRptEntryT ohRPT;
+ SaHpiDomainInfoT ohdi;
+ SaHpiUint32T ohupdate;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called, dev->device=%s"
+ , __FUNCTION__, dev->device);
+ }
+
+ if (dev->device == NULL || *dev->device == 0) {
+ LOG(PIL_CRIT, "Unconfigured stonith object in %s"
+ , __FUNCTION__);
+ return S_BADCONFIG;
+ }
+
+ ohrc = saHpiDomainInfoGet(dev->ohsession, &ohdi);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to get domain info in %s (%d)"
+ , __FUNCTION__, ohrc);
+ return S_BADCONFIG;
+ }
+
+try_again:
+ ohupdate = ohdi.RptUpdateCount;
+ dev->ohdevid = dev->ohsensid = dev->ohsensnum = 0;
+ ohnextid = SAHPI_FIRST_ENTRY;
+ do {
+ char blname[SAHPI_MAX_TEXT_BUFFER_LENGTH];
+ int blnum;
+
+ ohrc = saHpiRptEntryGet(dev->ohsession, ohnextid
+ , &ohnextid, &ohRPT);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to get RPT entry in %s (%d)"
+ , __FUNCTION__, ohrc);
+ free_bladehpi_hostlist(dev);
+ return S_BADCONFIG;
+ }
+
+ switch (get_resource_type(dev->device, &ohRPT)) {
+ case OHRES_BLADECENT:
+ dev->ohdevid = ohRPT.ResourceId;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "BladeCenter '%s' has id %d"
+ , (char*)ohRPT.ResourceTag.Data
+ , dev->ohdevid);
+ }
+ break;
+
+ case OHRES_MGMTMOD:
+ if (ohRPT.ResourceCapabilities&SAHPI_CAPABILITY_SENSOR){
+ dev->ohsensnum = get_sensor_num(dev->ohsession
+ , ohRPT.ResourceId);
+
+ if (dev->ohsensnum) {
+ dev->ohsensid = ohRPT.ResourceId;
+
+ if (Debug) {
+ LOG(PIL_DEBUG
+ , "MgmtModule '%s' has id %d "
+ "with sensor #%d"
+ , (char*)ohRPT.ResourceTag.Data
+ , dev->ohsensid
+ , dev->ohsensnum);
+ }
+ }
+ }
+ break;
+
+ case OHRES_BLADE:
+ if ((bi = (struct blade_info *)
+ MALLOC(sizeof(struct blade_info))) == NULL) {
+ LOG(PIL_CRIT, "Out of memory in %s"
+ , __FUNCTION__);
+ free_bladehpi_hostlist(dev);
+ return S_OOPS;
+ }
+
+ /*
+ * New format consists of "Blade N - name" while older
+ * format consists only of "name"; we only need to
+ * stash name because ResourceID is the important info
+ */
+ if (sscanf((char*)ohRPT.ResourceTag.Data, "Blade %d - %s"
+ , &blnum, blname) == 2) {
+ bi->name = STRDUP(blname);
+ } else {
+ bi->name = STRDUP((char*)ohRPT.ResourceTag.Data);
+ }
+ if (bi->name == NULL) {
+ LOG(PIL_CRIT, "Out of memory for strdup in %s"
+ , __FUNCTION__);
+ free_bladehpi_hostlist(dev);
+ return S_OOPS;
+ }
+
+ bi->resourceId = ohRPT.ResourceId;
+ bi->resourceCaps = ohRPT.ResourceCapabilities;
+ dev->hostlist = g_list_append(dev->hostlist, bi);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Blade '%s' has id %d, caps %x"
+ , bi->name, bi->resourceId, bi->resourceCaps);
+ }
+ break;
+ }
+ } while (ohrc == SA_OK && ohnextid != SAHPI_LAST_ENTRY);
+
+ ohrc = saHpiDomainInfoGet(dev->ohsession, &ohdi);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to get domain info in %s (%d)"
+ , __FUNCTION__, ohrc);
+ free_bladehpi_hostlist(dev);
+ return S_BADCONFIG;
+ }
+
+ if (ohupdate != ohdi.RptUpdateCount) {
+ free_bladehpi_hostlist(dev);
+ if(Debug){
+ LOG(PIL_DEBUG, "Looping through entries again,"
+ " count changed from %d to %d"
+ , ohupdate, ohdi.RptUpdateCount);
+ }
+ goto try_again;
+ }
+
+ dev->ohrptcnt = ohupdate;
+
+ return S_OK;
+}
+
+
+static void
+free_bladehpi_hostlist(struct pluginDevice *dev)
+{
+ if (dev->hostlist) {
+ GList *node;
+ while (NULL != (node = g_list_first(dev->hostlist))) {
+ dev->hostlist =
+ g_list_remove_link(dev->hostlist, node);
+ FREE(((struct blade_info *)node->data)->name);
+ FREE(node->data);
+ g_list_free(node);
+ }
+ dev->hostlist = NULL;
+ }
+ dev->ohdevid = dev->ohsensid = dev->ohsensnum = 0;
+}
+
+
+static int
+get_num_tokens(char *str)
+{
+ int namecount = 0;
+
+ while (*str != EOS) {
+ str += strspn(str, WHITESPACE);
+ if (*str == EOS)
+ break;
+ str += strcspn(str, WHITESPACE);
+ namecount++;
+ }
+ return namecount;
+}
diff --git a/lib/plugins/stonith/cyclades.c b/lib/plugins/stonith/cyclades.c
new file mode 100644
index 0000000..6744cd4
--- /dev/null
+++ b/lib/plugins/stonith/cyclades.c
@@ -0,0 +1,650 @@
+/*
+ * Stonith module for Cyclades AlterPath PM
+ * Bases off the SSH plugin
+ *
+ * Copyright (c) 2004 Cyclades corp.
+ *
+ * Author: Jon Taylor <jon.taylor@cyclades.com>
+ *
+ * Rewritten from scratch using baytech.c structure and code
+ * and currently maintained by
+ * Marcelo Tosatti <marcelo.tosatti@cyclades.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <lha_internal.h>
+
+#define DEVICE "Cyclades AlterPath PM"
+
+#define DOESNT_USE_STONITHSCANLINE
+
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN cyclades
+#define PIL_PLUGIN_S "cyclades"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+#include "stonith_signal.h"
+
+static StonithPlugin * cyclades_new(const char *);
+static void cyclades_destroy(StonithPlugin *);
+static int cyclades_set_config(StonithPlugin *, StonithNVpair *);
+static const char * const * cyclades_get_confignames(StonithPlugin * s);
+static const char * cyclades_get_info(StonithPlugin * s, int InfoType);
+static int cyclades_status(StonithPlugin *);
+static int cyclades_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** cyclades_hostlist(StonithPlugin *);
+
+
+
+static struct stonith_ops cycladesOps ={
+ cyclades_new, /* Create new STONITH object */
+ cyclades_destroy, /* Destroy STONITH object */
+ cyclades_get_info, /* Return STONITH info string */
+ cyclades_get_confignames, /* Return STONITH config vars */
+ cyclades_set_config, /* set configuration from vars */
+ cyclades_status, /* Return STONITH device status */
+ cyclades_reset_req, /* Request a reset */
+ cyclades_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+#include "stonith_expect_helpers.h"
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &cycladesOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * Cyclades STONITH device
+ *
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ char * device;
+ char * user;
+
+ int serial_port;
+
+ /* pid of ssh client process and its in/out file descriptors */
+ pid_t pid;
+ int rdfd, wrfd;
+};
+
+static struct Etoken StatusOutput[] = {
+ { "Outlet\t\tName\t\tStatus\t\tUsers\t\tInterval (s)", 1, 0},
+ { "Outlet\tName\t\t\tStatus\t\tInterval (s)\tUsers", 2, 0},
+ { "Outlet Name Status Post-on Delay(s)", 3, 0},
+ { NULL, 0, 0}
+};
+
+static struct Etoken CRNL[] = { {"\n\r",0,0},{"\r\n",0,0},{NULL,0,0}};
+
+
+/* Commands of PM devices */
+static char status_all[] = "status all";
+static char cycle[] = "cycle";
+
+static int CYC_robust_cmd(struct pluginDevice *, char *);
+
+static const char * pluginid = "CycladesDevice-Stonith";
+static const char * NOTpluginID = "Cyclades device has been destroyed";
+
+#define MAX_OUTLETS 128
+
+#define ST_SERIALPORT "serialport"
+
+#define ZEROEXPECT(fd,p,t) { \
+ if (StonithLookFor(fd, p, t) < 0) \
+ return(0); \
+ }
+
+#define RESETEXPECT(fd,p,t) { \
+ if (StonithLookFor(fd, p, t) < 0) { \
+ FREE(outletstr); \
+ return(errno == ETIMEDOUT \
+ ? S_RESETFAIL : S_OOPS); \
+ } \
+ }
+
+#include "stonith_config_xml.h"
+
+#define XML_SERIALPORT_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_SERIALPORT \
+ XML_PARM_SHORTDESC_END
+
+#define XML_SERIALPORT_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The serial port of the IPDU which can powercycle the node" \
+ XML_PARM_LONGDESC_END
+
+#define XML_SERIALPORT_PARM \
+ XML_PARAMETER_BEGIN(ST_SERIALPORT, "string", "1", "0") \
+ XML_SERIALPORT_SHORTDESC \
+ XML_SERIALPORT_LONGDESC \
+ XML_PARAMETER_END
+
+static const char *cycladesXML =
+ XML_PARAMETERS_BEGIN
+ XML_IPADDR_PARM
+ XML_LOGIN_PARM
+ XML_SERIALPORT_PARM
+ XML_PARAMETERS_END;
+
+static int
+CYCScanLine(struct pluginDevice *sd, int timeout, char * buf, int max)
+{
+ if (EXPECT_TOK(sd->rdfd, CRNL, timeout, buf, max, Debug) < 0) {
+ Stonithkillcomm(&sd->rdfd, &sd->wrfd, &sd->pid);
+ return(S_OOPS);
+ }
+ return(S_OK);
+}
+
+static int
+cyclades_status(StonithPlugin *s)
+{
+ struct pluginDevice *sd;
+ char *cmd = status_all;
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ sd = (struct pluginDevice*) s;
+
+ if (CYC_robust_cmd(sd, cmd) != S_OK) {
+ LOG(PIL_CRIT, "can't run status all command");
+ return(S_OOPS);
+ }
+
+ EXPECT(sd->rdfd, StatusOutput, 50);
+
+ return(S_OK);
+}
+
+static int CYC_run_command(struct pluginDevice *sd, char *cmd)
+{
+ char SshCommand[MAX_OUTLETS*4];
+
+ snprintf(SshCommand, sizeof(SshCommand),
+ "exec ssh -q %s@%s /bin/pmCommand %d %s 2>/dev/null",
+ sd->user, sd->device, sd->serial_port, cmd);
+
+ sd->pid = STARTPROC(SshCommand, &sd->rdfd, &sd->wrfd);
+
+ if (sd->pid <= 0) {
+ return(S_OOPS);
+ }
+
+ return(S_OK);
+}
+
+static int
+CYC_robust_cmd(struct pluginDevice *sd, char *cmd)
+{
+ int rc = S_OOPS;
+ int i;
+
+ for (i=0; i < 20 && rc != S_OK; i++) {
+
+ if (sd->pid > 0) {
+ Stonithkillcomm(&sd->rdfd, &sd->wrfd, &sd->pid);
+ }
+
+ if (CYC_run_command(sd, cmd) != S_OK) {
+ Stonithkillcomm(&sd->rdfd, &sd->wrfd, &sd->pid);
+ continue;
+ }
+ rc = S_OK;
+ }
+
+ return rc;
+}
+
+#define MAXSAVE 512
+static int CYCNametoOutlet(struct pluginDevice *sd, const char *host, int *outlets, int maxoutlet)
+{
+ char *cmd = status_all;
+ char savebuf[MAXSAVE];
+ int err;
+ int outlet, numoutlet = 0;
+ char name[17], locked[11], on[4];
+
+ if (CYC_robust_cmd(sd, cmd) != S_OK) {
+ LOG(PIL_CRIT, "can't run status all command");
+ return 0;
+ }
+
+ ZEROEXPECT(sd->rdfd, StatusOutput, 50);
+
+ ZEROEXPECT(sd->rdfd, CRNL, 50);
+
+ do {
+
+ memset(savebuf, 0, sizeof(savebuf));
+ memset(name, 0, sizeof(name));
+ memset(locked, 0, sizeof(locked));
+ memset(on, 0, sizeof(on));
+
+ err = CYCScanLine(sd, 2, savebuf, sizeof(savebuf));
+
+ if ((err == S_OK) &&
+ (sscanf(savebuf,"%3d %16s %10s %3s", &outlet,
+ name, locked, on) > 0)) {
+ if (!strncasecmp(name, host, strlen(host))) {
+ if (numoutlet >= maxoutlet) {
+ LOG(PIL_CRIT, "too many outlets");
+ return 0;
+ }
+ outlets[numoutlet++] = outlet;
+ }
+ }
+
+ } while (err == S_OK);
+
+ return (numoutlet);
+}
+
+
+/*
+ * Return the list of hosts configured for this Cyclades device
+ */
+
+static char **
+cyclades_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice* sd;
+ char *cmd = status_all;
+ char savebuf[MAXSAVE];
+ int err, i;
+ int outlet;
+ int numnames = 0;
+ char name[17], locked[11], on[4];
+ char *NameList[MAX_OUTLETS];
+ char **ret = NULL;
+
+ ERRIFNOTCONFIGED(s,NULL);
+
+ sd = (struct pluginDevice*) s;
+
+ if (CYC_robust_cmd(sd, cmd) != S_OK) {
+ LOG(PIL_CRIT, "can't run status all command");
+ return (NULL);
+ }
+
+ memset(savebuf, 0, sizeof(savebuf));
+
+ NULLEXPECT(sd->rdfd, StatusOutput, 50);
+
+ NULLEXPECT(sd->rdfd, CRNL, 50);
+
+ do {
+ char *nm;
+
+ memset(savebuf, 0, sizeof(savebuf));
+ memset(name, 0, sizeof(name));
+ memset(locked, 0, sizeof(locked));
+ memset(on, 0, sizeof(on));
+
+ err = CYCScanLine(sd, 2, savebuf, sizeof(savebuf));
+
+ if ((err == S_OK) &&
+ (sscanf(savebuf,"%3d %16s %10s %3s", &outlet,
+ name, locked, on) > 0)) {
+ nm = (char *) STRDUP (name);
+ if (!nm) {
+ goto out_of_memory;
+ }
+ strdown(nm);
+ NameList[numnames] = nm;
+ numnames++;
+ NameList[numnames] = NULL;
+ }
+
+ } while (err == S_OK);
+
+ if (numnames) {
+
+ ret = (char **)MALLOC((numnames+1)*sizeof(char*));
+ if (ret == NULL) {
+ goto out_of_memory;
+ } else {
+ memcpy(ret, NameList, (numnames+1)*sizeof(char*));
+ }
+ return (ret);
+ }
+ return(ret);
+
+out_of_memory:
+ LOG(PIL_CRIT, "out of memory");
+ for (i=0; i<numnames; i++) {
+ FREE(NameList[i]);
+ }
+
+ return (NULL);
+}
+
+
+static char *cyclades_outletstr(int *outlet, int numoutlet)
+{
+ int i, len;
+ char *ret;
+
+ /* maximum length per outlet is currently four (outlet is one to
+ * three digits, followed by either a comma or null), so add one
+ * for good measure */
+ len = numoutlet * 5 * sizeof(char);
+ if ((ret = MALLOC(len)) != NULL) {
+ snprintf(ret, len, "%d", outlet[0]);
+ for (i = 1; i < numoutlet; i++) {
+ char buf[5];
+ snprintf(buf, sizeof(buf), ",%d", outlet[i]);
+ strcat(ret, buf);
+ }
+ }
+ return(ret);
+}
+
+
+static int cyclades_onoff(struct pluginDevice *sd, int *outlet, int numoutlet,
+ const char *unitid, int req)
+{
+ const char * onoff;
+ char cmd[MAX_OUTLETS*4], expstring[64];
+ struct Etoken exp[] = {{NULL, 0, 0}, {NULL, 0, 0}};
+ char *outletstr;
+ int i;
+
+ onoff = (req == ST_POWERON ? "on" : "off");
+
+ memset(cmd, 0, sizeof(cmd));
+
+ outletstr = cyclades_outletstr(outlet, numoutlet);
+ if (outletstr == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return (S_OOPS);
+ }
+ snprintf(cmd, sizeof(cmd), "%s %s", onoff, outletstr);
+
+ if (CYC_robust_cmd(sd, cmd) != S_OK) {
+ LOG(PIL_CRIT, "can't run %s command", onoff);
+ FREE(outletstr);
+ return(S_OOPS);
+ }
+
+ for (i = 0; i < numoutlet; i++) {
+ memset(expstring, 0, sizeof(expstring));
+ snprintf(expstring, sizeof(expstring), "%d: Outlet turned %s."
+ , outlet[i], onoff);
+
+ exp[0].string = expstring;
+
+ /* FIXME: should handle "already powered on/off" case and inform
+ to log */
+
+ EXPECT(sd->rdfd, exp, 50);
+ }
+
+ LOG(PIL_DEBUG, "Power to host %s turned %s", unitid, onoff);
+
+ FREE(outletstr);
+ return (S_OK);
+}
+
+static int cyclades_reset(struct pluginDevice *sd, int *outlet, int numoutlet,
+ const char *unitid)
+{
+ char cmd[MAX_OUTLETS*4], expstring[64];
+ struct Etoken exp[] = {{NULL, 0, 0}, {NULL, 0, 0}};
+ char *outletstr;
+ int i;
+
+ memset(cmd, 0, sizeof(cmd));
+
+ outletstr = cyclades_outletstr(outlet, numoutlet);
+ if (outletstr == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return (S_OOPS);
+ }
+ snprintf(cmd, sizeof(cmd), "%s %s", cycle, outletstr);
+
+ LOG(PIL_INFO, "Host %s being rebooted.", unitid);
+
+ if (CYC_robust_cmd(sd, cmd) != S_OK) {
+ LOG(PIL_CRIT, "can't run cycle command");
+ FREE(outletstr);
+ return(S_OOPS);
+ }
+
+ for (i = 0; i < numoutlet; i++) {
+ memset(expstring, 0, sizeof(expstring));
+ snprintf(expstring, sizeof(expstring)
+ , "%d: Outlet turned off.", outlet[i]);
+
+ exp[0].string = expstring;
+ RESETEXPECT(sd->rdfd, exp, 50);
+ }
+
+ for (i = 0; i < numoutlet; i++) {
+ memset(expstring, 0, sizeof(expstring));
+ snprintf(expstring, sizeof(expstring)
+ , "%d: Outlet turned on.", outlet[i]);
+
+ exp[0].string = expstring;
+ RESETEXPECT(sd->rdfd, exp, 50);
+ }
+
+ FREE(outletstr);
+ return (S_OK);
+}
+
+/*
+ * Reset the given host on this Stonith device.
+ */
+static int
+cyclades_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ struct pluginDevice *sd;
+ int rc = 0;
+ int numoutlet, outlets[MAX_OUTLETS];
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ sd = (struct pluginDevice*) s;
+
+ numoutlet = CYCNametoOutlet(sd, host, outlets, MAX_OUTLETS);
+
+ if (!numoutlet) {
+ LOG(PIL_CRIT, "Unknown host %s to Cyclades PM", host);
+ return (S_OOPS);
+ }
+
+
+ switch (request) {
+ case ST_POWERON:
+ case ST_POWEROFF:
+ rc = cyclades_onoff(sd, outlets, numoutlet, host, request);
+ break;
+
+ case ST_GENERIC_RESET:
+ rc = cyclades_reset(sd, outlets, numoutlet, host);
+ break;
+ default:
+ rc = S_INVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static const char * const *
+cyclades_get_confignames(StonithPlugin * s)
+{
+ static const char * ret[] = {ST_IPADDR, ST_LOGIN, ST_SERIALPORT, NULL};
+ return ret;
+}
+
+/*
+ * Parse the config information in the given string, and stash it away...
+ */
+static int
+cyclades_set_config(StonithPlugin* s, StonithNVpair* list)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ int rc;
+ StonithNamesToGet namestocopy[] =
+ { {ST_IPADDR, NULL}
+ , {ST_LOGIN, NULL}
+ , {ST_SERIALPORT, NULL}
+ , {NULL, NULL}
+ };
+
+ ERRIFWRONGDEV(s, S_OOPS);
+ if (sd->sp.isconfigured) {
+ return S_OOPS;
+ }
+
+ if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ sd->device = namestocopy[0].s_value;
+ sd->user = namestocopy[1].s_value;
+ sd->serial_port = atoi(namestocopy[2].s_value);
+ FREE(namestocopy[2].s_value);
+
+ return(S_OK);
+}
+
+static const char *
+cyclades_get_info(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice * sd;
+ const char * ret;
+
+ ERRIFWRONGDEV(s, NULL);
+
+ sd = (struct pluginDevice*) s;
+
+ switch (reqtype) {
+ case ST_DEVICEID: /* What type of device? */
+ /* FIXME: could inform the exact PM model */
+ ret = sd->idinfo;
+ break;
+
+ case ST_DEVICENAME: /* What particular device? */
+ ret = sd->device;
+ break;
+
+ case ST_DEVICEDESCR: /* Description of dev type */
+ ret = "Cyclades AlterPath PM "
+ "series power switches (via TS/ACS/KVM).";
+ break;
+
+ case ST_DEVICEURL: /* Manufacturer's web site */
+ ret = "http://www.cyclades.com/";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = cycladesXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * Cyclades Stonith destructor...
+ */
+static void
+cyclades_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* sd;
+
+ VOIDERRIFWRONGDEV(s);
+
+ sd = (struct pluginDevice*) s;
+
+ sd->pluginid = NOTpluginID;
+ Stonithkillcomm(&sd->rdfd, &sd->wrfd, &sd->pid);
+ if (sd->device != NULL) {
+ FREE(sd->device);
+ sd->device = NULL;
+ }
+ if (sd->user != NULL) {
+ FREE(sd->user);
+ sd->user = NULL;
+ }
+
+ FREE(sd);
+}
+
+/* Create a new cyclades Stonith device */
+static StonithPlugin *
+cyclades_new(const char *plugin)
+{
+ struct pluginDevice* sd = ST_MALLOCT(struct pluginDevice);
+
+ if (sd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+
+ memset(sd, 0, sizeof(*sd));
+ sd->pluginid = pluginid;
+ sd->pid = -1;
+ sd->rdfd = -1;
+ sd->wrfd = -1;
+ sd->idinfo = DEVICE;
+ sd->sp.s_ops = &cycladesOps;
+
+ return &(sd->sp); /* same as sd */
+}
diff --git a/lib/plugins/stonith/drac3.c b/lib/plugins/stonith/drac3.c
new file mode 100644
index 0000000..95be775
--- /dev/null
+++ b/lib/plugins/stonith/drac3.c
@@ -0,0 +1,359 @@
+/*
+ * Stonith module for Dell DRACIII (Dell Remote Access Card)
+ *
+ * Copyright (C) 2003 Alfa21 Outsourcing
+ * Copyright (C) 2003 Roberto Moreda <moreda@alfa21.com>
+ * Tiny bits Copyright 2005 International Business Machines
+ * Significantly Mangled by Sun Jiang Dong <sunjd@cn.ibm.com>, IBM, 2005
+ *
+ * (Using snippets of other stonith modules code)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define DEVICE "Dell DRACIII Card"
+#include "stonith_plugin_common.h"
+
+#include <curl/curl.h>
+#include "drac3_command.h"
+
+#define PIL_PLUGIN drac3
+#define PIL_PLUGIN_S "drac3"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+#include "stonith_signal.h"
+
+static StonithPlugin * drac3_new(const char *);
+static void drac3_destroy(StonithPlugin *);
+static const char * const * drac3_get_confignames(StonithPlugin *);
+static int drac3_set_config(StonithPlugin *, StonithNVpair *);
+static const char * drac3_getinfo(StonithPlugin * s, int InfoType);
+static int drac3_status(StonithPlugin * );
+static int drac3_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** drac3_hostlist(StonithPlugin *);
+
+static struct stonith_ops drac3Ops ={
+ drac3_new, /* Create new STONITH object */
+ drac3_destroy, /* Destroy STONITH object */
+ drac3_getinfo, /* Return STONITH info string */
+ drac3_get_confignames, /* Return configuration parameters */
+ drac3_set_config, /* Set configuration */
+ drac3_status, /* Return STONITH device status */
+ drac3_reset_req, /* Request a reset */
+ drac3_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &drac3Ops
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+#define BUFLEN 1024
+#define ST_HOST "host"
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char *pluginid;
+ const char *idinfo;
+ CURL *curl;
+ char *host;
+ char *user;
+ char *pass;
+};
+
+static const char *pluginid = "Dell-DRACIII-Stonith";
+static const char *NOTpluginID = "Dell DRACIII device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+#define XML_HOST_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_HOST \
+ XML_PARM_SHORTDESC_END
+
+#define XML_HOST_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The hostname of the STONITH device" \
+ XML_PARM_LONGDESC_END
+
+#define XML_HOST_PARM \
+ XML_PARAMETER_BEGIN(ST_HOST, "string", "1", "1") \
+ XML_HOST_SHORTDESC \
+ XML_HOST_LONGDESC \
+ XML_PARAMETER_END
+
+static const char *drac3XML =
+ XML_PARAMETERS_BEGIN
+ XML_HOST_PARM
+ XML_LOGIN_PARM
+ XML_PASSWD_PARM
+ XML_PARAMETERS_END;
+
+/* ------------------------------------------------------------------ */
+/* STONITH PLUGIN API */
+/* ------------------------------------------------------------------ */
+static StonithPlugin *
+drac3_new(const char *subplugin)
+{
+ struct pluginDevice *drac3d = ST_MALLOCT(struct pluginDevice);
+
+ if (drac3d == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(drac3d, 0, sizeof(*drac3d));
+ drac3d->pluginid = pluginid;
+ drac3d->curl = curl_easy_init();
+ drac3InitCurl(drac3d->curl);
+ drac3d->host = NULL;
+ drac3d->user = NULL;
+ drac3d->pass = NULL;
+ drac3d->idinfo = DEVICE;
+ drac3d->sp.s_ops = &drac3Ops;
+ return (&(drac3d->sp));
+}
+
+/* ------------------------------------------------------------------ */
+static void
+drac3_destroy(StonithPlugin * s)
+{
+ struct pluginDevice *drac3d;
+
+ VOIDERRIFWRONGDEV(s);
+
+ drac3d = (struct pluginDevice *) s;
+
+ drac3d->pluginid = NOTpluginID;
+
+ /* release curl connection */
+ if (drac3d->curl != NULL) {
+ drac3Logout(drac3d->curl, drac3d->host);
+ curl_easy_cleanup(drac3d->curl);
+ drac3d->curl = NULL;
+ }
+
+ if (drac3d->host != NULL) {
+ FREE(drac3d->host);
+ drac3d->host = NULL;
+ }
+ if (drac3d->user != NULL) {
+ FREE(drac3d->user);
+ drac3d->user = NULL;
+ }
+ if (drac3d->pass != NULL) {
+ FREE(drac3d->pass);
+ drac3d->pass = NULL;
+ }
+
+ /* release stonith-object itself */
+ FREE(drac3d);
+}
+
+/* ------------------------------------------------------------------ */
+static const char * const *
+drac3_get_confignames(StonithPlugin * s)
+{
+ static const char * ret[] = {ST_HOST, ST_LOGIN, ST_PASSWD, NULL};
+ return ret;
+}
+
+/* ------------------------------------------------------------------ */
+static int
+drac3_set_config(StonithPlugin * s, StonithNVpair * list)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ int rc;
+ StonithNamesToGet namestocopy [] =
+ { {ST_HOST, NULL}
+ , {ST_LOGIN, NULL}
+ , {ST_PASSWD, NULL}
+ , {NULL, NULL}
+ };
+
+ ERRIFWRONGDEV(s, S_OOPS);
+ if (sd->sp.isconfigured) {
+ return S_OOPS;
+ }
+
+ if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ sd->host = namestocopy[0].s_value;
+ sd->user = namestocopy[1].s_value;
+ sd->pass = namestocopy[2].s_value;
+
+ return(S_OK);
+}
+
+/* ------------------------------------------------------------------ */
+const char *
+drac3_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice *drac3d;
+ const char *ret = NULL;
+
+ ERRIFWRONGDEV(s,NULL);
+
+ drac3d = (struct pluginDevice *) s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = drac3d->idinfo;
+ break;
+ case ST_DEVICENAME:
+ ret = drac3d->host;
+ break;
+ case ST_DEVICEDESCR:
+ ret = "Dell DRACIII (via HTTPS)\n"
+ "The Dell Remote Access Controller accepts XML "
+ "commands over HTTPS";
+ break;
+ case ST_DEVICEURL:
+ ret = "http://www.dell.com/";
+ break;
+ case ST_CONF_XML: /* XML metadata */
+ ret = drac3XML;
+ break;
+ default:
+ ret = NULL;
+ break;
+ }
+
+ return(ret);
+}
+
+/* ------------------------------------------------------------------ */
+int
+drac3_status(StonithPlugin *s)
+{
+ struct pluginDevice *drac3d;
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ drac3d = (struct pluginDevice *) s;
+
+ if (drac3VerifyLogin(drac3d->curl, drac3d->host)) {
+ if (drac3Login(drac3d->curl, drac3d->host,
+ drac3d->user, drac3d->pass)) {
+ LOG(PIL_CRIT, "%s: cannot log into %s at %s",
+ __FUNCTION__,
+ drac3d->idinfo,
+ drac3d->host);
+ return(S_ACCESS);
+ }
+ }
+
+ if (drac3GetSysInfo(drac3d->curl, drac3d->host)) {
+ return(S_ACCESS);
+ }else{
+ return(S_OK);
+ }
+}
+
+/* ------------------------------------------------------------------ */
+int
+drac3_reset_req(StonithPlugin * s, int request, const char *host)
+{
+ struct pluginDevice *drac3d;
+ int rc = S_OK;
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ drac3d = (struct pluginDevice *) s;
+
+ if (strcasecmp(host, drac3d->host)) {
+ LOG(PIL_CRIT, "%s doesn't control host [%s]"
+ , drac3d->idinfo, host);
+ return(S_BADHOST);
+ }
+
+ if (drac3VerifyLogin(drac3d->curl, drac3d->host)) {
+ if (drac3Login(drac3d->curl, drac3d->host,
+ drac3d->user, drac3d->pass)) {
+ LOG(PIL_CRIT, "%s: cannot log into %s at %s",
+ __FUNCTION__,
+ drac3d->idinfo,
+ drac3d->host);
+ return(S_ACCESS);
+ }
+ }
+
+ switch(request) {
+#if defined(ST_POWERON) && defined(ST_POWEROFF)
+ case ST_POWERON:
+ case ST_POWEROFF:
+ /* TODO... */
+#endif
+ case ST_GENERIC_RESET:
+ if (drac3PowerCycle(drac3d->curl, drac3d->host))
+ rc = S_ACCESS;
+ break;
+ default:
+ rc = S_INVAL;
+ break;
+ }
+
+ return(rc);
+}
+
+/* ------------------------------------------------------------------ */
+char **
+drac3_hostlist(StonithPlugin * s)
+{
+ struct pluginDevice *drac3d;
+ char **hl;
+
+ ERRIFNOTCONFIGED(s,NULL);
+
+ drac3d = (struct pluginDevice *) s;
+
+ hl = OurImports->StringToHostList(drac3d->host);
+ if (hl == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ } else {
+ strdown(hl[0]);
+ }
+
+ return(hl);
+}
diff --git a/lib/plugins/stonith/drac3_command.c b/lib/plugins/stonith/drac3_command.c
new file mode 100644
index 0000000..4d9002d
--- /dev/null
+++ b/lib/plugins/stonith/drac3_command.c
@@ -0,0 +1,342 @@
+/*
+ * Stonith module for Dell DRACIII (Dell Remote Access Card)
+ *
+ * Copyright (C) 2003 Alfa21 Outsourcing
+ * Copyright (C) 2003 Roberto Moreda <moreda@alfa21.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <lha_internal.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <curl/curl.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+
+#include "drac3_command.h"
+#include "drac3_hash.h"
+
+#define BUFLEN 1024 /* buffer */
+#define SBUFLEN 256 /* small buffer */
+#define MD5LEN 16 /* md5 buffer */
+
+#define DEBUG 0
+
+/* Hardcoded XML commands and response codes */
+#define CMD_POWERCYCLE "<?XML version=\"1.0\"?><?RMCXML version=\"1.0\"?><RMCSEQ><REQ CMD=\"serveraction\"><ACT>powercycle</ACT></REQ></RMCSEQ>\n"
+#define CMD_GETSYSINFO "<?XML version=\"1.0\"?><?RMCXML version=\"1.0\"?><RMCSEQ><REQ CMD=\"xml2cli\"><CMDINPUT>getsysinfo -A</CMDINPUT></REQ></RMCSEQ>\n"
+#define RC_OK "0x0\n"
+
+struct Chunk {
+ char *memory;
+ size_t size;
+};
+
+/* prototypes */
+int xmlGetXPathString (const char *str, const char * expr, char * rc, const int len);
+size_t writeFunction (void *ptr, size_t size, size_t nmemb, void *data);
+
+
+/* ---------------------------------------------------------------------- *
+ * XML PARSING *
+ * ---------------------------------------------------------------------- */
+
+int
+xmlGetXPathString (const char *str,
+ const char * expr,
+ char * rc,
+ const int len)
+{
+ xmlDocPtr doc;
+ xmlNodePtr cur;
+ xmlXPathContextPtr ctx;
+ xmlXPathObjectPtr path;
+ xmlChar *xmlRC;
+
+ if (!strchr(str,'<')) {
+ fprintf(stderr,"%s malformed\n", str);
+ rc[0] = 0x00;
+ return(1);
+ }
+
+ doc = xmlParseMemory(str, strlen(str));
+ xmlXPathInit();
+ ctx = xmlXPathNewContext(doc);
+ path = xmlXPathEvalExpression((const xmlChar *)expr, ctx);
+ cur = path->nodesetval->nodeTab[0];
+
+ if (cur != NULL) {
+ xmlRC = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+ snprintf(rc, len, "%s\n", xmlRC);
+ xmlFree(xmlRC);
+ xmlFreeDoc(doc);
+ xmlCleanupParser();
+ xmlXPathFreeObject(path);
+ xmlXPathFreeContext(ctx);
+
+ return(0);
+ } else {
+ fprintf(stderr,"error in obtaining XPath %s\n", expr);
+ xmlFreeDoc(doc);
+ xmlCleanupParser();
+ xmlXPathFreeObject(path);
+ xmlXPathFreeContext(ctx);
+
+ rc[0] = 0x00;
+ return(1);
+ }
+}
+
+
+/* ---------------------------------------------------------------------- *
+ * CURL CALLBACKS *
+ * ---------------------------------------------------------------------- */
+
+size_t
+writeFunction (void *ptr, size_t size, size_t nmemb, void *data)
+{
+
+ register int realsize = size * nmemb;
+ struct Chunk *mem = (struct Chunk *)data;
+
+ mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1);
+ if (mem->memory) {
+ memcpy(&(mem->memory[mem->size]), ptr, realsize);
+ mem->size += realsize;
+ mem->memory[mem->size] = 0;
+ }
+ return realsize;
+}
+
+
+/* ---------------------------------------------------------------------- *
+ * DRAC3 CURL COMMANDS *
+ * ---------------------------------------------------------------------- */
+
+int
+drac3InitCurl (CURL *curl)
+{
+#ifdef CURLOPT_NOSIGNAL
+ if (curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1)) return(1);
+#endif
+ if (curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30)) return(1);
+ if (curl_easy_setopt(curl, CURLOPT_VERBOSE, 0)) return(1);
+ if (curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeFunction)) return(1);
+ if (curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "/dev/null")) return(1);
+ if (curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0)) return(1);
+ if (curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0)) return(1);
+ return(0);
+}
+
+int
+drac3Login (CURL *curl,
+ const char *host,
+ const char *user,
+ const char *pass)
+{
+ char url[BUFLEN];
+ char chall[BUFLEN];
+ char token[BUFLEN];
+ char rc[SBUFLEN];
+ int status;
+ struct Chunk chunk;
+
+ chunk.memory = NULL;
+ chunk.size = 0;
+ if (curl_easy_setopt(curl, CURLOPT_FILE, (void *)&chunk))
+ return(1);
+
+ /* ask for challenge */
+ snprintf(url, BUFLEN, "https://%s/cgi/challenge", host);
+ url[BUFLEN-1] = 0x00;
+
+ if (curl_easy_setopt(curl, CURLOPT_URL, url))
+ return(1);
+ if (curl_easy_perform(curl))
+ return(1);
+
+ /* extract challenge */
+ status = xmlGetXPathString(chunk.memory, "//CHALLENGE", chall, BUFLEN);
+ if (status) {
+ free(chunk.memory);
+ return(1);
+ }
+
+ /* calculate authToken */
+ drac3AuthHash(chall, pass, token, BUFLEN);
+
+ if (DEBUG) printf("T: %s\n", token);
+
+ status = xmlGetXPathString(chunk.memory, "//RC", rc, SBUFLEN);
+ if (status) {
+ free(chunk.memory);
+ return(1);
+ }
+
+ if (DEBUG) printf("RC: %s\n", rc);
+
+ status = (strcmp(rc, RC_OK) == 0) ? 0 : 1;
+ free(chunk.memory);
+ if (status) return(1);
+ chunk.memory = NULL;
+ chunk.size = 0;
+
+ /* sends authToken */
+ snprintf(url, BUFLEN, "https://%s/cgi/login?user=%s&hash=%s",
+ host,
+ user,
+ token);
+ url[BUFLEN-1] = 0x00;
+
+ if (curl_easy_setopt(curl, CURLOPT_URL, url))
+ return(1);
+ if (curl_easy_perform(curl))
+ return(1);
+
+ if (DEBUG) printf("R: %s\n", chunk.memory);
+ status = xmlGetXPathString(chunk.memory, "//RC", rc, SBUFLEN);
+ if (status) {
+ free(chunk.memory);
+ return(1);
+ }
+
+ if (DEBUG) printf("RC: %s\n", rc);
+
+ status = (strcmp(rc, RC_OK) == 0) ? 0 : 1;
+ free(chunk.memory);
+ return(status);
+}
+
+int
+drac3PowerCycle (CURL *curl,
+ const char *host)
+{
+ char url[BUFLEN];
+ char cmd[]=CMD_POWERCYCLE;
+ char rc[SBUFLEN];
+ int status;
+ struct Chunk chunk;
+
+ chunk.memory = NULL;
+ chunk.size = 0;
+ if (curl_easy_setopt(curl, CURLOPT_FILE, (void *)&chunk)) return(1);
+
+ snprintf(url, BUFLEN, "https://%s/cgi/bin",
+ host);
+ url[BUFLEN-1] = 0x00;
+
+ if (curl_easy_setopt(curl, CURLOPT_URL, url)) return(1);
+ if (curl_easy_setopt(curl, CURLOPT_POSTFIELDS, cmd)) return(1);
+ if (curl_easy_perform(curl)) return(1);
+
+ if (DEBUG) printf("R: %s\n", chunk.memory);
+ status = xmlGetXPathString(chunk.memory, "//RC", rc, SBUFLEN);
+ if (status) {
+ free(chunk.memory);
+ return(1);
+ }
+ if (DEBUG) printf("RC: %s\n", rc);
+
+ status = (strcmp(rc, RC_OK) == 0) ? 0 : 1;
+ free(chunk.memory);
+ return(status);
+}
+
+
+int
+drac3GetSysInfo (CURL *curl,
+ const char *host)
+{
+ char url[BUFLEN];
+ char cmd[]=CMD_GETSYSINFO;
+ char rc[SBUFLEN];
+ int status;
+ struct Chunk chunk;
+
+ chunk.memory = NULL;
+ chunk.size = 0;
+ if (curl_easy_setopt(curl, CURLOPT_FILE, (void *)&chunk)) return(1);
+
+ snprintf(url, BUFLEN, "https://%s/cgi/bin",
+ host);
+ url[BUFLEN-1] = 0x00;
+
+ if (curl_easy_setopt(curl, CURLOPT_URL, url)) return(1);
+ if (curl_easy_setopt(curl, CURLOPT_POSTFIELDS, cmd)) return(1);
+ if (curl_easy_perform(curl)) return(1);
+
+ if (DEBUG) printf("R: %s\n", chunk.memory);
+ status = xmlGetXPathString(chunk.memory, "//RC", rc, SBUFLEN);
+ if (status) {
+ free(chunk.memory);
+ return(1);
+ }
+ if (DEBUG) printf("RC: %s\n", rc);
+
+ status = (strcmp(rc, RC_OK) == 0) ? 0 : 1;
+ free(chunk.memory);
+ return(status);
+}
+
+
+int
+drac3Logout (CURL *curl,
+ const char *host)
+{
+ char url[BUFLEN];
+ char rc[SBUFLEN];
+ int status;
+ struct Chunk chunk;
+
+ chunk.memory = NULL;
+ chunk.size = 0;
+ if (curl_easy_setopt(curl, CURLOPT_FILE, (void *)&chunk)) return(1);
+
+ snprintf(url, BUFLEN, "https://%s/cgi/logout",
+ host);
+ url[BUFLEN-1] = 0x00;
+
+ if (curl_easy_setopt(curl, CURLOPT_URL, url)) return(1);
+ if (curl_easy_perform(curl)) return(1);
+
+ if (DEBUG) printf("R: %s\n", chunk.memory);
+ status = xmlGetXPathString(chunk.memory, "//RC", rc, SBUFLEN);
+ if (status) {
+ free(chunk.memory);
+ return(1);
+ }
+ if (DEBUG) printf("RC: %s\n", rc);
+
+ status = (strcmp(rc, RC_OK) == 0) ? 0 : 1;
+ free(chunk.memory);
+ return(status);
+}
+
+int
+drac3VerifyLogin (CURL *curl,
+ const char *host)
+{
+ /*We try to do a GetSysInfo */
+ return(drac3GetSysInfo (curl, host));
+}
+
+/* -------------------------------------------------------------------- */
+
diff --git a/lib/plugins/stonith/drac3_command.h b/lib/plugins/stonith/drac3_command.h
new file mode 100644
index 0000000..cd03e15
--- /dev/null
+++ b/lib/plugins/stonith/drac3_command.h
@@ -0,0 +1,29 @@
+/*
+ * Stonith module for Dell DRACIII (Dell Remote Access Card)
+ *
+ * Copyright (C) 2003 Alfa21 Outsourcing
+ * Copyright (C) 2003 Roberto Moreda <moreda@alfa21.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+int drac3InitCurl (CURL *curl);
+int drac3Login (CURL *curl, const char *host, const char *user, const char *pass);
+int drac3PowerCycle (CURL *curl, const char *host);
+int drac3GetSysInfo (CURL *curl, const char *host);
+int drac3Logout (CURL *curl, const char *host);
+int drac3VerifyLogin (CURL *curl, const char *host);
+
diff --git a/lib/plugins/stonith/drac3_hash.c b/lib/plugins/stonith/drac3_hash.c
new file mode 100644
index 0000000..605a126
--- /dev/null
+++ b/lib/plugins/stonith/drac3_hash.c
@@ -0,0 +1,106 @@
+/*
+ * Stonith module for Dell DRACIII (Dell Remote Access Card)
+ *
+ * Copyright (C) 2003 Alfa21 Outsourcing
+ * Copyright (C) 2003 Roberto Moreda <moreda@alfa21.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <lha_internal.h>
+
+#include <string.h>
+#include <stdio.h>
+#include <clplumbing/base64.h>
+#include <clplumbing/md5.h>
+#include <glib.h>
+
+#include "drac3_hash.h"
+
+#define BUFLEN 1024 /* buffer */
+#define SBUFLEN 256 /* small buffer */
+#define MD5LEN 16 /* md5 buffer */
+
+/* Hash functions for DRAC3 authentication */
+
+guint16
+drac3Crc16(const char *str,
+ const int l) {
+
+ int i,j;
+ guint16 crc = 0;
+
+ for (i=0; i<l; i++) {
+ crc = crc ^ (str[i] << 8);
+ for (j=0; j<8; j++)
+ crc = ( (crc & 0x8000) == 32768 ? (crc<<1) ^ 0x1021 : crc<<1);
+ }
+ crc = crc & 0xFFFF;
+ return crc;
+}
+
+void
+drac3AuthHash(const char * chall,
+ const char * pass,
+ char * token,
+ int len) {
+
+ char * chall_dup;
+ char challBytes[MD5LEN];
+ char passMD5[MD5LEN];
+ char xorBytes[MD5LEN];
+ char xorBytesMD5[MD5LEN];
+ guint16 crc;
+ char response[MD5LEN+2];
+ char responseb64[SBUFLEN];
+ int i;
+
+ /* decodes chall -> challBytes */
+ memset(challBytes, 0, MD5LEN);
+ chall_dup = g_strdup(chall);
+ if (chall_dup[strlen(chall_dup) - 1] == '\n' ) {
+ chall_dup[strlen(chall_dup) - 1] = '\0';
+ }
+ base64_to_binary(chall_dup, strlen(chall_dup), challBytes, MD5LEN);
+
+ /* gets MD5 from pass -> passMD5 */
+ MD5((const unsigned char *)pass, strlen(pass), (unsigned char *)passMD5);
+
+ /* calculate challBytes and passMD5 xor -> xorBytes */
+ for (i=0; i<MD5LEN; i++) {
+ xorBytes[i] = challBytes[i] ^ passMD5[i];
+ }
+
+ /* calculate xorBytes MD5 -> xorBytesMD5 */
+ MD5((unsigned char *)xorBytes, MD5LEN, (unsigned char *)xorBytesMD5);
+
+ /* calculate xorBytesMD5 crc16 */
+ crc = drac3Crc16(xorBytesMD5, MD5LEN);
+
+ /* joins xorBytesMD5 and crc16 -> response */
+ memcpy(response, xorBytesMD5, MD5LEN);
+ memcpy(response+MD5LEN, &crc, 2);
+
+ /* calculate response base64 -> responseb64 */
+ memset(responseb64, 0, SBUFLEN);
+ binary_to_base64(response, MD5LEN+2, responseb64, SBUFLEN);
+
+ /* assuring null-termination */
+ responseb64[SBUFLEN-1]=0x00;
+
+ snprintf(token, len, "%s", responseb64);
+ token[len-1]=0x00;
+}
diff --git a/lib/plugins/stonith/drac3_hash.h b/lib/plugins/stonith/drac3_hash.h
new file mode 100644
index 0000000..fab2f58
--- /dev/null
+++ b/lib/plugins/stonith/drac3_hash.h
@@ -0,0 +1,28 @@
+/*
+ * Stonith module for Dell DRACIII (Dell Remote Access Card)
+ *
+ * Copyright (C) 2003 Alfa21 Outsourcing
+ * Copyright (C) 2003 Roberto Moreda <moreda@alfa21.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sys/types.h>
+#include <glib.h>
+
+guint16 drac3Crc16(const char *str, const int l);
+void drac3AuthHash(const char *chall, const char *pass, char *token, int len);
+
diff --git a/lib/plugins/stonith/external.c b/lib/plugins/stonith/external.c
new file mode 100644
index 0000000..da03665
--- /dev/null
+++ b/lib/plugins/stonith/external.c
@@ -0,0 +1,868 @@
+/*
+ * Stonith module for EXTERNAL Stonith device
+ *
+ * Copyright (c) 2001 SuSE Linux AG
+ * Portions Copyright (c) 2004, tummy.com, ltd.
+ *
+ * Based on ssh.c, Authors: Joachim Gleissner <jg@suse.de>,
+ * Lars Marowsky-Bree <lmb@suse.de>
+ * Modified for external.c: Scott Kleihege <scott@tummy.com>
+ * Reviewed, tested, and config parsing: Sean Reifschneider <jafo@tummy.com>
+ * And overhauled by Lars Marowsky-Bree <lmb@suse.de>, so the circle
+ * closes...
+ * Mangled by Zhaokai <zhaokai@cn.ibm.com>, IBM, 2005
+ * Changed to allow full-featured external plugins by Dave Blaschke
+ * <debltc@us.ibm.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <lha_internal.h>
+
+#include <dirent.h>
+
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN external
+#define PIL_PLUGIN_S "external"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+
+#include <pils/plugin.h>
+
+static StonithPlugin * external_new(const char *);
+static void external_destroy(StonithPlugin *);
+static int external_set_config(StonithPlugin *, StonithNVpair *);
+static const char * const * external_get_confignames(StonithPlugin *);
+static const char * external_getinfo(StonithPlugin * s, int InfoType);
+static int external_status(StonithPlugin * );
+static int external_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** external_hostlist(StonithPlugin *);
+
+static struct stonith_ops externalOps ={
+ external_new, /* Create new STONITH object */
+ external_destroy, /* Destroy STONITH object */
+ external_getinfo, /* Return STONITH info string */
+ external_get_confignames, /* Return STONITH info string */
+ external_set_config, /* Get configuration from NVpairs */
+ external_status, /* Return STONITH device status */
+ external_reset_req, /* Request a reset */
+ external_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &externalOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * EXTERNAL STONITH device
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ GHashTable * cmd_opts;
+ char * subplugin;
+ char ** confignames;
+ char * outputbuf;
+};
+
+static const char * pluginid = "ExternalDevice-Stonith";
+static const char * NOTpluginID = "External device has been destroyed";
+
+/* Prototypes */
+
+/* Run the command with op and return the exit status + the output
+ * (NULL -> discard output) */
+static int external_run_cmd(struct pluginDevice *sd, const char *op,
+ char **output);
+/* Just free up the configuration and the memory, if any */
+static void external_unconfig(struct pluginDevice *sd);
+
+static int
+external_status(StonithPlugin *s)
+{
+ struct pluginDevice * sd;
+ const char * op = "status";
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ sd = (struct pluginDevice*) s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(S_OOPS);
+ }
+
+ rc = external_run_cmd(sd, op, NULL);
+ if (rc != 0) {
+ LOG(PIL_WARN, "%s: '%s %s' failed with rc %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ }
+ else {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: running '%s %s' returned %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ }
+ }
+ return rc;
+}
+
+static int
+get_num_tokens(char *str)
+{
+ int namecount = 0;
+
+ while (*str != EOS) {
+ str += strspn(str, WHITESPACE);
+ if (*str == EOS)
+ break;
+ str += strcspn(str, WHITESPACE);
+ namecount++;
+ }
+ return namecount;
+}
+
+static char **
+external_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice* sd;
+ const char * op = "gethosts";
+ int rc, i, namecount;
+ char ** ret;
+ char * output = NULL;
+ char * tmp;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,NULL);
+
+ sd = (struct pluginDevice*) s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(NULL);
+ }
+
+ rc = external_run_cmd(sd, op, &output);
+ if (rc != 0) {
+ LOG(PIL_CRIT, "%s: '%s %s' failed with rc %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ if (output) {
+ LOG(PIL_CRIT, "plugin output: %s", output);
+ FREE(output);
+ }
+ return NULL;
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: running '%s %s' returned %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ }
+
+ if (!output) {
+ LOG(PIL_CRIT, "%s: '%s %s' returned an empty hostlist",
+ __FUNCTION__, sd->subplugin, op);
+ return NULL;
+ }
+
+ namecount = get_num_tokens(output);
+ ret = MALLOC((namecount+1)*sizeof(char *));
+ if (!ret) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ FREE(output);
+ return NULL;
+ }
+ memset(ret, 0, (namecount+1)*sizeof(char *));
+
+ /* White-space split the output here */
+ i = 0;
+ tmp = strtok(output, WHITESPACE);
+ while (tmp != NULL) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: %s host %s",
+ __FUNCTION__, sd->subplugin, tmp);
+ }
+ ret[i] = STRDUP(tmp);
+ if (!ret[i]) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ FREE(output);
+ stonith_free_hostlist(ret);
+ return NULL;
+ }
+ i++;
+ tmp = strtok(NULL, WHITESPACE);
+ }
+
+ FREE(output);
+
+ if (i == 0) {
+ LOG(PIL_CRIT, "%s: '%s %s' returned an empty hostlist",
+ __FUNCTION__, sd->subplugin, op);
+ stonith_free_hostlist(ret);
+ ret = NULL;
+ }
+
+ return(ret);
+}
+
+static int
+external_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ struct pluginDevice * sd;
+ const char * op;
+ int rc;
+ char * args1and2;
+ int argslen;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Host external-reset initiating on %s", host);
+ }
+
+ sd = (struct pluginDevice*) s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(S_OOPS);
+ }
+
+ switch (request) {
+ case ST_GENERIC_RESET:
+ op = "reset";
+ break;
+
+ case ST_POWEROFF:
+ op = "off";
+ break;
+
+ case ST_POWERON:
+ op = "on";
+ break;
+
+ default:
+ LOG(PIL_CRIT, "%s: Unknown stonith request %d",
+ __FUNCTION__, request);
+ return S_OOPS;
+ break;
+ }
+
+ argslen = strlen(op) + strlen(host) + 2 /* 1 for blank, 1 for EOS */;
+ args1and2 = (char *)MALLOC(argslen);
+ if (args1and2 == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory!", __FUNCTION__);
+ return S_OOPS;
+ }
+ rc = snprintf(args1and2, argslen, "%s %s", op, host);
+ if (rc <= 0 || rc >= argslen) {
+ FREE(args1and2);
+ return S_OOPS;
+ }
+
+ rc = external_run_cmd(sd, args1and2, NULL);
+ FREE(args1and2);
+ if (rc != 0) {
+ LOG(PIL_CRIT, "%s: '%s %s' for host %s failed with rc %d",
+ __FUNCTION__, sd->subplugin, op, host, rc);
+ return S_RESETFAIL;
+ }
+ else {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: running '%s %s' returned %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ }
+ return S_OK;
+ }
+
+}
+
+static int
+external_parse_config_info(struct pluginDevice* sd, StonithNVpair * info)
+{
+ char * key;
+ char * value;
+ StonithNVpair * nv;
+
+ sd->cmd_opts = g_hash_table_new(g_str_hash, g_str_equal);
+
+ /* TODO: Maybe treat "" as delimeters too so
+ * whitespace can be passed to the plugins... */
+ for (nv = info; nv->s_name; nv++) {
+ if (!nv->s_name || !nv->s_value) {
+ continue;
+ }
+
+ key = STRDUP(nv->s_name);
+ if (!key) {
+ goto err_mem;
+ }
+ value = STRDUP(nv->s_value);
+ if (!value) {
+ FREE(key);
+ goto err_mem;
+ }
+ g_hash_table_insert(sd->cmd_opts, key, value);
+ }
+
+ return(S_OK);
+
+err_mem:
+ LOG(PIL_CRIT, "%s: out of memory!", __FUNCTION__);
+ external_unconfig(sd);
+
+ return(S_OOPS);
+}
+
+static gboolean
+let_remove_eachitem(gpointer key, gpointer value, gpointer user_data)
+{
+ if (key) {
+ FREE(key);
+ }
+ if (value) {
+ FREE(value);
+ }
+ return TRUE;
+}
+
+static void
+external_unconfig(struct pluginDevice *sd) {
+ if (sd->cmd_opts) {
+ g_hash_table_foreach_remove(sd->cmd_opts,
+ let_remove_eachitem, NULL);
+ g_hash_table_destroy(sd->cmd_opts);
+ sd->cmd_opts = NULL;
+ }
+}
+
+/*
+ * Parse the information in the given string
+ * and stash it away...
+ */
+static int
+external_set_config(StonithPlugin* s, StonithNVpair *list)
+{
+ struct pluginDevice * sd;
+ char ** p;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ /* make sure that command has not already been set */
+ if (s->isconfigured) {
+ return(S_OOPS);
+ }
+
+ sd = (struct pluginDevice*) s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(S_OOPS);
+ }
+
+ if (sd->confignames == NULL) {
+ /* specified by name=value pairs, check required parms */
+ if (external_get_confignames(s) == NULL) {
+ return(S_OOPS);
+ }
+
+ for (p = sd->confignames; *p; p++) {
+ if (OurImports->GetValue(list, *p) == NULL) {
+ LOG(PIL_DEBUG, "Cannot get parameter %s from "
+ "StonithNVpair", *p);
+ }
+ }
+ }
+
+ return external_parse_config_info(sd, list);
+}
+
+
+/* Only interested in regular files that are also executable */
+static int
+exec_select(const struct dirent *dire)
+{
+ struct stat statf;
+ char filename[FILENAME_MAX];
+ int rc;
+
+ rc = snprintf(filename, FILENAME_MAX, "%s/%s",
+ STONITH_EXT_PLUGINDIR, dire->d_name);
+ if (rc <= 0 || rc >= FILENAME_MAX) {
+ return 0;
+ }
+
+ if ((stat(filename, &statf) == 0) &&
+ (S_ISREG(statf.st_mode)) &&
+ (statf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))) {
+ if (statf.st_mode & (S_IWGRP|S_IWOTH)) {
+ LOG(PIL_WARN, "Executable file %s ignored "
+ "(writable by group/others)", filename);
+ return 0;
+ }else{
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Return STONITH config vars
+ */
+static const char * const *
+external_get_confignames(StonithPlugin* p)
+{
+ struct pluginDevice * sd;
+ const char * op = "getconfignames";
+ int i, rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ sd = (struct pluginDevice *)p;
+
+ if (sd->subplugin != NULL) {
+ /* return list of subplugin's required parameters */
+ char *output = NULL, *pch;
+ int namecount;
+
+ rc = external_run_cmd(sd, op, &output);
+ if (rc != 0) {
+ LOG(PIL_CRIT, "%s: '%s %s' failed with rc %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ if (output) {
+ LOG(PIL_CRIT, "plugin output: %s", output);
+ FREE(output);
+ }
+ return NULL;
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: '%s %s' returned %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ if (output) {
+ LOG(PIL_DEBUG, "plugin output: %s", output);
+ }
+ }
+
+ namecount = get_num_tokens(output);
+ sd->confignames = (char **)MALLOC((namecount+1)*sizeof(char *));
+ if (sd->confignames == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ if (output) { FREE(output); }
+ return NULL;
+ }
+
+ /* now copy over confignames */
+ pch = strtok(output, WHITESPACE);
+ for (i = 0; i < namecount; i++) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: %s configname %s",
+ __FUNCTION__, sd->subplugin, pch);
+ }
+ sd->confignames[i] = STRDUP(pch);
+ pch = strtok(NULL, WHITESPACE);
+ }
+ FREE(output);
+ sd->confignames[namecount] = NULL;
+ }else{
+ /* return list of subplugins in external directory */
+ struct dirent ** files = NULL;
+ int dircount;
+
+ /* get the external plugin's confignames (list of subplugins) */
+ dircount = scandir(STONITH_EXT_PLUGINDIR, &files,
+ SCANSEL_CAST exec_select, NULL);
+ if (dircount < 0) {
+ return NULL;
+ }
+
+ sd->confignames = (char **)MALLOC((dircount+1)*sizeof(char *));
+ if (!sd->confignames) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ return NULL;
+ }
+
+ for (i = 0; i < dircount; i++) {
+ sd->confignames[i] = STRDUP(files[i]->d_name);
+ free(files[i]);
+ files[i] = NULL;
+ }
+ free(files);
+ sd->confignames[dircount] = NULL;
+ }
+
+ return (const char * const *)sd->confignames;
+}
+
+/*
+ * Return STONITH info string
+ */
+static const char *
+external_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* sd;
+ char * output = NULL;
+ const char * op;
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,NULL);
+
+ sd = (struct pluginDevice *)s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(NULL);
+ }
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ op = "getinfo-devid";
+ break;
+
+ case ST_DEVICENAME:
+ op = "getinfo-devname";
+ break;
+
+ case ST_DEVICEDESCR:
+ op = "getinfo-devdescr";
+ break;
+
+ case ST_DEVICEURL:
+ op = "getinfo-devurl";
+ break;
+
+ case ST_CONF_XML:
+ op = "getinfo-xml";
+ break;
+
+ default:
+ return NULL;
+ }
+
+ rc = external_run_cmd(sd, op, &output);
+ if (rc != 0) {
+ LOG(PIL_CRIT, "%s: '%s %s' failed with rc %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ if (output) {
+ LOG(PIL_CRIT, "plugin output: %s", output);
+ FREE(output);
+ }
+ }
+ else {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: '%s %s' returned %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ }
+ if (sd->outputbuf != NULL) {
+ FREE(sd->outputbuf);
+ }
+ sd->outputbuf = output;
+ return(output);
+ }
+ return(NULL);
+}
+
+/*
+ * EXTERNAL Stonith destructor...
+ */
+static void
+external_destroy(StonithPlugin *s)
+{
+ struct pluginDevice * sd;
+ char ** p;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ VOIDERRIFWRONGDEV(s);
+
+ sd = (struct pluginDevice *)s;
+
+ sd->pluginid = NOTpluginID;
+ external_unconfig(sd);
+ if (sd->confignames != NULL) {
+ for (p = sd->confignames; *p; p++) {
+ FREE(*p);
+ }
+ FREE(sd->confignames);
+ sd->confignames = NULL;
+ }
+ if (sd->subplugin != NULL) {
+ FREE(sd->subplugin);
+ sd->subplugin = NULL;
+ }
+ if (sd->outputbuf != NULL) {
+ FREE(sd->outputbuf);
+ sd->outputbuf = NULL;
+ }
+ FREE(sd);
+}
+
+/* Create a new external Stonith device */
+static StonithPlugin *
+external_new(const char *subplugin)
+{
+ struct pluginDevice* sd = ST_MALLOCT(struct pluginDevice);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ if (sd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(sd, 0, sizeof(*sd));
+ sd->pluginid = pluginid;
+ if (subplugin != NULL) {
+ sd->subplugin = STRDUP(subplugin);
+ if (sd->subplugin == NULL) {
+ FREE(sd);
+ return(NULL);
+ }
+ }
+ sd->sp.s_ops = &externalOps;
+ return &(sd->sp);
+}
+
+static void
+ext_add_to_env(gpointer key, gpointer value, gpointer user_data)
+{
+ if (setenv((char *)key, (char *)value, 1) != 0) {
+ LOG(PIL_CRIT, "%s: setenv failed.", __FUNCTION__);
+ }
+}
+
+static void
+ext_del_from_env(gpointer key, gpointer value, gpointer user_data)
+{
+ unsetenv((char *)key);
+}
+
+#define LOGTAG_VAR "HA_LOGTAG"
+
+/* Run the command with op as command line argument(s) and return the exit
+ * status + the output */
+static int
+external_run_cmd(struct pluginDevice *sd, const char *op, char **output)
+{
+ const int BUFF_LEN=4096;
+ char buff[BUFF_LEN];
+ int read_len = 0;
+ int status, rc;
+ char * data = NULL;
+ FILE * file;
+ char cmd[FILENAME_MAX+64];
+ struct stat buf;
+ int slen;
+ char *path, *new_path, *logtag, *savevar = NULL;
+ int new_path_len, logtag_len;
+ gboolean nodata;
+
+ rc = snprintf(cmd, FILENAME_MAX, "%s/%s",
+ STONITH_EXT_PLUGINDIR, sd->subplugin);
+ if (rc <= 0 || rc >= FILENAME_MAX) {
+ LOG(PIL_CRIT, "%s: external command too long.", __FUNCTION__);
+ return -1;
+ }
+
+ if (stat(cmd, &buf) != 0) {
+ LOG(PIL_CRIT, "%s: stat(2) of %s failed: %s",
+ __FUNCTION__, cmd, strerror(errno));
+ return -1;
+ }
+
+ if (!S_ISREG(buf.st_mode)
+ || (!(buf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))) {
+ LOG(PIL_CRIT, "%s: %s found NOT to be executable.",
+ __FUNCTION__, cmd);
+ return -1;
+ }
+
+ if (buf.st_mode & (S_IWGRP|S_IWOTH)) {
+ LOG(PIL_CRIT, "%s: %s found to be writable by group/others, "
+ "NOT executing for security purposes.",
+ __FUNCTION__, cmd);
+ return -1;
+ }
+
+ strcat(cmd, " ");
+ strcat(cmd, op);
+
+ /* We only have a global environment to use here. So we add our
+ * options to it, and then later remove them again. */
+ if (sd->cmd_opts) {
+ g_hash_table_foreach(sd->cmd_opts, ext_add_to_env, NULL);
+ }
+
+ /* external plugins need path to ha_log.sh */
+ path = getenv("PATH");
+ if (strncmp(GLUE_SHARED_DIR,path,strlen(GLUE_SHARED_DIR))) {
+ new_path_len = strlen(path)+strlen(GLUE_SHARED_DIR)+2;
+ new_path = (char *)g_malloc(new_path_len);
+ snprintf(new_path, new_path_len, "%s:%s", GLUE_SHARED_DIR, path);
+ setenv("PATH", new_path, 1);
+ g_free(new_path);
+ }
+
+ /* set the logtag appropriately */
+ logtag_len = strlen(PIL_PLUGIN_S)+strlen(sd->subplugin)+2;
+ logtag = (char *)g_malloc(logtag_len);
+ snprintf(logtag, logtag_len, "%s/%s", PIL_PLUGIN_S, sd->subplugin);
+ if (getenv(LOGTAG_VAR)) {
+ savevar = g_strdup(getenv(LOGTAG_VAR));
+ }
+ setenv(LOGTAG_VAR, logtag, 1);
+ g_free(logtag);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: Calling '%s'", __FUNCTION__, cmd );
+ }
+ file = popen(cmd, "r");
+ if (NULL==file) {
+ LOG(PIL_CRIT, "%s: Calling '%s' failed",
+ __FUNCTION__, cmd);
+ rc = -1;
+ goto out;
+ }
+
+ if (output) {
+ slen=0;
+ data = MALLOC(1);
+ data[slen] = EOS;
+ }
+ while (!feof(file)) {
+ nodata = TRUE;
+ if (output) {
+ read_len = fread(buff, 1, BUFF_LEN, file);
+ if (read_len > 0) {
+ data = REALLOC(data, slen+read_len+1);
+ if (data == NULL) {
+ break;
+ }
+ memcpy(data + slen, buff, read_len);
+ slen += read_len;
+ data[slen] = EOS;
+ nodata = FALSE;
+ }
+ } else {
+ if (fgets(buff, BUFF_LEN, file)) {
+ LOG(PIL_INFO, "%s: '%s' output: %s", __FUNCTION__, cmd, buff);
+ nodata = FALSE;
+ }
+ }
+ if (nodata) {
+ sleep(1);
+ }
+ }
+ if (output && !data) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ rc = -1;
+ goto out;
+ }
+
+ status = pclose(file);
+ if (WIFEXITED(status)) {
+ rc = WEXITSTATUS(status);
+ if (rc != 0 && Debug) {
+ LOG(PIL_DEBUG,
+ "%s: Calling '%s' returned %d", __FUNCTION__, cmd, rc);
+ }
+ } else {
+ if (WIFSIGNALED(status)) {
+ LOG(PIL_CRIT, "%s: '%s' got signal %d",
+ __FUNCTION__, cmd, WTERMSIG(status));
+ } else if (WIFSTOPPED(status)) {
+ LOG(PIL_INFO, "%s: '%s' stopped with signal %d",
+ __FUNCTION__, cmd, WSTOPSIG(status));
+ } else {
+ LOG(PIL_CRIT, "%s: '%s' exited abnormally (core dumped?)",
+ __FUNCTION__, cmd);
+ }
+ rc = -1;
+ }
+ if (Debug && output && data) {
+ LOG(PIL_DEBUG, "%s: '%s' output: %s", __FUNCTION__, cmd, data);
+ }
+
+out:
+ if (savevar) {
+ setenv(LOGTAG_VAR, savevar, 1);
+ g_free(savevar);
+ } else {
+ unsetenv(LOGTAG_VAR);
+ }
+ if (sd->cmd_opts) {
+ g_hash_table_foreach(sd->cmd_opts, ext_del_from_env, NULL);
+ }
+ if (!rc) {
+ if (output) {
+ *output = data;
+ }
+ } else {
+ if (data) {
+ FREE(data);
+ }
+ if (output) {
+ *output = NULL;
+ }
+ }
+ return rc;
+}
diff --git a/lib/plugins/stonith/external/Makefile.am b/lib/plugins/stonith/external/Makefile.am
new file mode 100644
index 0000000..42e0046
--- /dev/null
+++ b/lib/plugins/stonith/external/Makefile.am
@@ -0,0 +1,33 @@
+# Makefile.am for OCF RAs
+#
+# Author: Sun Jing Dong
+# Copyright (C) 2004 IBM
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+EXTRA_DIST = drac5 dracmc-telnet ibmrsa-telnet ipmi rackpdu vmware vcenter xen0 \
+ xen0-ha-dom0-stonith-helper kdumpcheck nut
+
+extdir = $(stonith_ext_plugindir)
+
+helperdir = $(stonith_plugindir)
+
+ext_SCRIPTS = drac5 dracmc-telnet ibmrsa ibmrsa-telnet ipmi riloe ssh vmware vcenter rackpdu xen0 hmchttp \
+ xen0-ha kdumpcheck ippower9258 nut libvirt \
+ hetzner
+
+helper_SCRIPTS = xen0-ha-dom0-stonith-helper
diff --git a/lib/plugins/stonith/external/drac5.in b/lib/plugins/stonith/external/drac5.in
new file mode 100644
index 0000000..218cbd3
--- /dev/null
+++ b/lib/plugins/stonith/external/drac5.in
@@ -0,0 +1,113 @@
+#!/bin/sh
+#
+# External STONITH module for DRAC5 adapters.
+#
+# Author: Jun Wang
+# License: GNU General Public License (GPL)
+#
+
+trap 'if [ -n "$outf" ]; then ha_log.sh err "`cat $outf`"; rm -f "$outf"; fi' 0
+outf=`mktemp` || {
+ ha_log.sh err "mktemp failed"
+ exit 1
+}
+
+sshlogin() {
+ if [ x = "x$ipaddr" -o x = "x$userid" ]
+ then
+ ha_log.sh err "ipaddr or userid missing; check configuration"
+ return 1
+ fi
+ @SSH@ -q -x -n $userid@$ipaddr racadm serveraction "$1" >$outf 2>&1
+}
+
+drac_reset() {
+ sshlogin hardreset
+}
+
+drac_on() {
+ sshlogin poweron
+}
+
+drac_off() {
+ sshlogin poweroff
+}
+
+drac_status() {
+ sshlogin powerstatus
+}
+
+case $1 in
+gethosts)
+ echo $hostname
+ ;;
+on)
+ drac_poweron
+ ;;
+off)
+ drac_poweroff
+ ;;
+reset)
+ drac_reset
+ ;;
+status)
+ drac_status
+ ;;
+getconfignames)
+ for i in hostname ipaddr userid; do
+ echo $i
+ done
+ ;;
+getinfo-devid)
+ echo "DRAC5 STONITH device"
+ ;;
+getinfo-devname)
+ echo "DRAC5 STONITH device"
+ ;;
+getinfo-devdescr)
+ echo "DRAC5 host reset/poweron/poweroff"
+ ;;
+getinfo-devurl)
+ echo "http://www.dell.com"
+ ;;
+getinfo-xml)
+ cat <<EOF
+<parameters>
+
+<parameter name="hostname" unique="1">
+<content type="string" />
+<shortdesc lang="en">
+Hostname
+</shortdesc>
+<longdesc lang="en">
+The hostname of the host to be managed by this STONITH device
+</longdesc>
+</parameter>
+
+<parameter name="ipaddr" unique="1">
+<content type="string" />
+<shortdesc lang="en">
+IP Address
+</shortdesc>
+<longdesc lang="en">
+The IP address of the STONITH device
+</longdesc>
+</parameter>
+
+<parameter name="userid" unique="1">
+<content type="string" />
+<shortdesc lang="en">
+Login
+</shortdesc>
+<longdesc lang="en">
+The username used for logging in to the STONITH device
+</longdesc>
+</parameter>
+
+</parameters>
+EOF
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/dracmc-telnet b/lib/plugins/stonith/external/dracmc-telnet
new file mode 100644
index 0000000..d993961
--- /dev/null
+++ b/lib/plugins/stonith/external/dracmc-telnet
@@ -0,0 +1,377 @@
+#!/usr/bin/env python
+# vim: set filetype=python
+#######################################################################
+#
+# dracmc-telnet - External stonith plugin for HAv2 (http://linux-ha.org/wiki)
+# Connects to Dell Drac/MC Blade Enclosure via a Cyclades
+# terminal server with telnet and switches power of named
+# blade servers appropriatelly.
+#
+# Required parameters:
+# nodename: The name of the server you want to touch on your network
+# cyclades_ip: The IP address of the cyclades terminal server
+# cyclades_port: The port for telnet to access on the cyclades (i.e. 7032)
+# servername: The DRAC/MC server name of the blade (i.e. Server-7)
+# username: The login user name for the DRAC/MC
+# password: The login password for the DRAC/MC
+#
+# Author: Alex Tsariounov <alext@novell.com>
+#
+# Based on ibmrsa-telnet external stonith plugin by Andreas Mock
+# (andreas.mock@web.de), Copyright by Adreas Mock and released as part
+# of HAv2.
+#
+# History:
+# 2009-10-12 First release.
+#
+# Copyright (c) 2009 Novell, Inc.
+# All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 or later of the GNU General Public
+# License as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+#######################################################################
+import sys
+import os
+import time
+import telnetlib
+import random
+import subprocess
+
+LOGINRETRIES = 10
+
+class TimeoutException(Exception):
+ def __init__(self, value=None):
+ Exception.__init__(self)
+ self.value = value
+
+ def __str__(self):
+ return repr(self.value)
+
+class DracMC(telnetlib.Telnet):
+ def __init__(self, *args, **kwargs):
+ telnetlib.Telnet.__init__(self)
+ self._timeout = 4
+ self._loggedin = 0
+ self._history = []
+ self._appl = os.path.basename(sys.argv[0])
+ self._server = args[0]
+
+ def _get_timestamp(self):
+ ct = time.time()
+ msecs = (ct - long(ct)) * 1000
+ return "%s,%03d" % (time.strftime("%Y-%m-%d %H:%M:%S",
+ time.localtime(ct)), msecs)
+
+ def write(self, buffer):
+ self._history.append(self._get_timestamp() + ': WRITE: ' + repr(buffer))
+ telnetlib.Telnet.write(self, buffer)
+
+ def read_until(self, what, timeout=2):
+ line = telnetlib.Telnet.read_until(self, what, timeout)
+ self._history.append(self._get_timestamp() + ': READ : ' + repr(line))
+ if not line.endswith(what):
+ raise TimeoutException("Timeout while waiting for '%s'." % (what, ))
+ return line
+
+ def login(self, user, passwd):
+ time.sleep(0.3)
+ try:
+ line = self.read_until('Login: ', self._timeout)
+ self.write(user)
+ self.write('\r')
+ line = self.read_until('Password: ', self._timeout)
+ self.write(passwd)
+ self.write('\r')
+ except:
+ self.write("\r")
+ line = self.read_until('Login: ', self._timeout)
+ self.write(user)
+ self.write('\r')
+ line = self.read_until('Password: ', self._timeout)
+ self.write(passwd)
+ self.write('\r')
+ try:
+ line = self.read_until('DRAC/MC:', self._timeout)
+ except:
+ self.write("\r")
+ line = self.read_until('DRAC/MC:', self._timeout)
+
+ def hardreset(self):
+ self.write('serveraction -s %s hardreset\r' % self._server)
+ line = self.read_until('OK', 10)
+ line = self.read_until('DRAC/MC:', self._timeout)
+
+ def powercycle(self):
+ self.write('serveraction -s %s powercycle\r' % self._server)
+ line = self.read_until('OK', 10)
+ line = self.read_until('DRAC/MC:', self._timeout)
+
+ def on(self):
+ self.write('serveraction -s %s powerup\r' % self._server)
+ line = self.read_until('OK', 10)
+ line = self.read_until('DRAC/MC:', self._timeout)
+
+ def off(self):
+ self.write('serveraction -s %s powerdown\r' % self._server)
+ line = self.read_until('OK', 10)
+ line = self.read_until('DRAC/MC:', self._timeout)
+
+ def exit(self):
+ self.write('exit\r')
+
+ def get_history(self):
+ return "\n".join(self._history)
+
+
+class DracMCStonithPlugin:
+ def __init__(self):
+ # define the external stonith plugin api
+ self._required_cmds = \
+ 'reset gethosts status getconfignames getinfo-devid ' \
+ 'getinfo-devname getinfo-devdescr getinfo-devurl ' \
+ 'getinfo-xml'
+ self._optional_cmds = 'on off'
+ self._required_cmds_list = self._required_cmds.split()
+ self._optional_cmds_list = self._optional_cmds.split()
+
+ # who am i
+ self._appl = os.path.basename(sys.argv[0])
+
+ # telnet connection object
+ self._connection = None
+
+ # the list of configuration names
+ self._confignames = ['nodename', 'cyclades_ip', 'cyclades_port',
+ 'servername', 'username', 'password']
+
+ # catch the parameters provided by environment
+ self._parameters = {}
+ for name in self._confignames:
+ try:
+ self._parameters[name] = os.environ.get(name, '').split()[0]
+ except IndexError:
+ self._parameters[name] = ''
+
+ def _get_timestamp(self):
+ ct = time.time()
+ msecs = (ct - long(ct)) * 1000
+ return "%s,%03d" % (time.strftime("%Y-%m-%d %H:%M:%S",
+ time.localtime(ct)), msecs)
+
+ def _echo_debug(self, *args):
+ subprocess.call("ha_log.sh debug '%s'" % ' '.join(args), shell=True)
+
+ def echo(self, *args):
+ what = ''.join([str(x) for x in args])
+ sys.stdout.write(what)
+ sys.stdout.write('\n')
+ sys.stdout.flush()
+ self._echo_debug("STDOUT:", what)
+
+ def echo_log(self, level, *args):
+ subprocess.call("ha_log.sh %s '%s'" % (level,' '.join(args)), shell=True)
+
+ def _get_connection(self):
+ if not self._connection:
+ c = DracMC(self._parameters['servername'])
+ self._echo_debug("Connecting to '%s:%s'" %
+ (self._parameters['cyclades_ip'],
+ self._parameters['cyclades_port']))
+ tries = 0
+ while tries < LOGINRETRIES:
+ try:
+ c.open(self._parameters['cyclades_ip'],
+ self._parameters['cyclades_port'])
+ c.login(self._parameters['username'],
+ self._parameters['password'])
+ except Exception, args:
+ if "Connection reset by peer" in str(args):
+ self._echo_debug("Someone is already logged in... retry=%s" % tries)
+ c.close()
+ time.sleep(random.uniform(1.0, 5.0))
+ else:
+ raise
+ else:
+ break
+ tries += 1
+
+ if tries == LOGINRETRIES:
+ c.close()
+ raise Exception("Could not log in to %s:%s" %
+ (self._parameters['cyclades_ip'],
+ self._parameters['cyclades_port']))
+ self._connection = c
+
+ def _end_connection(self):
+ if self._connection:
+ self._connection.exit()
+ self._connection.close()
+
+ def reset(self):
+ self._get_connection()
+ # self._connection.hardreset()
+ self._connection.powercycle()
+ self._end_connection()
+ self._echo_debug(self._connection.get_history())
+ self.echo_log("info", "Reset of node '%s' done" %
+ (self._parameters['nodename'],))
+ return(0)
+
+ def on(self):
+ self._get_connection()
+ self._connection.on()
+ self._end_connection()
+ self._echo_debug(self._connection.get_history())
+ self.echo_log("info", "Switched node '%s' ON" %
+ (self._parameters['nodename'],))
+ return(0)
+
+ def off(self):
+ self._get_connection()
+ self._connection.off()
+ self._end_connection()
+ self._echo_debug(self._connection.get_history())
+ self.echo_log("info", "Switched node '%s' OFF" %
+ (self._parameters['nodename'],))
+ return(0)
+
+ def gethosts(self):
+ self.echo(self._parameters['nodename'])
+ return(0)
+
+ def status(self):
+ self._get_connection()
+ self._end_connection()
+ self._echo_debug(self._connection.get_history())
+ return(0)
+
+ def getconfignames(self):
+ for name in ['nodename', 'cyclades_ip', 'cyclades_port', 'servername',
+ 'username', 'password']:
+ self.echo(name)
+ return(0)
+
+ def getinfo_devid(self):
+ self.echo("External Stonith Plugin for Dell DRAC/MC via Cyclades")
+ return(0)
+
+ def getinfo_devname(self):
+ self.echo("External Stonith Plugin for Dell Drac/MC connecting "
+ "via Telnet to a Cyclades port")
+ return(0)
+
+ def getinfo_devdescr(self):
+ self.echo("External stonith plugin for HAv2 which connects to "
+ "a Dell DRAC/MC connected via a Cyclades port with telnet. "
+ "Commands to turn on/off power and to reset server are sent "
+ "appropriately. "
+ "(c) 2009 by Novell, Inc. (alext@novell.com)")
+ return(0)
+
+ def getinfo_devurl(self):
+ self.echo("http://support.dell.com/support/edocs/software/smdrac3/dracmc/1.3/en/index.htm")
+
+ def getinfo_xml(self):
+ info = """<parameters>
+ <parameter name="nodename" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">nodename to shoot</shortdesc>
+ <longdesc lang="en">
+ Name of the node to be stonithed.
+ </longdesc>
+ </parameter>
+ <parameter name="cyclades_ip" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">hostname or ip address of cyclades</shortdesc>
+ <longdesc lang="en">
+ Hostname or IP address of Cyclades connected to DRAC/MC.
+ </longdesc>
+ </parameter>
+ <parameter name="cyclades_port" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">telnet port to use on cyclades</shortdesc>
+ <longdesc lang="en">
+ Port used with the Cyclades telnet interface which is connected to the DRAC/MC.
+ </longdesc>
+ </parameter>
+ <parameter name="servername" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">DRAC/MC name of blade to be stonithed</shortdesc>
+ <longdesc lang="en">
+ Name of server blade to be stonithed on the DRAC/MC (example: Server-7)
+ </longdesc>
+ </parameter>
+ <parameter name="username" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">username to login on the DRAC/MC</shortdesc>
+ <longdesc lang="en">
+ Username to login to the DRAC/MC once connected via the Cyclades port.
+ </longdesc>
+ </parameter>
+ <parameter name="password" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">password to login on the DRAC/MC</shortdesc>
+ <longdesc lang="en">
+ Password to login to the DRAC/MC once connected via the Cyclades port.
+ </longdesc>
+ </parameter>
+ </parameters>
+ """
+ self.echo(info)
+ return(0)
+
+ def not_implemented(self, cmd):
+ self.echo_log("err", "Command '%s' not implemented." % (cmd,))
+ return(1)
+
+ def usage(self):
+ usage = "Call me with one of the allowed commands: %s, %s" % (
+ ', '.join(self._required_cmds_list),
+ ', '.join(self._optional_cmds_list))
+ return usage
+
+ def process(self, argv):
+ self._echo_debug("========== Start =============")
+ if len(argv) < 1:
+ self.echo_log("err", 'At least one commandline argument required.')
+ return(1)
+ cmd = argv[0]
+ self._echo_debug("cmd:", cmd)
+ if cmd not in self._required_cmds_list and \
+ cmd not in self._optional_cmds_list:
+ self.echo_log("err", "Command '%s' not supported." % (cmd,))
+ return(1)
+ try:
+ cmd = cmd.lower().replace('-', '_')
+ func = getattr(self, cmd, self.not_implemented)
+ rc = func()
+ return(rc)
+ except Exception, args:
+ self.echo_log("err", 'Exception raised:', str(args))
+ if self._connection:
+ self.echo_log("err", self._connection.get_history())
+ self._connection.close()
+ return(1)
+
+
+if __name__ == '__main__':
+ stonith = DracMCStonithPlugin()
+ rc = stonith.process(sys.argv[1:])
+ sys.exit(rc)
diff --git a/lib/plugins/stonith/external/hetzner b/lib/plugins/stonith/external/hetzner
new file mode 100755
index 0000000..2b3e675
--- /dev/null
+++ b/lib/plugins/stonith/external/hetzner
@@ -0,0 +1,139 @@
+#!/bin/sh
+#
+# External STONITH module for Hetzner.
+#
+# Copyright (c) 2011 MMUL S.a.S. - Raoul Scarazzini <rasca@mmul.it>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+
+# Read parameters from config file, format is based upon the hetzner OCF resource agent
+# developed by Kumina: http://blog.kumina.nl/2011/02/hetzner-failover-ip-ocf-script/
+conf_file="/etc/hetzner.cfg"
+
+case $1 in
+ get*) ;; # don't print errors if conf_file not present
+ *)
+ user=`sed -n 's/^user.*=\ *//p' $conf_file`
+ pass=`sed -n 's/^pass.*=\ *//p' $conf_file`
+ ;;
+esac
+
+hetzner_server="https://robot-ws.your-server.de"
+
+check_http_response() {
+ # If the response is 200 then return 0
+ if [ $1 = 200 ]
+ then
+ return 0
+ else
+ # If the response is not 200 then display a description of the problem and return 1
+ case $1 in
+ 400) ha_log.sh err "INVALID_INPUT - Invalid input parameters"
+ ;;
+ 404) ha_log.sh err "SERVER_NOT_FOUND - Server with ip $remote_ip not found"
+ ;;
+ 409) ha_log.sh err "RESET_MANUAL_ACTIVE - There is already a running manual reset"
+ ;;
+ 500) ha_log.sh err "RESET_FAILED - Resetting failed due to an internal error"
+ ;;
+ esac
+ return 1
+ fi
+}
+
+case $1 in
+gethosts)
+ echo $hostname
+ exit 0
+ ;;
+on)
+ # Can't really be implemented because Hetzner's webservice cannot power on a system
+ ha_log.sh err "Power on is not available since Hetzner's webservice can't do this operation."
+ exit 1
+ ;;
+off)
+ # Can't really be implemented because Hetzner's webservice cannot power on a system
+ ha_log.sh err "Power off is not available since Hetzner's webservice can't do this operation."
+ exit 1
+ ;;
+reset)
+ # Launching the reset action via webservice
+ check_http_response $(curl --silent -o /dev/null -w '%{http_code}' -u $user:$pass $hetzner_server/reset/$remote_ip -d type=hw)
+ exit $?
+ ;;
+status)
+ # Check if we can contact the webservice
+ check_http_response "$(curl --silent -o /dev/null -w '%{http_code}' -u $user:$pass $hetzner_server/server/$remote_ip)"
+ exit $?
+ ;;
+getconfignames)
+ echo "hostname"
+ echo "remote_ip"
+ exit 0
+ ;;
+getinfo-devid)
+ echo "Hetzner STONITH device"
+ exit 0
+ ;;
+getinfo-devname)
+ echo "Hetzner STONITH external device"
+ exit 0
+ ;;
+getinfo-devdescr)
+ echo "Hetzner host reset"
+ echo "Manages the remote webservice for reset a remote server."
+ exit 0
+ ;;
+getinfo-devurl)
+ echo "http://wiki.hetzner.de/index.php/Robot_Webservice_en"
+ exit 0
+ ;;
+getinfo-xml)
+ cat << HETZNERXML
+<parameters>
+<parameter name="hostname" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Hostname
+</shortdesc>
+<longdesc lang="en">
+The name of the host to be managed by this STONITH device.
+</longdesc>
+</parameter>
+
+<parameter name="remote_ip" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Remote IP
+</shortdesc>
+<longdesc lang="en">
+The address of the remote IP that manages this server.
+</longdesc>
+</parameter>
+</parameters>
+HETZNERXML
+ exit 0
+ ;;
+*)
+ ha_log.sh err "Don't know what to do for '$remote_ip'"
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/hmchttp b/lib/plugins/stonith/external/hmchttp
new file mode 100644
index 0000000..9d111bc
--- /dev/null
+++ b/lib/plugins/stonith/external/hmchttp
@@ -0,0 +1,218 @@
+#!/bin/sh
+# External STONITH module for HMC web console
+#
+# Copyright (c) 2007 Xinwei Hu <hxinwei@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+
+#set -x
+hostlist=`echo $hostlist | tr ',' ' '`
+
+trap '[ ! -e "$COOKIEFILE" ] || rm -f "$COOKIEFILE"' 0
+COOKIEFILE=`mktemp` || exit 1
+
+: ${CURLBIN="/usr/bin/curl"}
+: ${user=admin}
+: ${password=admin}
+
+check_parameter() {
+ if [ ! -x $CURLBIN ]
+ then
+ ha_log.sh err "Curl can't be found in normal place. Set CURLBIN to override the default value"
+ exit 1
+ fi
+
+ if [ -z $hmc_ipaddr ]
+ then
+ ha_log.sh err "The address of HMC web console is not specified"
+ exit 1
+ fi
+}
+
+HMCUSERNAME=$user
+HMCPASSWORD=$password
+
+HMC_LOGIN_COMMAND="$CURLBIN -3 -k -c $COOKIEFILE -d user=$HMCUSERNAME -d password=$HMCPASSWORD -d lang=0 -d submit=Log+in "
+HMC_LOGOUT_COMMAND="$CURLBIN -3 -k -b $COOKIEFILE -d submit=Log+out "
+HMC_TOC_COMMAND="$CURLBIN -3 -k -b $COOKIEFILE -d form=2 "
+HMC_POWERON_COMMAND="$CURLBIN -3 -k -b $COOKIEFILE -d form=60 -d sp=255 -d is=0 -d om=4 -d id=1 -d ip=2 -d plt=1 -d pm=0 -d on=Save+settings+and+power+on "
+HMC_POWERSTATE_COMMAND="$CURLBIN -3 -k -b $COOKIEFILE -d form=60 "
+HMC_POWEROFF_COMMAND="$CURLBIN -3 -k -b $COOKIEFILE -d form=30 -d submit=Continue "
+HMC_REBOOT_COMMAND="$CURLBIN -3 -k -b $COOKIEFILE -d form=74 -d submit=Continue "
+
+hmc_login() {
+ iamin=0
+ while [ $iamin -eq 0 ]; do
+ $HMC_LOGIN_COMMAND https://$hmc_ipaddr/cgi-bin/cgi >/dev/null 2>&1
+ $HMC_TOC_COMMAND https://$hmc_ipaddr/cgi-bin/cgi 2>/dev/null | grep -q "Too many users"
+ iamin=$?
+ sleep 2
+ done
+}
+hmc_logout() {
+ $HMC_LOGOUT_COMMAND https://$hmc_ipaddr/cgi-bin/cgi 2>/dev/null
+}
+
+hmc_reboot() {
+ check_parameter
+ $HMC_REBOOT_COMMAND https://$hmc_ipaddr/cgi-bin/cgi 2>/dev/null
+}
+hmc_poweron() {
+ r=1
+ while [ 0 -ne $r ]; do
+ $HMC_POWERON_COMMAND https://$hmc_ipaddr/cgi-bin/cgi 2>/dev/null | grep -q "Operation completed successfully"
+ r=$?
+ done
+}
+hmc_poweroff() {
+ check_parameter
+ $HMC_POWEROFF_COMMAND https://$hmc_ipaddr/cgi-bin/cgi 2>/dev/null
+}
+hmc_powerstate() {
+ check_parameter
+ r=`$HMC_POWERSTATE_COMMAND https://$hmc_ipaddr/cgi-bin/cgi 2>/dev/null| grep "Current system power state:" | sed 's/<br>//g' | awk '{print $5}'`
+ echo $r
+}
+
+hmc_poweroffon() {
+ check_parameter
+ hmc_poweroff
+ while [ 1 ]; do
+ r=`hmc_powerstate`
+ ha_log.sh debug "power state: $r"
+ if [ $r = "Off" ]; then
+ break
+ fi
+ sleep 5
+ done
+ sleep 3
+ hmc_poweron
+}
+
+case $1 in
+gethosts)
+ for h in $hostlist; do
+ echo $h
+ done
+ exit 0
+ ;;
+status)
+ if
+ ping -w1 -c1 "$hmc_ipaddr" 2>&1
+ then
+ exit 0
+ fi
+ exit 1
+ ;;
+getconfignames)
+ for f in hostlist hmc_ipaddr user password; do
+ echo $f
+ done
+ exit 0
+ ;;
+getinfo-devid)
+ echo "HMC web console STONITH device"
+ exit 0
+ ;;
+getinfo-devname)
+ echo "HMC web console STONITH external device"
+ exit 0
+ ;;
+getinfo-devdescr)
+ echo "HMC web console based host power control"
+ echo "Use for i5, p5, pSeries and OpenPower systems that are managed via "
+ echo "web console through a direct connection to system's HMC port."
+ exit 0
+ ;;
+getinfo-devurl)
+ echo "http://www.ibm.com"
+ exit 0
+ ;;
+getinfo-xml)
+ cat << HMCXML
+<parameters>
+
+<parameter name="hostlist" unique="1" required="1">
+<content type="string"/>
+<shortdesc lang="en">Hostlist</shortdesc>
+<longdesc lang="en">
+The list of hosts that the STONITH device controls
+</longdesc>
+</parameter>
+
+<parameter name="hmc_ipaddr" unique="1" required="1">
+<content type="string"/>
+<shortdesc lang="en">HMC IPAddr</shortdesc>
+<longdesc lang="en">
+The IP address of the HMC web console
+</longdesc>
+</parameter>
+
+<parameter name="user" unique="1" required="1">
+<content type="string"/>
+<shortdesc lang="en">User</shortdesc>
+<longdesc lang="en">
+User name to log into HMC web console
+</longdesc>
+</parameter>
+
+<parameter name="password" unique="1" required="1">
+<content type="string"/>
+<shortdesc lang="en">Password</shortdesc>
+<longdesc lang="en">
+The password of user name to log into HMC web console
+</longdesc>
+</parameter>
+
+</parameters>
+HMCXML
+ exit 0
+ ;;
+esac
+
+case $1 in
+on|off|reset|powerstate|poweroffon)
+ hmc_login
+ case $1 in
+ on)
+ hmc_poweron $hmc_ipaddr
+ ;;
+ off)
+ hmc_poweroff $hmc_ipaddr
+ ;;
+ reset)
+# hmc_reboot $hmc_ipaddr
+ hmc_poweroffon $hmc_ipaddr
+ ;;
+ powerstate)
+ hmc_powerstate
+ ;;
+ poweroffon)
+ hmc_poweroffon
+ ;;
+ esac
+ hmc_logout
+ exit 0
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/ibmrsa b/lib/plugins/stonith/external/ibmrsa
new file mode 100644
index 0000000..7408465
--- /dev/null
+++ b/lib/plugins/stonith/external/ibmrsa
@@ -0,0 +1,157 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Dejan Muhamedagic <dmuhamedagic@at.ibm.com>, IBM Austria
+#
+# External STONITH module for IBM RSA adapters.
+# External STONITH module for IBM BMC.
+# This STONITH module depends on IBMmpcli.
+#
+
+trap 'rm -f "$outf"' 0
+outf=`mktemp` || {
+ ha_log.sh err 'mktemp failed'
+ exit 1
+}
+
+chkmpcli() {
+ test -x /opt/IBMmpcli/bin/MPCLI.sh
+}
+mpcli() {
+ chkmpcli || {
+ ha_log.sh err "IBM mpcli not installed"
+ return 1
+ }
+ if [ x = "x$ipaddr" -o x = "x$userid" -o x = "x$passwd" ]
+ then
+ ha_log.sh err "ipaddr, userid, or passwd missing; check configuration"
+ return 1
+ fi
+ type=${type:-"ibm"}
+
+ goodstg="SUCCESS"
+ failstg="FAILURE"
+ (
+ echo "logonip -h $ipaddr -u $userid -p $passwd -t $type"
+ echo "outputfile $outf"
+ cat
+ ) | /opt/IBMmpcli/bin/MPCLI.sh | grep -w $goodstg >/dev/null 2>&1
+ rc=$?
+ grep -w $failstg $outf >/dev/null
+ if [ $rc -eq 0 -a $? -eq 1 ]; then
+ return 0
+ else
+ ha_log.sh err "MPCLI.sh failed: `cat $outf`"
+ return 1
+ fi
+}
+ibmrsa_reboot() {
+ echo restart -now | mpcli
+}
+ibmrsa_poweron() {
+ echo poweron | mpcli
+}
+ibmrsa_poweroff() {
+ echo poweroff | mpcli
+}
+ibmrsa_status() {
+ echo | mpcli
+}
+
+hostname=`echo ${hostname} | tr ',' ' '`
+
+case $1 in
+gethosts)
+ echo $hostname
+ ;;
+on)
+ ibmrsa_poweron
+ ;;
+off)
+ ibmrsa_poweroff
+ ;;
+reset)
+ ibmrsa_reboot
+ ;;
+status)
+ ibmrsa_status
+ ;;
+getconfignames)
+ for i in hostname ipaddr userid passwd type; do
+ echo $i
+ done
+ ;;
+getinfo-devid)
+ echo "IBM MP STONITH device"
+ ;;
+getinfo-devname)
+ echo "IBM MP STONITH device"
+ ;;
+getinfo-devdescr)
+ echo "IBM MP host reboot/poweron/poweroff"
+ ;;
+getinfo-devurl)
+ echo "http://www.ibm.com"
+ ;;
+getinfo-xml)
+ cat <<EOF
+<parameters>
+
+<parameter name="hostname" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Hostname
+</shortdesc>
+<longdesc lang="en">
+The hostname of the host to be managed by this STONITH device
+</longdesc>
+</parameter>
+
+<parameter name="ipaddr" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+IP Address
+</shortdesc>
+<longdesc lang="en">
+The IP address of the STONITH device
+</longdesc>
+</parameter>
+
+<parameter name="userid" unique="0" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Login
+</shortdesc>
+<longdesc lang="en">
+The username used to login into the STONITH device
+</longdesc>
+</parameter>
+
+<parameter name="passwd" unique="0" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Password
+</shortdesc>
+<longdesc lang="en">
+The password used to login into the STONITH device
+</longdesc>
+</parameter>
+
+<parameter name="type" unique="0" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Management processor type
+</shortdesc>
+<longdesc lang="en">
+The type of the management processor. Possible values are
+"ibm" (default, typically used for RSA) and "ipmi"
+(for IPMI compliant processors such as BMC).
+</longdesc>
+</parameter>
+
+</parameters>
+EOF
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/ibmrsa-telnet b/lib/plugins/stonith/external/ibmrsa-telnet
new file mode 100644
index 0000000..4d75d9a
--- /dev/null
+++ b/lib/plugins/stonith/external/ibmrsa-telnet
@@ -0,0 +1,320 @@
+#!/usr/bin/python
+# vim: set filetype=python
+#######################################################################
+#
+# ibmrsa-telnet - External stonith plugin for HAv2 (http://linux-ha.org/wiki)
+# Connects to IBM RSA Board via telnet and switches power
+# of server appropriately.
+#
+# Author: Andreas Mock (andreas.mock@web.de)
+#
+# History:
+# 2007-10-19 Fixed bad commandline handling in case of stonithing
+# 2007-10-11 First release.
+#
+# Comment: Please send bug fixes and enhancements.
+# I hope the functionality of communicating via telnet is encapsulated
+# enough so that someone can use it for similar purposes.
+#
+# Description: IBM offers Remote Supervisor Adapters II for several
+# servers. These RSA boards can be accessed in different ways.
+# One of that is via telnet. Once logged in you can use 'help' to
+# show all available commands. With 'power' you can reset, power on and
+# off the controlled server. This command is used in combination
+# with python's standard library 'telnetlib' to do it automatically.
+#
+# cib-snippet: Please see README.ibmrsa-telnet for examples.
+#
+# Copyright (c) 2007 Andreas Mock (andreas.mock@web.de)
+# All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 or later of the GNU General Public
+# License as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+#######################################################################
+import sys
+import os
+import time
+import telnetlib
+import subprocess
+
+class TimeoutException(Exception):
+ def __init__(self, value=None):
+ Exception.__init__(self)
+ self.value = value
+
+ def __str__(self):
+ return repr(self.value)
+
+class RSABoard(telnetlib.Telnet):
+ def __init__(self, *args, **kwargs):
+ telnetlib.Telnet.__init__(self, *args, **kwargs)
+ self._timeout = 10
+ self._loggedin = 0
+ self._history = []
+ self._appl = os.path.basename(sys.argv[0])
+
+ def _get_timestamp(self):
+ ct = time.time()
+ msecs = (ct - long(ct)) * 1000
+ return "%s,%03d" % (time.strftime("%Y-%m-%d %H:%M:%S",
+ time.localtime(ct)), msecs)
+
+ def write(self, buffer, nolog = False):
+ self._history.append(self._get_timestamp() + ': WRITE: ' +
+ (nolog and '******' or repr(buffer)))
+ telnetlib.Telnet.write(self, buffer)
+
+ def expect(self, what, timeout=20):
+ line = telnetlib.Telnet.expect(self, what, timeout)
+ self._history.append(self._get_timestamp() + ': READ : ' + repr(line))
+ if not line:
+ raise TimeoutException("Timeout while waiting for '%s'." % (what, ))
+ return line
+
+ def login(self, user, passwd):
+ time.sleep(1)
+ line = self.expect(['\nlogin : ', '\nusername: '], self._timeout)
+ self.write(user)
+ self.write('\r')
+ line = self.expect(['\nPassword: ', '\npassword: '], self._timeout)
+ self.write(passwd, nolog = True)
+ self.write('\r')
+ line = self.expect(['\nsystem>', '> '], self._timeout)
+
+ def reset(self):
+ self.write('power cycle\r')
+ line = self.expect(['\nok'], self._timeout)
+ line = self.expect(['\nsystem>', '> '], self._timeout)
+
+ def on(self):
+ self.write('power on\r')
+ line = self.expect(['\nok'], self._timeout)
+ line = self.expect(['\nsystem>', '> '], self._timeout)
+
+ def off(self):
+ self.write('power off\r')
+ line = self.expect(['\nok'], self._timeout)
+ line = self.expect(['\nsystem>', '> '], self._timeout)
+
+ def exit(self):
+ self.write('exit\r')
+
+ def get_history(self):
+ return "\n".join(self._history)
+
+
+class RSAStonithPlugin:
+ def __init__(self):
+ # define the external stonith plugin api
+ self._required_cmds = \
+ 'reset gethosts status getconfignames getinfo-devid ' \
+ 'getinfo-devname getinfo-devdescr getinfo-devurl ' \
+ 'getinfo-xml'
+ self._optional_cmds = 'on off'
+ self._required_cmds_list = self._required_cmds.split()
+ self._optional_cmds_list = self._optional_cmds.split()
+
+ # who am i
+ self._appl = os.path.basename(sys.argv[0])
+
+ # telnet connection object
+ self._connection = None
+
+ # the list of configuration names
+ self._confignames = ['nodename', 'ip_address', 'username', 'password']
+
+ # catch the parameters provided by environment
+ self._parameters = {}
+ for name in self._confignames:
+ try:
+ self._parameters[name] = os.environ.get(name, '').split()[0]
+ except IndexError:
+ self._parameters[name] = ''
+
+ def _get_timestamp(self):
+ ct = time.time()
+ msecs = (ct - long(ct)) * 1000
+ return "%s,%03d" % (time.strftime("%Y-%m-%d %H:%M:%S",
+ time.localtime(ct)), msecs)
+
+ def _echo_debug(self, *args):
+ self.echo_log('debug', *args)
+
+ def echo(self, *args):
+ what = ''.join([str(x) for x in args])
+ sys.stdout.write(what)
+ sys.stdout.write('\n')
+ sys.stdout.flush()
+ self._echo_debug("STDOUT:", what)
+
+ def echo_log(self, level, *args):
+ subprocess.call(('ha_log.sh', level) + args)
+
+ def _get_connection(self):
+ if not self._connection:
+ c = RSABoard()
+ self._echo_debug("Connect to '%s'" %
+ (self._parameters['ip_address'],))
+ c.open(self._parameters['ip_address'])
+ self._echo_debug("Connection established")
+ c.login(self._parameters['username'],
+ self._parameters['password'])
+ self._connection = c
+
+ def _end_connection(self):
+ if self._connection:
+ self._connection.exit()
+ self._connection.close()
+
+ def reset(self):
+ self._get_connection()
+ self._connection.reset()
+ self._end_connection()
+ self._echo_debug(self._connection.get_history())
+ self.echo_log("info", "Reset of node '%s' done" %
+ (self._parameters['nodename'],))
+ return(0)
+
+ def on(self):
+ self._get_connection()
+ self._connection.on()
+ self._end_connection()
+ self._echo_debug(self._connection.get_history())
+ self.echo_log("info", "Switched node '%s' ON" %
+ (self._parameters['nodename'],))
+ return(0)
+
+ def off(self):
+ self._get_connection()
+ self._connection.off()
+ self._end_connection()
+ self._echo_debug(self._connection.get_history())
+ self.echo_log("info", "Switched node '%s' OFF" %
+ (self._parameters['nodename'],))
+ return(0)
+
+ def gethosts(self):
+ self.echo(self._parameters['nodename'])
+ return(0)
+
+ def status(self):
+ self._get_connection()
+ self._end_connection()
+ self._echo_debug(self._connection.get_history())
+ return(0)
+
+ def getconfignames(self):
+ for name in ['nodename', 'ip_address', 'username', 'password']:
+ self.echo(name)
+ return(0)
+
+ def getinfo_devid(self):
+ self.echo("External Stonith Plugin for IBM RSA Boards")
+ return(0)
+
+ def getinfo_devname(self):
+ self.echo("External Stonith Plugin for IBM RSA Boards connecting "
+ "via Telnet")
+ return(0)
+
+ def getinfo_devdescr(self):
+ self.echo("External stonith plugin for HAv2 which connects to "
+ "a RSA board on IBM servers via telnet. Commands to "
+ "turn on/off power and to reset server are sent "
+ "appropriately. "
+ "(c) 2007 by Andreas Mock (andreas.mock@web.de)")
+ return(0)
+
+ def getinfo_devurl(self):
+ self.echo("http://www.ibm.com/Search/?q=remote+supervisor+adapter")
+
+ def getinfo_xml(self):
+ info = """<parameters>
+ <parameter name="nodename" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">nodename to shoot</shortdesc>
+ <longdesc lang="en">
+ Name of the node which has to be stonithed in case.
+ </longdesc>
+ </parameter>
+ <parameter name="ip_address" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">hostname or ip address of RSA</shortdesc>
+ <longdesc lang="en">
+ Hostname or ip address of RSA board used to reset node.
+ </longdesc>
+ </parameter>
+ <parameter name="username" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">username to login on RSA board</shortdesc>
+ <longdesc lang="en">
+ Username to login on RSA board.
+ </longdesc>
+ </parameter>
+ <parameter name="password" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">password to login on RSA board</shortdesc>
+ <longdesc lang="en">
+ Password to login on RSA board.
+ </longdesc>
+ </parameter>
+ </parameters>
+ """
+ self.echo(info)
+ return(0)
+
+ def not_implemented(self, cmd):
+ self.echo_log("err", "Command '%s' not implemented." % (cmd,))
+ return(1)
+
+ def usage(self):
+ usage = "Call me with one of the allowed commands: %s, %s" % (
+ ', '.join(self._required_cmds_list),
+ ', '.join(self._optional_cmds_list))
+ return usage
+
+ def process(self, argv):
+ self._echo_debug("========== Start =============")
+ if len(argv) < 1:
+ self.echo_log("err", 'At least one commandline argument required.')
+ return(1)
+ cmd = argv[0]
+ self._echo_debug("cmd:", cmd)
+ if cmd not in self._required_cmds_list and \
+ cmd not in self._optional_cmds_list:
+ self.echo_log("err", "Command '%s' not supported." % (cmd,))
+ return(1)
+ try:
+ cmd = cmd.lower().replace('-', '_')
+ func = getattr(self, cmd, self.not_implemented)
+ rc = func()
+ return(rc)
+ except Exception, args:
+ self.echo_log("err", 'Exception raised:', str(args))
+ if self._connection:
+ self.echo_log("err", self._connection.get_history())
+ self._connection.close()
+ return(1)
+
+
+if __name__ == '__main__':
+ stonith = RSAStonithPlugin()
+ rc = stonith.process(sys.argv[1:])
+ sys.exit(rc)
diff --git a/lib/plugins/stonith/external/ipmi b/lib/plugins/stonith/external/ipmi
new file mode 100644
index 0000000..abadd5a
--- /dev/null
+++ b/lib/plugins/stonith/external/ipmi
@@ -0,0 +1,276 @@
+#!/bin/sh
+#
+# External STONITH module using IPMI.
+# This modules uses uses the ipmitool program available from
+# http://ipmitool.sf.net/ for actual communication with the
+# managed device.
+#
+# Copyright (c) 2007 Martin Bene <martin.bene@icomedias.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+
+# Initialization -- fix locale settings so we can parse output from
+# binaries if we need it
+LANG=C
+LC_ALL=C
+
+RESET="power reset"
+POWEROFF="power off"
+POWERON="power on"
+STATUS="power status"
+IPMITOOL=${ipmitool:-"`which ipmitool 2>/dev/null`"}
+
+have_ipmi() {
+ test -x "${IPMITOOL}"
+}
+
+# Wrapper function for ipmitool that sets the correct host IP address,
+# username, and password, and invokes ipmitool with any arguments
+# passed in
+run_ipmitool() {
+ local ipmitool_opts privlvl=""
+ have_ipmi || {
+ ha_log.sh err "ipmitool not installed"
+ return 1
+ }
+ if [ -z "${ipaddr}" -o -z "${userid}" -o -z "${passwd}" ]; then
+ ha_log.sh err "ipaddr, userid or passwd missing; check configuration"
+ return 1
+ fi
+
+ if [ -z "${interface}" ]; then
+ # default to "lan" interface
+ interface="lan"
+ fi
+ if [ -n "${priv}" ]; then
+ # default to "lan" interface
+ privlvl="-L $priv"
+ fi
+
+ ipmitool_opts="-I ${interface} -H ${ipaddr} $privlvl"
+
+ case "${passwd_method}" in
+ param|'')
+ passwd_method=param
+ M="-P"
+ ;;
+ env)
+ M="-E"
+ ;;
+ file)
+ M="-f"
+ ;;
+ *)
+ ha_log.sh err "invalid passwd_method: \"${passwd_method}\""
+ return 1
+ esac
+
+ action="$*"
+
+ if [ $passwd_method = env ]
+ then
+ IPMI_PASSWORD="${passwd}" ${IPMITOOL} $ipmitool_opts -U "${userid}" -E ${action}
+ else
+ ${IPMITOOL} $ipmitool_opts -U "${userid}" $M "${passwd}" ${action}
+ fi 2>&1
+}
+
+# Yet another convenience wrapper that invokes run_ipmitool, captures
+# its output, logs the output, returns either 0 (on success) or 1 (on
+# any error)
+do_ipmi() {
+ if outp=`run_ipmitool $*`; then
+ ha_log.sh debug "ipmitool output: `echo $outp`"
+ return 0
+ else
+ ha_log.sh err "error executing ipmitool: `echo $outp`"
+ return 1
+ fi
+}
+
+# Check if the managed node is powered on. To do so, issue the "power
+# status" command. Should return either "Chassis Power is on" or
+# "Chassis Power is off".
+ipmi_is_power_on() {
+ local outp
+ outp=`run_ipmitool ${STATUS}`
+ case "${outp}" in
+ *on)
+ return 0
+ ;;
+ *off)
+ return 1
+ ;;
+ esac
+}
+
+
+case ${1} in
+gethosts)
+ echo $hostname
+ exit 0
+ ;;
+on)
+ do_ipmi "${POWERON}"
+ exit
+ ;;
+off)
+ do_ipmi "${POWEROFF}"
+ exit
+ ;;
+reset)
+ if ipmi_is_power_on; then
+ do_ipmi "${RESET}"
+ else
+ do_ipmi "${POWERON}"
+ fi
+ exit
+ ;;
+status)
+ # "status" reflects the status of the stonith _device_, not
+ # the managed node. Hence, only check if we can contact the
+ # IPMI device with "power status" command, don't pay attention
+ # to whether the node is in fact powered on or off.
+ do_ipmi "${STATUS}"
+ exit $?
+ ;;
+getconfignames)
+ for i in hostname ipaddr userid passwd interface; do
+ echo $i
+ done
+ exit 0
+ ;;
+getinfo-devid)
+ echo "IPMI STONITH device"
+ exit 0
+ ;;
+getinfo-devname)
+ echo "IPMI STONITH external device"
+ exit 0
+ ;;
+getinfo-devdescr)
+ echo "ipmitool based power management. Apparently, the power off"
+ echo "method of ipmitool is intercepted by ACPI which then makes"
+ echo "a regular shutdown. If case of a split brain on a two-node"
+ echo "it may happen that no node survives. For two-node clusters"
+ echo "use only the reset method."
+ exit 0
+ ;;
+getinfo-devurl)
+ echo "http://ipmitool.sf.net/"
+ exit 0
+ ;;
+getinfo-xml)
+ cat << IPMIXML
+<parameters>
+<parameter name="hostname" unique="1">
+<content type="string" />
+<shortdesc lang="en">
+Hostname
+</shortdesc>
+<longdesc lang="en">
+The name of the host to be managed by this STONITH device.
+</longdesc>
+</parameter>
+
+<parameter name="ipaddr" unique="1">
+<content type="string" />
+<shortdesc lang="en">
+IP Address
+</shortdesc>
+<longdesc lang="en">
+The IP address of the STONITH device.
+</longdesc>
+</parameter>
+
+<parameter name="userid" unique="0">
+<content type="string" />
+<shortdesc lang="en">
+Login
+</shortdesc>
+<longdesc lang="en">
+The username used for logging in to the STONITH device.
+</longdesc>
+</parameter>
+
+<parameter name="passwd" unique="0">
+<content type="string" />
+<shortdesc lang="en">
+Password
+</shortdesc>
+<longdesc lang="en">
+The password used for logging in to the STONITH device.
+</longdesc>
+</parameter>
+
+<parameter name="passwd_method" unique="0">
+<content type="string" default="param"/>
+<shortdesc lang="en">
+Method for passing passwd parameter
+</shortdesc>
+<longdesc lang="en">
+Method for passing the passwd parameter to ipmitool
+ param: pass as parameter (-P)
+ env: pass via environment (-E)
+ file: value of "passwd" is actually a file name, pass with (-f)
+</longdesc>
+</parameter>
+
+<parameter name="interface" unique="0">
+<content type="string" default="lan"/>
+<shortdesc lang="en">
+IPMI interface
+</shortdesc>
+<longdesc lang="en">
+IPMI interface to use, such as "lan" or "lanplus".
+</longdesc>
+</parameter>
+
+<parameter name="priv" unique="0">
+<content type="string" default=""/>
+<shortdesc lang="en">
+The privilege level of the user.
+</shortdesc>
+<longdesc lang="en">
+The privilege level of the user, for instance OPERATOR. If
+unspecified the privilege level is ADMINISTRATOR. See
+ipmitool(1) -L option for more information.
+</longdesc>
+</parameter>
+
+<parameter name="ipmitool" unique="0">
+<content type="string" default=""/>
+<shortdesc lang="en">
+IPMI command(ipmitool)
+</shortdesc>
+<longdesc lang="en">
+Specify the full path to IPMI command.
+</longdesc>
+</parameter>
+
+</parameters>
+IPMIXML
+ exit 0
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/ippower9258.in b/lib/plugins/stonith/external/ippower9258.in
new file mode 100755
index 0000000..6ae7e02
--- /dev/null
+++ b/lib/plugins/stonith/external/ippower9258.in
@@ -0,0 +1,316 @@
+#!/bin/sh
+#
+# External STONITH module using IP Power 9258 or compatible devices.
+#
+# Copyright (c) 2010 Helmut Weymann (Helmut (at) h-weymann (dot) de)
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+
+#
+# Basic commands & parameters independent from individual device
+
+DEVICE="IP Power 9258"
+IPPowerOn="1"
+IPPowerOff="0"
+IPGetPower="Set.cmd?CMD=GetPower"
+IPSetPower="Set.cmd?CMD=SetPower"
+IPPort_name="P"
+IPPort0=60
+HTTP_COMMAND="wget -q -O - --"
+LOG_ERROR="ha_log.sh err"
+LOG_WARNING="ha_log.sh warn"
+LOG_INFO="ha_log.sh info"
+LOG_DEBUG="ha_log.sh debug"
+MY_COOKIES="cookies.txt"
+MY_TEMPFILE="temp.htm"
+PORT_STATUS="iocontrol.htm"
+UNDEFINED_HOSTNAME="*not-defined*"
+
+#
+# check MY_ROOT_PATH for IP Power 9258 and create it if necessary
+MY_ROOT_PATH="@GLUE_STATE_DIR@/heartbeat/rsctmp/ippower9258"
+
+#
+# script functions
+#
+
+get_challenge() {
+ #
+ # device sends a challenge for md5 encryption of username, password and challenge
+ send_web_command - "http://$deviceip/" | grep Challenge | grep input | cut -d '"' -f 6
+}
+
+get_cookie_from_device(){
+ # the form on the login page has these fields:
+ # Username, Password, Challenge, Response, ScreenWidth
+ #
+ challenge=`get_challenge`
+ response=`echo -n "$username$password$challenge" | md5sum | cut -b -32`
+ postdata="Username=$username&Password=&Challenge=&Response=$response&ScreenWidth=1024"
+ send_web_command " $MY_PATH/$MY_TEMPFILE --post-data=$postdata" "http://$deviceip/tgi/login.tgi"
+ if grep -qs "Invalid User name or Password" $MY_PATH/$MY_TEMPFILE
+ then
+ $LOG_ERROR "Login to device $deviceip failed."
+ $LOG_ERROR "Received Challenge = <<<$challenge>>>."
+ $LOG_ERROR "Sent postdata = <<<$postdata>>>."
+ exit 1
+ fi
+}
+
+get_data_from_device() {
+ # If successful all device info is available in MY_PATH
+ rm -f "$MY_PATH/$PORT_STATUS"
+ send_web_command "$MY_PATH/$PORT_STATUS" "http://$deviceip/$PORT_STATUS"
+ if grep -qs "Cookie Time Out" $MY_PATH/$PORT_STATUS
+ then
+ $LOG_ERROR "received no port data from $deviceip (Cookie Time Out)"
+ exit 1
+ fi
+}
+
+send_http_request() {
+ # ececution of http commands supported by the device
+ $HTTP_COMMAND "http://$username:$password@$deviceip/$1"
+}
+
+send_web_command(){
+ # ececution of web commands through the web-interface
+ WEB_COMMAND="wget -q --keep-session-cookies"
+ WEB_COMMAND="$WEB_COMMAND --load-cookies $MY_PATH/$MY_COOKIES"
+ WEB_COMMAND="$WEB_COMMAND --save-cookies $MY_PATH/$MY_COOKIES"
+ $WEB_COMMAND -O $1 -- $2
+}
+
+name2port() {
+ local name=$1
+ local i=$IPPort0
+ for h in $device_hostlist ; do
+ if [ $h = $name ]; then
+ echo $IPPort_name$i
+ return
+ fi
+ i=`expr $i + 1`
+ done
+ echo "invalid"
+}
+
+set_port() {
+ #
+ # port status is always set. Even if requested status is current status.
+ # host status is not considered.
+ local host=$1
+ local requested_status=$2 # 0 or 1
+ local port=`name2port $host`
+ if [ "$port" = "invalid" ]
+ then
+ $LOG_ERROR "Host $host is not in hostlist ($hostlist) for $deviceip."
+ exit 1
+ fi
+ ret=`send_http_request "$IPSetPower+$port=$requested_status" | cut -b 11`
+ if [ "$ret" != "$requested_status" ]
+ then
+ $LOG_ERROR "$DEVICE at $deviceip responds with wrong status $ret for host $host at port $port."
+ exit 1
+ fi
+ return 0
+}
+
+build_device_hostlist() {
+ #
+ # hostnames are available from http://$deviceip/iocontrol.htm"
+ # check for number of ports
+ #
+ device_hostlist=$(
+ w3m -dump $MY_PATH/$PORT_STATUS | grep 'Power[1-8]' |
+ sed 's/[^[]*\[//;s/\].*//;s/ *//' |
+ while read h; do
+ [ -z "$h" ] &&
+ echo $UNDEFINED_HOSTNAME ||
+ echo $h
+ done
+ )
+ if [ x = x"$device_hostlist" ]; then
+ $LOG_ERROR "cannot get hostlist for $deviceip"
+ exit 1
+ fi
+ $LOG_DEBUG "Got new hostlist ($device_hostlist) from $deviceip"
+}
+
+filter_device_hostlist() {
+ # check the given hostlist against the device hostlist
+ local host
+ for host in $device_hostlist; do
+ [ "$host" != "$UNDEFINED_HOSTNAME" ] &&
+ echo $host
+ done
+}
+
+check_hostlist() {
+ # check the given hostlist against the device hostlist
+ local cnt=`echo "$hostlist" | wc -w`
+ local cnt2=0
+ local host
+ for host in $hostlist; do
+ if [ `name2port $host` != "invalid" ]; then
+ cnt2=$((cnt2+1))
+ else
+ $LOG_ERROR "host $host not defined at $deviceip"
+ fi
+ done
+ [ $cnt -ne $cnt2 ] &&
+ exit 1
+}
+
+get_http_status() {
+ pattern="P60=[01],P61=[01],P62=[01],P63=[01],P64=[01],P65=[01],P66=[01],P67=[01]"
+ ret=`send_http_request "$IPGetPower" | grep $pattern`
+ if [ "X$ret" = "X" ]
+ then
+ $LOG_ERROR "$DEVICE at $deviceip returns invalid or no string."
+ exit 1
+ fi
+}
+
+hostlist=`echo $hostlist | tr ',' ' '`
+case $1 in
+gethosts|on|off|reset|status)
+ # need environment from stonithd
+ # and device information from individual device
+ #
+ # default device username is admin
+ # IP Power 9258 does not allow user management.
+ #
+ if [ "X$username" = "X" ]
+ then
+ username="admin"
+ fi
+
+ mkdir -p $MY_ROOT_PATH
+ tmp_path="$deviceip" # ensure a simple unique pathname
+ MY_PATH="$MY_ROOT_PATH/$tmp_path"
+ mkdir -p $MY_PATH
+ get_cookie_from_device
+ get_data_from_device
+ build_device_hostlist
+ if [ "X$hostlist" = "X" ]; then
+ hostlist="`filter_device_hostlist`"
+ else
+ check_hostlist
+ fi
+ ;;
+*)
+ # the client is asking for meta-data
+ ;;
+esac
+
+target=`echo $2 | sed 's/[.].*//'`
+# the necessary actions for stonithd
+case $1 in
+gethosts)
+ echo $hostlist
+ ;;
+on)
+ set_port $target $IPPowerOn
+ ;;
+off)
+ set_port $target $IPPowerOff
+ ;;
+reset)
+ set_port $target $IPPowerOff
+ sleep 5
+ set_port $target $IPPowerOn
+ ;;
+status)
+ # werify http command interface
+ get_http_status
+ ;;
+getconfignames)
+ # return all the config names
+ for ipparam in deviceip username password hostlist
+ do
+ echo $ipparam
+ done
+ ;;
+getinfo-devid)
+ echo "IP Power 9258"
+ ;;
+getinfo-devname)
+ echo "IP Power 9258 power switch"
+ ;;
+getinfo-devdescr)
+ echo "Power switch IP Power 9258 with 4 or 8 power outlets."
+ echo "WARNING: It is different from IP Power 9258 HP"
+ ;;
+getinfo-devurl)
+ echo "http://www.aviosys.com/manual.htm"
+ ;;
+getinfo-xml)
+ cat << IPPOWERXML
+<parameters>
+<parameter name="deviceip" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+IP address or hostname of the device.
+</shortdesc>
+<longdesc lang="en">
+The IP Address or the hostname of the device.
+</longdesc>
+</parameter>
+
+<parameter name="password" unique="0" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Password
+</shortdesc>
+<longdesc lang="en">
+The password to log in with.
+</longdesc>
+</parameter>
+
+<parameter name="hostlist" unique="0" required="0">
+<content type="string" />
+<shortdesc lang="en">
+Hostlist
+</shortdesc>
+<longdesc lang="en">
+The list of hosts that the device controls.
+If you leave this list empty, we will retrieve the hostnames from the device.
+</longdesc>
+</parameter>
+
+<parameter name="username" unique="0" required="0">
+<content type="string" default="admin"/>
+<shortdesc lang="en">
+Account Name
+</shortdesc>
+<longdesc lang="en">
+The user to log in with.
+</longdesc>
+</parameter>
+
+</parameters>
+IPPOWERXML
+ ;;
+*)
+ $LOG_ERROR "Unexpected command $1 for $DEVICE at $deviceip."
+ exit 1;
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/kdumpcheck.in b/lib/plugins/stonith/external/kdumpcheck.in
new file mode 100644
index 0000000..7f3f752
--- /dev/null
+++ b/lib/plugins/stonith/external/kdumpcheck.in
@@ -0,0 +1,274 @@
+#!/bin/sh
+#
+# External STONITH module to check kdump.
+#
+# Copyright (c) 2008 NIPPON TELEGRAPH AND TELEPHONE CORPORATION
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+
+SSH_COMMAND="@SSH@ -q -x -o PasswordAuthentication=no -o StrictHostKeyChecking=no -n"
+#Set default user name.
+USERNAME="kdumpchecker"
+#Initialize identity file-path options for ssh command
+IDENTITY_OPTS=""
+
+#Rewrite the hostlist to accept "," as a delimeter for hostnames too.
+hostlist=`echo ${hostlist} | tr ',' ' '`
+
+##
+# Check the parameter hostlist is set or not.
+# If not, exit with 6 (ERR_CONFIGURED).
+##
+check_hostlist() {
+ if [ -z "${hostlist}" ]; then
+ ha_log.sh err "hostlist is empty"
+ exit 6 #ERR_CONFIGURED
+ fi
+}
+
+##
+# Set kdump check user name to USERNAME.
+# always return 0.
+##
+get_username() {
+ kdump_conf="/etc/kdump.conf"
+
+ if [ ! -f "${kdump_conf}" ]; then
+ ha_log.sh debug "${kdump_conf} doesn't exist"
+ return 0
+ fi
+
+ tmp=""
+ while read config_opt config_val; do
+ if [ "${config_opt}" = "kdump_check_user" ]; then
+ tmp="${config_val}"
+ fi
+ done < "${kdump_conf}"
+ if [ -n "${tmp}" ]; then
+ USERNAME="${tmp}"
+ fi
+
+ ha_log.sh debug "kdump check user name is ${USERNAME}."
+}
+
+##
+# Check the specified or default identity file exists or not.
+# If not, exit with 6 (ERR_CONFIGURED).
+##
+check_identity_file() {
+ IDENTITY_OPTS=""
+ if [ -n "${identity_file}" ]; then
+ if [ ! -f "${identity_file}" ]; then
+ ha_log.sh err "${identity_file} doesn't exist."
+ exit 6 #ERR_CONFIGURED
+ fi
+ IDENTITY_OPTS="-i ${identity_file}"
+ else
+ flg_file_exists=0
+ homedir=`eval echo "~${USERNAME}"`
+ for filename in "${homedir}/.ssh/id_rsa" \
+ "${homedir}/.ssh/id_dsa" \
+ "${homedir}/.ssh/identity"
+ do
+ if [ -f "${filename}" ]; then
+ flg_file_exists=1
+ IDENTITY_OPTS="${IDENTITY_OPTS} -i ${filename}"
+ fi
+ done
+ if [ ${flg_file_exists} -eq 0 ]; then
+ ha_log.sh err "${USERNAME}'s identity file for ssh command" \
+ " doesn't exist."
+ exit 6 #ERR_CONFIGURED
+ fi
+ fi
+}
+
+##
+# Check the user to check doing kdump exists or not.
+# If not, exit with 6 (ERR_CONFIGURED).
+##
+check_user_existence() {
+
+ # Get kdump check user name and check whether he exists or not.
+ grep -q "^${USERNAME}:" /etc/passwd > /dev/null 2>&1
+ ret=$?
+ if [ ${ret} != 0 ]; then
+ ha_log.sh err "user ${USERNAME} doesn't exist." \
+ "please confirm \"kdump_check_user\" setting in /etc/kdump.conf." \
+ "(default user name is \"kdumpchecker\")"
+ exit 6 #ERR_CONFIGURED
+ fi
+}
+
+##
+# Check the target node is kdumping or not.
+# arg1 : target node name.
+# ret : 0 -> the target is kdumping.
+# : 1 -> the target is _not_ kdumping.
+# : else -> failed to check.
+##
+check_kdump() {
+ target_node="$1"
+
+ # Get kdump check user name.
+ get_username
+ check_user_existence
+ exec_cmd="${SSH_COMMAND} -l ${USERNAME}"
+
+ # Specify kdump check user's identity file for ssh command.
+ check_identity_file
+ exec_cmd="${exec_cmd} ${IDENTITY_OPTS}"
+
+ # Now, check the target!
+ # In advance, Write the following setting at the head of
+ # kdump_check_user's public key in authorized_keys file on target node.
+ # command="test -s /proc/vmcore", \
+ # no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty
+ ha_log.sh debug "execute the command [${exec_cmd} ${target_node}]."
+ ${exec_cmd} ${target_node} > /dev/null 2>&1
+ ret=$?
+ ha_log.sh debug "the command's result is ${ret}."
+
+ #ret -> 0 : vmcore file's size is not zero. the node is kdumping.
+ #ret -> 1 : the node is _not_ kdumping (vmcore didn't exist or
+ # its size is zero). It still needs to be STONITH'ed.
+ #ret -> 255 : ssh command is failed.
+ # else : Maybe command strings in authorized_keys is wrong...
+ return ${ret}
+}
+
+###
+#
+# Main function.
+#
+###
+case $1 in
+gethosts)
+ check_hostlist
+ for hostname in ${hostlist} ; do
+ echo "${hostname}"
+ done
+ exit 0
+ ;;
+on)
+ # This plugin does only check whether a target node is kdumping or not.
+ exit 1
+ ;;
+reset|off)
+ check_hostlist
+ ret=1
+ h_target=`echo $2 | tr A-Z a-z`
+ for hostname in ${hostlist}
+ do
+ hostname=`echo $hostname | tr A-Z a-z`
+ if [ "${hostname}" != "$h_target" ]; then
+ continue
+ fi
+ while [ 1 ]
+ do
+ check_kdump "$2"
+ ret=$?
+ if [ ${ret} -ne 255 ]; then
+ exit ${ret}
+ fi
+ #255 means ssh command itself is failed.
+ #For example, connection failure as if network doesn't start yet
+ #in 2nd kernel on the target node.
+ #So, retry to check after a little while.
+ sleep 1
+ done
+ done
+ exit ${ret}
+ ;;
+status)
+ check_hostlist
+ for hostname in ${hostlist}
+ do
+ if ping -w1 -c1 "${hostname}" 2>&1 | grep "unknown host"
+ then
+ exit 1
+ fi
+ done
+ get_username
+ check_user_existence
+ check_identity_file
+ exit 0
+ ;;
+getconfignames)
+ echo "hostlist identity_file"
+ exit 0
+ ;;
+getinfo-devid)
+ echo "kdump check STONITH device"
+ exit 0
+ ;;
+getinfo-devname)
+ echo "kdump check STONITH external device"
+ exit 0
+ ;;
+getinfo-devdescr)
+ echo "ssh-based kdump checker"
+ echo "To check whether a target node is dumping or not."
+ exit 0
+ ;;
+getinfo-devurl)
+ echo "kdump -> http://lse.sourceforge.net/kdump/"
+ echo "ssh -> http://openssh.org"
+ exit 0
+ ;;
+getinfo-xml)
+ cat << SSHXML
+<parameters>
+<parameter name="hostlist" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Hostlist
+</shortdesc>
+<longdesc lang="en">
+The list of hosts that the STONITH device controls
+</longdesc>
+</parameter>
+
+<parameter name="identity_file" unique="1" required="0">
+<content type="string" />
+<shortdesc lang="en">
+Identity file's full path for kdump check user
+</shortdesc>
+<longdesc lang="en">
+The full path of kdump check user's identity file for ssh command.
+The identity in the specified file have to be restricted to execute
+only the following command.
+"test -s /proc/vmcore"
+Default: kdump check user's default identity file path.
+NOTE: You can specify kdump check user name in /etc/kdump.conf.
+ The parameter name is "kdump_check_user".
+ Default user is "kdumpchecker".
+</longdesc>
+</parameter>
+
+</parameters>
+SSHXML
+ exit 0
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/libvirt b/lib/plugins/stonith/external/libvirt
new file mode 100644
index 0000000..494b048
--- /dev/null
+++ b/lib/plugins/stonith/external/libvirt
@@ -0,0 +1,298 @@
+#!/bin/sh
+#
+# External STONITH module for a libvirt managed hypervisor (kvm/Xen).
+# Uses libvirt as a STONITH device to control guest.
+#
+# Copyright (c) 2010 Holger Teutsch <holger.teutsch@web.de>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+
+# start a domain
+libvirt_start() {
+ out=$($VIRSH -c $hypervisor_uri start $domain_id 2>&1)
+ if [ $? -eq 0 ]
+ then
+ ha_log.sh notice "Domain $domain_id was started"
+ return 0
+ fi
+
+ $VIRSH -c $hypervisor_uri dominfo $domain_id 2>&1 |
+ egrep -q '^State:.*(running|idle)|already active'
+ if [ $? -eq 0 ]
+ then
+ ha_log.sh notice "Domain $domain_id is already active"
+ return 0
+ fi
+
+ ha_log.sh err "Failed to start domain $domain_id"
+ ha_log.sh err "$out"
+ return 1
+}
+# reboot a domain
+# return
+# 0: success
+# 1: error
+libvirt_reboot() {
+ local rc out
+ out=$($VIRSH -c $hypervisor_uri reboot $domain_id 2>&1)
+ rc=$?
+ if [ $rc -eq 0 ]
+ then
+ ha_log.sh notice "Domain $domain_id was rebooted"
+ return 0
+ fi
+ ha_log.sh err "Failed to reboot domain $domain_id (exit code: $rc)"
+ ha_log.sh err "$out"
+ return 1
+}
+
+# stop a domain
+# return
+# 0: success
+# 1: error
+# 2: was already stopped
+libvirt_stop() {
+ out=$($VIRSH -c $hypervisor_uri destroy $domain_id 2>&1)
+ if [ $? -eq 0 ]
+ then
+ ha_log.sh notice "Domain $domain_id was stopped"
+ return 0
+ fi
+
+ $VIRSH -c $hypervisor_uri dominfo $domain_id 2>&1 |
+ egrep -q '^State:.*shut off|not found|not running'
+ if [ $? -eq 0 ]
+ then
+ ha_log.sh notice "Domain $domain_id is already stopped"
+ return 2
+ fi
+
+ ha_log.sh err "Failed to stop domain $domain_id"
+ ha_log.sh err "$out"
+ return 1
+}
+
+# get status of stonith device (*NOT* of the domain).
+# If we can retrieve some info from the hypervisor
+# the stonith device is OK.
+libvirt_status() {
+ out=$($VIRSH -c $hypervisor_uri version 2>&1)
+ if [ $? -eq 0 ]
+ then
+ return 0
+ fi
+
+ ha_log.sh err "Failed to get status for $hypervisor_uri"
+ ha_log.sh err "$out"
+ return 1
+}
+
+# check config and set variables
+# does not return on error
+libvirt_check_config() {
+ VIRSH=`which virsh 2>/dev/null`
+
+ if [ ! -x "$VIRSH" ]
+ then
+ ha_log.sh err "virsh not installed"
+ exit 1
+ fi
+
+ if [ -z "$hostlist" -o -z "$hypervisor_uri" ]
+ then
+ ha_log.sh err "hostlist or hypervisor_uri missing; check configuration"
+ exit 1
+ fi
+
+ case "$reset_method" in
+ power_cycle|reboot) : ;;
+ *)
+ ha_log.sh err "unrecognized reset_method: $reset_method"
+ exit 1
+ ;;
+ esac
+}
+
+# set variable domain_id for the host specified as arg
+libvirt_set_domain_id ()
+{
+ for h in $hostlist
+ do
+ case $h in
+ $1:*)
+ domain_id=`expr $h : '.*:\(.*\)'`
+ return
+ ;;
+
+ $1)
+ domain_id=$1
+ return
+ esac
+ done
+
+ ha_log.sh err "Should never happen: Called for host $1 but $1 is not in $hostlist."
+ exit 1
+}
+
+libvirt_info() {
+cat << LVIRTXML
+<parameters>
+<parameter name="hostlist" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+List of hostname[:domain_id]..
+</shortdesc>
+<longdesc lang="en">
+List of controlled hosts: hostname[:domain_id]..
+The optional domain_id defaults to the hostname.
+</longdesc>
+</parameter>
+
+<parameter name="hypervisor_uri" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Hypervisor URI
+</shortdesc>
+<longdesc lang="en">
+URI for connection to the hypervisor.
+driver[+transport]://[username@][hostlist][:port]/[path][?extraparameters]
+e.g.
+qemu+ssh://my_kvm_server.mydomain.my/system (uses ssh for root)
+xen://my_kvm_server.mydomain.my/ (uses TLS for client)
+
+virsh must be installed (e.g. libvir-client package) and access control must
+be configured for your selected URI.
+</longdesc>
+</parameter>
+
+<parameter name="reset_method" required="0">
+<content type="string" default="power_cycle"/>
+<shortdesc lang="en">
+How to reset a guest.
+</shortdesc>
+<longdesc lang="en">
+A guest reset may be done by a sequence of off and on commands
+(power_cycle) or by the reboot command. Which method works
+depend on the hypervisor and guest configuration management.
+</longdesc>
+</parameter>
+</parameters>
+LVIRTXML
+exit 0
+}
+
+#############
+# Main code #
+#############
+
+# don't fool yourself when testing with stonith(8)
+# and transport ssh
+unset SSH_AUTH_SOCK
+
+# support , as a separator as well
+hostlist=`echo $hostlist| sed -e 's/,/ /g'`
+
+reset_method=${reset_method:-"power_cycle"}
+
+case $1 in
+ gethosts)
+ hostnames=`echo $hostlist|sed -e 's/:[^ ]*//g'`
+ for h in $hostnames
+ do
+ echo $h
+ done
+ exit 0
+ ;;
+
+ on)
+ libvirt_check_config
+ libvirt_set_domain_id $2
+
+ libvirt_start
+ exit $?
+ ;;
+
+ off)
+ libvirt_check_config
+ libvirt_set_domain_id $2
+
+ libvirt_stop
+ [ $? = 1 ] && exit 1
+ exit 0
+ ;;
+
+ reset)
+ libvirt_check_config
+ libvirt_set_domain_id $2
+
+ if [ "$reset_method" = "power_cycle" ]; then
+ libvirt_stop
+ [ $? = 1 ] && exit 1
+ sleep 2
+ libvirt_start
+ else
+ libvirt_reboot
+ fi
+ exit $?
+ ;;
+
+ status)
+ libvirt_check_config
+ libvirt_status
+ exit $?
+ ;;
+
+ getconfignames)
+ echo "hostlist hypervisor_uri reboot_method"
+ exit 0
+ ;;
+
+ getinfo-devid)
+ echo "libvirt STONITH device"
+ exit 0
+ ;;
+
+ getinfo-devname)
+ echo "libvirt STONITH external device"
+ exit 0
+ ;;
+
+ getinfo-devdescr)
+ echo "libvirt-based host reset for Xen/KVM guest domain through hypervisor"
+ exit 0
+ ;;
+
+ getinfo-devurl)
+ echo "http://libvirt.org/uri.html http://linux-ha.org/wiki"
+ exit 0
+ ;;
+
+ getinfo-xml)
+ libvirt_info
+ echo 0;
+ ;;
+
+ *)
+ exit 1
+ ;;
+esac
+
+# vi:et:ts=4:sw=4
diff --git a/lib/plugins/stonith/external/nut b/lib/plugins/stonith/external/nut
new file mode 100644
index 0000000..9e51bb8
--- /dev/null
+++ b/lib/plugins/stonith/external/nut
@@ -0,0 +1,302 @@
+#!/bin/sh
+
+# External STONITH module that uses the NUT daemon to control an external UPS.
+# See the comments below, and the various NUT man pages, for how this
+# script works. It should work unchanged with most modern "smart" APC UPSes in
+# a Redhat/Fedora/RHEL-style distribution with the nut package installed.
+
+# Author: William Seligman <seligman@nevis.columbia.edu>
+# License: GPLv2
+
+# As you're designing your UPS and STONITH set-up, it may help to consider that
+# there can be potentially three computers involved:
+# 1) the machine running this STONITH module;
+# 2) the machine being controlled by this STONITH module ($hostname);
+# 3) the machine that can send commands to the UPS.
+
+# On my cluster, all the UPSes have SNMP smartcards, so every host can communicate
+# with every UPS; in other words, machines (1) and (3) are the same. If your UPSes
+# are controlled via serial or USB connections, then you might have a
+# situation in which $hostname is plugged into a UPS, which has a serial connection
+# to some master "power-control" computer, and can potentially be STONITHed
+# by any other machine in your cluster.
+
+# In general, you'll probably need the nut daemon running on both the hosts (1) and
+# (3) in the above list. The NUT daemon will also have to run on (2) if you want the
+# reset command to gracefully reboot $hostname.
+
+# The NUT command default locations. In the RHEL-type nut packages, these binaries
+# are in /usr/bin.
+RHELUPSCMD="/usr/bin/upscmd"
+RHELUPSC="/usr/bin/upsc"
+
+# Defaults for APC smart UPSes:
+
+# Reset = reboot $hostname; this will be a graceful reboot if the host
+# is running NUT and monitoring $ups.
+APCRESET="shutdown.return"
+
+# Poweroff = turn off $hostname immediately by cutting the power on $ups.
+# For a graceful shutdown, use shutdown.stayoff instead of load.off,
+# but it might take a few minutes to shutdown in this way.
+APCPOWEROFF="load.off"
+
+# Poweron = turn on the power to $ups, which will presumably turn on $hostname.
+# (Did you set $hostname's BIOS to boot up on AC power restore, as opposed to
+# "last state"?)
+APCPOWERON="load.on"
+
+# Status = returns a short string with the $ups status; OL = on-line, OFF = off-line, etc.
+APCSTATUSVAR="ups.status"
+
+
+# Stick in the defaults, if needed.
+if [ -z "${poweron}" ]; then
+ poweron=${APCPOWERON}
+fi
+if [ -z "${poweroff}" ]; then
+ poweroff=${APCPOWEROFF}
+fi
+if [ -z "${reset}" ]; then
+ reset=${APCRESET}
+fi
+if [ -z "${statusvar}" ]; then
+ statusvar=${APCSTATUSVAR}
+fi
+if [ -z "${upscmd}" ]; then
+ upscmd=${RHELUPSCMD}
+fi
+if [ -z "${upsc}" ]; then
+ upsc=${RHELUPSC}
+fi
+
+
+# Define the command to fetch the UPS status.
+STATUSCMD="${upsc} ${ups} ${statusvar}"
+
+usage() {
+ echo "Usage: $0 {on|off|reset|status|gethosts|getconfignames|getinfo-devid|getinfo-devname|getinfo-devdescr|getinfo-devurl|getinfo-xml}"
+}
+
+# Can we find the NUT binary?
+have_nut() {
+ test -x "${upscmd}"
+}
+have_upsc() {
+ test -x "${upsc}"
+}
+
+do_nut() {
+ have_nut || {
+ echo "Can't find NUT upscmd command"
+ return 1
+ }
+ if [ -z "${username}" -o -z "${password}" -o -z "${ups}" ]; then
+ echo "username, password or ups name missing; check configuration"
+ return 1
+ fi
+ # Execute the command given in argument 1.
+ ${upscmd} -u ${username} -p ${password} ${ups} ${1} || {
+ echo "error executing nut command"
+ return 1
+ }
+}
+
+case ${1} in
+gethosts)
+ echo ${hostname}
+ exit 0
+ ;;
+on)
+ result=1
+ do_nut "${poweron}"
+ result=$?
+ exit ${result}
+ ;;
+off)
+ result=1
+ do_nut "${poweroff}"
+ result=$?
+ exit ${result}
+ ;;
+reset)
+ result=1
+ do_nut "${reset}"
+ result=$?
+ exit $result
+ ;;
+status)
+ have_upsc || {
+ echo "Can't find NUT upsc command"
+ exit 1
+ }
+ ${STATUSCMD}
+ exit $?
+ ;;
+getconfignames)
+ echo "hostname ups username password poweron poweroff reset statusvar upscmd upsc"
+ exit 0
+ ;;
+getinfo-devid)
+ echo "NUT STONITH device"
+ exit 0
+ ;;
+getinfo-devname)
+ echo "NUT STONITH external device"
+ exit 0
+ ;;
+getinfo-devdescr)
+ echo "A STONITH device based on NUT (Network UPS Tools)."
+ echo " "
+ echo "For this STONITH script to work, the following conditions have"
+ echo "to be met:"
+ echo " "
+ echo "- NUT has to be installed on both the host running this script"
+ echo " and the host that controls the UPS (on RHEL systems, NUT is"
+ echo " in packages nut and nut-client) and the nut daemon services"
+ echo " (normally called the ups or upsd service) must be running"
+ echo " on both systems."
+ echo " "
+ echo "- The UPS name has to be defined in ups.conf on the host"
+ echo " that controls the UPS."
+ echo " "
+ echo "- The username/password to access the UPS must be defined in"
+ echo " upsd.users on the host that controls the UPS, with the instcmds"
+ echo " for poweron, poweroff, and reset allowed."
+ echo " "
+ echo "- The host that is running this script must be allowed access"
+ echo " via upsd.conf and upsd.users on the host the controls the UPS."
+ echo " "
+ echo "On RHEL systems, the files listed above are in /etc/ups."
+ echo " "
+ echo "The defaults will probably work with APC UPS devices. It might"
+ echo "work on others; 'upscmd -l (ups)' and 'upsc (ups)' will list"
+ echo "the commands and variables, and you can change the values"
+ echo "for poweron, poweroff, reset, and statusvar to suit your UPS."
+ echo "Change upscmd and upsc if your NUT binaries are not in /usr/bin."
+ exit 0
+ ;;
+getinfo-devurl)
+ echo "http://www.networkupstools.org/"
+ exit 0
+ ;;
+getinfo-xml)
+cat << nutXML
+<parameters>
+
+<parameter name="hostname" unique="1" required="1">
+<content type="string" default="" />
+<shortdesc lang="en">Hostname</shortdesc>
+<longdesc lang="en">
+The name of the host to be managed by this STONITH device.
+The nut daemon must be running on the host controllng the
+UPS _and_ on the host running this script; this script does
+not start/stop the daemons for you.
+</longdesc>
+</parameter>
+
+<parameter name="ups" required="1">
+<content type="string" default="" />
+<shortdesc lang="en">UPS name</shortdesc>
+<longdesc lang="en">
+The name of the UPS as defined in ups.conf on the host
+controlling the UPS. The format for this option is
+upsname[@controlhost[:port]]. The default controlhost is
+"localhost".
+</longdesc>
+</parameter>
+
+<parameter name="username" required="1">
+<content type="string" default="" />
+<shortdesc lang="en">Username</shortdesc>
+<longdesc lang="en">
+The username used for accessing the UPS. This is defined in
+upsd.conf on the host controlling the UPS.
+</longdesc>
+</parameter>
+
+<parameter name="password" required="1">
+<content type="string" default="" />
+<shortdesc lang="en">Password</shortdesc>
+<longdesc lang="en">
+The password used for logging in to the UPS for the host
+controlling the UPS, as defined in upsd.conf on that host.
+</longdesc>
+</parameter>
+
+<parameter name="poweron">
+<content type="string" default="$APCPOWERON" />
+<shortdesc lang="en">UPS Power On command</shortdesc>
+<longdesc lang="en">
+The NUT hardware command to turn on the UPS. The default
+should work for most "smart" APC UPSes. For a list of
+commands that your UPS can support, type 'upscmd -l (ups)'
+on the command line.</longdesc>
+</parameter>
+
+<parameter name="poweroff">
+<content type="string" default="$APCPOWEROFF" />
+<shortdesc lang="en">UPS Power Off command</shortdesc>
+<longdesc lang="en">
+The NUT hardware command to turn off the UPS. On most APC
+"smart" UPSes, the command shutdown.stayoff will result
+in a graceful shutdown, provided the host is running the
+nut daemon, but this might take a few minutes; load.off
+will cut the power immediately. For a list of commands that
+your UPS can support, type 'upscmd -l (ups)' on the command
+line.
+</longdesc>
+</parameter>
+
+<parameter name="reset">
+<content type="string" default="$APCRESET" />
+<shortdesc lang="en">UPS Reset command</shortdesc>
+<longdesc lang="en">
+The NUT hardware command to reset the host. On most APC
+"smart" UPSes, the command shutdown.return will result
+in a graceful shutdown, with power restored after perhaps
+a short interval. For a list of commands that your UPS can
+ support, type 'upscmd -l (ups)' on the command line.
+</longdesc>
+</parameter>
+
+<parameter name="statusvar">
+<content type="string" default="$APCSTATUSVAR" />
+<shortdesc lang="en">UPS Status variable</shortdesc>
+<longdesc lang="en">
+The NUT variable that returns the status of the UPS. On APC
+UPSes, the value of ups.status will be "OL" if the UPS is
+"on-line." For a list of variables that your UPS supports,
+type 'upsc (ups)' on the command line.
+</longdesc>
+</parameter>
+
+<parameter name="upscmd">
+<content type="string" default="$RHELUPSCMD" />
+<shortdesc lang="en">upscmd binary location</shortdesc>
+<longdesc lang="en">
+The full path to the NUT binary command 'upscmd'. On RHEL
+systems with the nut RPM installed, this location is
+/usr/bin/upscmd.
+</longdesc>
+</parameter>
+
+<parameter name="upsc">
+<content type="string" default="$RHELUPSC" />
+<shortdesc lang="en">upsc binary location</shortdesc>
+<longdesc lang="en">
+The full path to the NUT binary command 'upsc'. On RHEL
+systems with the nut RPM installed, this location is
+/usr/bin/upsc.
+</longdesc>
+</parameter>
+
+</parameters>
+nutXML
+exit 0
+;;
+*)
+ usage
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/rackpdu b/lib/plugins/stonith/external/rackpdu
new file mode 100644
index 0000000..7d0e20b
--- /dev/null
+++ b/lib/plugins/stonith/external/rackpdu
@@ -0,0 +1,280 @@
+#!/bin/sh
+#
+# External STONITH module for APC Switched Rack PDU
+#
+# Copyright (c) 2008 Sergey Maznichenko <msergeyb@gmail.com> <inbox@it-consultant.su>
+# Version 1.2
+#
+# See http://www.it-consultant.su/rackpdu
+# for additional information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+
+SWITCH_ON="1"
+SWITCH_OFF="2"
+SWITCH_RESET="3"
+
+DEFAULT_NAMES_OID=".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2"
+DEFAULT_COMMAND_OID=".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4"
+
+if [ -z "$oid" ]; then
+ oid=$DEFAULT_COMMAND_OID
+fi
+
+if [ -z "$names_oid" ]; then
+ names_oid=$DEFAULT_NAMES_OID
+fi
+
+if [ -z "$outlet_config" ]; then
+ outlet_config="none"
+fi
+
+GetOutletNumber() {
+ local nodename=$1
+
+ if [ "$outlet_config" != "none" ]; then
+ # Get outlet number from file
+
+ if [ -f "$outlet_config" ]; then
+ local outlet_num=`grep $nodename $outlet_config | tr -d ' ' | cut -f2 -d'='`
+ if [ -z "$outlet_num" ]; then
+ ha_log.sh err "Outlet number not found for node $nodename. Check configuration file $outlet_config"
+ return 0
+ fi
+ return $outlet_num
+ else
+ ha_log.sh err "File $outlet_config not found."
+ return 0
+ fi
+ else
+ # Get outlet number from device
+
+ local outlet_num=1
+ local snmp_result
+ snmp_result=`snmpwalk -v1 -c $community $pduip $names_oid 2>&1`
+ if [ $? -ne 0 ]; then
+ ha_log.sh err "snmpwalk $community $pduip $names_oid failed. Result: $snmp_result"
+ return 0
+ fi
+
+ local names
+ names=`echo "$snmp_result" | cut -f2 -d'"' | tr ' ' '_' | tr '\012' ' '`
+ for name in $names; do
+ if [ "$name" != "$nodename" ]; then
+ local outlet_num=`expr $outlet_num + 1`
+ continue
+ fi
+
+ return $outlet_num
+ done
+
+ ha_log.sh err "Outlet number not found for node $nodename. Result: $snmp_result"
+ return 0
+ fi
+}
+
+SendCommand() {
+
+ local host=$1
+ local command=$2
+
+ GetOutletNumber $host
+ local outlet=$?
+
+ if [ $outlet -gt 0 ]; then
+ local set_result
+ set_result=`snmpset -v1 -c $community $pduip $oid.$outlet i $command 2>&1`
+ if [ $? -ne 0 ]; then
+ ha_log.sh err "Write SNMP to $pduip value $oid.$outlet=$command failed. Result: $set_result"
+ return 1
+ fi
+ if echo "$set_result" | grep -qs "Timeout"; then
+ ha_log.sh err "Write SNMP to $pduip value $oid.$outlet=$command timed out. Result: $set_result"
+ return 1
+ fi
+ return 0
+ else
+ return 1
+ fi
+}
+
+hostlist=`echo $hostlist | tr ',' ' '`
+incommand=$1
+innode=$2
+
+case $incommand in
+gethosts)
+ if [ "$hostlist" = "AUTO" ]; then
+ snmp_result=`snmpwalk -v1 -c $community $pduip $names_oid 2>&1`
+ if [ $? -ne 0 ]; then
+ ha_log.sh err "snmpwalk $community $pduip $names_oid failed. Result: $snmp_result"
+ exit 1
+ fi
+ if echo "$snmp_result" | grep -qs "Timeout"; then
+ ha_log.sh err "snmpwalk $community $pduip $names_oid timed out. Result: $snmp_result"
+ exit 1
+ else
+ hostlist=`echo "$snmp_result" | cut -f2 -d'"' | tr ' ' '_' | tr '\012' ' '`
+ fi
+ fi
+
+ for h in $hostlist ; do
+ echo $h
+ done
+
+ exit 0
+ ;;
+on)
+ if
+ SendCommand $innode $SWITCH_ON
+ then
+ exit 0
+ else
+ exit 1
+ fi
+ ;;
+off)
+ if
+ SendCommand $innode $SWITCH_OFF
+ then
+ exit 0
+ else
+ exit 1
+ fi
+ ;;
+reset)
+ if
+ SendCommand $innode $SWITCH_RESET
+ then
+ exit 0
+ else
+ exit 1
+ fi
+ ;;
+status)
+ if [ -z "$pduip" ]; then
+ exit 1
+ fi
+
+ if ping -w1 -c1 $pduip >/dev/null 2>&1; then
+ exit 0
+ else
+ exit 1
+ fi
+ ;;
+getconfignames)
+ echo "hostlist pduip community"
+ exit 0
+ ;;
+getinfo-devid)
+ echo "rackpdu STONITH device"
+ exit 0
+ ;;
+getinfo-devname)
+ echo "rackpdu STONITH external device"
+ exit 0
+ ;;
+getinfo-devdescr)
+ echo "APC Switched Rack PDU"
+ exit 0
+ ;;
+getinfo-devurl)
+ echo "http://www.apcc.com/products/family/index.cfm?id=30"
+ exit 0
+ ;;
+getinfo-xml)
+ cat << PDUXML
+<parameters>
+ <parameter name="hostlist" unique="1" required="1">
+ <content type="string" default="AUTO" />
+ <shortdesc lang="en">Hostlist</shortdesc>
+ <longdesc lang="en">
+The list of hosts that the STONITH device controls (comma or space separated).
+If you set value of this parameter to AUTO, list of hosts will be get from Rack PDU device.
+ </longdesc>
+ </parameter>
+
+ <parameter name="pduip" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">Name or IP address of Rack PDU device.</shortdesc>
+ <longdesc lang="en">Name or IP address of Rack PDU device.</longdesc>
+ </parameter>
+
+ <parameter name="community" unique="1" required="1">
+ <content type="string" default="private" />
+ <shortdesc lang="en">Name of write community.</shortdesc>
+ <longdesc lang="en">Name of write community.</longdesc>
+ </parameter>
+
+ <parameter name="oid" unique="1" required="0">
+ <content type="string" />
+ <shortdesc lang="en">
+ The OID without the outlet number.
+ </shortdesc>
+ <longdesc lang="en">
+The SNMP OID for the PDU. minus the outlet number.
+Try .1.3.6.1.4.1.318.1.1.12.3.3.1.1.4 (default value)
+or use mib from ftp://ftp.apcc.com/apc/public/software/pnetmib/mib/
+Varies on different APC hardware and firmware.
+Warning! No dot at the end of OID
+ </longdesc>
+ </parameter>
+
+ <parameter name="names_oid" unique="1" required="0">
+ <content type="string" />
+ <shortdesc lang="en">The OID for getting names of outlets.</shortdesc>
+ <longdesc lang="en">
+The SNMP OID for getting names of outlets.
+It is required to recognize outlet number by nodename.
+Try ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2" (default value)
+or use mib from ftp://ftp.apcc.com/apc/public/software/pnetmib/mib/
+Names of nodes must be equal names of outlets, in other way use outlet_config parameter.
+If you set 'names_oid' parameter then parameter outlet_config must not be use.
+Varies on different APC hardware and firmware.
+Warning! No dot at the end of OID
+ </longdesc>
+ </parameter>
+
+ <parameter name="outlet_config" unique="1" required="0">
+ <content type="string" />
+ <shortdesc lang="en">Configuration file. Other way to recognize outlet number by nodename.</shortdesc>
+ <longdesc lang="en">
+Configuration file. Other way to recognize outlet number by nodename.
+Configuration file which contains
+node_name=outlet_number
+strings.
+
+Example:
+server1=1
+server2=2
+
+If you use outlet_config parameter then names_oid parameter can have any value and it is not uses.
+ </longdesc>
+ </parameter>
+
+</parameters>
+PDUXML
+ exit 0
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/riloe b/lib/plugins/stonith/external/riloe
new file mode 100644
index 0000000..ce98847
--- /dev/null
+++ b/lib/plugins/stonith/external/riloe
@@ -0,0 +1,530 @@
+#!/usr/bin/env python
+#
+# Stonith module for RILOE Stonith device
+#
+# Copyright (c) 2004 Alain St-Denis <alain.st-denis@ec.gc.ca>
+#
+# Modified by Alan Robertson <alanr@unix.sh> for STONITH external compatibility.
+#
+# Extended and merged by Tijl Van den broeck <subspawn@gmail.com>
+# with ilo-v2 script from Guy Coates
+#
+# Cleanup by Andrew Beekhof <abeekhof@suse.de>
+#
+# Rewritten by Dejan Muhamedagic <dejan@suse.de>
+# Now, the plugin actually reads replies from iLO.
+#
+# Extended by Jochen Roeder <jochen.roeder@novell.com>
+# to enable access via proxies
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+import sys
+import os
+import socket
+import subprocess
+import xml.dom.minidom
+import httplib
+import time
+import re
+
+def log_msg(level,msg):
+ subprocess.call("ha_log.sh %s '%s'" % (level,msg), shell=True)
+def my_err(msg):
+ log_msg("err", msg)
+def my_warn(msg):
+ log_msg("warn", msg)
+def my_debug(msg):
+ log_msg("debug", msg)
+def fatal(msg):
+ my_err(msg)
+ sys.exit(1)
+
+argv = sys.argv
+
+try:
+ cmd = argv[1]
+except IndexError:
+ my_err("Not enough arguments")
+ sys.exit(1)
+
+legacy_RI_HOST = os.environ.get('RI_HOST', '')
+legacy_RI_HOSTRI = os.environ.get('RI_HOSTRI', '')
+legacy_RI_LOGIN = os.environ.get('RI_LOGIN', 'Administrator')
+legacy_RI_PASSWORD = os.environ.get('RI_PASSWORD', '')
+
+reset_ok = os.environ.get('ilo_can_reset', '0')
+ilo_protocol = os.environ.get('ilo_protocol', '1.2')
+power_method = os.environ.get('ilo_powerdown_method', 'power')
+
+realhost = os.environ.get('hostlist', legacy_RI_HOST)
+rihost = os.environ.get('ilo_hostname', legacy_RI_HOSTRI)
+ilouser = os.environ.get('ilo_user', legacy_RI_LOGIN)
+ilopass = os.environ.get('ilo_password', legacy_RI_PASSWORD)
+iloproxyhost = os.environ.get('ilo_proxyhost', '')
+try:
+ iloproxyport = int(os.environ.get('ilo_proxyport', 3128))
+except ValueError:
+ my_err("ilo_proxyport is not a number")
+ sys.exit(1)
+
+xmlinfo = '''<parameters>
+ <parameter name="hostlist" unique="1" required="1">
+ <content type="string"/>
+ <shortdesc lang="en">ilo target hostname</shortdesc>
+ <longdesc lang="en">
+ Contains the hostname that the ilo controls
+ </longdesc>
+ </parameter>
+<parameter name="ilo_hostname" unique="1" required="1">
+ <content type="string"/>
+ <shortdesc lang="en">ilo device hostname</shortdesc>
+ <longdesc lang="en">
+ The hostname of the ilo device
+ </longdesc>
+ </parameter>
+<parameter name="ilo_user" unique="0" required="1">
+ <content type="string" default="Administrator"/>
+ <shortdesc lang="en">ilo user</shortdesc>
+ <longdesc lang="en">
+ The user for connecting to the ilo device
+ </longdesc>
+ </parameter>
+<parameter name="ilo_password" unique="0" required="1">
+ <content type="string" default=""/>
+ <shortdesc lang="en">password</shortdesc>
+ <longdesc lang="en">
+ The password for the ilo device user
+ </longdesc>
+ </parameter>
+<parameter name="ilo_can_reset" unique="0" required="0">
+ <content type="string" default="0"/>
+ <shortdesc lang="en">Device can reset</shortdesc>
+ <longdesc lang="en">
+ Does the ILO device support RESET commands (hint: older ones cannot)
+ </longdesc>
+ </parameter>
+<parameter name="ilo_protocol" unique="0" required="0">
+ <content type="string" default="1.2"/>
+ <shortdesc lang="en">ILO Protocol</shortdesc>
+ <longdesc lang="en">
+ Protocol version supported by the ILO device.
+ Known supported versions: 1.2, 2.0
+ </longdesc>
+ </parameter>
+<parameter name="ilo_powerdown_method" unique="0" required="0">
+ <content type="string" default="power"/>
+ <shortdesc lang="en">Power down method</shortdesc>
+ <longdesc lang="en">
+ The method to powerdown the host in question.
+ * button - Emulate holding down the power button
+ * power - Emulate turning off the machines power
+
+ NB: A button request takes around 20 seconds. The power method
+ about half a minute.
+ </longdesc>
+ </parameter>
+<parameter name="ilo_proxyhost" unique="0" required="0">
+ <content type="string" default=""/>
+ <shortdesc lang="en">Proxy hostname</shortdesc>
+ <longdesc lang="en">
+ proxy hostname if required to access ILO board
+ </longdesc>
+ </parameter>
+<parameter name="ilo_proxyport" unique="0" required="0">
+ <content type="string" default="3128"/>
+ <shortdesc lang="en">Proxy port</shortdesc>
+ <longdesc lang="en">
+ proxy port if required to access ILO board
+ parameter will be ignored if proxy hostname is not set
+ </longdesc>
+ </parameter>
+
+</parameters>'''
+
+info = {
+ 'getinfo-devid': 'iLO2',
+ 'getinfo-devname': 'ilo2 ' + rihost,
+ 'getinfo-devdescr': 'HP/COMPAQ iLO2 STONITH device',
+ 'getinfo-devurl': 'http://www.hp.com/',
+ 'gethosts': realhost,
+ 'getinfo-xml': xmlinfo
+}
+
+if cmd in info:
+ print info[cmd]
+ sys.exit(0)
+
+if cmd == 'getconfignames':
+ for arg in [ "hostlist", "ilo_hostname", "ilo_user", "ilo_password", "ilo_can_reset", "ilo_protocol", "ilo_powerdown_method", "ilo_proxyhost", "ilo_proxyport"]:
+ print arg
+ sys.exit(0)
+
+if not rihost:
+ fatal("ILO device hostname not specified")
+
+if not realhost:
+ fatal("Host controlled by this ILO device not specified")
+
+if not power_method in ("power","button"):
+ my_err('unknown power method %s, setting to "power"')
+ power_method = "power"
+
+# XML elements
+E_RIBCL = "RIBCL"
+E_LOGIN = "LOGIN"
+E_SERVER_INFO = "SERVER_INFO"
+
+# power mgmt methods
+E_RESET = "RESET_SERVER" # error if powered off
+E_COLD_BOOT = "COLD_BOOT_SERVER" # error if powered off
+E_WARM_BOOT = "WARM_BOOT_SERVER" # error if powered off
+E_PRESS_BUTTON = "PRESS_PWR_BTN"
+E_HOLD_BUTTON = "HOLD_PWR_BTN"
+
+# get/set status elements
+E_SET_POWER = "SET_HOST_POWER"
+E_GET_PSTATUS = "GET_HOST_POWER_STATUS"
+
+# whatever this means, but we have to use it to get good XML
+E_LOCFG = "LOCFG"
+LOCFG_VER = '2.21'
+
+# attributes
+A_VERSION = "VERSION" # ilo_protocol
+A_USER = "USER_LOGIN"
+A_PWD = "PASSWORD"
+A_MODE = "MODE" # info mode (read or write)
+A_POWER_SW = "HOST_POWER" # "Y" or "N"
+A_POWER_STATE = "HOST_POWER" # "ON" or "OFF"
+
+def new_power_req(tag, name = None, value = None):
+ '''
+ Create a new RIBCL request (as XML).
+ '''
+ my_debug("creating power request: %s,%s,%s"%(tag,name,value))
+ doc = xml.dom.minidom.Document()
+ locfg = doc.createElement(E_LOCFG)
+ locfg.setAttribute(A_VERSION,LOCFG_VER)
+ ribcl = doc.createElement(E_RIBCL)
+ ribcl.setAttribute(A_VERSION,ilo_protocol)
+ login = doc.createElement(E_LOGIN)
+ login.setAttribute(A_USER,ilouser)
+ login.setAttribute(A_PWD,ilopass)
+ serv_info = doc.createElement(E_SERVER_INFO)
+ # read or write, it doesn't really matter, i.e. even if we
+ # say "write" that doesn't mean we can't read
+ serv_info.setAttribute(A_MODE,"write")
+ doc.appendChild(locfg)
+ locfg.appendChild(ribcl)
+ ribcl.appendChild(login)
+ login.appendChild(serv_info)
+ el_node = doc.createElement(tag)
+ if name:
+ el_node.setAttribute(name,value)
+ serv_info.appendChild(el_node)
+ s = doc.toprettyxml()
+ doc.unlink()
+ # work around an iLO bug: last line containing "</LOCFG>"
+ # produces a syntax error
+ lines = s.split('\n')
+ return '\n'.join(lines[:-2])
+
+E_RESPONSE = "RESPONSE"
+E_HOST_POWER = "GET_HOST_POWER"
+A_STATUS = "STATUS"
+# documentation mentions both; better safe than sorry
+A_MSG = "MSG"
+A_MSG2 = "MESSAGE"
+
+def is_element(xmlnode):
+ return xmlnode.nodeType == xmlnode.ELEMENT_NODE
+
+def read_resp(node):
+ '''
+ Check if the RESPONSE XML is OK.
+ '''
+ msg = ""
+ str_status = ""
+ for attr in node.attributes.keys():
+ if attr == A_STATUS:
+ str_status = node.getAttribute(attr)
+ elif attr == A_MSG:
+ msg = node.getAttribute(attr)
+ elif attr == A_MSG2:
+ msg = node.getAttribute(attr)
+ else:
+ my_warn("unexpected attribute %s in %s" % (attr,E_RESPONSE))
+ if not str_status:
+ my_err("no status in response")
+ return -1
+ try:
+ status = int(str_status,16)
+ except ValueError:
+ my_err("unexpected status %s in response" % str_status)
+ return -1
+ if status != 0:
+ my_err("%s (rc: %s)"%(msg,str_status))
+ return -1
+ return 0
+
+def read_power(node):
+ '''
+ Read the power from the XML node. Set the global power
+ variable correspondingly.
+ '''
+ global power
+ for attr in node.attributes.keys():
+ if attr == A_POWER_STATE:
+ power_state = node.getAttribute(attr).upper()
+ else:
+ my_warn("unexpected attribute %s in %s" % (attr,node.tagName))
+ if not power_state:
+ my_err("no %s attribute in %s" % (A_POWER_STATE,node.tagName))
+ return -1
+ if power_state not in ("ON","OFF"):
+ my_err("unexpected value for %s: %s" % (A_POWER_STATE,power_state))
+ return -1
+ power = (power_state == "ON")
+ my_debug("Host has power: %s"%power)
+ return 0
+
+el_parsers = {
+ E_RESPONSE:read_resp,
+ E_HOST_POWER:read_power
+}
+def proc_resp(doc):
+ '''
+ Process one iLO reply. Real work is done in el_parsers.
+ '''
+ ribcl = doc.childNodes[0]
+ if not is_element(ribcl) or ribcl.tagName != E_RIBCL:
+ my_err("unexpected top element in response")
+ return -1
+ for child in ribcl.childNodes:
+ if not is_element(child):
+ continue
+ if child.tagName in el_parsers:
+ rc = el_parsers[child.tagName](child)
+ if rc != 0:
+ return -1
+ else:
+ my_warn("unexpected element in response: %s" % child.toxml())
+ return 0
+
+def open_ilo(host):
+ # open https connection
+ try:
+ if iloproxyhost != "" and iloproxyport != 0:
+ proxy=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
+ proxy.connect((iloproxyhost, iloproxyport))
+ proxy_connect='CONNECT %s:%s HTTP/1.1\r\n'%(host,443)
+ user_agent='User-Agent: python\r\n'
+ proxy_pieces=proxy_connect+user_agent+'\r\n'
+ proxy.sendall(proxy_pieces)
+ response=proxy.recv(8192)
+ status=response.split()[1]
+ if status!=str(200):
+ fatal("Error status=: %s" %(response))
+ import ssl
+ sock = ssl.wrap_socket(proxy)
+ h=httplib.HTTPConnection('localhost')
+ h.sock=sock
+ return h
+ else:
+ return httplib.HTTPSConnection(host)
+ except socket.gaierror, msg:
+ fatal("%s: %s" %(msg,host))
+ except socket.sslerror, msg:
+ fatal("%s for %s" %(msg,host))
+ except socket.error, msg:
+ fatal("%s while talking to %s" %(msg,host))
+ except ImportError, msg:
+ fatal("ssl support missing (%s)" %msg)
+
+def send_request(req,proc_f):
+ '''
+ 1. After every request, the iLO closes the connection.
+ 2. For every request, there are multiple replies. Each reply
+ is an XML document. Most of replies are just a kind of
+ (verbose) XML "OK".
+ '''
+ t_begin = time.time()
+ c = open_ilo(rihost)
+ try:
+ c.send(req+'\r\n')
+ except socket.error, msg:
+ fatal("%s, while talking to %s" %(msg,rihost))
+ t_end = time.time()
+ my_debug("request sent in %0.2f s" % ((t_end-t_begin)))
+
+ t_begin = time.time()
+ result = []
+ while True:
+ try:
+ reply = c.sock.recv(1024)
+ if not reply:
+ break
+ result.append(reply)
+ except socket.error, msg:
+ if msg[0] == 6: # connection closed
+ break
+ my_err("%s, while talking to %s" %(msg,rihost))
+ return -1
+ c.close()
+ t_end = time.time()
+
+ if not result:
+ fatal("no response from %s within %0.2f s"%(rihost,(t_end-t_begin)))
+ for reply in result:
+ # work around the iLO bug, i.e. element RIBCL closed twice
+ if re.search("</RIBCL", reply) and re.search("<RIBCL.*/>", reply):
+ reply = re.sub("<(RIBCL.*)/>", r"<\1>", reply)
+ try:
+ doc = xml.dom.minidom.parseString(reply)
+ except xml.parsers.expat.ExpatError,msg:
+ fatal("malformed response: %s\n%s"%(msg,reply))
+ rc = proc_f(doc)
+ doc.unlink()
+ if rc != 0:
+ break
+ my_debug("iLO processed request (rc=%d) in %0.2f s" % (rc,(t_end-t_begin)))
+ return rc
+
+def manage_power(cmd):
+ '''
+ Before trying to send a request we have to check the power
+ state.
+ '''
+ rc = 0
+ req = ''
+ # it won't do to turn it on if it's already on!
+ if cmd == "on" and not power:
+ req = new_power_req(E_SET_POWER,A_POWER_SW,"Y")
+ # also to turn it off if it's already off
+ elif cmd == "off" and power:
+ req = new_power_req(E_SET_POWER,A_POWER_SW,"N")
+ elif cmd == "cold_boot" and power:
+ req = new_power_req(E_COLD_BOOT)
+ elif cmd == "warm_boot" and power:
+ req = new_power_req(E_WARM_BOOT)
+ elif cmd == "reset":
+ if power:
+ req = new_power_req(E_RESET)
+ # reset doesn't work if the host's off
+ else:
+ req = new_power_req(E_SET_POWER,A_POWER_SW,"Y")
+ if req:
+ rc = send_request(req,proc_resp)
+ return rc
+def power_on():
+ '''
+ Update the power variable without checking the power state.
+ The iLO is slow at times to report the actual power state, so
+ we assume that it changed if the request succeeded.
+ '''
+ rc = manage_power("on")
+ if rc == 0:
+ global power
+ power = True
+ return rc
+def power_off():
+ rc = manage_power("off")
+ if rc == 0:
+ global power
+ power = False
+ return rc
+def cold_boot():
+ rc = manage_power("cold_boot")
+ return rc
+def warm_boot():
+ rc = manage_power("warm_boot")
+ return rc
+def reset():
+ rc = manage_power("reset")
+ if rc == 0:
+ global power
+ power = True
+ return rc
+def hold_button():
+ '''
+ Hold the power button. Got this error message when tried
+ without the TOGGLE attribute:
+ Command without TOGGLE="Yes" attribute is ignored
+ when host power is off. (rc: 0x0054)
+ Didn't find any documentation about TOGGLE.
+ '''
+ if power:
+ req = new_power_req(E_HOLD_BUTTON)
+ else:
+ req = new_power_req(E_HOLD_BUTTON,"TOGGLE","Yes")
+ rc = send_request(req,proc_resp)
+ return rc
+def read_power_state():
+ req = new_power_req(E_GET_PSTATUS)
+ rc = send_request(req,proc_resp)
+ return rc
+
+def reset_power():
+ '''
+ Three methods to reset:
+ - hold power button
+ - reset (only if host has power and user said that reset is ok)
+ - power off/on
+ '''
+ do_power_on = False
+ if power_method == 'button':
+ rc = hold_button()
+ elif reset_ok != '0':
+ if power:
+ return reset()
+ else:
+ return power_on()
+ else:
+ do_power_on = True
+ rc = power_off()
+ if rc == 0:
+ rc = read_power_state()
+ if do_power_on:
+ while rc == 0 and power: # wait for the power state to go off
+ time.sleep(5)
+ rc = read_power_state()
+ if rc == 0 and do_power_on and not power:
+ rc = power_on()
+ return rc
+
+# track state of host power
+power = -1
+
+todo = {
+'reset':reset_power,
+'on':power_on,
+'off':power_off,
+'cold':cold_boot,
+'warm':warm_boot,
+'status':lambda: 0 # just return 0, we already read the state
+}
+
+rc = read_power_state()
+if rc == 0:
+ if cmd in todo:
+ rc = todo[cmd]()
+ else:
+ fatal('Invalid command: %s' % cmd)
+if rc != 0:
+ fatal("request failed")
+sys.exit(rc)
+
+# vi:ts=4:sw=4:et:
diff --git a/lib/plugins/stonith/external/ssh.in b/lib/plugins/stonith/external/ssh.in
new file mode 100644
index 0000000..2a8eb73
--- /dev/null
+++ b/lib/plugins/stonith/external/ssh.in
@@ -0,0 +1,176 @@
+#!/bin/sh
+#
+# External STONITH module for ssh.
+#
+# Copyright (c) 2004 SUSE LINUX AG - Lars Marowsky-Bree <lmb@suse.de>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+
+SSH_COMMAND="@SSH@ -q -x -o PasswordAuthentication=no -o StrictHostKeyChecking=no -n -l root"
+#SSH_COMMAND="@SSH@ -q -x -n -l root"
+
+REBOOT_COMMAND="echo 'sleep 2; @REBOOT@ @REBOOT_OPTIONS@' | SHELL=/bin/sh at now >/dev/null 2>&1"
+
+# Warning: If you select this poweroff command, it'll physically
+# power-off the machine, and quite a number of systems won't be remotely
+# revivable.
+# TODO: Probably should touch a file on the server instead to just
+# prevent heartbeat et al from being started after the reboot.
+# POWEROFF_COMMAND="echo 'sleep 2; /sbin/poweroff -nf' | SHELL=/bin/sh at now >/dev/null 2>&1"
+POWEROFF_COMMAND="echo 'sleep 2; @REBOOT@ @REBOOT_OPTIONS@' | SHELL=/bin/sh at now >/dev/null 2>&1"
+
+# Rewrite the hostlist to accept "," as a delimeter for hostnames too.
+hostlist=`echo $hostlist | tr ',' ' '`
+
+is_host_up() {
+ for j in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ do
+ if
+ ping -w1 -c1 "$1" >/dev/null 2>&1
+ then
+ sleep 1
+ else
+ return 1
+ fi
+ done
+ return 0
+}
+
+
+case $1 in
+gethosts)
+ for h in $hostlist ; do
+ echo $h
+ done
+ exit 0
+ ;;
+on)
+ # Can't really be implemented because ssh cannot power on a system
+ # when it is powered off.
+ exit 1
+ ;;
+off)
+ # Shouldn't really be implemented because if ssh cannot power on a
+ # system, it shouldn't be allowed to power it off.
+ exit 1
+ ;;
+reset)
+ h_target=`echo $2 | tr A-Z a-z`
+ for h in $hostlist
+ do
+ h=`echo $h | tr A-Z a-z`
+ [ "$h" != "$h_target" ] &&
+ continue
+ if
+ case ${livedangerously} in
+ [Yy]*) is_host_up $h;;
+ *) true;;
+ esac
+ then
+ $SSH_COMMAND "$2" "$REBOOT_COMMAND"
+ # Good thing this is only for testing...
+ if
+ is_host_up $h
+ then
+ exit 1
+ else
+ exit 0
+ fi
+ else
+ # well... Let's call it successful, after all this is only for testing...
+ exit 0
+ fi
+ done
+ exit 1
+ ;;
+status)
+ if
+ [ -z "$hostlist" ]
+ then
+ exit 1
+ fi
+ for h in $hostlist
+ do
+ if
+ ping -w1 -c1 "$h" 2>&1 | grep "unknown host"
+ then
+ exit 1
+ fi
+ done
+ exit 0
+ ;;
+getconfignames)
+ echo "hostlist"
+ exit 0
+ ;;
+getinfo-devid)
+ echo "ssh STONITH device"
+ exit 0
+ ;;
+getinfo-devname)
+ echo "ssh STONITH external device"
+ exit 0
+ ;;
+getinfo-devdescr)
+ echo "ssh-based host reset"
+ echo "Fine for testing, but not suitable for production!"
+ echo "Only reboot action supported, no poweroff, and, surprisingly enough, no poweron."
+ exit 0
+ ;;
+getinfo-devurl)
+ echo "http://openssh.org"
+ exit 0
+ ;;
+getinfo-xml)
+ cat << SSHXML
+<parameters>
+<parameter name="hostlist" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Hostlist
+</shortdesc>
+<longdesc lang="en">
+The list of hosts that the STONITH device controls
+</longdesc>
+</parameter>
+
+<parameter name="livedangerously" unique="0" required="0">
+<content type="enum" />
+<shortdesc lang="en">
+Live Dangerously!!
+</shortdesc>
+<longdesc lang="en">
+Set to "yes" if you want to risk your system's integrity.
+Of course, since this plugin isn't for production, using it
+in production at all is a bad idea. On the other hand,
+setting this parameter to yes makes it an even worse idea.
+Viva la Vida Loca!
+</longdesc>
+</parameter>
+
+</parameters>
+SSHXML
+ exit 0
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/vcenter b/lib/plugins/stonith/external/vcenter
new file mode 100755
index 0000000..71a6302
--- /dev/null
+++ b/lib/plugins/stonith/external/vcenter
@@ -0,0 +1,280 @@
+#!/usr/bin/env perl
+#
+# External STONITH module for VMWare vCenter/ESX
+#
+# Author: Nhan Ngo Dinh
+# License: GNU General Public License (GPL)
+#
+
+require 5.010;
+
+use strict;
+use warnings;
+
+sub dielog {
+ my $msg = "[";
+ $msg .= "$ARGV[0]" if defined($ARGV[0]);
+ $msg .= " $ARGV[1]" if defined($ARGV[1]);
+ $msg .= "]";
+ ( $_ ) = @_;
+ $msg .= " $_";
+ system("ha_log.sh", "err", "$msg");
+ die();
+}
+
+# Define command groups
+my @configCommands = qw{getconfignames getinfo-devid getinfo-devname getinfo-devdescr getinfo-devurl getinfo-xml};
+my @actionCommands = qw{reset on off};
+my @netCommands = (@actionCommands, qw{status gethosts listvms});
+
+# Process command line arguments
+my $command = $ARGV[0] || dielog("No command specified\n");
+
+# Command belongs to the group of commands that do not require any connection to VMware vCenter
+if ($command ~~ @configCommands) {
+ if ($command eq "getconfignames") {
+ print "VI_SERVER\nVI_PORTNUMBER\nVI_PROTOCOL\nVI_SERVICEPATH\nVI_CREDSTORE\nHOSTLIST\nRESETPOWERON\n";
+ }
+ elsif ($command eq "getinfo-devid") {
+ print "VMware vCenter STONITH device\n";
+ }
+ elsif ($command eq "getinfo-devname") {
+ print "VMware vCenter STONITH device\n";
+ }
+ elsif ($command eq "getinfo-devdescr") {
+ print "VMWare vCenter STONITH device\n";
+ }
+ elsif ($command eq "getinfo-devurl") {
+ print "http://www.vmware.com/\n";
+ }
+ elsif ($command eq "getinfo-xml") {
+ print q{<parameters>
+<parameter name="HOSTLIST" required="1">
+<content type="string"/>
+<shortdesc lang="en">List of hosts and virtual machines (required)</shortdesc>
+<longdesc lang="en">
+The list of hosts that the VMware vCenter STONITH device controls.
+Syntax is:
+ hostname1[=VirtualMachineName1];hostname2[=VirtualMachineName2]
+
+NOTE: omit =VirtualMachineName if hostname and virtual machine names are identical
+
+Example:
+ cluster1=VMCL1;cluster2=VMCL2
+</longdesc>
+</parameter>
+<parameter name="VI_SERVER">
+<content type="string" default="localhost"/>
+<shortdesc lang="en">VMware vCenter address</shortdesc>
+<longdesc lang="en">
+The VMware vCenter address
+</longdesc>
+</parameter>
+<parameter name="VI_PROTOCOL">
+<content type="string" default="https"/>
+<shortdesc lang="en">VMware vCenter protocol</shortdesc>
+<longdesc lang="en">
+The VMware vCenter protocol
+</longdesc>
+</parameter>
+<parameter name="VI_PORTNUMBER">
+<content type="string" default="443"/>
+<shortdesc lang="en">VMware vCenter port number</shortdesc>
+<longdesc lang="en">
+The VMware vCenter port number
+</longdesc>
+</parameter>
+<parameter name="VI_SERVICEPATH">
+<content type="string" default="/sdk"/>
+<shortdesc lang="en">VMware vCenter service path</shortdesc>
+<longdesc lang="en">
+The VMware vCenter services path
+</longdesc>
+</parameter>
+<parameter name="VI_CREDSTORE" required="1">
+<content type="string"/>
+<shortdesc lang="en">VMware vCenter credentials store file</shortdesc>
+<longdesc lang="en">
+VMware vCenter credentials store file
+</longdesc>
+</parameter>
+<parameter name="RESETPOWERON">
+<content type="string" default="1"/>
+<shortdesc lang="en">PowerOnVM on reset</shortdesc>
+<longdesc lang="en">
+Enable/disable a PowerOnVM on reset when the target virtual machine is off
+Allowed values: 0, 1
+</longdesc>
+</parameter>
+<parameter name="PERL_LWP_SSL_VERIFY_HOSTNAME">
+<content type="string"/>
+<shortdesc lang="en">Enable or disable SSL hostname verification</shortdesc>
+<longdesc lang="en">
+To disable SSL hostname verification set this option to 0.
+To enable hostname verification, set this option to 1.
+This option is actually part of the LWP Perl library.
+See LWP(3pm) for more information.
+</longdesc>
+</parameter>
+</parameters>} . "\n";
+ }
+ else { dielog("Invalid command specified: $command\n"); }
+}
+
+# Command belongs to the group of commands that require connecting to VMware vCenter
+elsif ($command ~~ @netCommands) {
+
+ eval { require VMware::VIRuntime; }
+ or dielog("Missing perl module VMware::VIRuntime. Download and install 'VMware Infrastructure (VI) Perl Toolkit', available at http://www.vmware.com/support/developer/viperltoolkit/ \n");
+
+ # A valid VI_CREDSTORE is required to avoid interactive prompt
+ ( exists $ENV{'VI_CREDSTORE'} ) || dielog("VI_CREDSTORE not specified\n");
+
+ # HOSTLIST is mandatory
+ exists $ENV{'HOSTLIST'} || dielog("HOSTLIST not specified\n");
+
+ # Parse HOSTLIST to %host_to_vm and %vm_to_host
+ my @hostlist = split(';', $ENV{'HOSTLIST'});
+ my %host_to_vm = ();
+ my %vm_to_host = ();
+ foreach my $host (@hostlist) {
+ my @config = split(/=/, $host);
+ my $key = $config[0]; my $value = $config[1];
+ if (!defined($value)) { $value = $config[0]; }
+ $host_to_vm{$key} = $value;
+ $vm_to_host{(lc $value)} = $key;
+ }
+
+ eval {
+ # VI API: reads options from the environment variables into appropriate data structures for validation.
+ Opts::parse();
+ # VI API: ensures that input values from environment variable are complete, consistent and valid.
+ Opts::validate();
+ # VI API: establishes a session with the VirtualCenter Management Server or ESX Server Web service
+ Util::connect();
+ };
+ if ($@) {
+ # This is just a placeholder for any error handling procedure
+ dielog($@);
+ }
+
+ # Command belongs to the group of commands that performs actions on Virtual Machines
+ if ($command ~~ @actionCommands) {
+
+ my $targetHost = $ARGV[1] || dielog("No target specified\n");
+
+ # Require that specified target host exists in the specified HOSTLIST
+ if (exists $host_to_vm{$targetHost}) {
+
+ my $vm;
+ my $esx;
+ eval {
+ # VI API: searches the inventory tree for a VirtualMachine managed entity whose name matches
+ # the name of the virtual machine assigned to the target host in HOSTLIST
+ $vm = Vim::find_entity_view(view_type => "VirtualMachine", filter => { name => qr/^\Q$host_to_vm{$targetHost}\E/i });
+ if (!defined $vm) {
+ dielog("Machine $targetHost was not found");
+ }
+
+ # VI API: retrieves the properties of the managed object reference runtime.host of the VirtualMachine
+ # managed entity obtained by the previous command
+ # NOTE: This is essentially a workaround to vSphere Perl SDK
+ # to allow pointing to the right HostSystem. This is probably
+ # done by changing the current HostSystem in the Web Service
+ # session context. WARNING: Do not use the same session for any
+ # other concurrent operation.
+ $esx = Vim::get_view(mo_ref => $vm->{"runtime"}{"host"})->name;
+ };
+ if ($@) {
+ if (ref($@) eq "SoapFault") { dielog("$@->detail\n"); }
+ dielog($@);
+ }
+
+ my $powerState = $vm->get_property('runtime.powerState')->val;
+ if ($powerState eq "suspended") {
+ # This implementation assumes that suspending a cluster node can cause
+ # severe failures on shared resources, thus any failover operation should
+ # be blocked.
+ dielog("Machine $esx:$vm->{'name'} is in a suspended state\n");
+ }
+
+ eval {
+ if ($command eq "reset") {
+ if ($powerState eq "poweredOn") {
+ $vm->ResetVM();
+ system("ha_log.sh", "info", "Machine $esx:$vm->{'name'} has been reset");
+ } else {
+ system("ha_log.sh", "warn", "Tried to ResetVM $esx:$vm->{'name'} that was $powerState");
+ # Start a virtual machine on reset only if explicitly allowed by RESETPOWERON
+ if ($powerState eq "poweredOff" && (! exists $ENV{'RESETPOWERON'} || $ENV{'RESETPOWERON'} ne 0)) {
+ $vm->PowerOnVM();
+ system("ha_log.sh", "info", "Machine $esx:$vm->{'name'} has been powered on");
+ } else {
+ dielog("Could not complete $esx:$vm->{'name'} power cycle");
+ }
+ }
+ }
+ elsif ($command eq "off") {
+ if ($powerState eq "poweredOn") {
+ $vm->PowerOffVM();
+ system("ha_log.sh", "info", "Machine $esx:$vm->{'name'} has been powered off");
+ } else {
+ system("ha_log.sh", "warn", "Tried to PowerOffVM $esx:$vm->{'name'} that was $powerState");
+
+ }
+ }
+ elsif ($command eq "on") {
+ if ($powerState eq "poweredOff") {
+ $vm->PowerOnVM();
+ system("ha_log.sh", "info", "Machine $esx:$vm->{'name'} has been powered on");
+ } else {
+ system("ha_log.sh", "warn", "Tried to PowerOnVM $esx:$vm->{'name'} that was $powerState");
+ }
+ }
+ else { dielog("Invalid command specified: $command\n"); }
+ };
+ if ($@) {
+ if (ref($@) eq "SoapFault") { dielog("$@->detail\n"); }
+ dielog($@);
+ }
+
+ } else { dielog("Invalid target specified\n"); }
+ } else {
+ # Command belongs to the group of commands that lookup the status of VMware vCenter and/or virtual machines
+ if ($command eq "status") {
+ # we already connect to the vcenter, no need to do
+ # anything else in status
+ ;
+ }
+ elsif ($command eq "gethosts") {
+ foreach my $key (keys(%host_to_vm)) {
+ print "$key \n";
+ }
+ }
+ elsif ($command eq "listvms") {
+ eval {
+ # VI API: Searches the inventory tree for all VirtualMachine managed objects
+ my $vms = Vim::find_entity_views(view_type => "VirtualMachine");
+ if (defined $vms) {
+ printf(STDERR "%-50s %-20s\n", "VM Name", "Power state");
+ print STDERR "-" x 70 . "\n";
+ foreach my $vm (@$vms) {
+ my $powerState = $vm->get_property('runtime.powerState')->val;
+ printf("%-50s %-20s\n", $vm->{name}, $powerState);
+ }
+ }
+ };
+ }
+ else { dielog("Invalid command specified: $command\n"); }
+ }
+ eval {
+ Util::disconnect();
+ };
+ if ($@) {
+ # This is just a placeholder for any error handling procedure
+ dielog($@);
+ }
+}
+else { dielog("Invalid command specified: $command\n"); }
+
+exit(0);
diff --git a/lib/plugins/stonith/external/vmware b/lib/plugins/stonith/external/vmware
new file mode 100644
index 0000000..55966ba
--- /dev/null
+++ b/lib/plugins/stonith/external/vmware
@@ -0,0 +1,216 @@
+#!/usr/bin/perl
+# External STONITH module for VMWare Server Guests
+#
+# Copyright (c) 2004 SUSE LINUX AG - Andrew Beekhof <abeekhof@suse.de>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+
+sub supply_default
+{
+ my $name = $_[0];
+ my $value = $_[1];
+
+ if ( defined $ENV{$name} ) {
+ #print "Set: $name=$ENV{$name}\n";
+ } else {
+ $ENV{$name} = $value;
+ #print "Default: $name=$ENV{$name}\n";
+ }
+}
+
+sub vmware_command
+{
+ my $config = $_[0];
+ my $action = $_[1];
+ my @lines;
+
+ my $device = $ENV{'device_host'};
+
+ if ( $device =~ /localhost/ ) {
+ @lines = readpipe "vmware-cmd $config $action";
+
+ } else {
+ @lines = readpipe "ssh $device \"vmware-cmd \\\"$config\\\" $action\"";
+ }
+
+ #print @lines;
+ return @lines;
+}
+
+sub is_host_active
+{
+ my $config = config_for_host($_[0]);
+ my @lines = vmware_command($config, "getstate");
+ foreach $line (@lines) {
+ if ( $line =~ /getstate.* = on/ ) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+sub supported_hosts
+{
+ my $line;
+ my @lines;
+
+ if ( defined $ENV{'host_map'} ) {
+ @lines = split(/;/, $ENV{'host_map'});
+ foreach $line (@lines){
+ @config = split(/=/, $line);
+ $host = $config[0];
+ if ( is_host_active($host) == 1 ) {
+ print "$host\n";
+ }
+ }
+
+ } else {
+ @lines = vmware_command("-l");
+ foreach $line (@lines){
+ my @elements = split(/\//, $line);
+ $host = $elements[$#elements-1];
+ if ( is_host_active($host) == 1 ) {
+ print "$host\n";
+ }
+ }
+ }
+}
+
+sub config_for_host
+{
+ my $line;
+ my @lines;
+ my $target = $_[0];
+
+ if ( defined $ENV{'host_map'} ) {
+ @lines = split(/;/, $ENV{'host_map'});
+ foreach $line (@lines){
+ if ( $line =~ /^$target=/ ) {
+ @config = split(/=/, $line);
+ return $config[1];
+ }
+ }
+
+ } else {
+ @lines = vmware_command("-l");
+
+ foreach $line (@lines){
+ if ( $line =~ /\/$target\// ) {
+ chop($line);
+ return $line;
+ }
+ }
+ }
+}
+
+$command = $ARGV[0];
+if ( defined $ARGV[1] ) {
+ $targetHost = $ARGV[1];
+}
+
+supply_default("device_host", "localhost");
+
+if ( $command =~ /^gethosts$/ ) {
+ supported_hosts;
+
+} elsif ( $command =~ /^getconfignames$/ ) {
+ print "device_host\n";
+
+} elsif ( $command =~ /^getinfo-devid$/ ) {
+ print "VMware Server STONITH device\n";
+} elsif ( $command =~ /^getinfo-devname$/ ) {
+ print "VMware Server STONITH device\n";
+} elsif ( $command =~ /^getinfo-devdescr$/ ) {
+ print "VMware Server STONITH device\n";
+} elsif ( $command =~ /^getinfo-devurl$/ ) {
+ print "http://www.vmware.com/";
+
+} elsif ( $command =~ /^on$/ ) {
+ $config = config_for_host($targetHost);
+ my @lines = vmware_command($config, "start hard");
+ print @lines;
+
+} elsif ( $command =~ /^off$/ ) {
+ $config = config_for_host($targetHost);
+ my @lines = vmware_command($config, "stop hard");
+ print @lines;
+
+} elsif ( $command =~ /^reset$/ ) {
+ $config = config_for_host($targetHost);
+ my @lines = vmware_command($config, "reset hard");
+ print @lines;
+
+} elsif ( $command =~ /^status$/ ) {
+ my $rc = 7;
+ my $device = $ENV{'device_host'};
+ if ( $device =~ /localhost/ ) {
+ $rc = 0;
+ # TODO: Check for the vmware process
+ print "Local version: always running\n";
+
+ } else {
+ print "Remote version: running ping\n";
+ @lines = readpipe "ping -c1 $device";
+ print @lines;
+
+ foreach $line ( @lines ) {
+ if ( $line =~ /0% packet loss/ ) {
+ $rc = 0;
+ last;
+ }
+ }
+ }
+ exit($rc);
+
+} elsif ( $command =~ /^getinfo-xml$/ ) {
+ $metadata = <<END;
+<parameters>
+<parameter name="host_map" unique="0" required="0">
+<content type="string" />
+<shortdesc lang="en">
+Host Map
+</shortdesc>
+<longdesc lang="en">
+A mapping of hostnames to config paths supported by this device.
+Eg. host1=/config/path/1;host2=/config/path/2;host3=/config/path/3;
+</longdesc>
+</parameter>
+<parameter name="device_host" unique="0" required="0">
+<content type="string" />
+<shortdesc lang="en">
+Device Host
+</shortdesc>
+<longdesc lang="en">
+The machine _hosting_ the virtual machines
+</longdesc>
+</parameter>
+</parameters>
+END
+
+print $metadata;
+
+} else {
+ print "Command $command: not supported\n";
+ exit(3); # Not implemented
+}
+
+
+exit(0);
diff --git a/lib/plugins/stonith/external/xen0 b/lib/plugins/stonith/external/xen0
new file mode 100644
index 0000000..ef1ee40
--- /dev/null
+++ b/lib/plugins/stonith/external/xen0
@@ -0,0 +1,253 @@
+#!/bin/sh
+#
+# External STONITH module for Xen Dom0 through ssh.
+#
+# Description: Uses Xen Dom0 Domain as a STONITH device
+# to control DomUs.
+#
+#
+# Author: Serge Dubrouski (sergeyfd@gmail.com)
+# Inspired by Lars Marowsky-Bree's external/ssh agent.
+#
+# Copyright 2007 Serge Dubrouski <sergeyfd@gmail.com>
+# License: GNU General Public License (GPL)
+#
+
+STOP_COMMAND="xm destroy"
+START_COMMAND="xm create"
+DUMP_COMMAND="xm dump-core"
+DEFAULT_XEN_DIR="/etc/xen"
+SSH_COMMAND="/usr/bin/ssh -q -x -n"
+
+# Rewrite the hostlist to accept "," as a delimeter for hostnames too.
+hostlist=`echo $hostlist | tr ',' ' '`
+
+CheckIfDead() {
+ for j in 1 2 3 4 5
+ do
+ if ! ping -w1 -c1 "$1" >/dev/null 2>&1
+ then
+ return 0
+ fi
+ sleep 1
+ done
+
+ return 1
+}
+
+CheckHostList() {
+ if [ "x" = "x$hostlist" ]
+ then
+ ha_log.sh err "hostlist isn't set"
+ exit 1
+ fi
+}
+
+CheckDom0() {
+ if [ "x" = "x$dom0" ]
+ then
+ ha_log.sh err "dom0 isn't set"
+ exit 1
+ fi
+}
+
+RunCommand() {
+ CheckHostList
+ CheckDom0
+
+ for h in $hostlist
+ do
+ CIFS=$IFS
+ IFS=:
+ read node cfg << -!
+$h
+-!
+ IFS=$CIFS
+
+ if [ "x" = "x$node" ]
+ then
+ ha_log.sh err "Syntax error in host list"
+ exit 1
+ fi
+
+ if [ "x" = "x$cfg" ]
+ then
+ cfg="${DEFAULT_XEN_DIR}/${node}.cfg"
+ fi
+
+ if [ "$node" != "$1" ]
+ then
+ continue
+ fi
+
+ case $2 in
+ stop)
+ kill_node=`$SSH_COMMAND $dom0 "grep ^[[:space:]]*name $cfg" | cut -f 2 -d '=' | sed -e 's,",,g'`
+ if [ "x" = "x$kill_node" ]
+ then
+ ha_log.sh err "Couldn't find a node name to stop"
+ exit 1
+ fi
+
+ if [ "x$run_dump" != "x" ]
+ then
+ #Need to run core dump
+ if [ "x$dump_dir" != "x" ]
+ then
+ #Dump with the specified core file
+ TIMESTAMP=`date +%Y-%m%d-%H%M.%S`
+ DOMAINNAME=`printf "%s" $kill_node`
+ COREFILE=$dump_dir/$TIMESTAMP-$DOMAINNAME.core
+ $SSH_COMMAND $dom0 "(mkdir -p $dump_dir; $DUMP_COMMAND $kill_node $COREFILE) >/dev/null 2>&1"
+ else
+ $SSH_COMMAND $dom0 "$DUMP_COMMAND $kill_node >/dev/null 2>&1"
+ fi
+ fi
+ $SSH_COMMAND $dom0 "(sleep 2; $STOP_COMMAND $kill_node) >/dev/null 2>&1 &"
+ break;;
+ start)
+ $SSH_COMMAND $dom0 "(sleep 2; $START_COMMAND $cfg) >/dev/null 2>&1 &"
+ break;;
+ esac
+ exit 0
+ done
+}
+
+
+# Main code
+
+case $1 in
+gethosts)
+ CheckHostList
+
+ for h in $hostlist ; do
+ CIFS=$IFS
+ IFS=:
+ read node cfg << -!
+$h
+-!
+ IFS=$CIFS
+
+ echo $node
+ done
+ exit 0
+ ;;
+on)
+ RunCommand $2 start
+ exit $?
+ ;;
+off)
+ if RunCommand $2 stop
+ then
+ if CheckIfDead $2
+ then
+ exit 0
+ fi
+ fi
+
+ exit 1
+ ;;
+reset)
+ RunCommand $2 stop
+
+ if CheckIfDead $2
+ then
+ RunCommand $2 start
+ exit 0
+ fi
+
+ exit 1
+ ;;
+status)
+ CheckHostList
+
+ for h in $hostlist
+ do
+ CIFS=$IFS
+ IFS=:
+ read node cfg << -!
+$h
+-!
+ IFS=$CIFS
+
+ echo $node
+ if ping -w1 -c1 "$node" 2>&1 | grep "unknown host"
+ then
+ exit 1
+ fi
+ done
+ exit 0
+ ;;
+getconfignames)
+ echo "hostlist dom0"
+ exit 0
+ ;;
+getinfo-devid)
+ echo "xen0 STONITH device"
+ exit 0
+ ;;
+getinfo-devname)
+ echo "xen0 STONITH external device"
+ exit 0
+ ;;
+getinfo-devdescr)
+ echo "ssh-based host reset for Xen DomU trough Dom0"
+ echo "Fine for testing, but not really suitable for production!"
+ exit 0
+ ;;
+getinfo-devurl)
+ echo "http://openssh.org http://www.xensource.com/ http://linux-ha.org/wiki"
+ exit 0
+ ;;
+getinfo-xml)
+ cat << SSHXML
+<parameters>
+<parameter name="hostlist" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Hostlist
+</shortdesc>
+<longdesc lang="en">
+The list of controlled nodes in a format node[:config_file].
+For example: "node1:/opt/xen/node1.cfg node2"
+If config file isn't set it defaults to /etc/xen/{node_name}.cfg
+</longdesc>
+</parameter>
+<parameter name="dom0" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Dom0
+</shortdesc>
+<longdesc lang="en">
+Name of the Dom0 Xen node. Root user shall be able to ssh to that node.
+</longdesc>
+</parameter>
+<parameter name="run_dump" unique="0" required="0">
+<content type="string" />
+<shortdesc lang="en">
+Run dump-core
+</shortdesc>
+<longdesc lang="en">
+If set plugin will call "xm dump-core" before killing DomU
+</longdesc>
+</parameter>
+<parameter name="dump_dir" unique="1" required="0">
+<content type="string" />
+<shortdesc lang="en">
+Run dump-core with the specified directory
+</shortdesc>
+<longdesc lang="en">
+This parameter can indicate the dump destination.
+Should be set as a full path format, ex.) "/var/log/dump"
+The above example would dump the core, like;
+/var/log/dump/2009-0316-1403.37-domU.core
+</longdesc>
+</parameter>
+</parameters>
+SSHXML
+ exit 0
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/xen0-ha-dom0-stonith-helper b/lib/plugins/stonith/external/xen0-ha-dom0-stonith-helper
new file mode 100755
index 0000000..b313f8b
--- /dev/null
+++ b/lib/plugins/stonith/external/xen0-ha-dom0-stonith-helper
@@ -0,0 +1,72 @@
+#!/bin/bash
+# Author: Lars Marowsky-Bree
+#
+# Copyright 2008 Lars Marowsky-Bree
+# License: GNU General Public License (GPL)
+
+# This is not an external/stonith plugin by itself, but instead a helper
+# script which gets installed in Dom0.
+
+# TODO:
+# - Error handling
+# - How to handle if the DomU resource doesn't exist?
+# - Does this truly work with split-brain?
+# - Is the handling of non-existent resources adequate?
+# ...
+# Basically: more testing. This is proof-of-concept and works, but deserves
+# validation.
+
+CMD="$1"
+DOMU="$2"
+TIMEOUT="$3"
+
+# Make sure the timeout is an integer:
+if [ "0$TIMEOUT" -eq 0 ]; then
+ TIMEOUT=300
+fi
+
+SetTargetRole() {
+ local new_role="$1"
+ crm_resource -r $DOMU --meta -p target_role -v $new_role
+
+ local timeout="$TIMEOUT"
+
+ # We only need to wait for "stopped".
+ if [ "$new_role" != "stopped" ]; then
+ return 0
+ fi
+
+ while [ $timeout -gt 0 ]; do
+ local rc
+ crm_resource -W -r $DOMU 2>&1 | grep -q "is NOT running"
+ rc=$?
+ if [ $rc -eq 0 ]; then
+ return 0
+ fi
+ timeout=$[timeout-1];
+ sleep 1
+ done
+ return 1
+}
+
+
+case $CMD in
+on) SetTargetRole started
+ exit $?
+ ;;
+off) SetTargetRole stopped
+ exit $?
+ ;;
+reset) SetTargetRole stopped || exit 1
+ SetTargetRole started
+ exit $?
+ ;;
+status) exit 0
+ ;;
+*) ha_log.sh err "Called with unknown command: $CMD"
+ exit 1
+ ;;
+esac
+
+exit 1
+
diff --git a/lib/plugins/stonith/external/xen0-ha.in b/lib/plugins/stonith/external/xen0-ha.in
new file mode 100755
index 0000000..cb42cbc
--- /dev/null
+++ b/lib/plugins/stonith/external/xen0-ha.in
@@ -0,0 +1,96 @@
+#!/bin/bash
+#
+# This STONITH script integrates a cluster running within DomUs
+# with the CRM/Pacemaker cluster running in Dom0.
+#
+# Author: Lars Marowsky-Bree
+# Copyright: 2008 Lars Marowsky-Bree
+# License: GNU General Public License (GPL)
+#
+
+SSH_COMMAND="@SSH@ -q -x -n"
+HVM_HELPER="@stonith_plugindir@/xen0-ha-dom0-stonith-helper"
+
+# Rewrite the hostlist to accept "," as a delimeter for hostnames too.
+hostlist=`echo $hostlist | tr ',' ' '`
+
+# Runs a command on the host, waiting for it to return
+RunHVMCommand() {
+ $SSH_COMMAND $dom0_cluster_ip "$HVM_HELPER $1 $2 $stop_timeout"
+}
+
+# Main code
+case $1 in
+gethosts)
+ echo $hostlist
+ exit 0
+ ;;
+on|off|reset|status)
+ RunHVMCommand $1 $2
+ exit $?
+ ;;
+getconfignames)
+ echo "hostlist dom0_cluster_ip timeout"
+ exit 0
+ ;;
+getinfo-devid)
+ echo "xen0-ha DomU/Dom0 device"
+ exit 0
+ ;;
+getinfo-devname)
+ echo "xen0-ha DomU/Dom0 external device"
+ exit 0
+ ;;
+getinfo-devdescr)
+ echo "Allows STONITH to control DomUs managed by a CRM/Pacemaker Dom0."
+ echo "Requires Xen + CRM/Pacemaker at both layers."
+ echo "Proof-of-concept code!"
+ exit 0
+ ;;
+getinfo-devurl)
+ echo "http://linux-ha.org/wiki/DomUClusters"
+ exit 0
+ ;;
+getinfo-xml)
+ cat << SSHXML
+<parameters>
+<parameter name="hostlist" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Hostlist
+</shortdesc>
+<longdesc lang="en">
+The list of controlled DomUs, separated by whitespace.
+These must be configured as Xen RA resources with a name with a matching
+id.
+For example: "xen-1 xen-2 xen-3"
+</longdesc>
+</parameter>
+<parameter name="dom0_cluster_ip" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Dom0 cluster ip
+</shortdesc>
+<longdesc lang="en">
+The cluster IP address associated with Dom0.
+Root user must be able to ssh to that node.
+</longdesc>
+</parameter>
+<parameter name="stop_timeout">
+<content type="integer" />
+<shortdesc lang="en">
+Stop timeout
+</shortdesc>
+<longdesc lang="en">
+The timeout, in seconds, for which to wait for Dom0 to report that the
+DomU has been stopped, before aborting with a failure.
+</longdesc>
+</parameter>
+</parameters>
+SSHXML
+ exit 0
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/ibmhmc.c b/lib/plugins/stonith/ibmhmc.c
new file mode 100644
index 0000000..d33fea9
--- /dev/null
+++ b/lib/plugins/stonith/ibmhmc.c
@@ -0,0 +1,1261 @@
+/*
+ * Stonith module for IBM Hardware Management Console (HMC)
+ *
+ * Author: Huang Zhen <zhenh@cn.ibm.com>
+ * Support for HMC V4+ added by Dave Blaschke <debltc@us.ibm.com>
+ *
+ * Copyright (c) 2004 International Business Machines
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ *
+ * This code has been tested in following environment:
+ *
+ * Hardware Management Console (HMC): Release 3, Version 2.4
+ * - Both FullSystemPartition and LPAR Partition:
+ * - p630 7028-6C4 two LPAR partitions
+ * - p650 7038-6M2 one LPAR partition and FullSystemPartition
+ *
+ * Hardware Management Console (HMC): Version 4, Release 2.1
+ * - OP720 1000-6CA three LPAR partitions
+ *
+ * Note: Only SSH access to the HMC devices are supported.
+ *
+ * This command would make a nice status command:
+ *
+ * lshmc -r -F ssh
+ *
+ * The following V3 command will get the list of systems we control and their
+ * mode:
+ *
+ * lssyscfg -r sys -F name:mode --all
+ *
+ * 0 indicates full system partition
+ * 255 indicates the system is partitioned
+ *
+ * The following V4 command will get the list of systems we control:
+ *
+ * lssyscfg -r sys -F name
+ *
+ * The following V3 command will get the list of partitions for a given managed
+ * system running partitioned:
+ *
+ * lssyscfg -m managed-system -r lpar -F name --all
+ *
+ * Note that we should probably only consider partitions whose boot mode
+ * is normal (1). (that's my guess, anyway...)
+ *
+ * The following V4 command will get the list of partitions for a given managed
+ * system running partitioned:
+ *
+ * lssyscfg -m managed-system -r lpar -F name
+ *
+ * The following V3 commands provide the reset/on/off actions:
+ *
+ * FULL SYSTEM:
+ * on: chsysstate -m %1 -r sys -o on -n %1 -c full
+ * off: chsysstate -m %1 -r sys -o off -n %1 -c full -b norm
+ * reset:chsysstate -m %1 -r sys -o reset -n %1 -c full -b norm
+ *
+ * Partitioned SYSTEM:
+ * on: chsysstate -m %1 -r lpar -o on -n %2
+ * off: reset_partition -m %1 -p %2 -t hard
+ * reset:do off action above, followed by on action...
+ *
+ * where %1 is managed-system, %2 is-lpar name
+ *
+ * The following V4 commands provide the reset/on/off actions:
+ *
+ * on: chsysstate -m %1 -r lpar -o on -n %2 -f %3
+ * off: chsysstate -m %1 -r lpar -o shutdown -n %2 --immed
+ * reset:chsysstate -m %1 -r lpar -o shutdown -n %2 --immed --restart
+ *
+ * where %1 is managed-system, %2 is lpar-name, %3 is profile-name
+ *
+ * Of course, to do all this, we need to track which partition name goes with
+ * which managed system's name, and which systems on the HMC are partitioned
+ * and which ones aren't...
+ */
+
+#include <lha_internal.h>
+
+#define DEVICE "IBM HMC"
+
+#include "stonith_plugin_common.h"
+
+#ifndef SSH_CMD
+# define SSH_CMD "ssh"
+#endif
+#ifndef HMCROOT
+# define HMCROOT "hscroot"
+#endif
+
+#define PIL_PLUGIN ibmhmc
+#define PIL_PLUGIN_S "ibmhmc"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+#define MAX_HOST_NAME_LEN (256*4)
+#define MAX_CMD_LEN 2048
+#define FULLSYSTEMPARTITION "FullSystemPartition"
+#define MAX_POWERON_RETRY 10
+#define MAX_HMC_NAME_LEN 256
+
+#define ST_MANSYSPAT "managedsyspat"
+#define NOPASS "nopass"
+
+#define STATE_UNKNOWN -1
+#define STATE_OFF 0
+#define STATE_ON 1
+#define STATE_INVALID 2
+
+#define HMCURL "http://publib-b.boulder.ibm.com/redbooks.nsf/RedbookAbstracts"\
+ "/SG247038.html"
+
+static StonithPlugin * ibmhmc_new(const char *);
+static void ibmhmc_destroy(StonithPlugin *);
+static const char * ibmhmc_getinfo(StonithPlugin * s, int InfoType);
+static const char * const * ibmhmc_get_confignames(StonithPlugin* p);
+static int ibmhmc_status(StonithPlugin * );
+static int ibmhmc_reset_req(StonithPlugin * s,int request,const char* host);
+static char ** ibmhmc_hostlist(StonithPlugin *);
+static int ibmhmc_set_config(StonithPlugin *, StonithNVpair*);
+
+static struct stonith_ops ibmhmcOps = {
+ ibmhmc_new, /* Create new STONITH object */
+ ibmhmc_destroy, /* Destroy STONITH object */
+ ibmhmc_getinfo, /* Return STONITH info string */
+ ibmhmc_get_confignames, /* Return configuration parameters */
+ ibmhmc_set_config, /* Set configuration */
+ ibmhmc_status, /* Return STONITH device status */
+ ibmhmc_reset_req, /* Request a reset */
+ ibmhmc_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &ibmhmcOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ char * idinfo;
+ char * hmc;
+ GList* hostlist;
+ int hmcver;
+ char * password;
+ char ** mansyspats;
+};
+
+static const char * pluginid = "HMCDevice-Stonith";
+static const char * NOTpluginID = "IBM HMC device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+#define XML_MANSYSPAT_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_MANSYSPAT \
+ XML_PARM_SHORTDESC_END
+
+#define XML_MANSYSPAT_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "White-space delimited list of patterns used to match managed system names; if last character is '*', all names that begin with the pattern are matched" \
+ XML_PARM_LONGDESC_END
+
+#define XML_MANSYSPAT_PARM \
+ XML_PARAMETER_BEGIN(ST_MANSYSPAT, "string", "0", "0") \
+ XML_MANSYSPAT_SHORTDESC \
+ XML_MANSYSPAT_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_OPTPASSWD_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "Password for " HMCROOT " if passwordless ssh access to HMC has NOT been setup (to do so, it is necessary to create a public/private key pair with empty passphrase - see \"Configure the OpenSSH Client\" in the redbook at " HMCURL " for more details)" \
+ XML_PARM_LONGDESC_END
+
+#define XML_OPTPASSWD_PARM \
+ XML_PARAMETER_BEGIN(ST_PASSWD, "string", "0", "0") \
+ XML_PASSWD_SHORTDESC \
+ XML_OPTPASSWD_LONGDESC \
+ XML_PARAMETER_END
+
+static const char *ibmhmcXML =
+ XML_PARAMETERS_BEGIN
+ XML_IPADDR_PARM
+ XML_MANSYSPAT_PARM
+ XML_OPTPASSWD_PARM
+ XML_PARAMETERS_END;
+
+static int get_hmc_hostlist(struct pluginDevice* dev);
+static void free_hmc_hostlist(struct pluginDevice* dev);
+static int get_hmc_mansyspats(struct pluginDevice* dev, const char* mansyspats);
+static void free_hmc_mansyspats(struct pluginDevice* dev);
+static char* do_shell_cmd(const char* cmd, int* status, const char* password);
+static int check_hmc_status(struct pluginDevice* dev);
+static int get_num_tokens(char *str);
+static gboolean pattern_match(char **patterns, char *string);
+/* static char* do_shell_cmd_fake(const char* cmd, int* status); */
+
+static int
+ibmhmc_status(StonithPlugin *s)
+{
+ struct pluginDevice* dev = NULL;
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: called\n", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ dev = (struct pluginDevice*) s;
+
+ return check_hmc_status(dev);
+}
+
+
+/*
+ * Return the list of hosts configured for this HMC device
+ */
+
+static char **
+ibmhmc_hostlist(StonithPlugin *s)
+{
+ int j;
+ struct pluginDevice* dev;
+ int numnames = 0;
+ char** ret = NULL;
+ GList* node = NULL;
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: called\n", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,NULL);
+
+ dev = (struct pluginDevice*) s;
+
+ /* refresh the hostlist */
+ free_hmc_hostlist(dev);
+ if (S_OK != get_hmc_hostlist(dev)){
+ LOG(PIL_CRIT, "unable to obtain list of managed systems in %s"
+ , __FUNCTION__);
+ return NULL;
+ }
+
+ numnames = g_list_length(dev->hostlist);
+ if (numnames < 0) {
+ LOG(PIL_CRIT, "unconfigured stonith object in %s"
+ , __FUNCTION__);
+ return(NULL);
+ }
+
+ ret = (char **)MALLOC((numnames+1)*sizeof(char*));
+ if (ret == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return ret;
+ }
+
+ memset(ret, 0, (numnames+1)*sizeof(char*));
+ for (node = g_list_first(dev->hostlist), j = 0
+ ; NULL != node
+ ; j++, node = g_list_next(node)) {
+ char* host = strchr((char*)node->data, '/');
+ ret[j] = STRDUP(++host);
+ if (ret[j] == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ stonith_free_hostlist(ret);
+ return NULL;
+ }
+ strdown(ret[j]);
+ }
+ return ret;
+}
+
+
+static const char * const *
+ibmhmc_get_confignames(StonithPlugin* p)
+{
+ static const char * names[] = {ST_IPADDR, NULL};
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+ return names;
+}
+
+
+/*
+ * Reset the given host, and obey the request type.
+ * We should reset without power cycle for the non-partitioned case
+ */
+
+static int
+ibmhmc_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ GList* node = NULL;
+ struct pluginDevice* dev = NULL;
+ char off_cmd[MAX_CMD_LEN];
+ char on_cmd[MAX_CMD_LEN];
+ char reset_cmd[MAX_CMD_LEN];
+ gchar** names = NULL;
+ int i;
+ int is_lpar = FALSE;
+ int status;
+ char* pch;
+ char* output = NULL;
+ char state_cmd[MAX_CMD_LEN];
+ int state = STATE_UNKNOWN;
+
+ status = 0;
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: called, host=%s\n", __FUNCTION__, host);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ if (NULL == host) {
+ LOG(PIL_CRIT, "invalid argument to %s", __FUNCTION__);
+ return(S_OOPS);
+ }
+
+ dev = (struct pluginDevice*) s;
+
+ for (node = g_list_first(dev->hostlist)
+ ; NULL != node
+ ; node = g_list_next(node)) {
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: node->data=%s\n"
+ , __FUNCTION__, (char*)node->data);
+ }
+
+ if ((pch = strchr((char*)node->data, '/')) != NULL
+ && 0 == strcasecmp(++pch, host)) {
+ break;
+ }
+ }
+
+ if (!node) {
+ LOG(PIL_CRIT
+ , "Host %s is not configured in this STONITH module. "
+ "Please check your configuration information.", host);
+ return (S_OOPS);
+ }
+
+ names = g_strsplit((char*)node->data, "/", 2);
+ /* names[0] will be the name of managed system */
+ /* names[1] will be the name of the lpar partition */
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: names[0]=%s, names[1]=%s\n"
+ , __FUNCTION__, names[0], names[1]);
+ }
+
+ if (dev->hmcver < 4) {
+ if (0 == strcasecmp(names[1], FULLSYSTEMPARTITION)) {
+ is_lpar = FALSE;
+
+ snprintf(off_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s chsysstate"
+ " -r sys -m %s -o off -n %s -c full"
+ , dev->hmc, dev->hmc, names[0]);
+
+ snprintf(on_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s chsysstate"
+ " -r sys -m %s -o on -n %s -c full -b norm"
+ , dev->hmc, names[0], names[0]);
+
+ snprintf(reset_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s chsysstate"
+ " -r sys -m %s -o reset -n %s -c full -b norm"
+ , dev->hmc, names[0], names[0]);
+
+ *state_cmd = 0;
+ }else{
+ is_lpar = TRUE;
+
+ snprintf(off_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s reset_partition"
+ " -m %s -p %s -t hard"
+ , dev->hmc, names[0], names[1]);
+
+ snprintf(on_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s chsysstate"
+ " -r lpar -m %s -o on -n %s"
+ , dev->hmc, names[0], names[1]);
+
+ *reset_cmd = 0;
+
+ snprintf(state_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s lssyscfg"
+ " -r lpar -m %s -F state -n %s"
+ , dev->hmc, names[0], names[1]);
+ }
+ }else{
+ is_lpar = TRUE;
+
+ snprintf(off_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s chsysstate"
+ " -m %s -r lpar -o shutdown -n \"%s\" --immed"
+ , dev->hmc, names[0], names[1]);
+
+ snprintf(on_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s lssyscfg"
+ " -m %s -r lpar -F \"default_profile\""
+ " --filter \"lpar_names=%s\""
+ , dev->hmc, names[0], names[1]);
+
+ output = do_shell_cmd(on_cmd, &status, dev->password);
+ if (output == NULL) {
+ LOG(PIL_CRIT, "command %s failed", on_cmd);
+ return (S_OOPS);
+ }
+ if ((pch = strchr(output, '\n')) != NULL) {
+ *pch = 0;
+ }
+ snprintf(on_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s chsysstate"
+ " -m %s -r lpar -o on -n %s -f %s"
+ , dev->hmc, names[0], names[1], output);
+ FREE(output);
+ output = NULL;
+
+ snprintf(reset_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s chsysstate"
+ " -m %s -r lpar -o shutdown -n %s --immed --restart"
+ , dev->hmc, names[0], names[1]);
+
+ snprintf(state_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s lssyscfg"
+ " -m %s -r lpar -F state --filter \"lpar_names=%s\""
+ , dev->hmc, names[0], names[1]);
+ }
+ g_strfreev(names);
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: off_cmd=%s, on_cmd=%s,"
+ "reset_cmd=%s, state_cmd=%s\n"
+ , __FUNCTION__, off_cmd, on_cmd, reset_cmd, state_cmd);
+ }
+
+ output = do_shell_cmd(state_cmd, &status, dev->password);
+ if (output == NULL) {
+ LOG(PIL_CRIT, "command %s failed", on_cmd);
+ return S_OOPS;
+ }
+ if ((pch = strchr(output, '\n')) != NULL) {
+ *pch = 0;
+ }
+ if (strcmp(output, "Running") == 0
+ || strcmp(output, "Starting") == 0
+ || strcmp(output, "Open Firmware") == 0) {
+ state = STATE_ON;
+ }else if (strcmp(output, "Shutting Down") == 0
+ || strcmp(output, "Not Activated") == 0
+ || strcmp(output, "Ready") == 0) {
+ state = STATE_OFF;
+ }else if (strcmp(output, "Not Available") == 0
+ || strcmp(output, "Error") == 0) {
+ state = STATE_INVALID;
+ }
+ FREE(output);
+ output = NULL;
+
+ if (state == STATE_INVALID) {
+ LOG(PIL_CRIT, "host %s in invalid state", host);
+ return S_OOPS;
+ }
+
+ switch (request) {
+ case ST_POWERON:
+ if (state == STATE_ON) {
+ LOG(PIL_INFO, "host %s already on", host);
+ return S_OK;
+ }
+
+ output = do_shell_cmd(on_cmd, &status, dev->password);
+ if (0 != status) {
+ LOG(PIL_CRIT, "command %s failed", on_cmd);
+ return S_OOPS;
+ }
+ break;
+ case ST_POWEROFF:
+ if (state == STATE_OFF) {
+ LOG(PIL_INFO, "host %s already off", host);
+ return S_OK;
+ }
+
+ output = do_shell_cmd(off_cmd, &status, dev->password);
+ if (0 != status) {
+ LOG(PIL_CRIT, "command %s failed", off_cmd);
+ return S_OOPS;
+ }
+ break;
+ case ST_GENERIC_RESET:
+ if (dev->hmcver < 4) {
+ if (is_lpar) {
+ if (state == STATE_ON) {
+ output = do_shell_cmd(off_cmd
+ , &status, dev->password);
+ if (0 != status) {
+ LOG(PIL_CRIT, "command %s "
+ "failed", off_cmd);
+ return S_OOPS;
+ }
+ }
+ for (i = 0; i < MAX_POWERON_RETRY; i++) {
+ char *output2;
+ output2 = do_shell_cmd(on_cmd
+ , &status, dev->password);
+ if (output2 != NULL) {
+ FREE(output2);
+ }
+ if (0 != status) {
+ sleep(1);
+ }else{
+ break;
+ }
+ }
+ if (MAX_POWERON_RETRY == i) {
+ LOG(PIL_CRIT, "command %s failed"
+ , on_cmd);
+ return S_OOPS;
+ }
+ }else{
+ output = do_shell_cmd(reset_cmd
+ , &status, dev->password);
+ if (0 != status) {
+ LOG(PIL_CRIT, "command %s failed" , reset_cmd);
+ return S_OOPS;
+ }
+ break;
+ }
+ }else{
+ if (state == STATE_ON) {
+ output = do_shell_cmd(reset_cmd
+ , &status, dev->password);
+ }else{
+ output = do_shell_cmd(on_cmd
+ , &status, dev->password);
+ }
+ if (0 != status) {
+ LOG(PIL_CRIT, "command %s failed", reset_cmd);
+ return S_OOPS;
+ }
+ }
+ break;
+ default:
+ return S_INVAL;
+ }
+
+ if (output != NULL) {
+ FREE(output);
+ }
+
+ LOG(PIL_INFO, "Host %s %s %d.", host, __FUNCTION__, request);
+
+ return S_OK;
+}
+
+
+/*
+ * Parse the information in the given configuration file,
+ * and stash it away...
+ */
+
+static int
+ibmhmc_set_config(StonithPlugin * s, StonithNVpair* list)
+{
+ struct pluginDevice* dev = NULL;
+ StonithNamesToGet namestocopy [] =
+ { {ST_IPADDR, NULL}
+ , {NULL, NULL}
+ };
+ int rc;
+ char get_hmcver[MAX_CMD_LEN];
+ char firstchar;
+ int firstnum;
+ char* output = NULL;
+ int status;
+ const char *mansyspats;
+ int len;
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: called\n", __FUNCTION__);
+ }
+
+ dev = (struct pluginDevice*) s;
+
+ if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: ipaddr=%s\n", __FUNCTION__
+ , namestocopy[0].s_value);
+ }
+
+ if (get_num_tokens(namestocopy[0].s_value) == 1) {
+ /* name=value pairs on command line, look for managedsyspat */
+ mansyspats = OurImports->GetValue(list, ST_MANSYSPAT);
+ if (mansyspats != NULL) {
+ if (get_hmc_mansyspats(dev, mansyspats) != S_OK) {
+ FREE(namestocopy[0].s_value);
+ return S_OOPS;
+ }
+ }
+ /* look for password */
+ dev->password = STRDUP(OurImports->GetValue(list, ST_PASSWD));
+ dev->hmc = namestocopy[0].s_value;
+ }else{
+ /* -p or -F option with args "ipaddr [managedsyspat]..." */
+ char *pch = namestocopy[0].s_value;
+
+ /* skip over ipaddr and null-terminate */
+ pch += strcspn(pch, WHITESPACE);
+ *pch = EOS;
+
+ /* skip over white-space up to next token */
+ pch++;
+ pch += strspn(pch, WHITESPACE);
+ if (get_hmc_mansyspats(dev, pch) != S_OK) {
+ FREE(namestocopy[0].s_value);
+ return S_OOPS;
+ }
+
+ dev->hmc = STRDUP(namestocopy[0].s_value);
+ FREE(namestocopy[0].s_value);
+ }
+
+ /* check whether the HMC has ssh command enabled */
+ if (check_hmc_status(dev) != S_OK) {
+ LOG(PIL_CRIT, "HMC %s does not have remote "
+ "command execution using the ssh facility enabled", dev->hmc);
+ return S_BADCONFIG;
+ }
+
+ /* get the HMC's version info */
+ snprintf(get_hmcver, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s lshmc -v | grep RM", dev->hmc);
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: get_hmcver=%s", __FUNCTION__, get_hmcver);
+ }
+
+ output = do_shell_cmd(get_hmcver, &status, dev->password);
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: output=%s\n", __FUNCTION__
+ , output ? output : "(nil)");
+ }
+ if (output == NULL) {
+ return S_BADCONFIG;
+ }
+
+ /* parse the HMC's version info (i.e. "*RM V4R2.1" or "*RM R3V2.6") */
+ if ((sscanf(output, "*RM %c%1d", &firstchar, &firstnum) == 2)
+ && ((firstchar == 'V') || (firstchar == 'R'))) {
+ dev->hmcver = firstnum;
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: HMC %s version is %d"
+ , __FUNCTION__, dev->hmc, dev->hmcver);
+ }
+ }else{
+ LOG(PIL_CRIT, "%s: unable to determine HMC %s version"
+ , __FUNCTION__, dev->hmc);
+ FREE(output);
+ return S_BADCONFIG;
+ }
+
+ len = strlen(output+4) + sizeof(DEVICE) + 1;
+ if (dev->idinfo != NULL) {
+ FREE(dev->idinfo);
+ dev->idinfo = NULL;
+ }
+ dev->idinfo = MALLOC(len * sizeof(char));
+ if (dev->idinfo == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ FREE(output);
+ return S_OOPS;
+ }
+ snprintf(dev->idinfo, len, "%s %s", DEVICE, output+4);
+ FREE(output);
+
+ if (S_OK != get_hmc_hostlist(dev)){
+ LOG(PIL_CRIT, "unable to obtain list of managed systems in %s"
+ , __FUNCTION__);
+ return S_BADCONFIG;
+ }
+
+ return S_OK;
+}
+
+
+static const char*
+ibmhmc_getinfo(StonithPlugin* s, int reqtype)
+{
+ struct pluginDevice* dev;
+ const char* ret;
+
+ ERRIFWRONGDEV(s,NULL);
+
+ dev = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = dev->idinfo;
+ break;
+
+ case ST_DEVICENAME:
+ ret = dev->hmc;
+ break;
+
+ case ST_DEVICEDESCR:
+ ret = "IBM Hardware Management Console (HMC)\n"
+ "Use for IBM i5, p5, pSeries and OpenPower systems "
+ "managed by HMC\n"
+ " Optional parameter name " ST_MANSYSPAT " is "
+ "white-space delimited list of\n"
+ "patterns used to match managed system names; if last "
+ "character is '*',\n"
+ "all names that begin with the pattern are matched\n"
+ " Optional parameter name " ST_PASSWD " is password "
+ "for " HMCROOT " if passwordless\n"
+ "ssh access to HMC has NOT been setup (to do so, it "
+ "is necessary to create\n"
+ "a public/private key pair with empty passphrase - "
+ "see \"Configure the\n"
+ "OpenSSH client\" in the redbook for more details)";
+ break;
+
+ case ST_DEVICEURL:
+ ret = HMCURL;
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = ibmhmcXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+
+/*
+ * HMC Stonith destructor...
+ */
+
+static void
+ibmhmc_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* dev;
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s : called\n", __FUNCTION__);
+ }
+
+ VOIDERRIFWRONGDEV(s);
+
+ dev = (struct pluginDevice *)s;
+
+ dev->pluginid = NOTpluginID;
+ if (dev->hmc) {
+ FREE(dev->hmc);
+ dev->hmc = NULL;
+ }
+ if (dev->password) {
+ FREE(dev->password);
+ dev->password = NULL;
+ }
+ if (dev->idinfo) {
+ FREE(dev->idinfo);
+ dev->idinfo = NULL;
+ }
+ free_hmc_hostlist(dev);
+ free_hmc_mansyspats(dev);
+
+ FREE(dev);
+}
+
+
+static StonithPlugin *
+ibmhmc_new(const char *subplugin)
+{
+ struct pluginDevice* dev = ST_MALLOCT(struct pluginDevice);
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: called\n", __FUNCTION__);
+ }
+
+ if (dev == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ return(NULL);
+ }
+
+ memset(dev, 0, sizeof(*dev));
+
+ dev->pluginid = pluginid;
+ dev->hmc = NULL;
+ dev->password = NULL;
+ dev->hostlist = NULL;
+ dev->mansyspats = NULL;
+ dev->hmcver = -1;
+ REPLSTR(dev->idinfo, DEVICE);
+ if (dev->idinfo == NULL) {
+ FREE(dev);
+ return(NULL);
+ }
+ dev->sp.s_ops = &ibmhmcOps;
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: returning successfully\n", __FUNCTION__);
+ }
+
+ return((void *)dev);
+}
+
+static int
+get_hmc_hostlist(struct pluginDevice* dev)
+{
+ int i, j, status;
+ char* output = NULL;
+ char get_syslist[MAX_CMD_LEN];
+ char host[MAX_HOST_NAME_LEN];
+ gchar** syslist = NULL;
+ gchar** name_mode = NULL;
+ char get_lpar[MAX_CMD_LEN];
+ gchar** lparlist = NULL;
+ char* pch;
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: called, dev->hmc=%s\n", __FUNCTION__
+ , dev->hmc);
+ }
+
+ if (dev->hmc == NULL || *dev->hmc == 0){
+ return S_BADCONFIG;
+ }
+
+ /* get the managed system's names of the hmc */
+ if (dev->hmcver < 4) {
+ snprintf(get_syslist, MAX_CMD_LEN, SSH_CMD " -l " HMCROOT
+ " %s lssyscfg -r sys -F name:mode --all", dev->hmc);
+ }else{
+ snprintf(get_syslist, MAX_CMD_LEN, SSH_CMD
+ " -l " HMCROOT " %s lssyscfg -r sys -F name", dev->hmc);
+ }
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: get_syslist=%s", __FUNCTION__, get_syslist);
+ }
+
+ output = do_shell_cmd(get_syslist, &status, dev->password);
+ if (output == NULL) {
+ return S_BADCONFIG;
+ }
+ syslist = g_strsplit(output, "\n", 0);
+ FREE(output);
+
+ /* for each managed system */
+ for (i = 0; syslist[i] != NULL && syslist[i][0] != 0; i++) {
+ if (dev->hmcver < 4) {
+ name_mode = g_strsplit(syslist[i], ":", 2);
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: name_mode0=%s, name_mode1=%s\n"
+ , __FUNCTION__, name_mode[0], name_mode[1]);
+ }
+
+ if (dev->mansyspats != NULL
+ && !pattern_match(dev->mansyspats, name_mode[0])) {
+ continue;
+ }
+
+ /* if it is in fullsystempartition */
+ if (NULL != name_mode[1]
+ && 0 == strncmp(name_mode[1], "0", 1)) {
+ /* add the FullSystemPartition */
+ snprintf(host, MAX_HOST_NAME_LEN
+ , "%s/FullSystemPartition", name_mode[0]);
+ dev->hostlist = g_list_append(dev->hostlist
+ , STRDUP(host));
+ }else if (NULL != name_mode[1]
+ && 0 == strncmp(name_mode[1], "255", 3)){
+ /* get its lpars */
+ snprintf(get_lpar, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT
+ " %s lssyscfg -m %s -r lpar -F name --all"
+ , dev->hmc, name_mode[0]);
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: get_lpar=%s\n"
+ , __FUNCTION__, get_lpar);
+ }
+
+ output = do_shell_cmd(get_lpar
+ , &status, dev->password);
+ if (output == NULL) {
+ g_strfreev(name_mode);
+ g_strfreev(syslist);
+ return S_BADCONFIG;
+ }
+ lparlist = g_strsplit(output, "\n", 0);
+ FREE(output);
+
+ /* for each lpar */
+ for (j = 0
+ ; NULL != lparlist[j] && 0 != lparlist[j][0]
+ ; j++) {
+ /* skip the full system partition */
+ if (0 == strncmp(lparlist[j]
+ , FULLSYSTEMPARTITION
+ , strlen(FULLSYSTEMPARTITION))) {
+ continue;
+ }
+ /* add the lpar */
+ snprintf(host, MAX_HOST_NAME_LEN
+ , "%s/%s", name_mode[0]
+ , lparlist[j]);
+ dev->hostlist =
+ g_list_append(dev->hostlist
+ , STRDUP(host));
+ }
+ g_strfreev(lparlist);
+ }
+ g_strfreev(name_mode);
+ }else{
+ if (dev->mansyspats != NULL
+ && !pattern_match(dev->mansyspats, syslist[i])) {
+ continue;
+ }
+
+ /* get its state */
+ snprintf(get_lpar, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT
+ " %s lssyscfg -m %s -r sys -F state"
+ , dev->hmc, syslist[i]);
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: get_lpar=%s\n"
+ , __FUNCTION__, get_lpar);
+ }
+
+ output = do_shell_cmd(get_lpar, &status, dev->password);
+ if (output == NULL) {
+ g_strfreev(syslist);
+ return S_BADCONFIG;
+ }
+ if ((pch = strchr(output, '\n')) != NULL) {
+ *pch = 0;
+ }
+ if (!strcmp(output, "No Connection")){
+ FREE(output);
+ continue;
+ }
+ FREE(output);
+
+ /* get its lpars */
+ snprintf(get_lpar, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT
+ " %s lssyscfg -m %s -r lpar -F name"
+ , dev->hmc, syslist[i]);
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: get_lpar=%s\n"
+ , __FUNCTION__, get_lpar);
+ }
+
+ output = do_shell_cmd(get_lpar, &status, dev->password);
+ if (output == NULL) {
+ g_strfreev(syslist);
+ return S_BADCONFIG;
+ }
+ lparlist = g_strsplit(output, "\n", 0);
+ FREE(output);
+
+ /* for each lpar */
+ for (j = 0
+ ; NULL != lparlist[j] && 0 != lparlist[j][0]
+ ; j++) {
+ /* add the lpar */
+ snprintf(host, MAX_HOST_NAME_LEN
+ , "%s/%s", syslist[i],lparlist[j]);
+ dev->hostlist = g_list_append(dev->hostlist
+ , STRDUP(host));
+ }
+ g_strfreev(lparlist);
+ }
+ }
+ g_strfreev(syslist);
+
+ return S_OK;
+}
+
+static void
+free_hmc_hostlist(struct pluginDevice* dev)
+{
+ if (dev->hostlist) {
+ GList* node;
+ while (NULL != (node=g_list_first(dev->hostlist))) {
+ dev->hostlist = g_list_remove_link(dev->hostlist, node);
+ FREE(node->data);
+ g_list_free(node);
+ }
+ dev->hostlist = NULL;
+ }
+}
+
+static int
+get_hmc_mansyspats(struct pluginDevice * dev, const char *mansyspats)
+{
+ char *patscopy;
+ int numpats;
+ int i;
+ char *tmp;
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: called, mansyspats=%s\n"
+ , __FUNCTION__, mansyspats);
+ }
+
+ patscopy = STRDUP(mansyspats);
+ if (patscopy == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ return S_OOPS;
+ }
+
+ numpats = get_num_tokens(patscopy);
+ if (numpats > 0) {
+ dev->mansyspats = MALLOC((numpats+1)*sizeof(char *));
+ if (dev->mansyspats == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory"
+ , __FUNCTION__);
+ FREE(patscopy);
+ return S_OOPS;
+ }
+
+ memset(dev->mansyspats, 0, (numpats+1)*sizeof(char *));
+
+ /* White-space split the output here */
+ i = 0;
+ tmp = strtok(patscopy, WHITESPACE);
+ while (tmp != NULL) {
+ dev->mansyspats[i] = STRDUP(tmp);
+ if (dev->mansyspats[i] == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory"
+ , __FUNCTION__);
+ free_hmc_mansyspats(dev);
+ dev->mansyspats = NULL;
+ FREE(patscopy);
+ return S_OOPS;
+ }
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: adding pattern %s\n"
+ , __FUNCTION__, dev->mansyspats[i]);
+ }
+
+ /* no patterns necessary if all specified */
+ if (strcmp(dev->mansyspats[i], "*") == 0) {
+ stonith_free_hostlist(dev->mansyspats);
+ dev->mansyspats = NULL;
+ break;
+ }
+
+ i++;
+ tmp = strtok(NULL, WHITESPACE);
+ }
+ }
+ FREE(patscopy);
+ return S_OK;
+}
+
+static void
+free_hmc_mansyspats(struct pluginDevice* dev)
+{
+ if (dev->mansyspats) {
+ stonith_free_hostlist(dev->mansyspats);
+ dev->mansyspats = NULL;
+ }
+}
+
+static char*
+do_shell_cmd(const char* cmd, int* status, const char* password)
+{
+ const int BUFF_LEN=4096;
+ int read_len = 0;
+ char buff[BUFF_LEN];
+ char cmd_password[MAX_CMD_LEN];
+ char* data = NULL;
+ GString* g_str_tmp = NULL;
+
+ FILE* file;
+ if (NULL == password) {
+ file = popen(cmd, "r");
+ } else {
+ snprintf(cmd_password, MAX_CMD_LEN
+ ,"umask 077;"
+ "if [ ! -d " HA_VARRUNDIR "/heartbeat/rsctmp/ibmhmc ];"
+ "then mkdir " HA_VARRUNDIR "/heartbeat/rsctmp/ibmhmc 2>/dev/null;"
+ "fi;"
+ "export ibmhmc_tmp=`mktemp -p " HA_VARRUNDIR "/heartbeat/rsctmp/ibmhmc/`;"
+ "echo \"echo '%s'\">$ibmhmc_tmp;"
+ "chmod +x $ibmhmc_tmp;"
+ "unset SSH_AGENT_SOCK SSH_AGENT_PID;"
+ "SSH_ASKPASS=$ibmhmc_tmp DISPLAY=ibmhmc_foo setsid %s;"
+ "rm $ibmhmc_tmp -f;"
+ "unset ibmhmc_tmp"
+ ,password, cmd);
+ file = popen(cmd_password, "r");
+ }
+ if (NULL == file) {
+ return NULL;
+ }
+
+ g_str_tmp = g_string_new("");
+ while(!feof(file)) {
+ memset(buff, 0, BUFF_LEN);
+ read_len = fread(buff, 1, BUFF_LEN, file);
+ if (0 < read_len) {
+ g_string_append(g_str_tmp, buff);
+ }else{
+ sleep(1);
+ }
+ }
+ data = (char*)MALLOC(g_str_tmp->len+1);
+ if (data != NULL) {
+ data[0] = data[g_str_tmp->len] = 0;
+ strncpy(data, g_str_tmp->str, g_str_tmp->len);
+ }
+ g_string_free(g_str_tmp, TRUE);
+ *status = pclose(file);
+ return data;
+}
+
+static int
+check_hmc_status(struct pluginDevice* dev)
+{
+ int status;
+ char check_status[MAX_CMD_LEN];
+ char* output = NULL;
+ int rc = S_OK;
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: called, hmc=%s\n", __FUNCTION__, dev->hmc);
+ }
+
+ snprintf(check_status, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s lshmc -r -F ssh", dev->hmc);
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: check_status %s\n", __FUNCTION__
+ , check_status);
+ }
+
+ output = do_shell_cmd(check_status, &status, dev->password);
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: status=%d, output=%s\n", __FUNCTION__
+ , status, output ? output : "(nil)");
+ }
+
+ if (NULL == output || strncmp(output, "enable", 6) != 0) {
+ rc = S_BADCONFIG;
+ }
+ if (NULL != output) {
+ FREE(output);
+ }
+ return rc;
+}
+
+static int
+get_num_tokens(char *str)
+{
+ int namecount = 0;
+
+ while (*str != EOS) {
+ str += strspn(str, WHITESPACE);
+ if (*str == EOS)
+ break;
+ str += strcspn(str, WHITESPACE);
+ namecount++;
+ }
+ return namecount;
+}
+
+static gboolean
+pattern_match(char **patterns, char *string)
+{
+ char **pattern;
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: called, string=%s\n", __FUNCTION__, string);
+ }
+
+ for (pattern = patterns; *pattern; pattern++) {
+ int patlen = strlen(*pattern);
+
+ if (pattern[0][patlen-1] == '*') {
+ /* prefix match */
+ if (strncmp(string, *pattern, patlen-1) == 0) {
+ return TRUE;
+ }
+ }else{
+ /* exact match */
+ if (strcmp(string, *pattern) == 0) {
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+/*
+static char*
+do_shell_cmd_fake(const char* cmd, int* status)
+{
+ printf("%s()\n", __FUNCTION__);
+ printf("cmd:%s\n", cmd);
+ *status=0;
+ return NULL;
+}
+*/
diff --git a/lib/plugins/stonith/ipmi_os_handler.c b/lib/plugins/stonith/ipmi_os_handler.c
new file mode 100644
index 0000000..bdb6d6e
--- /dev/null
+++ b/lib/plugins/stonith/ipmi_os_handler.c
@@ -0,0 +1,257 @@
+/*
+ * This program is largely based on the ipmicmd.c program that's part of OpenIPMI package.
+ *
+ * Copyright Intel Corp.
+ * Yixiong.Zou@intel.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <stdio.h>
+#include <OpenIPMI/os_handler.h>
+#include <OpenIPMI/selector.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+
+#include <OpenIPMI/ipmi_int.h>
+
+#include <time.h>
+
+extern selector_t *os_sel;
+
+#if 0
+static void check_no_locks(os_handler_t *handler);
+#define CHECK_NO_LOCKS(handler) check_no_locks(handler)
+#else
+#define CHECK_NO_LOCKS(handler) do {} while(0)
+#endif
+
+struct os_hnd_fd_id_s
+{
+ int fd;
+ void *cb_data;
+ os_data_ready_t data_ready;
+ os_handler_t *handler;
+};
+
+static void
+fd_handler(int fd, void *data)
+{
+
+ os_hnd_fd_id_t *fd_data = (os_hnd_fd_id_t *) data;
+
+ CHECK_NO_LOCKS(fd_data->handler);
+ fd_data->data_ready(fd, fd_data->cb_data, fd_data);
+ CHECK_NO_LOCKS(fd_data->handler);
+}
+
+static int
+add_fd(os_handler_t *handler,
+ int fd,
+ os_data_ready_t data_ready,
+ void *cb_data,
+ os_hnd_fd_id_t **id)
+{
+ os_hnd_fd_id_t *fd_data;
+
+ fd_data = ipmi_mem_alloc(sizeof(*fd_data));
+ if (!fd_data)
+ return ENOMEM;
+
+ fd_data->fd = fd;
+ fd_data->cb_data = cb_data;
+ fd_data->data_ready = data_ready;
+ fd_data->handler = handler;
+ sel_set_fd_handlers(os_sel, fd, fd_data, fd_handler, NULL, NULL, NULL);
+ sel_set_fd_read_handler(os_sel, fd, SEL_FD_HANDLER_ENABLED);
+ sel_set_fd_write_handler(os_sel, fd, SEL_FD_HANDLER_DISABLED);
+ sel_set_fd_except_handler(os_sel, fd, SEL_FD_HANDLER_DISABLED);
+
+ *id = fd_data;
+ return 0;
+}
+
+static int
+remove_fd(os_handler_t *handler, os_hnd_fd_id_t *fd_data)
+{
+ sel_clear_fd_handlers(os_sel, fd_data->fd);
+ sel_set_fd_read_handler(os_sel, fd_data->fd, SEL_FD_HANDLER_DISABLED);
+ ipmi_mem_free(fd_data);
+ return 0;
+}
+
+struct os_hnd_timer_id_s
+{
+ void *cb_data;
+ os_timed_out_t timed_out;
+ sel_timer_t *timer;
+ int running;
+ os_handler_t *handler;
+};
+
+static void
+timer_handler(selector_t *sel,
+ sel_timer_t *timer,
+ void *data)
+{
+ os_hnd_timer_id_t *timer_data = (os_hnd_timer_id_t *) data;
+ void *cb_data;
+ os_timed_out_t timed_out;
+
+ CHECK_NO_LOCKS(timer_data->handler);
+ timed_out = timer_data->timed_out;
+ cb_data = timer_data->cb_data;
+ timer_data->running = 0;
+ timed_out(cb_data, timer_data);
+ CHECK_NO_LOCKS(timer_data->handler);
+}
+
+static int
+start_timer(os_handler_t *handler,
+ os_hnd_timer_id_t *id,
+ struct timeval *timeout,
+ os_timed_out_t timed_out,
+ void *cb_data)
+{
+ struct timeval now;
+
+ if (id->running)
+ return EBUSY;
+
+ id->running = 1;
+ id->cb_data = cb_data;
+ id->timed_out = timed_out;
+
+ gettimeofday(&now, NULL);
+ now.tv_sec += timeout->tv_sec;
+ now.tv_usec += timeout->tv_usec;
+ while (now.tv_usec >= 1000000) {
+ now.tv_usec -= 1000000;
+ now.tv_sec += 1;
+ }
+
+ return sel_start_timer(id->timer, &now);
+}
+
+static int
+stop_timer(os_handler_t *handler, os_hnd_timer_id_t *timer_data)
+{
+ return sel_stop_timer(timer_data->timer);
+}
+
+static int
+alloc_timer(os_handler_t *handler,
+ os_hnd_timer_id_t **id)
+{
+ os_hnd_timer_id_t *timer_data;
+ int rv;
+
+ timer_data = ipmi_mem_alloc(sizeof(*timer_data));
+ if (!timer_data)
+ return ENOMEM;
+
+ timer_data->running = 0;
+ timer_data->timed_out = NULL;
+ timer_data->handler = handler;
+
+ rv = sel_alloc_timer(os_sel, timer_handler, timer_data,
+ &(timer_data->timer));
+ if (rv) {
+ ipmi_mem_free(timer_data);
+ return rv;
+ }
+
+ *id = timer_data;
+ return 0;
+}
+
+static int
+free_timer(os_handler_t *handler, os_hnd_timer_id_t *timer_data)
+{
+ sel_free_timer(timer_data->timer);
+ ipmi_mem_free(timer_data);
+ return 0;
+}
+
+static int
+get_random(os_handler_t *handler, void *data, unsigned int len)
+{
+ int fd = open("/dev/urandom", O_RDONLY);
+ int rv;
+
+ if (fd == -1)
+ return errno;
+
+ rv = read(fd, data, len);
+
+ close(fd);
+ return rv;
+}
+
+static void
+sui_log(os_handler_t *handler,
+ enum ipmi_log_type_e log_type,
+ char *format,
+ ...)
+{
+ return;
+}
+
+static void
+sui_vlog(os_handler_t *handler,
+ enum ipmi_log_type_e log_type,
+ char *format,
+ va_list ap)
+{
+ return;
+}
+
+
+os_handler_t ipmi_os_cb_handlers =
+{
+ .add_fd_to_wait_for = add_fd,
+ .remove_fd_to_wait_for = remove_fd,
+
+ .start_timer = start_timer,
+ .stop_timer = stop_timer,
+ .alloc_timer = alloc_timer,
+ .free_timer = free_timer,
+
+ .create_lock = NULL,
+ .destroy_lock = NULL,
+ .is_locked = NULL,
+ .lock = NULL,
+ .unlock = NULL,
+ .create_rwlock = NULL,
+ .destroy_rwlock = NULL,
+ .read_lock = NULL,
+ .write_lock = NULL,
+ .read_unlock = NULL,
+ .write_unlock = NULL,
+ .is_readlocked = NULL,
+ .is_writelocked = NULL,
+
+ .get_random = get_random,
+
+ .log = sui_log,
+ .vlog = sui_vlog
+};
+
+
diff --git a/lib/plugins/stonith/ipmilan.c b/lib/plugins/stonith/ipmilan.c
new file mode 100644
index 0000000..1efdfee
--- /dev/null
+++ b/lib/plugins/stonith/ipmilan.c
@@ -0,0 +1,587 @@
+/*
+ * Stonith module for ipmi lan Stonith device
+ *
+ * Copyright (c) 2003 Intel Corp.
+ * Yixiong Zou <yixiong.zou@intel.com>
+ *
+ * Mangled by Sun Jiang Dong <sunjd@cn.ibm.com>, IBM, 2005.
+ * And passed the compiling with OpenIPMI-1.4.8.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+
+/*
+ * See README.ipmi for information regarding this plugin.
+ *
+ */
+
+#define DEVICE "IPMI Over LAN"
+
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN ipmilan
+#define PIL_PLUGIN_S "ipmilan"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+#include <OpenIPMI/ipmi_types.h>
+#include <OpenIPMI/ipmi_auth.h>
+
+#include "ipmilan.h"
+
+static StonithPlugin * ipmilan_new(const char *);
+static void ipmilan_destroy(StonithPlugin *);
+static const char * const * ipmilan_get_confignames(StonithPlugin *);
+static int ipmilan_set_config(StonithPlugin *, StonithNVpair *);
+static const char * ipmilan_getinfo(StonithPlugin * s, int InfoType);
+static int ipmilan_status(StonithPlugin * );
+static int ipmilan_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** ipmilan_hostlist(StonithPlugin *);
+
+static struct stonith_ops ipmilanOps ={
+ ipmilan_new, /* Create new STONITH object */
+ ipmilan_destroy, /* Destroy STONITH object */
+ ipmilan_getinfo, /* Return STONITH info string */
+ ipmilan_get_confignames,/* Get configuration parameter names */
+ ipmilan_set_config, /* Set configuration */
+ ipmilan_status, /* Return STONITH device status */
+ ipmilan_reset_req, /* Request a reset */
+ ipmilan_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug);
+const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &ipmilanOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * ipmilan STONITH device.
+ *
+ * ipmilanHostInfo is a double linked list. Where the prev of the head always
+ * points to the tail. This is a little wierd. But it saves me from looping
+ * around to find the tail when destroying the list.
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ int hostcount;
+ struct ipmilanHostInfo * hostlist;
+};
+
+static const char * pluginid = "IPMI-LANDevice-Stonith";
+static const char * NOTpluginid = "IPMI-LAN device has been destroyed";
+
+#define ST_HOSTNAME "hostname"
+#define ST_PORT "port"
+#define ST_AUTH "auth"
+#define ST_PRIV "priv"
+#define ST_RESET_METHOD "reset_method"
+
+#include "stonith_config_xml.h"
+
+#define XML_HOSTNAME_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_HOSTNAME \
+ XML_PARM_SHORTDESC_END
+
+#define XML_HOSTNAME_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The hostname of the STONITH device" \
+ XML_PARM_LONGDESC_END
+
+#define XML_HOSTNAME_PARM \
+ XML_PARAMETER_BEGIN(ST_HOSTNAME, "string", "1", "1") \
+ XML_HOSTNAME_SHORTDESC \
+ XML_HOSTNAME_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_PORT_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_PORT \
+ XML_PARM_SHORTDESC_END
+
+#define XML_PORT_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The port number to where the IPMI message is sent" \
+ XML_PARM_LONGDESC_END
+
+#define XML_PORT_PARM \
+ XML_PARAMETER_BEGIN(ST_PORT, "string", "1", "0") \
+ XML_PORT_SHORTDESC \
+ XML_PORT_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_AUTH_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_AUTH \
+ XML_PARM_SHORTDESC_END
+
+#define XML_AUTH_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The authorization type of the IPMI session (\"none\", \"straight\", \"md2\", or \"md5\")" \
+ XML_PARM_LONGDESC_END
+
+#define XML_AUTH_PARM \
+ XML_PARAMETER_BEGIN(ST_AUTH, "string", "1", "0") \
+ XML_AUTH_SHORTDESC \
+ XML_AUTH_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_PRIV_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_PRIV \
+ XML_PARM_SHORTDESC_END
+
+#define XML_PRIV_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The privilege level of the user (\"operator\" or \"admin\")" \
+ XML_PARM_LONGDESC_END
+
+#define XML_PRIV_PARM \
+ XML_PARAMETER_BEGIN(ST_PRIV, "string", "1", "0") \
+ XML_PRIV_SHORTDESC \
+ XML_PRIV_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_RESET_METHOD_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_RESET_METHOD \
+ XML_PARM_SHORTDESC_END
+
+#define XML_RESET_METHOD_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "How to reset the host (\"power_cycle\" or \"hard_reset\")" \
+ XML_PARM_LONGDESC_END
+
+#define XML_RESET_METHOD_PARM \
+ XML_PARAMETER_BEGIN(ST_RESET_METHOD, "string", "0", "0") \
+ XML_RESET_METHOD_SHORTDESC \
+ XML_RESET_METHOD_LONGDESC \
+ XML_PARAMETER_END
+
+static const char *ipmilanXML =
+ XML_PARAMETERS_BEGIN
+ XML_HOSTNAME_PARM
+ XML_IPADDR_PARM
+ XML_PORT_PARM
+ XML_AUTH_PARM
+ XML_PRIV_PARM
+ XML_LOGIN_PARM
+ XML_PASSWD_PARM
+ XML_PARAMETERS_END;
+
+/*
+ * Check the status of the IPMI Lan STONITH device.
+ *
+ * NOTE: not sure what we should do here since each host is configured
+ * seperately.
+ *
+ * Two options:
+ * 1) always return S_OK.
+ * 2) using IPMI ping to confirm the status for every host that's
+ * configured.
+ *
+ * For now I choose the option 1 hoping that I can get by. Maybe we should
+ * change it to option 2 later.
+ */
+
+static int
+ipmilan_status(StonithPlugin *s)
+{
+ struct pluginDevice * nd;
+ struct ipmilanHostInfo * node;
+ int ret, rv;
+ int i;
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ ret = S_OK;
+
+ nd = (struct pluginDevice *)s;
+ for( i=0, node = nd->hostlist;
+ i < nd->hostcount; i++, node = node->next ) {
+ rv = do_ipmi_cmd(node, ST_IPMI_STATUS);
+ if (rv) {
+ LOG(PIL_INFO, "Host %s ipmilan status failure."
+ , node->hostname);
+ ret = S_ACCESS;
+ } else {
+ LOG(PIL_INFO, "Host %s ipmilan status OK."
+ , node->hostname);
+ }
+
+ }
+
+ return ret;
+}
+
+/*
+ * This function returns the list of hosts that's configured.
+ *
+ * The detailed configuration is disabled because the STONITH command can be
+ * run by anyone so there is a security risk if that to be exposed.
+ */
+
+static char *
+get_config_string(struct pluginDevice * nd, int index)
+{
+ struct ipmilanHostInfo * host;
+ int i;
+
+ if (index >= nd->hostcount || index < 0) {
+ return (NULL);
+ }
+
+ host = nd->hostlist;
+ for (i = 0; i < index; i++) {
+ host = host->next;
+ }
+
+ return STRDUP(host->hostname);
+}
+
+
+/*
+ * Return the list of hosts configured for this ipmilan device
+ *
+ */
+
+static char **
+ipmilan_hostlist(StonithPlugin *s)
+{
+ int numnames = 0;
+ char ** ret = NULL;
+ struct pluginDevice* nd;
+ int j;
+
+ ERRIFWRONGDEV(s,NULL);
+
+ nd = (struct pluginDevice*) s;
+ if (nd->hostcount < 0) {
+ LOG(PIL_CRIT
+ , "unconfigured stonith object in ipmi_hostlist");
+ return(NULL);
+ }
+ numnames = nd->hostcount;
+
+ ret = (char **)MALLOC((numnames + 1)*sizeof(char*));
+ if (ret == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return (ret);
+ }
+
+ memset(ret, 0, (numnames + 1)*sizeof(char*));
+
+ for (j = 0; j < numnames; ++j) {
+ ret[j] = get_config_string(nd, j);
+ if (!ret[j]) {
+ stonith_free_hostlist(ret);
+ ret = NULL;
+ break;
+ }
+ strdown(ret[j]);
+ }
+
+ return(ret);
+}
+
+/*
+ * Parse the config information, and stash it away...
+ *
+ * The buffer for each string is MAX_IPMI_STRING_LEN bytes long.
+ * Right now it is set to 64. Hope this is enough.
+ *
+ */
+
+#define MAX_IPMI_STRING_LEN 64
+
+/*
+ * Reset the given host on this StonithPlugin device.
+ */
+static int
+ipmilan_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ int rc = 0;
+ struct pluginDevice * nd;
+ struct ipmilanHostInfo * node;
+ int i;
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ nd = (struct pluginDevice *)s;
+ for( i=0, node = nd->hostlist;
+ i < nd->hostcount; i++, node = node->next ) {
+ if (strcasecmp(node->hostname, host) == 0) {
+ break;
+ }
+ }
+
+ if (i >= nd->hostcount) {
+ LOG(PIL_CRIT, "Host %s is not configured in this STONITH "
+ " module. Please check your configuration file.", host);
+ return (S_OOPS);
+ }
+
+ rc = do_ipmi_cmd(node, request);
+ if (!rc) {
+ LOG(PIL_INFO, "Host %s ipmilan-reset.", host);
+ } else {
+ LOG(PIL_INFO, "Host %s ipmilan-reset error. Error = %d."
+ , host, rc);
+ }
+ return rc;
+}
+
+/*
+ * Get configuration parameter names
+ */
+static const char * const *
+ipmilan_get_confignames(StonithPlugin * s)
+{
+ static const char * ret[] =
+ { ST_HOSTNAME, ST_IPADDR, ST_PORT, ST_AUTH,
+ ST_PRIV, ST_LOGIN, ST_PASSWD, ST_RESET_METHOD, NULL};
+ return ret;
+}
+
+/*
+ * Set the configuration parameters
+ */
+static int
+ipmilan_set_config(StonithPlugin* s, StonithNVpair * list)
+{
+ struct pluginDevice* nd;
+ int rc;
+ struct ipmilanHostInfo * tmp;
+ const char *reset_opt;
+
+ StonithNamesToGet namestocopy [] =
+ { {ST_HOSTNAME, NULL}
+ , {ST_IPADDR, NULL}
+ , {ST_PORT, NULL}
+ , {ST_AUTH, NULL}
+ , {ST_PRIV, NULL}
+ , {ST_LOGIN, NULL}
+ , {ST_PASSWD, NULL}
+ , {NULL, NULL}
+ };
+
+ ERRIFWRONGDEV(s,S_OOPS);
+ nd = (struct pluginDevice *)s;
+
+ ERRIFWRONGDEV(s, S_OOPS);
+ if (nd->sp.isconfigured) {
+ return S_OOPS;
+ }
+
+ if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+
+ tmp = ST_MALLOCT(struct ipmilanHostInfo);
+ tmp->hostname = namestocopy[0].s_value;
+ tmp->ipaddr = namestocopy[1].s_value;
+ tmp->portnumber = atoi(namestocopy[2].s_value);
+ FREE(namestocopy[2].s_value);
+ if (namestocopy[3].s_value == NULL) {
+ LOG(PIL_CRIT, "ipmilan auth type is NULL. See "
+ "README.ipmilan for allowed values");
+ return S_OOPS;
+ } else if (strcmp(namestocopy[3].s_value, "none") == 0) {
+ tmp->authtype = 0;
+ } else if (strcmp(namestocopy[3].s_value, "md2") == 0) {
+ tmp->authtype = 1;
+ } else if (strcmp(namestocopy[3].s_value, "md5") == 0) {
+ tmp->authtype = 2;
+ } else if (strcmp(namestocopy[3].s_value, "key") == 0 ||
+ strcmp(namestocopy[3].s_value, "password") == 0 ||
+ strcmp(namestocopy[3].s_value, "straight") == 0) {
+ tmp->authtype = 4;
+ } else {
+ LOG(PIL_CRIT, "ipmilan auth type '%s' invalid. See "
+ "README.ipmilan for allowed values", namestocopy[3].s_value);
+ return S_OOPS;
+ }
+ FREE(namestocopy[3].s_value);
+ if (namestocopy[4].s_value == NULL) {
+ LOG(PIL_CRIT, "ipmilan priv value is NULL. See "
+ "README.ipmilan for allowed values");
+ return S_OOPS;
+ } else if (strcmp(namestocopy[4].s_value, "operator") == 0) {
+ tmp->privilege = 3;
+ } else if (strcmp(namestocopy[4].s_value, "admin") == 0) {
+ tmp->privilege = 4;
+ } else {
+ LOG(PIL_CRIT, "ipmilan priv value '%s' invalid. See "
+ "README.ipmilan for allowed values", namestocopy[4].s_value);
+ return(S_OOPS);
+ }
+ FREE(namestocopy[4].s_value);
+ tmp->username = namestocopy[5].s_value;
+ tmp->password = namestocopy[6].s_value;
+ reset_opt = OurImports->GetValue(list, ST_RESET_METHOD);
+ if (!reset_opt || !strcmp(reset_opt, "power_cycle")) {
+ tmp->reset_method = 0;
+ } else if (!strcmp(reset_opt, "hard_reset")) {
+ tmp->reset_method = 1;
+ } else {
+ LOG(PIL_CRIT, "ipmilan reset_method '%s' invalid", reset_opt);
+ return S_OOPS;
+ }
+
+ if (nd->hostlist == NULL ) {
+ nd->hostlist = tmp;
+ nd->hostlist->prev = tmp;
+ nd->hostlist->next = tmp;
+ } else {
+ tmp->prev = nd->hostlist->prev;
+ tmp->next = nd->hostlist;
+ nd->hostlist->prev->next = tmp;
+ nd->hostlist->prev = tmp;
+ }
+ nd->hostcount++;
+
+ return(S_OK);
+}
+
+static const char *
+ipmilan_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice * nd;
+ const char * ret;
+
+ ERRIFWRONGDEV(s,NULL);
+ /*
+ * We look in the ST_TEXTDOMAIN catalog for our messages
+ */
+ nd = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = nd->idinfo;
+ break;
+
+ case ST_DEVICENAME:
+ ret = nd->hostlist ? nd->hostlist->hostname : NULL;
+ break;
+
+ case ST_DEVICEDESCR:
+ ret = "IPMI LAN STONITH device\n";
+ break;
+
+ case ST_DEVICEURL:
+ ret = "http://www.intel.com/design/servers/ipmi/";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = ipmilanXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * ipmilan StonithPlugin destructor...
+ *
+ * The hostlist is a link list. So have to iterate through.
+ */
+static void
+ipmilan_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* nd;
+ struct ipmilanHostInfo * host;
+ int i;
+
+ VOIDERRIFWRONGDEV(s);
+
+ nd = (struct pluginDevice *)s;
+
+ nd->pluginid = NOTpluginid;
+
+ if (nd->hostlist) {
+ host = nd->hostlist->prev;
+ for (i = 0; i < nd->hostcount; i++) {
+ struct ipmilanHostInfo * host_prev = host->prev;
+
+ FREE(host->hostname);
+ FREE(host->ipaddr);
+ FREE(host->username);
+ FREE(host->password);
+
+ FREE(host);
+ host = host_prev;
+ }
+ }
+
+ nd->hostcount = -1;
+ FREE(nd);
+ ipmi_leave();
+}
+
+/* Create a new ipmilan StonithPlugin device. Too bad this function can't be static */
+static StonithPlugin *
+ipmilan_new(const char *subplugin)
+{
+ struct pluginDevice* nd = ST_MALLOCT(struct pluginDevice);
+
+ if (nd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ LOG(PIL_WARN, "The ipmilan stonith plugin is deprecated! Please use external/ipmi.");
+ memset(nd, 0, sizeof(*nd));
+ nd->pluginid = pluginid;
+ nd->hostlist = NULL;
+ nd->hostcount = 0;
+ nd->idinfo = DEVICE;
+ nd->sp.s_ops = &ipmilanOps;
+ return(&(nd->sp));
+}
diff --git a/lib/plugins/stonith/ipmilan.h b/lib/plugins/stonith/ipmilan.h
new file mode 100644
index 0000000..fb548f0
--- /dev/null
+++ b/lib/plugins/stonith/ipmilan.h
@@ -0,0 +1,41 @@
+/*
+ * Stonith module for ipmi lan Stonith device
+ *
+ * Copyright (c) 2003 Intel Corp.
+ * Yixiong Zou <yixiong.zou@intel.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define ST_IPMI_STATUS 4
+#include <time.h>
+
+struct ipmilanHostInfo {
+ char * hostname;
+ char * ipaddr;
+ int portnumber;
+ int authtype;
+ int privilege;
+ char * username;
+ char * password;
+ int reset_method;
+
+ struct ipmilanHostInfo * prev;
+ struct ipmilanHostInfo * next;
+};
+
+int do_ipmi_cmd(struct ipmilanHostInfo * host, int request);
+void ipmi_leave(void);
diff --git a/lib/plugins/stonith/ipmilan_command.c b/lib/plugins/stonith/ipmilan_command.c
new file mode 100644
index 0000000..a3de493
--- /dev/null
+++ b/lib/plugins/stonith/ipmilan_command.c
@@ -0,0 +1,399 @@
+/*
+ * This program is largely based on the ipmicmd.c program that's part of OpenIPMI package.
+ *
+ * Copyright Intel Corp.
+ * Yixiong.Zou@intel.com
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <stdio.h>
+
+#include <stdlib.h> /* malloc() */
+#include <unistd.h> /* getopt() */
+#include <string.h> /* strerror() */
+#include <netdb.h> /* gethostbyname() */
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <OpenIPMI/ipmiif.h>
+#include <OpenIPMI/selector.h>
+#include <OpenIPMI/ipmi_conn.h>
+#include <OpenIPMI/ipmi_lan.h>
+#include <OpenIPMI/ipmi_smi.h>
+#include <OpenIPMI/ipmi_auth.h>
+#include <OpenIPMI/ipmi_msgbits.h>
+#include <OpenIPMI/ipmi_posix.h>
+#include <OpenIPMI/ipmi_debug.h>
+
+#include "ipmilan.h"
+#include <stonith/stonith.h>
+#include <clplumbing/cl_log.h>
+
+#include <pils/plugin.h>
+extern const PILPluginImports* PluginImports;
+
+/* #define DUMP_MSG 0 */
+#define OPERATION_TIME_OUT 10
+
+os_handler_t *os_hnd=NULL;
+selector_t *os_sel;
+static ipmi_con_t *con;
+extern os_handler_t ipmi_os_cb_handlers;
+static int reset_method;
+
+static int request_done = 0;
+static int op_done = 0;
+
+typedef enum ipmi_status {
+ /*
+ IPMI_CONNECTION_FAILURE,
+ IPMI_SEND_FAILURE,
+ IPMI_BAD_REQUEST,
+ IPMI_REQUEST_FAILED,
+ IPMI_TIME_OUT,
+ */
+ IPMI_RUNNING = 99,
+} ipmi_status_t;
+
+static ipmi_status_t gstatus;
+
+typedef enum chassis_control_request {
+ POWER_DOWN = 0X00,
+ POWER_UP = 0X01,
+ POWER_CYCLE = 0X02,
+ HARD_RESET = 0X03,
+ PULSE_DIAGNOSTIC_INTERRUPT = 0X04,
+ SOFT_SHUTDOWN = 0X05
+} chassis_control_request_t;
+
+void dump_msg_data(ipmi_msg_t *msg, ipmi_addr_t *addr, const char *type);
+int rsp_handler(ipmi_con_t *ipmi, ipmi_msgi_t *rspi);
+
+void send_ipmi_cmd(ipmi_con_t *con, int request);
+
+void timed_out(selector_t *sel, sel_timer_t *timer, void *data);
+
+void
+timed_out(selector_t *sel, sel_timer_t *timer, void *data)
+{
+ PILCallLog(PluginImports->log,PIL_CRIT, "IPMI operation timed out... :(\n");
+ gstatus = S_TIMEOUT;
+}
+
+void
+dump_msg_data(ipmi_msg_t *msg, ipmi_addr_t *addr, const char *type)
+{
+ ipmi_system_interface_addr_t *smi_addr = NULL;
+ int i;
+ ipmi_ipmb_addr_t *ipmb_addr = NULL;
+
+ if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) {
+ smi_addr = (struct ipmi_system_interface_addr *) addr;
+
+ fprintf(stderr, "%2.2x %2.2x %2.2x %2.2x ",
+ addr->channel,
+ msg->netfn,
+ smi_addr->lun,
+ msg->cmd);
+ } else if ((addr->addr_type == IPMI_IPMB_ADDR_TYPE)
+ || (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)) {
+ ipmb_addr = (struct ipmi_ipmb_addr *) addr;
+
+ fprintf(stderr, "%2.2x %2.2x %2.2x %2.2x ",
+ addr->channel,
+ msg->netfn,
+ ipmb_addr->lun,
+ msg->cmd);
+ }
+
+ for (i = 0; i < msg->data_len; i++) {
+ if (((i%16) == 0) && (i != 0)) {
+ printf("\n ");
+ }
+ fprintf(stderr, "%2.2x ", msg->data[i]);
+ }
+ fprintf(stderr, "\n");
+}
+
+/*
+ * This function gets called after the response comes back
+ * from the IPMI device.
+ *
+ * Some IPMI device does not return success, 0x00, to the
+ * remote node when the power-reset was issued.
+ *
+ * The host who sent the ipmi cmd might get a 0xc3,
+ * a timeout instead. This creates problems for
+ * STONITH operation, where status is critical. :(
+ *
+ * Right now I am only checking 0xc3 as the return.
+ * If your IPMI device returns some wired code after
+ * reset, you might want to add it in this code block.
+ *
+ */
+
+int
+rsp_handler(ipmi_con_t *ipmi, ipmi_msgi_t *rspi)
+{
+ int rv;
+ long request;
+
+ /*dump_msg_data(&rspi->msg, &rspi->addr, "response");*/
+ request = (long) rspi->data1;
+
+ op_done = 1;
+ if( !rspi || !(rspi->msg.data) ) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "No data received\n");
+ gstatus = S_RESETFAIL;
+ return IPMI_MSG_ITEM_NOT_USED;
+ }
+ rv = rspi->msg.data[0];
+ /* some IPMI device might not issue 0x00, success, for reset command.
+ instead, a 0xc3, timeout, is returned. */
+ if (rv == 0x00) {
+ gstatus = S_OK;
+ } else if((rv == 0xc3 || rv == 0xff) && request == ST_GENERIC_RESET) {
+ PILCallLog(PluginImports->log,PIL_WARN ,
+ "IPMI reset request failed: %x, but we assume that it succeeded\n", rv);
+ gstatus = S_OK;
+ } else {
+ PILCallLog(PluginImports->log,PIL_INFO
+ , "IPMI request %ld failed: %x\n", request, rv);
+ gstatus = S_RESETFAIL;
+ }
+ return IPMI_MSG_ITEM_NOT_USED;
+}
+
+void
+send_ipmi_cmd(ipmi_con_t *con, int request)
+{
+ ipmi_addr_t addr;
+ unsigned int addr_len;
+ ipmi_msg_t msg;
+ struct ipmi_system_interface_addr *si;
+ int rv;
+ ipmi_msgi_t *rspi;
+ /* chassis control command request is only 1 byte long */
+ unsigned char cc_data = POWER_CYCLE;
+
+ si = (void *) &addr;
+ si->lun = 0x00;
+ si->channel = IPMI_BMC_CHANNEL;
+ si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+ addr_len = sizeof(*si);
+
+ msg.netfn = IPMI_CHASSIS_NETFN;
+ msg.cmd = IPMI_CHASSIS_CONTROL_CMD;
+ msg.data = &cc_data;
+ msg.data_len = 1;
+
+ switch (request) {
+ case ST_POWERON:
+ cc_data = POWER_UP;
+ break;
+
+ case ST_POWEROFF:
+ cc_data = POWER_DOWN;
+ break;
+
+ case ST_GENERIC_RESET:
+ cc_data = (reset_method ? POWER_CYCLE : HARD_RESET);
+ break;
+
+ case ST_IPMI_STATUS:
+ msg.netfn = IPMI_APP_NETFN;
+ msg.cmd = IPMI_GET_DEVICE_ID_CMD;
+ msg.data_len = 0;
+ break;
+
+ default:
+ gstatus = S_INVAL;
+ return;
+ }
+
+ gstatus = S_ACCESS;
+ rspi = calloc(1, sizeof(ipmi_msgi_t));
+ if (NULL == rspi) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "Error sending IPMI command: Out of memory\n");
+ } else {
+ rspi->data1 = (void *) (long) request;
+ rv = con->send_command(con, &addr, addr_len, &msg, rsp_handler, rspi);
+ if (rv == -1) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "Error sending IPMI command: %x\n", rv);
+ } else {
+ request_done = 1;
+ }
+ }
+
+ return;
+}
+
+static void
+con_changed_handler(ipmi_con_t *ipmi, int err, unsigned int port_num,
+ int still_connected, void *cb_data)
+{
+ int * request;
+
+ if (err) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "Unable to setup connection: %x\n", err);
+ return;
+ }
+
+ if( !request_done ) {
+ request = (int *) cb_data;
+ send_ipmi_cmd(ipmi, *request);
+ }
+}
+
+static int
+setup_ipmi_conn(struct ipmilanHostInfo * host, int *request)
+{
+ int rv;
+
+ struct hostent *ent;
+ struct in_addr lan_addr[2];
+ int lan_port[2];
+ int num_addr = 1;
+ int authtype = 0;
+ int privilege = 0;
+ char username[17];
+ char password[17];
+
+ /*DEBUG_MSG_ENABLE();*/
+
+ os_hnd = ipmi_posix_get_os_handler();
+ if (!os_hnd) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "ipmi_smi_setup_con: Unable to allocate os handler");
+ return 1;
+ }
+
+ rv = sel_alloc_selector(os_hnd, &os_sel);
+ if (rv) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "Could not allocate selector\n");
+ return rv;
+ }
+
+ ipmi_posix_os_handler_set_sel(os_hnd, os_sel);
+
+ rv = ipmi_init(os_hnd);
+ if (rv) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "ipmi_init erro: %d ", rv);
+ return rv;
+ }
+
+ ent = gethostbyname(host->ipaddr);
+ if (!ent) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "gethostbyname failed: %s\n", strerror(h_errno));
+ return 1;
+ }
+
+ memcpy(&lan_addr[0], ent->h_addr_list[0], ent->h_length);
+ lan_port[0] = host->portnumber;
+ lan_port[1] = 0;
+
+ authtype = host->authtype;
+ privilege = host->privilege;
+
+ memcpy(username, host->username, sizeof(username));
+ memcpy(password, host->password, sizeof(password));
+
+ reset_method = host->reset_method;
+
+ rv = ipmi_lan_setup_con(lan_addr, lan_port, num_addr,
+ authtype, privilege,
+ username, strlen(username),
+ password, strlen(password),
+ os_hnd, os_sel,
+ &con);
+
+ if (rv) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "ipmi_lan_setup_con: %s\n", strerror(rv));
+ return S_ACCESS;
+ }
+
+#if OPENIPMI_VERSION_MAJOR < 2
+ con->set_con_change_handler(con, con_changed_handler, request);
+#else
+ con->add_con_change_handler(con, con_changed_handler, request);
+#endif
+
+ gstatus = IPMI_RUNNING;
+
+ rv = con->start_con(con);
+ if (rv) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "Could not start IPMI connection: %x\n", rv);
+ gstatus = S_BADCONFIG;
+ return rv;
+ }
+ return S_OK;
+}
+
+void
+ipmi_leave()
+{
+ if( con && con->close_connection ) {
+ con->close_connection(con);
+ con = NULL;
+ }
+ if( os_sel ) {
+ sel_free_selector(os_sel);
+ os_sel = NULL;
+ }
+}
+
+int
+do_ipmi_cmd(struct ipmilanHostInfo * host, int request)
+{
+ int rv;
+ sel_timer_t * timer;
+ struct timeval timeout;
+
+ request_done = 0;
+ op_done = 0;
+
+ if( !os_hnd ) {
+ rv = setup_ipmi_conn(host, &request);
+ if( rv ) {
+ return rv;
+ }
+ } else {
+ send_ipmi_cmd(con, request);
+ }
+
+ gettimeofday(&timeout, NULL);
+ timeout.tv_sec += OPERATION_TIME_OUT;
+ timeout.tv_usec += 0;
+
+ sel_alloc_timer(os_sel, timed_out, NULL, &timer);
+ sel_start_timer(timer, &timeout);
+
+ while (!op_done) {
+ rv = sel_select(os_sel, NULL, 0, NULL, NULL);
+ if (rv == -1) {
+ break;
+ }
+ }
+
+ sel_free_timer(timer);
+ return gstatus;
+}
+
+#if OPENIPMI_VERSION_MAJOR < 2
+void
+posix_vlog(char *format, enum ipmi_log_type_e log_type, va_list ap)
+{
+}
+#endif
diff --git a/lib/plugins/stonith/ipmilan_test.c b/lib/plugins/stonith/ipmilan_test.c
new file mode 100644
index 0000000..47859a0
--- /dev/null
+++ b/lib/plugins/stonith/ipmilan_test.c
@@ -0,0 +1,63 @@
+/*
+ * Stonith module for ipmi lan Stonith device
+ *
+ * Copyright (c) 2003 Intel Corp.
+ * Yixiong Zou <yixiong.zou@intel.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ * A quick test program to verify that IPMI host is setup correctly.
+ *
+ * You will need to modify the values in user, pass, ip, and port.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "ipmilan.h"
+#include <OpenIPMI/ipmi_auth.h>
+
+int main(int argc, char * argv[])
+{
+ struct ipmilanHostInfo host;
+ int request = 2;
+ int rv;
+
+ char user[] = "joe";
+ char pass[] = "blow";
+ char ip[] = "192.168.1.7";
+
+ host.hostname = NULL;
+ host.portnumber = 999;
+ host.authtype = IPMI_AUTHTYPE_NONE;
+ host.privilege = IPMI_PRIVILEGE_ADMIN;
+
+ host.ipaddr = ip;
+ memcpy(host.username, user, sizeof(user));
+ memcpy(host.password, pass, sizeof(pass));
+ /*
+ memset(host.username, 0, sizeof(host.username));
+ memset(host.password, 0, sizeof(host.password));
+ */
+
+ rv = do_ipmi_cmd(&host, request);
+ if (rv)
+ printf("rv = %d, operation failed. \n", rv);
+ else
+ printf("operation succeeded. \n");
+ return rv;
+}
diff --git a/lib/plugins/stonith/meatware.c b/lib/plugins/stonith/meatware.c
new file mode 100644
index 0000000..029ba35
--- /dev/null
+++ b/lib/plugins/stonith/meatware.c
@@ -0,0 +1,351 @@
+/*
+ * Stonith module for Human Operator Stonith device
+ *
+ * Copyright (c) 2001 Gregor Binder <gbinder@sysfive.com>
+ *
+ * This module is largely based on the "NULL Stonith device", written
+ * by Alan Robertson <alanr@unix.sh>, using code by David C. Teigland
+ * <teigland@sistina.com> originally appeared in the GFS stomith
+ * meatware agent.
+ *
+ * Mangled by Zhaokai <zhaokai@cn.ibm.com>, IBM, 2005
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <lha_internal.h>
+
+#define DEVICE "Meatware STONITH device"
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN meatware
+#define PIL_PLUGIN_S "meatware"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+static StonithPlugin * meatware_new(const char *);
+static void meatware_destroy(StonithPlugin *);
+static int meatware_set_config(StonithPlugin *, StonithNVpair *);
+static const char * const * meatware_get_confignames(StonithPlugin *);
+static const char * meatware_getinfo(StonithPlugin * s, int InfoType);
+static int meatware_status(StonithPlugin * );
+static int meatware_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** meatware_hostlist(StonithPlugin *);
+
+static struct stonith_ops meatwareOps ={
+ meatware_new, /* Create new STONITH object */
+ meatware_destroy, /* Destroy STONITH object */
+ meatware_getinfo, /* Return STONITH info string */
+ meatware_get_confignames,/* Return STONITH info string */
+ meatware_set_config, /* Get configuration from NVpairs */
+ meatware_status, /* Return STONITH device status */
+ meatware_reset_req, /* Request a reset */
+ meatware_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &meatwareOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * Meatware STONITH device.
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ char ** hostlist;
+ int hostcount;
+};
+
+static const char * pluginid = "MeatwareDevice-Stonith";
+static const char * NOTpluginID = "Meatware device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+static const char *meatwareXML =
+ XML_PARAMETERS_BEGIN
+ XML_HOSTLIST_PARM
+ XML_PARAMETERS_END;
+
+static int
+meatware_status(StonithPlugin *s)
+{
+ ERRIFWRONGDEV(s,S_OOPS);
+ return S_OK;
+}
+
+
+/*
+ * Return the list of hosts configured for this Meat device
+ */
+
+static char **
+meatware_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice* nd;
+
+ ERRIFWRONGDEV(s,NULL);
+ nd = (struct pluginDevice*) s;
+ if (nd->hostcount < 0) {
+ LOG(PIL_CRIT
+ , "unconfigured stonith object in Meatware_list_hosts");
+ return(NULL);
+ }
+
+ return OurImports->CopyHostList((const char * const *)nd->hostlist);
+}
+
+/*
+ * Parse the config information, and stash it away...
+ */
+
+static int
+Meat_parse_config_info(struct pluginDevice* nd, const char * info)
+{
+ LOG(PIL_INFO , "parse config info info=%s",info);
+ if (nd->hostcount >= 0) {
+ return(S_OOPS);
+ }
+
+ nd->hostlist = OurImports->StringToHostList(info);
+ if (nd->hostlist == NULL) {
+ LOG(PIL_CRIT,"StringToHostList() failed");
+ return S_OOPS;
+ }
+ for (nd->hostcount = 0; nd->hostlist[nd->hostcount]; nd->hostcount++) {
+ strdown(nd->hostlist[nd->hostcount]);
+ }
+ return(S_OK);
+}
+
+
+/*
+ * Indicate that host must be power cycled manually.
+ */
+static int
+meatware_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ int fd, rc;
+ const char * meatpipe_pr = HA_VARRUNDIR "/meatware"; /* if you intend to
+ change this, modify
+ meatclient.c as well */
+
+ char line[256], meatpipe[256];
+ char resp_addr[50], resp_mw[50], resp_result[50];
+
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ snprintf(meatpipe, 256, "%s.%s", meatpipe_pr, host);
+ umask(0);
+ unlink(meatpipe);
+
+ rc = mkfifo(meatpipe, (S_IRUSR | S_IWUSR));
+
+ if (rc < 0) {
+ LOG(PIL_CRIT, "cannot create FIFO for Meatware_reset_host");
+ return S_OOPS;
+ }
+
+ LOG(PIL_CRIT, "OPERATOR INTERVENTION REQUIRED to reset %s.", host);
+ LOG(PIL_CRIT, "Run \"meatclient -c %s\" AFTER power-cycling the "
+ "machine.", host);
+
+ fd = open(meatpipe, O_RDONLY);
+
+ if (fd < 0) {
+ LOG(PIL_CRIT, "cannot open FIFO for Meatware_reset_host");
+ return S_OOPS;
+ }
+
+ alarm(600);
+ memset(line, 0, 256);
+ rc = read(fd, line, 256);
+ alarm(0);
+
+ if (rc < 0) {
+ LOG(PIL_CRIT, "read error on FIFO for Meatware_reset_host");
+ return S_OOPS;
+ }
+
+ memset(resp_mw, 0, 50);
+ memset(resp_result, 0, 50);
+ memset(resp_addr, 0, 50);
+
+ if (sscanf(line, "%s %s %s", resp_mw, resp_result, resp_addr) < 3) {
+ LOG(PIL_CRIT, "Format error - failed to Meatware-reset node %s",
+ host);
+ return S_RESETFAIL;
+ }
+
+ strdown(resp_addr);
+
+ if (strncmp(resp_mw, "meatware", 8) ||
+ strncmp(resp_result, "reply", 5) ||
+ strncasecmp(resp_addr, host, strlen(resp_addr))) {
+ LOG(PIL_CRIT, "failed to Meatware-reset node %s", host);
+ return S_RESETFAIL;
+ }else{
+ LOG(PIL_INFO, "node Meatware-reset: %s", host);
+ unlink(meatpipe);
+ return S_OK;
+ }
+}
+
+/*
+ * Parse the information in the given string
+ * and stash it away...
+ */
+static int
+meatware_set_config(StonithPlugin* s, StonithNVpair *list)
+{
+
+ struct pluginDevice* nd;
+ int rc;
+ StonithNamesToGet namestocopy [] =
+ { {ST_HOSTLIST, NULL}
+ , {NULL, NULL}
+ };
+
+ ERRIFWRONGDEV(s,S_OOPS);
+ nd = (struct pluginDevice*) s;
+
+ if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ rc = Meat_parse_config_info(nd, namestocopy[0].s_value);
+ FREE(namestocopy[0].s_value);
+ return rc;
+}
+
+/*
+ * Return STONITH config vars
+ */
+static const char * const *
+meatware_get_confignames(StonithPlugin* p)
+{
+ static const char * MeatwareParams[] = {ST_HOSTLIST, NULL };
+ return MeatwareParams;
+}
+
+/*
+ * Return STONITH info string
+ */
+static const char *
+meatware_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* nd;
+ const char * ret;
+
+ ERRIFWRONGDEV(s,NULL);
+ /*
+ * We look in the ST_TEXTDOMAIN catalog for our messages
+ */
+ nd = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = nd->idinfo;
+ break;
+ case ST_DEVICENAME:
+ ret = "Your Name Here";
+ break;
+ case ST_DEVICEDESCR:
+ ret = "Human (meatware) intervention STONITH device.\n"
+ "This STONITH agent prompts a human to reset a machine.\n"
+ "The human tells it when the reset was completed.";
+ break;
+ case ST_CONF_XML: /* XML metadata */
+ ret = meatwareXML;
+ break;
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * Meat Stonith destructor...
+ */
+static void
+meatware_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* nd;
+
+ VOIDERRIFWRONGDEV(s);
+ nd = (struct pluginDevice *)s;
+
+ nd->pluginid = NOTpluginID;
+ if (nd->hostlist) {
+ stonith_free_hostlist(nd->hostlist);
+ nd->hostlist = NULL;
+ }
+ nd->hostcount = -1;
+ FREE(nd);
+}
+
+/* Create a new Meatware Stonith device. */
+
+static StonithPlugin *
+meatware_new(const char *subplugin)
+{
+ struct pluginDevice* nd = ST_MALLOCT(struct pluginDevice);
+
+ if (nd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(nd, 0, sizeof(*nd));
+ nd->pluginid = pluginid;
+ nd->hostlist = NULL;
+ nd->hostcount = -1;
+ nd->idinfo = DEVICE;
+ nd->sp.s_ops = &meatwareOps;
+
+ return &(nd->sp);
+}
diff --git a/lib/plugins/stonith/null.c b/lib/plugins/stonith/null.c
new file mode 100644
index 0000000..0d0cf04
--- /dev/null
+++ b/lib/plugins/stonith/null.c
@@ -0,0 +1,260 @@
+/*
+ * Stonith module for NULL Stonith device
+ *
+ * Copyright (c) 2000 Alan Robertson <alanr@unix.sh>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <lha_internal.h>
+
+#define DEVICE "NULL STONITH device"
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN null
+#define PIL_PLUGIN_S "null"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ char ** hostlist;
+ int hostcount;
+};
+
+static StonithPlugin* null_new(const char *);
+static void null_destroy(StonithPlugin *);
+static int null_set_config(StonithPlugin*
+, StonithNVpair*);
+static const char * const * null_get_confignames(StonithPlugin*);
+static const char * null_getinfo(StonithPlugin * s, int InfoType);
+static int null_status(StonithPlugin * );
+static int null_reset_req(StonithPlugin * s
+, int request, const char * host);
+static char ** null_hostlist(StonithPlugin *);
+
+static struct stonith_ops nullOps ={
+ null_new, /* Create new STONITH object */
+ null_destroy, /* Destroy STONITH object */
+ null_getinfo, /* Return STONITH info string */
+ null_get_confignames, /* Return list of config params */
+ null_set_config, /* configure fron NV pairs */
+ null_status, /* Return STONITH device status */
+ null_reset_req, /* Request a reset */
+ null_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &nullOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * Null STONITH device. We are very agreeable, but don't do much :-)
+ */
+
+
+static const char * pluginid = "nullDevice-Stonith";
+static const char * NOTpluginID = "Null device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+static const char *nullXML =
+ XML_PARAMETERS_BEGIN
+ XML_HOSTLIST_PARM
+ XML_PARAMETERS_END;
+
+static int
+null_status(StonithPlugin *s)
+{
+
+ ERRIFWRONGDEV(s, S_OOPS);
+ return S_OK;
+}
+
+
+/*
+ * Return the list of hosts configured for this NULL device
+ */
+
+static char **
+null_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice* nd = (struct pluginDevice*)s;
+
+ ERRIFWRONGDEV(s, NULL);
+ return OurImports->CopyHostList((const char * const *)nd->hostlist);
+}
+
+
+/*
+ * Pretend to reset the given host on this Stonith device.
+ * (we don't even error check the "request" type)
+ */
+static int
+null_reset_req(StonithPlugin * s, int request, const char * host)
+{
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ /* Real devices need to pay attention to the "request" */
+ /* (but we don't care ;-)) */
+
+ LOG(PIL_INFO, "Host null-reset: %s", host);
+ return S_OK;
+}
+
+
+static const char * const *
+null_get_confignames(StonithPlugin* p)
+{
+ static const char * NullParams[] = {ST_HOSTLIST, NULL };
+ return NullParams;
+}
+
+/*
+ * Parse the config information in the given string,
+ * and stash it away...
+ */
+static int
+null_set_config(StonithPlugin* s, StonithNVpair* list)
+{
+ struct pluginDevice* nd = (struct pluginDevice*) s;
+ StonithNamesToGet namestocopy [] =
+ { {ST_HOSTLIST, NULL}
+ , {NULL, NULL}
+ };
+ int rc;
+
+ ERRIFWRONGDEV(s, S_OOPS);
+
+ if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ nd->hostlist = OurImports->StringToHostList(namestocopy[0].s_value);
+ FREE(namestocopy[0].s_value);
+ if (nd->hostlist == NULL) {
+ LOG(PIL_CRIT,"StringToHostList() failed");
+ return S_OOPS;
+ }
+ for (nd->hostcount = 0; nd->hostlist[nd->hostcount]
+ ; nd->hostcount++) {
+ strdown(nd->hostlist[nd->hostcount]);
+ }
+ return nd->hostcount ? S_OK : S_BADCONFIG;
+}
+
+static const char *
+null_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* nd = (struct pluginDevice*) s;
+ const char * ret;
+
+ ERRIFWRONGDEV(s, NULL);
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = nd->idinfo;
+ break;
+
+ case ST_DEVICENAME:
+ ret = "(nil)";
+ break;
+
+ case ST_DEVICEDESCR:
+ ret = "Dummy (do-nothing) STONITH device\n"
+ "FOR TESTING ONLY!";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = nullXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * NULL Stonith destructor...
+ */
+static void
+null_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* nd;
+
+ VOIDERRIFWRONGDEV(s);
+ nd = (struct pluginDevice *)s;
+
+ nd->pluginid = NOTpluginID;
+ if (nd->hostlist) {
+ stonith_free_hostlist(nd->hostlist);
+ nd->hostlist = NULL;
+ }
+ nd->hostcount = -1;
+ FREE(s);
+}
+
+/* Create a new Null Stonith device.
+ * Too bad this function can't be static
+ */
+static StonithPlugin *
+null_new(const char *subplugin)
+{
+ struct pluginDevice* nd = ST_MALLOCT(struct pluginDevice);
+
+ if (nd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(nd, 0, sizeof(*nd));
+ nd->pluginid = pluginid;
+ nd->idinfo = DEVICE;
+ nd->sp.s_ops = &nullOps;
+ return (StonithPlugin *)nd;
+}
diff --git a/lib/plugins/stonith/nw_rpc100s.c b/lib/plugins/stonith/nw_rpc100s.c
new file mode 100644
index 0000000..5ba0827
--- /dev/null
+++ b/lib/plugins/stonith/nw_rpc100s.c
@@ -0,0 +1,779 @@
+/*
+ * Stonith module for Night/Ware RPC100S
+ *
+ * Original code from baytech.c by
+ * Copyright (c) 2000 Alan Robertson <alanr@unix.sh>
+ *
+ * Modifications for NW RPC100S
+ * Copyright (c) 2000 Computer Generation Incorporated
+ * Eric Z. Ayers <eric.ayers@compgen.com>
+ *
+ * Mangled by Zhaokai <zhaokai@cn.ibm.com>, IBM, 2005
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <lha_internal.h>
+#define DEVICE "NW RPC100S Power Switch"
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN nw_rpc100s
+#define PIL_PLUGIN_S "nw_rpc100s"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#define MAX_CFGLINE 256
+#include <pils/plugin.h>
+
+static StonithPlugin * nw_rpc100s_new(const char *);
+static void nw_rpc100s_destroy(StonithPlugin *);
+static int nw_rpc100s_set_config(StonithPlugin *, StonithNVpair *);
+static const char * const * nw_rpc100s_get_confignames(StonithPlugin *);
+static const char * nw_rpc100s_getinfo(StonithPlugin * s, int InfoType);
+static int nw_rpc100s_status(StonithPlugin * );
+static int nw_rpc100s_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** nw_rpc100s_hostlist(StonithPlugin *);
+
+static struct stonith_ops nw_rpc100sOps ={
+ nw_rpc100s_new, /* Create new STONITH object */
+ nw_rpc100s_destroy, /* Destroy STONITH object */
+ nw_rpc100s_getinfo, /* Return STONITH info string */
+ nw_rpc100s_get_confignames,/* Return STONITH info string */
+ nw_rpc100s_set_config, /* Get configuration from NVpairs */
+ nw_rpc100s_status, /* Return STONITH device status */
+ nw_rpc100s_reset_req, /* Request a reset */
+ nw_rpc100s_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+#include "stonith_signal.h"
+
+#define DOESNT_USE_STONITHKILLCOMM
+#define DOESNT_USE_STONITHSCANLINE
+#include "stonith_expect_helpers.h"
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &nw_rpc100sOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ The Nightware RPS-100S is manufactured by:
+
+ Micro Energetics Corp
+ +1 703 250-3000
+ http://www.nightware.com/
+
+ Thank you to David Hicks of Micro Energetics Corp. for providing
+ a demo unit to write this software.
+
+ This switch has a very simple protocol,
+ You issue a command and it gives a response.
+ Sample commands are conveniently documented on a sticker on the
+ bottom of the device.
+
+ The switch accepts a single command of the form
+
+ //0,yyy,zzz[/m][/h]<CR>
+
+ Where yyy is the wait time before activiting the relay.
+ zzz is the relay time.
+
+ The default is that the relay is in a default state of ON, which
+ means that usually yyy is the number of seconds to wait
+ before shutting off the power and zzz is the number of seconds the
+ power remains off. There is a dip switch to change the default
+ state to 'OFF'. Don't set this switch. It will screw up this code.
+
+ An asterisk can be used for zzz to specify an infinite switch time.
+ The /m /and /h command options will convert the specified wait and
+ switch times to either minutewes or hours.
+
+ A response is either
+ <cr><lf>OK<cr><lf>
+ or
+ <cr><lf>Invalid Entry<cr><lf>
+
+
+ As far as THIS software is concerned, we have to implement 4 commands:
+
+ status --> //0,0,BOGUS; # Not a real command, this is just a
+ # probe to see if switch is alive
+ open(on) --> //0,0,0; # turn power to default state (on)
+ close(off) --> //0,0,*; # leave power off indefinitely
+ reboot --> //0,0,10; # immediately turn power off for 10 seconds.
+
+ and expect the response 'OK' to confirm that the unit is operational.
+*/
+
+
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+
+ int fd; /* FD open to the serial port */
+
+ char * device; /* Serial device name to use to communicate
+ to this RPS10
+ */
+
+ char * node; /* Name of the node that this is controlling */
+
+};
+
+/* This string is used to identify this type of object in the config file */
+static const char * pluginid = "NW_RPC100S";
+static const char * NOTrpcid = "NW RPC100S device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+static const char *nw_rpc100sXML =
+ XML_PARAMETERS_BEGIN
+ XML_TTYDEV_PARM
+ XML_HOSTLIST_PARM
+ XML_PARAMETERS_END;
+
+/*
+ * Different expect strings that we get from the NW_RPC100S
+ * Remote Power Controllers...
+ */
+
+static struct Etoken NWtokOK[] = { {"OK", 0, 0}, {NULL,0,0}};
+static struct Etoken NWtokInvalidEntry[] = { {"Invalid Entry", 0, 0}, {NULL,0,0}};
+/* Accept either a CR/NL or an NL/CR */
+static struct Etoken NWtokCRNL[] = { {"\n\r",0,0},{"\r\n",0,0},{NULL,0,0}};
+
+static int RPCConnect(struct pluginDevice * ctx);
+static int RPCDisconnect(struct pluginDevice * ctx);
+
+static int RPCReset(struct pluginDevice*, int unitnum, const char * rebootid);
+#if defined(ST_POWERON)
+static int RPCOn(struct pluginDevice*, int unitnum, const char * rebootid);
+#endif
+#if defined(ST_POWEROFF)
+static int RPCOff(struct pluginDevice*, int unitnum, const char * rebootid);
+#endif
+static int RPCNametoOutlet ( struct pluginDevice * ctx, const char * host );
+
+/*static int RPC_parse_config_info(struct pluginDevice* ctx, const char * info);*/
+
+
+#define SENDCMD(cmd, timeout) { \
+ int return_val = RPCSendCommand(ctx, cmd, timeout); \
+ if (return_val != S_OK) { \
+ return return_val; \
+ } \
+ }
+
+/*
+ * RPCSendCommand - send a command to the specified outlet
+ */
+static int
+RPCSendCommand (struct pluginDevice *ctx, const char *command, int timeout)
+{
+ char writebuf[64]; /* All commands are short.
+ They should be WAY LESS
+ than 64 chars long!
+ */
+ int return_val; /* system call result */
+ fd_set rfds, wfds, xfds;
+ /* list of FDs for select() */
+ struct timeval tv; /* */
+
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ FD_ZERO(&xfds);
+
+ snprintf (writebuf, sizeof(writebuf), "%s\r", command);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Sending %s", writebuf);
+ }
+
+ /* Make sure the serial port won't block on us. use select() */
+ FD_SET(ctx->fd, &wfds);
+ FD_SET(ctx->fd, &xfds);
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ return_val = select(ctx->fd+1, NULL, &wfds,&xfds, &tv);
+ if (return_val == 0) {
+ /* timeout waiting on serial port */
+ LOG(PIL_CRIT, "%s: Timeout writing to %s"
+ , pluginid, ctx->device);
+ return S_TIMEOUT;
+ } else if ((return_val == -1) || FD_ISSET(ctx->fd, &xfds)) {
+ /* an error occured */
+ LOG(PIL_CRIT, "%s: Error before writing to %s: %s"
+ , pluginid, ctx->device, strerror(errno));
+ return S_OOPS;
+ }
+
+ /* send the command */
+ if (write(ctx->fd, writebuf, strlen(writebuf)) !=
+ (int)strlen(writebuf)) {
+ LOG(PIL_CRIT, "%s: Error writing to %s : %s"
+ , pluginid, ctx->device, strerror(errno));
+ return S_OOPS;
+ }
+
+ /* suceeded! */
+ return S_OK;
+
+} /* end RPCSendCommand() */
+
+/*
+ * RPCReset - Reset (power-cycle) the given outlet number
+ *
+ * This device can only control one power outlet - unitnum is ignored.
+ *
+ */
+static int
+RPCReset(struct pluginDevice* ctx, int unitnum, const char * rebootid)
+{
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Calling RPCReset (%s)", pluginid);
+ }
+
+ if (ctx->fd < 0) {
+ LOG(PIL_CRIT, "%s: device %s is not open!", pluginid
+ , ctx->device);
+ return S_OOPS;
+ }
+
+ /* send the "toggle power" command */
+ SENDCMD("//0,0,10;\r\n", 12);
+
+ /* Expect "OK" */
+ EXPECT(ctx->fd, NWtokOK, 5);
+ if (Debug) {
+ LOG(PIL_DEBUG, "Got OK");
+ }
+ EXPECT(ctx->fd, NWtokCRNL, 2);
+ if (Debug) {
+ LOG(PIL_DEBUG, "Got NL");
+ }
+
+ return(S_OK);
+
+} /* end RPCReset() */
+
+
+#if defined(ST_POWERON)
+/*
+ * RPCOn - Turn OFF the given outlet number
+ */
+static int
+RPCOn(struct pluginDevice* ctx, int unitnum, const char * host)
+{
+
+ if (ctx->fd < 0) {
+ LOG(PIL_CRIT, "%s: device %s is not open!", pluginid
+ , ctx->device);
+ return S_OOPS;
+ }
+
+ /* send the "On" command */
+ SENDCMD("//0,0,0;\r\n", 10);
+
+ /* Expect "OK" */
+ EXPECT(ctx->fd, NWtokOK, 5);
+ EXPECT(ctx->fd, NWtokCRNL, 2);
+
+ return(S_OK);
+
+} /* end RPCOn() */
+#endif
+
+
+#if defined(ST_POWEROFF)
+/*
+ * RPCOff - Turn Off the given outlet number
+ */
+static int
+RPCOff(struct pluginDevice* ctx, int unitnum, const char * host)
+{
+
+ if (ctx->fd < 0) {
+ LOG(PIL_CRIT, "%s: device %s is not open!", pluginid
+ , ctx->device);
+ return S_OOPS;
+ }
+
+ /* send the "Off" command */
+ SENDCMD("//0,0,*;\r\n", 10);
+
+ /* Expect "OK" */
+ EXPECT(ctx->fd, NWtokOK, 5);
+ EXPECT(ctx->fd, NWtokCRNL, 2);
+
+ return(S_OK);
+
+} /* end RPCOff() */
+#endif
+
+
+/*
+ * nw_rpc100s_status - API entry point to probe the status of the stonith device
+ * (basically just "is it reachable and functional?", not the
+ * status of the individual outlets)
+ *
+ * Returns:
+ * S_OOPS - some error occured
+ * S_OK - if the stonith device is reachable and online.
+ */
+static int
+nw_rpc100s_status(StonithPlugin *s)
+{
+ struct pluginDevice* ctx;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Calling nw_rpc100s_status (%s)", pluginid);
+ }
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ ctx = (struct pluginDevice*) s;
+ if (RPCConnect(ctx) != S_OK) {
+ return(S_OOPS);
+ }
+
+ /* The "connect" really does enough work to see if the
+ controller is alive... It verifies that it is returning
+ RPS-10 Ready
+ */
+
+ return(RPCDisconnect(ctx));
+}
+
+/*
+ * nw_rpc100s_hostlist - API entry point to return the list of hosts
+ * for the devices on this NW_RPC100S unit
+ *
+ * This type of device is configured from the config file,
+ * so we don't actually have to connect to figure this
+ * out, just peruse the 'ctx' structure.
+ * Returns:
+ * NULL on error
+ * a malloced array, terminated with a NULL,
+ * of null-terminated malloc'ed strings.
+ */
+static char **
+nw_rpc100s_hostlist(StonithPlugin *s)
+{
+ char ** ret = NULL; /* list to return */
+ struct pluginDevice* ctx;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Calling nw_rpc100s_hostlist (%s)", pluginid);
+ }
+
+ ERRIFNOTCONFIGED(s,NULL);
+
+ ctx = (struct pluginDevice*) s;
+
+ ret = OurImports->StringToHostList(ctx->node);
+ if (ret == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ } else {
+ strdown(ret[0]);
+ }
+
+ return(ret);
+} /* end si_hostlist() */
+
+/*
+ * Parse the given configuration information, and stash it away...
+ *
+ * <info> contains the parameters specific to this type of object
+ *
+ * The format of <parameters> for this module is:
+ * <serial device> <remotenode> <outlet> [<remotenode> <outlet>] ...
+ *
+ * e.g. A machine named 'nodea' can kill a machine named 'nodeb' through
+ * a device attached to serial port /dev/ttyS0.
+ * A machine named 'nodeb' can kill machines 'nodea' and 'nodec'
+ * through a device attached to serial port /dev/ttyS1 (outlets 0
+ * and 1 respectively)
+ *
+ * stonith nodea NW_RPC100S /dev/ttyS0 nodeb 0
+ * stonith nodeb NW_RPC100S /dev/ttyS0 nodea 0 nodec 1
+ *
+ * Another possible configuration is for 2 stonith devices accessible
+ * through 2 different serial ports on nodeb:
+ *
+ * stonith nodeb NW_RPC100S /dev/ttyS0 nodea 0
+ * stonith nodeb NW_RPC100S /dev/ttyS1 nodec 0
+ */
+
+/*static int
+RPC_parse_config_info(struct pluginDevice* ctx, const char * info)
+{
+}*/
+
+
+/*
+ * RPCConnect -
+ *
+ * Connect to the given NW_RPC100S device.
+ * Side Effects
+ * ctx->fd now contains a valid file descriptor to the serial port
+ * ??? LOCK THE SERIAL PORT ???
+ *
+ * Returns
+ * S_OK on success
+ * S_OOPS on error
+ * S_TIMEOUT if the device did not respond
+ *
+ */
+static int
+RPCConnect(struct pluginDevice * ctx)
+{
+
+ /* Open the serial port if it isn't already open */
+ if (ctx->fd < 0) {
+ struct termios tio;
+
+ if (OurImports->TtyLock(ctx->device) < 0) {
+ LOG(PIL_CRIT, "%s: TtyLock failed.", pluginid);
+ return S_OOPS;
+ }
+
+ ctx->fd = open (ctx->device, O_RDWR);
+ if (ctx->fd <0) {
+ LOG(PIL_CRIT, "%s: Can't open %s : %s"
+ , pluginid, ctx->device, strerror(errno));
+ return S_OOPS;
+ }
+
+ /* set the baudrate to 9600 8 - N - 1 */
+ memset (&tio, 0, sizeof(tio));
+
+ /* ??? ALAN - the -tradtitional flag on gcc causes the
+ CRTSCTS constant to generate a warning, and warnings
+ are treated as errors, so I can't set this flag! - EZA ???
+
+ Hmmm. now that I look at the documentation, RTS
+ is just wired high on this device! we don't need it.
+ */
+ /* tio.c_cflag = B9600 | CS8 | CLOCAL | CREAD | CRTSCTS ;*/
+ tio.c_cflag = B9600 | CS8 | CLOCAL | CREAD ;
+ tio.c_lflag = ICANON;
+
+ if (tcsetattr (ctx->fd, TCSANOW, &tio) < 0) {
+ LOG(PIL_CRIT, "%s: Can't set attributes %s : %s"
+ , pluginid, ctx->device, strerror(errno));
+ close (ctx->fd);
+ OurImports->TtyUnlock(ctx->device);
+ ctx->fd=-1;
+ return S_OOPS;
+ }
+ /* flush all data to and fro the serial port before we start */
+ if (tcflush (ctx->fd, TCIOFLUSH) < 0) {
+ LOG(PIL_CRIT, "%s: Can't flush %s : %s"
+ , pluginid, ctx->device, strerror(errno));
+ close (ctx->fd);
+ OurImports->TtyUnlock(ctx->device);
+ ctx->fd=-1;
+ return S_OOPS;
+ }
+
+ }
+
+
+ /* Send a BOGUS string */
+ SENDCMD("//0,0,BOGUS;\r\n", 10);
+
+ /* Should reply with "Invalid Command" */
+ if (Debug) {
+ LOG(PIL_DEBUG, "Waiting for \"Invalid Entry\"");
+ }
+ EXPECT(ctx->fd, NWtokInvalidEntry, 12);
+ if (Debug) {
+ LOG(PIL_DEBUG, "Got Invalid Entry");
+ }
+ EXPECT(ctx->fd, NWtokCRNL, 2);
+ if (Debug) {
+ LOG(PIL_DEBUG, "Got NL");
+ }
+
+ return(S_OK);
+}
+
+static int
+RPCDisconnect(struct pluginDevice * ctx)
+{
+
+ if (ctx->fd >= 0) {
+ /* Flush the serial port, we don't care what happens to the characters
+ and failing to do this can cause close to hang.
+ */
+ tcflush(ctx->fd, TCIOFLUSH);
+ close (ctx->fd);
+ if (ctx->device != NULL) {
+ OurImports->TtyUnlock(ctx->device);
+ }
+ }
+ ctx->fd = -1;
+
+ return S_OK;
+}
+
+/*
+ * RPCNametoOutlet - Map a hostname to an outlet number on this stonith device.
+ *
+ * Returns:
+ * 0 on success ( the outlet number on the RPS10 - there is only one )
+ * -1 on failure (host not found in the config file)
+ *
+ */
+static int
+RPCNametoOutlet ( struct pluginDevice * ctx, const char * host )
+{
+ int rc = -1;
+
+ if (!strcasecmp(ctx->node, host)) {
+ rc = 0;
+ }
+
+ return rc;
+}
+
+
+/*
+ * nw_rpc100s_reset - API call to Reset (reboot) the given host on
+ * this Stonith device. This involves toggling the power off
+ * and then on again, OR just calling the builtin reset command
+ * on the stonith device.
+ */
+static int
+nw_rpc100s_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ int rc = S_OK;
+ int lorc = S_OK;
+ int outletnum = -1;
+ struct pluginDevice* ctx;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Calling nw_rpc100s_reset (%s)", pluginid);
+ }
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ ctx = (struct pluginDevice*) s;
+
+ if ((rc = RPCConnect(ctx)) != S_OK) {
+ return(rc);
+ }
+
+ outletnum = RPCNametoOutlet(ctx, host);
+ LOG(PIL_DEBUG, "zk:outletname=%d", outletnum);
+
+ if (outletnum < 0) {
+ LOG(PIL_WARN, "%s doesn't control host [%s]"
+ , ctx->device, host);
+ RPCDisconnect(ctx);
+ return(S_BADHOST);
+ }
+
+ switch(request) {
+
+#if defined(ST_POWERON)
+ case ST_POWERON:
+ rc = RPCOn(ctx, outletnum, host);
+ break;
+#endif
+#if defined(ST_POWEROFF)
+ case ST_POWEROFF:
+ rc = RPCOff(ctx, outletnum, host);
+ break;
+#endif
+ case ST_GENERIC_RESET:
+ rc = RPCReset(ctx, outletnum, host);
+ break;
+ default:
+ rc = S_INVAL;
+ break;
+ }
+
+ lorc = RPCDisconnect(ctx);
+
+ return(rc != S_OK ? rc : lorc);
+}
+
+/*
+ * Parse the information in the given string
+ * and stash it away...
+ */
+static int
+nw_rpc100s_set_config(StonithPlugin* s, StonithNVpair *list)
+{
+ struct pluginDevice* ctx;
+ StonithNamesToGet namestocopy [] =
+ { {ST_TTYDEV, NULL}
+ , {ST_HOSTLIST, NULL}
+ , {NULL, NULL}
+ };
+ int rc;
+
+
+ ERRIFWRONGDEV(s,S_OOPS);
+ if (s->isconfigured) {
+ return S_OOPS;
+ }
+
+ ctx = (struct pluginDevice*) s;
+
+ if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ ctx->device = namestocopy[0].s_value;
+ ctx->node = namestocopy[1].s_value;
+
+ return S_OK;
+}
+
+/*
+ * Return STONITH config vars
+ */
+static const char * const *
+nw_rpc100s_get_confignames(StonithPlugin* p)
+{
+ static const char * RpcParams[] = {ST_TTYDEV , ST_HOSTLIST, NULL };
+ return RpcParams;
+}
+
+
+
+/*
+ * nw_rpc100s_getinfo - API entry point to retrieve something from the handle
+ */
+static const char *
+nw_rpc100s_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* ctx;
+ const char * ret;
+
+ ERRIFWRONGDEV(s,NULL);
+
+ /*
+ * We look in the ST_TEXTDOMAIN catalog for our messages
+ */
+ ctx = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = ctx->idinfo;
+ break;
+ case ST_DEVICENAME:
+ ret = ctx->device;
+ break;
+ case ST_DEVICEDESCR:
+ ret = "Micro Energetics Night/Ware RPC100S";
+ break;
+ case ST_DEVICEURL:
+ ret = "http://www.microenergeticscorp.com/";
+ break;
+ case ST_CONF_XML: /* XML metadata */
+ ret = nw_rpc100sXML;
+ break;
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * nw_rpc100s_destroy - API entry point to destroy a NW_RPC100S Stonith object.
+ */
+static void
+nw_rpc100s_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* ctx;
+
+ VOIDERRIFWRONGDEV(s);
+
+ ctx = (struct pluginDevice *)s;
+
+ ctx->pluginid = NOTrpcid;
+
+ /* close the fd if open and set ctx->fd to invalid */
+ RPCDisconnect(ctx);
+
+ if (ctx->device != NULL) {
+ FREE(ctx->device);
+ ctx->device = NULL;
+ }
+ if (ctx->node != NULL) {
+ FREE(ctx->node);
+ ctx->node = NULL;
+ }
+ FREE(ctx);
+}
+
+/*
+ * nw_rpc100s_new - API entry point called to create a new NW_RPC100S Stonith
+ * device object.
+ */
+static StonithPlugin *
+nw_rpc100s_new(const char *subplugin)
+{
+ struct pluginDevice* ctx = ST_MALLOCT(struct pluginDevice);
+
+ if (ctx == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->pluginid = pluginid;
+ ctx->fd = -1;
+ ctx->device = NULL;
+ ctx->node = NULL;
+ ctx->idinfo = DEVICE;
+ ctx->sp.s_ops = &nw_rpc100sOps;
+
+ return &(ctx->sp);
+}
diff --git a/lib/plugins/stonith/rcd_serial.c b/lib/plugins/stonith/rcd_serial.c
new file mode 100644
index 0000000..f1396a7
--- /dev/null
+++ b/lib/plugins/stonith/rcd_serial.c
@@ -0,0 +1,602 @@
+/*
+ * Stonith module for RCD_SERIAL Stonith device
+ *
+ * Original code from null.c by
+ * Copyright (c) 2000 Alan Robertson <alanr@unix.sh>
+ *
+ * Copious borrowings from nw_rpc100s.c by
+ * Copyright (c) 2000 Computer Generation Incorporated
+ * Eric Z. Ayers <eric.ayers@compgen.com>
+ *
+ * and from apcsmart.c by
+ * Copyright (c) 2000 Andreas Piesk <a.piesk@gmx.net>
+ *
+ * Modifications for RC Delayed Serial Ciruit by
+ * Copyright (c) 2002 John Sutton <john@scl.co.uk>
+ *
+ * Mangled by Zhaokai <zhaokai@cn.ibm.com>, IBM, 2005
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <lha_internal.h>
+
+#define DEVICE "RC Delayed Serial"
+#include "stonith_plugin_common.h"
+#include "stonith_signal.h"
+
+#define PIL_PLUGIN rcd_serial
+#define PIL_PLUGIN_S "rcd_serial"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+
+#define ST_DTRRTS "dtr_rts"
+#define ST_MSDURATION "msduration"
+#define MAX_RCD_SERIALLINE 512
+
+#include <pils/plugin.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+static StonithPlugin* rcd_serial_new(const char *);
+static void rcd_serial_destroy(StonithPlugin *);
+static int rcd_serial_set_config(StonithPlugin *, StonithNVpair *);
+static const char * const * rcd_serial_get_confignames(StonithPlugin *);
+static const char * rcd_serial_getinfo(StonithPlugin * s, int InfoType);
+static int rcd_serial_status(StonithPlugin * );
+static int rcd_serial_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** rcd_serial_hostlist(StonithPlugin *);
+
+static struct stonith_ops rcd_serialOps ={
+ rcd_serial_new, /* Create new STONITH object */
+ rcd_serial_destroy, /* Destroy STONITH object */
+ rcd_serial_getinfo, /* Return STONITH info string */
+ rcd_serial_get_confignames,/* Return STONITH info string */
+ rcd_serial_set_config, /* Get configuration from NVpairs */
+ rcd_serial_status, /* Return STONITH device status */
+ rcd_serial_reset_req, /* Request a reset */
+ rcd_serial_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &rcd_serialOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/* ------------------- RCD specific stuff -------------- */
+
+/*
+ A diagram of a circuit suitable for use with this plugin is in
+ README.rcd_serial which should be somewhere in the distribution (if Alan
+ includes it ;-) and/or at http://www.scl.co.uk/rcd_serial/ (if I remember
+ to put it there ;-).
+
+ Once you've got this built, you can test things using the stonith command
+ as follows:
+
+ stonith -L
+ will show a list of plugin types, including rcd_serial
+
+ stonith -t rcd_serial testhost
+ will show required parameters
+
+ In these 3 you can either pass the params after the -p option or you can
+ put them in a config file and use -F configname instead of -p "param ...".
+
+ stonith -t rcd_serial -p "testhost /dev/ttyS0 rts 1500" -S
+ will show the status of the device
+
+ stonith -t rcd_serial -p "testhost /dev/ttyS0 rts 1500" -l
+ will list the single host testhost
+
+ stonith -t rcd_serial -p "testhost /dev/ttyS0 rts 1500" testhost
+ will reset testhost (provided testhost has its reset pins
+ suitably wired to the RTS signal coming out of port /dev/ttyS0
+ and that 1.5s is enough time to cause a reset ;-)
+*/
+
+/*
+ Define RCD_NOPAUSE if you are using the serial port for some purpose
+ _in_addition_ to using it as a stonith device. For example, I use one
+ of the input pins on the same serial port for monitoring the state of a
+ power supply. Periodically, a cron job has to open the port to read the
+ state of this input and thus has to clear down the output pins DTR and RTS
+ in order to avoid causing a spurious stonith reset. Now, if it should
+ happen that just at the same time as we are _really_ trying to do a stonith
+ reset, this cron job starts up, then the stonith reset won't occur ;-(.
+ To avoid this (albeit unlikely) outcome, you should #define RCD_NOPAUSE.
+ The effect of this is that instead of setting the line high just once and
+ then falling into a pause until an alarm goes off, rather, the program falls
+ into a loop which is continuously setting the line high. That costs us a bit
+ of CPU as compared with sitting in a pause, but hey, how often is this code
+ going to get exercised! Never, we hope...
+*/
+#undef RCD_NOPAUSE
+
+#ifdef RCD_NOPAUSE
+static int RCD_alarmcaught;
+#endif
+
+/*
+ * own prototypes
+ */
+
+static void RCD_alarm_handler(int sig);
+static int RCD_open_serial_port(char *device);
+static int RCD_close_serial_port(char *device, int fd);
+
+static void
+RCD_alarm_handler(int sig) {
+#if !defined(HAVE_POSIX_SIGNALS)
+ if (sig) {
+ signal(sig, SIG_DFL);
+ }else{
+ signal(sig, RCD_alarm_handler);
+ }
+#else
+ struct sigaction sa;
+ sigset_t sigmask;
+
+ /* Maybe a bit naughty but it works and it saves duplicating all */
+ /* this setup code - if handler called with 0 for sig, we install */
+ /* ourself as handler. */
+ if (sig) {
+ sa.sa_handler = (void (*)(int))SIG_DFL;
+ }else{
+ sa.sa_handler = RCD_alarm_handler;
+ }
+
+ sigemptyset(&sigmask);
+ sa.sa_mask = sigmask;
+ sa.sa_flags = 0;
+ sigaction(SIGALRM, &sa, NULL);
+#endif
+
+#ifdef RCD_NOPAUSE
+ RCD_alarmcaught = 1;
+#endif
+ return;
+}
+
+static int
+RCD_open_serial_port(char *device) {
+ int fd;
+ int status;
+ int bothbits;
+
+ if (OurImports->TtyLock(device) < 0) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: ttylock failed.", __FUNCTION__);
+ }
+ return -1;
+ }
+
+ bothbits = TIOCM_RTS | TIOCM_DTR;
+
+ if ((fd = open(device, O_RDONLY | O_NDELAY)) != -1) {
+ /*
+ Opening the device always sets DTR & CTS high.
+ Clear them down immediately.
+ */
+ status = ioctl(fd, TIOCMBIC, &bothbits);
+ /* If there was an error clearing bits, set the fd to -1
+ * ( indicates error ) */
+ if (status != 0 ) {
+ fd = -1;
+ }
+ }
+
+ return fd;
+}
+
+static int
+RCD_close_serial_port(char *device, int fd) {
+ int rc = close(fd);
+ if (device != NULL) {
+ OurImports->TtyUnlock(device);
+ }
+ return rc;
+}
+
+/*
+ * RCD_Serial STONITH device.
+ */
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ char ** hostlist; /* name of single host we can reset */
+ int hostcount; /* i.e. 1 after initialisation */
+ char * device; /* serial device name */
+ char * signal; /* either rts or dtr */
+ long msduration; /* how long (ms) to assert the signal */
+};
+
+static const char * pluginid = "RCD_SerialDevice-Stonith";
+static const char * NOTrcd_serialID = "RCD_Serial device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+#define XML_DTRRTS_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_DTRRTS \
+ XML_PARM_SHORTDESC_END
+
+#define XML_DTRRTS_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The hardware handshaking technique to use with " ST_TTYDEV "(\"dtr\" or \"rts\")" \
+ XML_PARM_LONGDESC_END
+
+#define XML_DTRRTS_PARM \
+ XML_PARAMETER_BEGIN(ST_DTRRTS, "string", "1", "0") \
+ XML_DTRRTS_SHORTDESC \
+ XML_DTRRTS_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_MSDURATION_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_MSDURATION \
+ XML_PARM_SHORTDESC_END
+
+#define XML_MSDURATION_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The delay duration (in milliseconds) between the assertion of the control signal on " ST_TTYDEV " and the closing of the reset switch" \
+ XML_PARM_LONGDESC_END
+
+#define XML_MSDURATION_PARM \
+ XML_PARAMETER_BEGIN(ST_MSDURATION, "string", "1", "0") \
+ XML_MSDURATION_SHORTDESC \
+ XML_MSDURATION_LONGDESC \
+ XML_PARAMETER_END
+
+static const char *rcd_serialXML =
+ XML_PARAMETERS_BEGIN
+ XML_HOSTLIST_PARM
+ XML_TTYDEV_PARM
+ XML_DTRRTS_PARM
+ XML_MSDURATION_PARM
+ XML_PARAMETERS_END;
+
+static int
+rcd_serial_status(StonithPlugin *s)
+{
+ struct pluginDevice* rcd;
+ int fd;
+ const char * err;
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ rcd = (struct pluginDevice*) s;
+
+ /*
+ All we can do is make sure the serial device exists and
+ can be opened and closed without error.
+ */
+
+ if ((fd = RCD_open_serial_port(rcd->device)) == -1) {
+ err = strerror(errno);
+ LOG(PIL_CRIT, "%s: open of %s failed - %s",
+ __FUNCTION__, rcd->device, err);
+ return(S_OOPS);
+ }
+
+ if (RCD_close_serial_port(rcd->device, fd) != 0) {
+ err = strerror(errno);
+ LOG(PIL_CRIT, "%s: close of %s failed - %s",
+ __FUNCTION__, rcd->device, err);
+ return(S_OOPS);
+ }
+
+ return S_OK;
+}
+
+
+/*
+ * Return the list of hosts configured for this RCD_SERIAL device
+ */
+static char **
+rcd_serial_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice* rcd;
+
+ ERRIFWRONGDEV(s,NULL);
+ rcd = (struct pluginDevice*) s;
+ if (rcd->hostcount < 0) {
+ LOG(PIL_CRIT
+ , "unconfigured stonith object in RCD_SERIAL_list_hosts");
+ return(NULL);
+ }
+
+ return OurImports->CopyHostList((const char * const *)rcd->hostlist);
+}
+
+/*
+ * At last, we really do it! I don't know what the request argument
+ * is so am just ignoring it...
+ */
+static int
+rcd_serial_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ struct pluginDevice* rcd;
+ int fd;
+ int sigbit;
+ struct itimerval timer;
+ const char * err;
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ rcd = (struct pluginDevice *) s;
+
+ /* check that host matches */
+ if (strcasecmp(host, rcd->hostlist[0])) {
+ LOG(PIL_CRIT, "%s: host '%s' not in hostlist.",
+ __FUNCTION__, host);
+ return(S_BADHOST);
+ }
+
+ /* Set the appropriate bit for the signal */
+ sigbit = *(rcd->signal)=='r' ? TIOCM_RTS : TIOCM_DTR;
+
+ /* Set up the timer */
+ timer.it_interval.tv_sec = 0;
+ timer.it_interval.tv_usec = 0;
+ timer.it_value.tv_sec = rcd->msduration / 1000;
+ timer.it_value.tv_usec = (rcd->msduration % 1000) * 1000;
+
+ /* Open the device */
+ if ((fd = RCD_open_serial_port(rcd->device)) == -1) {
+#ifdef HAVE_STRERROR
+ err = strerror(errno);
+#else
+ err = sys_errlist[errno];
+#endif
+ LOG(PIL_CRIT, "%s: open of %s failed - %s",
+ __FUNCTION__, rcd->device, err);
+ return(S_OOPS);
+ }
+
+ /* Start the timer */
+ RCD_alarm_handler(0);
+#ifdef RCD_NOPAUSE
+ RCD_alarmcaught = 0;
+#endif
+ setitimer(ITIMER_REAL, &timer, 0);
+
+ /* Set the line high */
+ ioctl(fd, TIOCMBIS, &sigbit);
+
+ /* Wait for the alarm signal */
+#ifdef RCD_NOPAUSE
+ while(!RCD_alarmcaught) ioctl(fd, TIOCMBIS, &sigbit);
+#else
+ pause();
+#endif
+
+ /* Clear the line low */
+ ioctl(fd, TIOCMBIC, &sigbit);
+
+ /* Close the port */
+ if (RCD_close_serial_port(rcd->device, fd) != 0) {
+ err = strerror(errno);
+ LOG(PIL_CRIT, "%s: close of %s failed - %s",
+ __FUNCTION__, rcd->device, err);
+ return(S_OOPS);
+ }
+
+ LOG(PIL_INFO,"Host rcd_serial-reset: %s", host);
+ return S_OK;
+}
+
+/*
+ * Parse the information in the given string
+ * and stash it away...
+ */
+static int
+rcd_serial_set_config(StonithPlugin* s, StonithNVpair *list)
+{
+ struct pluginDevice* rcd;
+ StonithNamesToGet namestocopy [] =
+ { {ST_HOSTLIST, NULL}
+ , {ST_TTYDEV, NULL}
+ , {ST_DTRRTS, NULL}
+ , {ST_MSDURATION, NULL}
+ , {NULL, NULL}
+ };
+ char *endptr;
+ int rc = 0;
+
+ LOG(PIL_DEBUG, "%s:called", __FUNCTION__);
+
+ ERRIFWRONGDEV(s,S_OOPS);
+ if (s->isconfigured) {
+ return S_OOPS;
+ }
+
+ rcd = (struct pluginDevice*) s;
+
+ if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+
+ if ((rcd->hostlist = (char **)MALLOC(2*sizeof(char*))) == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory!", __FUNCTION__);
+ FREE(namestocopy[0].s_value);
+ FREE(namestocopy[1].s_value);
+ FREE(namestocopy[2].s_value);
+ FREE(namestocopy[3].s_value);
+ return S_OOPS;
+ }
+ rcd->hostlist[0] = namestocopy[0].s_value;
+ strdown(rcd->hostlist[0]);
+ rcd->hostlist[1] = NULL;
+ rcd->hostcount = 1;
+ rcd->device = namestocopy[1].s_value;
+ rcd->signal = namestocopy[2].s_value;
+ if (strcmp(rcd->signal, "rts") && strcmp(rcd->signal, "dtr")) {
+ LOG(PIL_CRIT, "%s: Invalid signal name '%s'",
+ pluginid, rcd->signal);
+ FREE(namestocopy[3].s_value);
+ return S_BADCONFIG;
+ }
+
+ errno = 0;
+ rcd->msduration = strtol(namestocopy[3].s_value, &endptr, 0);
+ if (((errno == ERANGE)
+ && (rcd->msduration == LONG_MIN || rcd->msduration == LONG_MAX))
+ || *endptr != 0 || rcd->msduration < 1) {
+ LOG(PIL_CRIT, "%s: Invalid msduration '%s'",
+ pluginid, namestocopy[3].s_value);
+ FREE(namestocopy[3].s_value);
+ return S_BADCONFIG;
+ }
+ FREE(namestocopy[3].s_value);
+
+ return S_OK;
+}
+
+/*
+ * Return STONITH config vars
+ */
+static const char * const *
+rcd_serial_get_confignames(StonithPlugin* p)
+{
+ static const char * RcdParams[] = {ST_HOSTLIST, ST_TTYDEV
+ , ST_DTRRTS, ST_MSDURATION, NULL };
+ return RcdParams;
+}
+
+/*
+ * Return STONITH info string
+ */
+static const char *
+rcd_serial_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* rcd;
+ const char * ret;
+
+ ERRIFWRONGDEV(s,NULL);
+ /*
+ * We look in the ST_TEXTDOMAIN catalog for our messages
+ */
+ rcd = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = rcd->idinfo;
+ break;
+ case ST_DEVICENAME:
+ ret = rcd->device;
+ break;
+ case ST_DEVICEDESCR:
+ ret = "RC Delayed Serial STONITH Device\n"
+ "This device can be constructed cheaply from"
+ " readily available components,\n"
+ "with sufficient expertise and testing.\n"
+ "See README.rcd_serial for circuit diagram.\n";
+ break;
+ case ST_DEVICEURL:
+ ret = "http://www.scl.co.uk/rcd_serial/";
+ break;
+ case ST_CONF_XML: /* XML metadata */
+ ret = rcd_serialXML;
+ break;
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * RCD_SERIAL Stonith destructor...
+ */
+static void
+rcd_serial_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* rcd;
+
+ VOIDERRIFWRONGDEV(s);
+
+ rcd = (struct pluginDevice *)s;
+
+ rcd->pluginid = NOTrcd_serialID;
+ if (rcd->hostlist) {
+ stonith_free_hostlist(rcd->hostlist);
+ rcd->hostlist = NULL;
+ }
+ rcd->hostcount = -1;
+ if (rcd->device) {
+ FREE(rcd->device);
+ }
+ if (rcd->signal) {
+ FREE(rcd->signal);
+ }
+ FREE(rcd);
+}
+
+/*
+ * Create a new RCD_Serial Stonith device.
+ * Too bad this function can't be static. (Hmm, weird, it _is_ static?)
+ */
+static StonithPlugin *
+rcd_serial_new(const char *subplugin)
+{
+ struct pluginDevice* rcd = ST_MALLOCT(struct pluginDevice);
+
+ if (rcd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(rcd, 0, sizeof(*rcd));
+
+ rcd->pluginid = pluginid;
+ rcd->hostlist = NULL;
+ rcd->hostcount = -1;
+ rcd->device = NULL;
+ rcd->signal = NULL;
+ rcd->msduration = 0;
+ rcd->idinfo = DEVICE;
+ rcd->sp.s_ops = &rcd_serialOps;
+
+ return &(rcd->sp);
+}
diff --git a/lib/plugins/stonith/rhcs.c b/lib/plugins/stonith/rhcs.c
new file mode 100644
index 0000000..293a081
--- /dev/null
+++ b/lib/plugins/stonith/rhcs.c
@@ -0,0 +1,1035 @@
+/*
+ * Stonith module for RedHat Cluster Suite fencing plugins
+ *
+ * Copyright (c) 2001 SuSE Linux AG
+ * Portions Copyright (c) 2004, tummy.com, ltd.
+ *
+ * Based on ssh.c, Authors: Joachim Gleissner <jg@suse.de>,
+ * Lars Marowsky-Bree <lmb@suse.de>
+ * Modified for external.c: Scott Kleihege <scott@tummy.com>
+ * Reviewed, tested, and config parsing: Sean Reifschneider <jafo@tummy.com>
+ * And overhauled by Lars Marowsky-Bree <lmb@suse.de>, so the circle
+ * closes...
+ * Mangled by Zhaokai <zhaokai@cn.ibm.com>, IBM, 2005
+ * Changed to allow full-featured external plugins by Dave Blaschke
+ * <debltc@us.ibm.com>
+ * Modified for rhcs.c: Dejan Muhamedagic <dejan@suse.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <lha_internal.h>
+
+#include <dirent.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/xmlreader.h>
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN rhcs
+#define PIL_PLUGIN_S "rhcs"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+
+#include <pils/plugin.h>
+
+static StonithPlugin * rhcs_new(const char *);
+static void rhcs_destroy(StonithPlugin *);
+static int rhcs_set_config(StonithPlugin *, StonithNVpair *);
+static const char * const * rhcs_get_confignames(StonithPlugin *);
+static const char * rhcs_getinfo(StonithPlugin * s, int InfoType);
+static int rhcs_status(StonithPlugin * );
+static int rhcs_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** rhcs_hostlist(StonithPlugin *);
+
+static struct stonith_ops rhcsOps ={
+ rhcs_new, /* Create new STONITH object */
+ rhcs_destroy, /* Destroy STONITH object */
+ rhcs_getinfo, /* Return STONITH info string */
+ rhcs_get_confignames, /* Return STONITH info string */
+ rhcs_set_config, /* Get configuration from NVpairs */
+ rhcs_status, /* Return STONITH device status */
+ rhcs_reset_req, /* Request a reset */
+ rhcs_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &rhcsOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * RHCS STONITH device
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ GHashTable * cmd_opts;
+ char * subplugin;
+ char ** confignames;
+ char * hostlist;
+ char * outputbuf;
+ xmlDoc * metadata;
+};
+
+static const char * pluginid = "RHCSDevice-Stonith";
+static const char * NOTpluginID = "RHCS device has been destroyed";
+
+/* Prototypes */
+
+/* Run the command with op and return the exit status + the output
+ * (NULL -> discard output) */
+static int rhcs_run_cmd(struct pluginDevice *sd, const char *op,
+ const char *host, char **output);
+/* Just free up the configuration and the memory, if any */
+static void rhcs_unconfig(struct pluginDevice *sd);
+
+static int
+rhcs_status(StonithPlugin *s)
+{
+ struct pluginDevice * sd;
+ const char * op = "monitor";
+ int rc;
+ char * output = NULL;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ sd = (struct pluginDevice*) s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(S_OOPS);
+ }
+
+ rc = rhcs_run_cmd(sd, op, NULL, &output);
+ if (rc != 0) {
+ LOG(PIL_CRIT, "%s: '%s %s' failed with rc %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ if (output) {
+ LOG(PIL_CRIT, "plugin output: %s", output);
+ }
+ }
+ else {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: running '%s %s' returned %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ }
+ }
+ if (output) {
+ FREE(output);
+ }
+ return rc;
+}
+
+static int
+get_num_tokens(char *str)
+{
+ int namecount = 0;
+
+ if (!str)
+ return namecount;
+ while (*str != EOS) {
+ str += strspn(str, WHITESPACE);
+ if (*str == EOS)
+ break;
+ str += strcspn(str, WHITESPACE);
+ namecount++;
+ }
+ return namecount;
+}
+
+static char **
+rhcs_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice* sd;
+ const char * op = "gethosts";
+ int i, namecount;
+ char ** ret;
+ char * tmp;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,NULL);
+
+ sd = (struct pluginDevice*) s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(NULL);
+ }
+
+ namecount = get_num_tokens(sd->hostlist);
+ ret = MALLOC((namecount+1)*sizeof(char *));
+ if (!ret) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ return NULL;
+ }
+ memset(ret, 0, (namecount+1)*sizeof(char *));
+
+ /* White-space split the sd->hostlist here */
+ i = 0;
+ tmp = strtok(sd->hostlist, WHITESPACE);
+ while (tmp != NULL) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: %s host %s",
+ __FUNCTION__, sd->subplugin, tmp);
+ }
+ ret[i] = STRDUP(tmp);
+ if (!ret[i]) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ stonith_free_hostlist(ret);
+ return NULL;
+ }
+ i++;
+ tmp = strtok(NULL, WHITESPACE);
+ }
+
+ if (i == 0) {
+ LOG(PIL_CRIT, "%s: '%s %s' returned an empty hostlist",
+ __FUNCTION__, sd->subplugin, op);
+ stonith_free_hostlist(ret);
+ ret = NULL;
+ }
+
+ return(ret);
+}
+
+static int
+rhcs_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ struct pluginDevice * sd;
+ const char * op;
+ int rc;
+ char * output = NULL;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Host rhcs-reset initiating on %s", host);
+ }
+
+ sd = (struct pluginDevice*) s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(S_OOPS);
+ }
+
+ switch (request) {
+ case ST_GENERIC_RESET:
+ op = "reboot";
+ break;
+
+ case ST_POWEROFF:
+ op = "off";
+ break;
+
+ case ST_POWERON:
+ op = "on";
+ break;
+
+ default:
+ LOG(PIL_CRIT, "%s: Unknown stonith request %d",
+ __FUNCTION__, request);
+ return S_OOPS;
+ break;
+ }
+
+ rc = rhcs_run_cmd(sd, op, host, &output);
+ if (rc != 0) {
+ LOG(PIL_CRIT, "%s: '%s %s' for host %s failed with rc %d",
+ __FUNCTION__, sd->subplugin, op, host, rc);
+ if (output) {
+ LOG(PIL_CRIT, "plugin output: %s", output);
+ FREE(output);
+ }
+ return S_RESETFAIL;
+ }
+ else {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: running '%s %s' returned %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ }
+ if (output) {
+ LOG(PIL_INFO, "plugin output: %s", output);
+ FREE(output);
+ }
+ return S_OK;
+ }
+
+}
+
+static int
+rhcs_parse_config_info(struct pluginDevice* sd, StonithNVpair * info)
+{
+ char * key;
+ char * value;
+ StonithNVpair * nv;
+
+ sd->hostlist = NULL;
+ sd->cmd_opts = g_hash_table_new(g_str_hash, g_str_equal);
+
+ /* TODO: Maybe treat "" as delimeters too so
+ * whitespace can be passed to the plugins... */
+ for (nv = info; nv->s_name; nv++) {
+ if (!nv->s_name || !nv->s_value) {
+ continue;
+ }
+ key = STRDUP(nv->s_name);
+ if (!key) {
+ goto err_mem;
+ }
+ value = STRDUP(nv->s_value);
+ if (!value) {
+ FREE(key);
+ goto err_mem;
+ }
+ if (!strcmp(key,"hostlist")) {
+ sd->hostlist = value;
+ FREE(key);
+ } else {
+ g_hash_table_insert(sd->cmd_opts, key, value);
+ }
+ }
+
+ return(S_OK);
+
+err_mem:
+ LOG(PIL_CRIT, "%s: out of memory!", __FUNCTION__);
+ rhcs_unconfig(sd);
+
+ return(S_OOPS);
+}
+
+static gboolean
+let_remove_eachitem(gpointer key, gpointer value, gpointer user_data)
+{
+ if (key) {
+ FREE(key);
+ }
+ if (value) {
+ FREE(value);
+ }
+ return TRUE;
+}
+
+static void
+rhcs_unconfig(struct pluginDevice *sd) {
+ if (sd->cmd_opts) {
+ g_hash_table_foreach_remove(sd->cmd_opts,
+ let_remove_eachitem, NULL);
+ g_hash_table_destroy(sd->cmd_opts);
+ sd->cmd_opts = NULL;
+ }
+ if (sd->hostlist) {
+ FREE(sd->hostlist);
+ sd->hostlist = NULL;
+ }
+ if (sd->metadata) {
+ xmlFreeDoc(sd->metadata);
+ xmlCleanupParser();
+ sd->metadata = NULL;
+ }
+}
+
+/*
+ * Parse the information in the given string
+ * and stash it away...
+ */
+static int
+rhcs_set_config(StonithPlugin* s, StonithNVpair *list)
+{
+ struct pluginDevice * sd;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ /* make sure that command has not already been set */
+ if (s->isconfigured) {
+ return(S_OOPS);
+ }
+
+ sd = (struct pluginDevice*) s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(S_OOPS);
+ }
+
+#if 0
+ /* the required parameters may be acquired from the metadata
+ * */
+ if (sd->confignames == NULL) {
+ /* specified by name=value pairs, check required parms */
+ if (rhcs_get_confignames(s) == NULL) {
+ return(S_OOPS);
+ }
+
+ for (p = sd->confignames; *p; p++) {
+ if (OurImports->GetValue(list, *p) == NULL) {
+ LOG(PIL_INFO, "Cannot get parameter %s from "
+ "StonithNVpair", *p);
+ }
+ }
+ }
+#endif
+
+ return rhcs_parse_config_info(sd, list);
+}
+
+
+/* Only interested in regular files starting with fence_ that are also executable */
+static int
+rhcs_exec_select(const struct dirent *dire)
+{
+ struct stat statf;
+ char filename[FILENAME_MAX];
+ int rc;
+
+ rc = snprintf(filename, FILENAME_MAX, "%s/%s",
+ STONITH_RHCS_PLUGINDIR, dire->d_name);
+ if (rc <= 0 || rc >= FILENAME_MAX) {
+ return 0;
+ }
+
+ if ((stat(filename, &statf) == 0) &&
+ (S_ISREG(statf.st_mode)) &&
+ (statf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))) {
+ if (statf.st_mode & (S_IWGRP|S_IWOTH)) {
+ LOG(PIL_WARN, "Executable file %s ignored "
+ "(writable by group/others)", filename);
+ return 0;
+ }else{
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static xmlDoc *
+load_metadata(struct pluginDevice * sd)
+{
+ xmlDoc *doc = NULL;
+ const char *op = "metadata";
+ int rc;
+ char *ret = NULL;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ rc = rhcs_run_cmd(sd, op, NULL, &ret);
+ if (rc != 0) {
+ LOG(PIL_CRIT, "%s: '%s %s' failed with rc %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ if (ret) {
+ LOG(PIL_CRIT, "plugin output: %s", ret);
+ FREE(ret);
+ }
+ goto err;
+ }
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: '%s %s' returned %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ }
+
+ doc = xmlParseMemory(ret, strlen(ret));
+ if (!doc) {
+ LOG(PIL_CRIT, "%s: could not parse metadata",
+ __FUNCTION__);
+ goto err;
+ }
+ sd->metadata = doc;
+
+err:
+ if (ret) {
+ FREE(ret);
+ }
+ return doc;
+}
+
+static const char *skip_attrs[] = {
+ "action", "verbose", "debug", "version", "help", "separator",
+ NULL
+};
+/* XML stuff */
+typedef int (*node_proc)
+ (xmlNodeSet *nodes, struct pluginDevice *sd);
+
+static int
+proc_xpath(const char *xpathexp, struct pluginDevice *sd, node_proc fun)
+{
+ xmlXPathObject *xpathObj = NULL;
+ xmlXPathContext *xpathCtx = NULL;
+ int rc = 1;
+
+ if (!sd->metadata && !load_metadata(sd)) {
+ LOG(PIL_INFO, "%s: no metadata", __FUNCTION__);
+ return 1;
+ }
+
+ /* Create xpath evaluation context */
+ xpathCtx = xmlXPathNewContext(sd->metadata);
+ if(xpathCtx == NULL) {
+ LOG(PIL_CRIT, "%s: unable to create new XPath context", __FUNCTION__);
+ return 1;
+ }
+ /* Evaluate xpath expression */
+ xpathObj = xmlXPathEvalExpression((const xmlChar*)xpathexp, xpathCtx);
+ if(xpathObj == NULL) {
+ LOG(PIL_CRIT, "%s: unable to evaluate expression %s",
+ __FUNCTION__, xpathexp);
+ goto err;
+ }
+
+ if (sd->outputbuf != NULL) {
+ FREE(sd->outputbuf);
+ sd->outputbuf = NULL;
+ }
+ rc = fun(xpathObj->nodesetval, sd);
+err:
+ if (xpathObj)
+ xmlXPathFreeObject(xpathObj);
+ if (xpathCtx)
+ xmlXPathFreeContext(xpathCtx);
+ return rc;
+}
+
+static int
+load_confignames(xmlNodeSet *nodes, struct pluginDevice *sd)
+{
+ xmlChar *attr;
+ const char * const*skip;
+ xmlNode *cur;
+ int i, j, namecount;
+
+ namecount = nodes->nodeNr;
+ if (!namecount) {
+ LOG(PIL_INFO, "%s: no configuration parameters", __FUNCTION__);
+ return 1;
+ }
+ sd->confignames = (char **)MALLOC((namecount+1)*sizeof(char *));
+ if (sd->confignames == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ return 1;
+ }
+
+ /* now copy over confignames */
+ j = 0;
+ for (i = 0; i < nodes->nodeNr; i++) {
+ cur = nodes->nodeTab[i];
+ attr = xmlGetProp(cur, (const xmlChar*)"name");
+ for (skip = skip_attrs; *skip; skip++) {
+ if (!strcmp(*skip,(char *)attr))
+ goto skip;
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: %s configname %s",
+ __FUNCTION__, sd->subplugin, (char *)attr);
+ }
+ sd->confignames[j++] = strdup((char *)attr);
+ xmlFree(attr);
+ skip:
+ continue;
+ }
+ sd->confignames[j] = NULL;
+
+ return 0;
+}
+
+static int
+dump_content(xmlNodeSet *nodes, struct pluginDevice *sd)
+{
+ xmlChar *content = NULL;
+ xmlNode *cur;
+ int rc = 1;
+
+ if (!nodes || !nodes->nodeTab || !nodes->nodeTab[0]) {
+ LOG(PIL_WARN, "%s: %s no nodes",
+ __FUNCTION__, sd->subplugin);
+ return 1;
+ }
+ cur = nodes->nodeTab[0];
+ content = xmlNodeGetContent(cur);
+ if (content && strlen((char *)content) > 0) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: %s found content for %s",
+ __FUNCTION__, sd->subplugin, cur->name);
+ }
+ sd->outputbuf = STRDUP((char *)content);
+ rc = !(*sd->outputbuf);
+ } else {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: %s no content for %s",
+ __FUNCTION__, sd->subplugin, cur->name);
+ }
+ rc = 1;
+ }
+
+ if (content)
+ xmlFree(content);
+ return rc;
+}
+
+static int
+dump_params_xml(xmlNodeSet *nodes, struct pluginDevice *sd)
+{
+ int len = 0;
+ xmlNode *cur;
+ xmlBuffer *xml_buffer = NULL;
+ int rc = 0;
+
+ xml_buffer = xmlBufferCreate();
+ if (!xml_buffer) {
+ LOG(PIL_CRIT, "%s: failed to create xml buffer", __FUNCTION__);
+ return 1;
+ }
+ cur = nodes->nodeTab[0];
+ len = xmlNodeDump(xml_buffer, sd->metadata, cur, 0, TRUE);
+ if (len <= 0) {
+ LOG(PIL_CRIT, "%s: could not dump xml for %s",
+ __FUNCTION__, (char *)xmlGetProp(cur, (const xmlChar*)"name"));
+ rc = 1;
+ goto err;
+ }
+ sd->outputbuf = STRDUP((char *)xml_buffer->content);
+err:
+ xmlBufferFree(xml_buffer);
+ return rc;
+}
+
+/*
+ * Return STONITH config vars
+ */
+static const char * const *
+rhcs_get_confignames(StonithPlugin* p)
+{
+ struct pluginDevice * sd;
+ int i;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ sd = (struct pluginDevice *)p;
+
+ if (sd->subplugin != NULL) {
+ if (!sd->metadata && !load_metadata(sd)) {
+ return NULL;
+ }
+ proc_xpath("/resource-agent/parameters/parameter", sd, load_confignames);
+ } else {
+ /* return list of subplugins in rhcs directory */
+ struct dirent ** files = NULL;
+ int dircount;
+
+ /* get the rhcs plugin's confignames (list of subplugins) */
+ dircount = scandir(STONITH_RHCS_PLUGINDIR, &files,
+ SCANSEL_CAST rhcs_exec_select, NULL);
+ if (dircount < 0) {
+ return NULL;
+ }
+
+ sd->confignames = (char **)MALLOC((dircount+1)*sizeof(char *));
+ if (!sd->confignames) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ return NULL;
+ }
+
+ for (i = 0; i < dircount; i++) {
+ sd->confignames[i] = STRDUP(files[i]->d_name+strlen("fence_"));
+ free(files[i]);
+ files[i] = NULL;
+ }
+ free(files);
+ sd->confignames[dircount] = NULL;
+ }
+
+ return (const char * const *)sd->confignames;
+}
+
+/*
+ * Return STONITH info string
+ */
+static const char *
+fake_op(struct pluginDevice * sd, const char *op)
+{
+ const char *pfx = "RHCS plugin ";
+ char *ret = NULL;
+
+ LOG(PIL_INFO, "rhcs plugins don't really support %s", op);
+ ret = MALLOC(strlen(pfx) + strlen(op) + 1);
+ strcpy(ret, pfx);
+ strcat(ret, op);
+ sd->outputbuf = ret;
+ return(ret);
+}
+
+static const char *
+rhcs_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* sd;
+ const char * op;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,NULL);
+
+ sd = (struct pluginDevice *)s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(NULL);
+ }
+
+ if (!sd->metadata && !load_metadata(sd)) {
+ return NULL;
+ }
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ op = "getinfo-devid";
+ return fake_op(sd, op);
+ break;
+
+ case ST_DEVICENAME:
+ if (!proc_xpath("/resource-agent/shortdesc", sd, dump_content)) {
+ return sd->outputbuf;
+ } else {
+ op = "getinfo-devname";
+ return fake_op(sd, op);
+ }
+ break;
+
+ case ST_DEVICEDESCR:
+ if (!proc_xpath("/resource-agent/longdesc", sd, dump_content)) {
+ return sd->outputbuf;
+ } else {
+ op = "getinfo-devdescr";
+ return fake_op(sd, op);
+ }
+ break;
+
+ case ST_DEVICEURL:
+ op = "getinfo-devurl";
+ return fake_op(sd, op);
+ break;
+
+ case ST_CONF_XML:
+ if (!proc_xpath("/resource-agent/parameters", sd, dump_params_xml)) {
+ return sd->outputbuf;
+ }
+ break;
+
+ default:
+ return NULL;
+ }
+ return NULL;
+}
+
+/*
+ * RHCS Stonith destructor...
+ */
+static void
+rhcs_destroy(StonithPlugin *s)
+{
+ struct pluginDevice * sd;
+ char ** p;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ VOIDERRIFWRONGDEV(s);
+
+ sd = (struct pluginDevice *)s;
+
+ sd->pluginid = NOTpluginID;
+ rhcs_unconfig(sd);
+ if (sd->confignames != NULL) {
+ for (p = sd->confignames; *p; p++) {
+ FREE(*p);
+ }
+ FREE(sd->confignames);
+ sd->confignames = NULL;
+ }
+ if (sd->subplugin != NULL) {
+ FREE(sd->subplugin);
+ sd->subplugin = NULL;
+ }
+ if (sd->outputbuf != NULL) {
+ FREE(sd->outputbuf);
+ sd->outputbuf = NULL;
+ }
+ FREE(sd);
+}
+
+/* Create a new rhcs Stonith device */
+static StonithPlugin *
+rhcs_new(const char *subplugin)
+{
+ struct pluginDevice* sd = ST_MALLOCT(struct pluginDevice);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ if (sd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(sd, 0, sizeof(*sd));
+ sd->pluginid = pluginid;
+ if (subplugin != NULL) {
+ sd->subplugin = STRDUP(subplugin);
+ if (sd->subplugin == NULL) {
+ FREE(sd);
+ return(NULL);
+ }
+ }
+ sd->sp.s_ops = &rhcsOps;
+ return &(sd->sp);
+}
+
+#define MAXLINE 512
+
+static void
+printparam_to_fd(int fd, const char *key, const char *value)
+{
+ char arg[MAXLINE];
+ int cnt;
+
+ cnt = snprintf(arg, MAXLINE, "%s=%s\n", key, value);
+ if (cnt <= 0 || cnt >= MAXLINE) {
+ LOG(PIL_CRIT, "%s: param/value pair too large", __FUNCTION__);
+ return;
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "set rhcs plugin param '%s=%s'", key, value);
+ }
+ if (write(fd, arg, cnt) < 0) {
+ LOG(PIL_CRIT, "%s: write: %m", __FUNCTION__);
+ }
+}
+
+static void
+rhcs_print_var(gpointer key, gpointer value, gpointer user_data)
+{
+ printparam_to_fd(GPOINTER_TO_UINT(user_data), (char *)key, (char *)value);
+}
+
+/* Run the command with op as command line argument(s) and return the exit
+ * status + the output */
+
+static int
+rhcs_run_cmd(struct pluginDevice *sd, const char *op, const char *host, char **output)
+{
+ const int BUFF_LEN=4096;
+ char buff[BUFF_LEN];
+ int read_len = 0;
+ int rc;
+ char * data = NULL;
+ char cmd[FILENAME_MAX+64];
+ struct stat buf;
+ int slen;
+ int pid, status;
+ int fd1[2]; /* our stdout/their stdin */
+ int fd2[2]; /* our stdin/their stdout and stderr */
+
+ rc = snprintf(cmd, FILENAME_MAX, "%s/fence_%s",
+ STONITH_RHCS_PLUGINDIR, sd->subplugin);
+ if (rc <= 0 || rc >= FILENAME_MAX) {
+ LOG(PIL_CRIT, "%s: external command too long.", __FUNCTION__);
+ return -1;
+ }
+
+ if (stat(cmd, &buf) != 0) {
+ LOG(PIL_CRIT, "%s: stat(2) of %s failed: %s",
+ __FUNCTION__, cmd, strerror(errno));
+ return -1;
+ }
+
+ if (!S_ISREG(buf.st_mode)
+ || (!(buf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))) {
+ LOG(PIL_CRIT, "%s: %s found NOT to be executable.",
+ __FUNCTION__, cmd);
+ return -1;
+ }
+
+ if (buf.st_mode & (S_IWGRP|S_IWOTH)) {
+ LOG(PIL_CRIT, "%s: %s found to be writable by group/others, "
+ "NOT executing for security purposes.",
+ __FUNCTION__, cmd);
+ return -1;
+ }
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: Calling '%s'", __FUNCTION__, cmd );
+ }
+
+ if (pipe(fd1) || pipe(fd2))
+ goto err;
+
+ pid = fork();
+ if (pid < 0) {
+ LOG(PIL_CRIT, "%s: fork: %m", __FUNCTION__);
+ goto err;
+ }
+ if (pid) { /* parent */
+ close(fd1[0]);
+ close(fd2[1]);
+
+ if (sd->cmd_opts) {
+ printparam_to_fd(fd1[1], "agent", sd->subplugin);
+ printparam_to_fd(fd1[1], "action", op);
+ if( host )
+ printparam_to_fd(fd1[1], "nodename", host);
+ g_hash_table_foreach(sd->cmd_opts, rhcs_print_var,
+ GUINT_TO_POINTER(fd1[1]));
+ }
+ close(fd1[1]); /* we have nothing more to say */
+
+ fcntl(fd2[0], F_SETFL, fcntl(fd2[0], F_GETFL, 0) | O_NONBLOCK);
+ data = NULL;
+ slen=0;
+ data = MALLOC(1);
+ /* read stdout/stderr from the fence agent */
+ do {
+ data[slen]=EOS;
+ read_len = read(fd2[0], buff, BUFF_LEN);
+ if (read_len > 0) {
+ data=REALLOC(data, slen+read_len+1);
+ if (data == NULL) {
+ goto err;
+ }
+ memcpy(data+slen, buff, read_len);
+ slen += read_len;
+ data[slen] = EOS;
+ } else if (read_len < 0) {
+ if (errno == EAGAIN)
+ continue;
+ LOG(PIL_CRIT, "%s: read from pipe: %m", __FUNCTION__);
+ goto err;
+ }
+ } while (read_len);
+
+ if (!data) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ goto err;
+ }
+ close(fd2[0]);
+ waitpid(pid, &status, 0);
+ if (!WIFEXITED(status)) {
+ LOG(PIL_CRIT, "%s: fence agent failed: %m", __FUNCTION__);
+ goto err;
+ } else {
+ rc = WEXITSTATUS(status);
+ if (rc) {
+ LOG(PIL_CRIT, "%s: fence agent exit code: %d",
+ __FUNCTION__, rc);
+ goto err;
+ }
+ }
+ } else { /* child */
+ close(fd1[1]);
+ close(fd2[0]);
+ close(STDIN_FILENO);
+ if (dup(fd1[0]) < 0)
+ goto err;
+ close(fd1[0]);
+ close(STDOUT_FILENO);
+ if (dup(fd2[1]) < 0)
+ goto err;
+ close(STDERR_FILENO);
+ if (dup(fd2[1]) < 0)
+ goto err;
+ close(fd2[1]);
+ rc = sd->cmd_opts ?
+ execlp(cmd, cmd, NULL) : execlp(cmd, cmd, "-o", op, NULL);
+ if (rc < 0) {
+ LOG(PIL_CRIT, "%s: Calling '%s' failed: %m",
+ __FUNCTION__, cmd);
+ }
+ goto err;
+ }
+
+ if (Debug && data) {
+ LOG(PIL_DEBUG, "%s: '%s' output: %s", __FUNCTION__, cmd, data);
+ }
+
+ if (output) {
+ *output = data;
+ } else {
+ FREE(data);
+ }
+
+ return 0;
+
+err:
+ if (data) {
+ FREE(data);
+ }
+ if (output) {
+ *output = NULL;
+ }
+
+ return(-1);
+
+}
diff --git a/lib/plugins/stonith/ribcl.py.in b/lib/plugins/stonith/ribcl.py.in
new file mode 100644
index 0000000..14e070c
--- /dev/null
+++ b/lib/plugins/stonith/ribcl.py.in
@@ -0,0 +1,101 @@
+#!@PYTHON@
+
+
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+import sys
+import socket
+from httplib import *
+from time import sleep
+
+
+argv = sys.argv
+
+
+try:
+ host = argv[1].split('.')[0]+'-rm'
+ cmd = argv[2]
+except IndexError:
+ print "Not enough arguments"
+ sys.exit(1)
+
+
+login = [ '<RIBCL VERSION="1.2">',
+ '<LOGIN USER_LOGIN="Administrator" PASSWORD="********">' ]
+
+
+logout = [ '</LOGIN>', '</RIBCL>' ]
+
+
+status = [ '<SERVER_INFO MODE="read">', '<GET_HOST_POWER_STATUS/>',
+ '</SERVER_INFO>' ]
+
+
+reset = [ '<SERVER_INFO MODE="write">', '<RESET_SERVER/>', '</SERVER_INFO>' ]
+
+
+off = [ '<SERVER_INFO MODE = "write">', '<SET_HOST_POWER HOST_POWER = "N"/>',
+ '</SERVER_INFO>' ]
+
+
+on = [ '<SERVER_INFO MODE = "write">', '<SET_HOST_POWER HOST_POWER = "Y"/>',
+ '</SERVER_INFO>' ]
+
+
+todo = { 'reset':reset, 'on':on, 'off':off, 'status':status }
+
+
+acmds=[]
+try:
+ if cmd == 'reset' and host.startswith('gfxcl'):
+ acmds.append(login + todo['off'] + logout)
+ acmds.append(login + todo['on'] + logout)
+ else:
+ acmds.append(login + todo[cmd] + logout)
+except KeyError:
+ print "Invalid command: "+ cmd
+ sys.exit(1)
+
+
+try:
+ for cmds in acmds:
+
+
+ c=HTTPSConnection(host)
+ c.send('<?xml version="1.0"?>\r\n')
+ c.sock.recv(1024)
+
+
+ for line in cmds:
+ c.send(line+'\r\n')
+ c.sock.recv(1024)
+
+
+ c.close()
+ sleep(1)
+
+
+except socket.gaierror, msg:
+ print msg
+ sys.exit(1)
+except socket.sslerror, msg:
+ print msg
+ sys.exit(1)
+except socket.error, msg:
+ print msg
+ sys.exit(1)
+
diff --git a/lib/plugins/stonith/riloe.c b/lib/plugins/stonith/riloe.c
new file mode 100644
index 0000000..a4a8312
--- /dev/null
+++ b/lib/plugins/stonith/riloe.c
@@ -0,0 +1,338 @@
+/*
+ * Stonith module for RILOE Stonith device
+ *
+ * Copyright (c) 2004 Alain St-Denis <alain.st-denis@ec.gc.ca>
+ *
+ * Mangled by Zhaokai <zhaokai@cn.ibm.com>, IBM, 2005
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define DEVICE "Compaq RILOE"
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN riloe
+#define PIL_PLUGIN_S "riloe"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+static StonithPlugin * riloe_new(const char *);
+static void riloe_destroy(StonithPlugin *);
+static int riloe_set_config(StonithPlugin *, StonithNVpair *);
+static const char * const * riloe_get_confignames(StonithPlugin * );
+static const char * riloe_getinfo(StonithPlugin * s, int InfoType);
+static int riloe_status(StonithPlugin * );
+static int riloe_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** riloe_hostlist(StonithPlugin *);
+
+static struct stonith_ops riloeOps ={
+ riloe_new, /* Create new STONITH object */
+ riloe_destroy, /* Destroy STONITH object */
+ riloe_getinfo, /* Return STONITH info string */
+ riloe_get_confignames, /* Return STONITH info string */
+ riloe_set_config, /* Get configuration from NVpairs */
+ riloe_status, /* Return STONITH device status */
+ riloe_reset_req, /* Request a reset */
+ riloe_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &riloeOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+#define RILOE_COMMAND STONITH_MODULES "/ribcl.py"
+
+/*
+ * Riloe STONITH device. We are very agreeable, but don't do much :-)
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ char ** hostlist;
+ int hostcount;
+};
+
+static const char * pluginid = "RiloeDevice-Stonith";
+static const char * NOTriloeID = "Riloe device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+static const char *riloeXML =
+ XML_PARAMETERS_BEGIN
+ XML_HOSTLIST_PARM
+ XML_PARAMETERS_END;
+
+static int
+riloe_status(StonithPlugin *s)
+{
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+ return S_OK;
+}
+
+
+/*
+ * Return the list of hosts configured for this RILOE device
+ */
+
+static char **
+riloe_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice* nd;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,NULL);
+ nd = (struct pluginDevice*) s;
+ if (nd->hostcount < 0) {
+ LOG(PIL_CRIT
+ , "unconfigured stonith object in %s", __FUNCTION__);
+ return(NULL);
+ }
+
+ return OurImports->CopyHostList((const char * const*)nd->hostlist);
+}
+
+/*
+ * Parse the config information, and stash it away...
+ */
+
+static int
+RILOE_parse_config_info(struct pluginDevice* nd, const char * info)
+{
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ if (nd->hostcount >= 0) {
+ return(S_OOPS);
+ }
+
+ nd->hostlist = OurImports->StringToHostList(info);
+ if (nd->hostlist == NULL) {
+ LOG(PIL_CRIT,"StringToHostList() failed");
+ return S_OOPS;
+ }
+ for (nd->hostcount = 0; nd->hostlist[nd->hostcount]; nd->hostcount++) {
+ strdown(nd->hostlist[nd->hostcount]);
+ }
+ return(S_OK);
+}
+
+
+/*
+ * Pretend to reset the given host on this Stonith device.
+ * (we don't even error check the "request" type)
+ */
+static int
+riloe_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ char cmd[4096];
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ snprintf(cmd, sizeof(cmd), "%s %s reset", RILOE_COMMAND, host);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "command %s will be executed", cmd);
+ }
+
+ if (system(cmd) == 0) {
+ return S_OK;
+ } else {
+ LOG(PIL_CRIT, "command %s failed", cmd);
+ return(S_RESETFAIL);
+ }
+}
+
+/*
+ * Parse the information in the given string,
+ * and stash it away...
+ */
+static int
+riloe_set_config(StonithPlugin* s, StonithNVpair *list)
+{
+ StonithNamesToGet namestocopy [] =
+ { {ST_HOSTLIST, NULL}
+ , {NULL, NULL}
+ };
+ struct pluginDevice* nd;
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+ nd = (struct pluginDevice*) s;
+
+ if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+
+ rc = RILOE_parse_config_info(nd , namestocopy[0].s_value);
+ FREE(namestocopy[0].s_value);
+ return rc;
+}
+
+/*
+ * Return the Stonith plugin configuration parameter
+ */
+static const char* const *
+riloe_get_confignames(StonithPlugin* p)
+{
+ static const char * RiloeParams[] = {ST_HOSTLIST, NULL };
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ return RiloeParams;
+}
+
+/*
+ * Return STONITH info string
+ */
+
+static const char *
+riloe_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* nd;
+ const char * ret;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,NULL);
+ /*
+ * We look in the ST_TEXTDOMAIN catalog for our messages
+ */
+ nd = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = nd->idinfo;
+ break;
+ case ST_DEVICEDESCR:
+ ret = "Compaq RILOE STONITH device\n"
+ "Very early version!";
+ break;
+ case ST_DEVICEURL:
+ ret = "http://www.hp.com/";
+ break;
+ case ST_CONF_XML: /* XML metadata */
+ ret = riloeXML;
+ break;
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * RILOE Stonith destructor...
+ */
+static void
+riloe_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* nd;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ VOIDERRIFWRONGDEV(s);
+ nd = (struct pluginDevice *)s;
+
+ nd->pluginid = NOTriloeID;
+ if (nd->hostlist) {
+ stonith_free_hostlist(nd->hostlist);
+ nd->hostlist = NULL;
+ }
+ nd->hostcount = -1;
+ FREE(nd);
+}
+
+/* Create a new Riloe Stonith device. Too bad this function can't be static */
+static StonithPlugin *
+riloe_new(const char *subplugin)
+{
+ struct pluginDevice* nd = ST_MALLOCT(struct pluginDevice);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ if (nd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(nd, 0, sizeof(*nd));
+ nd->pluginid = pluginid;
+ nd->hostlist = NULL;
+ nd->hostcount = -1;
+ nd->idinfo = DEVICE;
+ nd->sp.s_ops = &riloeOps;
+
+ return &(nd->sp);
+}
diff --git a/lib/plugins/stonith/rps10.c b/lib/plugins/stonith/rps10.c
new file mode 100644
index 0000000..08d9873
--- /dev/null
+++ b/lib/plugins/stonith/rps10.c
@@ -0,0 +1,1070 @@
+/*
+ * Stonith module for WTI Remote Power Controllers (RPS-10M device)
+ *
+ * Original code from baytech.c by
+ * Copyright (c) 2000 Alan Robertson <alanr@unix.sh>
+ *
+ * Modifications for WTI RPS10
+ * Copyright (c) 2000 Computer Generation Incorporated
+ * Eric Z. Ayers <eric.ayers@compgen.com>
+ *
+ * Mangled by Zhaokai <zhaokai@cn.ibm.com>, IBM, 2005
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <lha_internal.h>
+
+#define DEVICE "WTI RPS10 Power Switch"
+#include "stonith_plugin_common.h"
+
+#include <termios.h>
+#define PIL_PLUGIN rps10
+#define PIL_PLUGIN_S "rps10"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#define ST_RPS10 "serial_to_targets"
+#define MAX_PRSID 256
+#include <pils/plugin.h>
+
+static StonithPlugin * rps10_new(const char *);
+static void rps10_destroy(StonithPlugin *);
+static int rps10_set_config(StonithPlugin *, StonithNVpair *);
+static const char * const * rps10_get_confignames(StonithPlugin *);
+static const char * rps10_getinfo(StonithPlugin * s, int InfoType);
+static int rps10_status(StonithPlugin * );
+static int rps10_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** rps10_hostlist(StonithPlugin *);
+
+static struct stonith_ops rps10Ops ={
+ rps10_new, /* Create new STONITH object */
+ rps10_destroy, /* Destroy STONITH object */
+ rps10_getinfo, /* Return STONITH info string */
+ rps10_get_confignames, /* Return STONITH info string */
+ rps10_set_config, /* Get configuration from NVpairs */
+ rps10_status, /* Return STONITH device status */
+ rps10_reset_req, /* Request a reset */
+ rps10_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+#include "stonith_signal.h"
+#define DOESNT_USE_STONITHKILLCOMM
+#define DOESNT_USE_STONITHSCANLINE
+#include "stonith_expect_helpers.h"
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &rps10Ops
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * This was written for a Western Telematic Inc. (WTI)
+ * Remote Power Switch - RPS-10M.
+ *
+ * It has a DB9 serial port, a Rotary Address Switch,
+ * and a pair of RJ-11 jacks for linking multiple switches
+ * together. The 'M' unit is a master unit which can control
+ * up to 9 additional slave units. (the master unit also has an
+ * A/C outlet, so you can control up to 10 devices)
+ *
+ * There are a set of dip switches. The default shipping configuration
+ * is with all dip switches down. I highly recommend that you flip
+ * switch #3 up, so that when the device is plugged in, the power
+ * to the unit comes on.
+ *
+ * The serial interface is fixed at 9600 BPS (well, you *CAN*
+ * select 2400 BPS with a dip switch, but why?) 8-N-1
+ *
+ * The ASCII command string is:
+ *
+ * ^B^X^X^B^X^Xac^M
+ *
+ * ^B^X^X^B^X^X "fixed password" prefix (CTRL-B CTRL-X ... )
+ * ^M the carriage return character
+ *
+ * a = 0-9 Indicates the address of the module to receive the command
+ * a = * Sends the command to all modules
+ *
+ * c = 0 Switch the AC outlet OFF
+ * Returns:
+ * Plug 0 Off
+ * Complete
+ *
+ * c = 1 Switch the AC outlet ON
+ * Returns:
+ * Plug 0 On
+ * Complete
+ *
+ * c = T Toggle AC OFF (delay) then back ON
+ * Returns:
+ * Plug 0 Off
+ * Plug 0 On
+ * Complete
+ *
+ * c = ? Read and display status of the selected module
+ * Returns:
+ * Plug 0 On # or Plug 0 Off
+ * Complete
+ *
+ * e.g. ^B^X^X^B^X^X0T^M toggles the power on plug 0 OFF and then ON
+ *
+ * 21 September 2000
+ * Eric Z. Ayers
+ * Computer Generation, Inc.
+ */
+
+struct cntrlr_str {
+ char outlet_id; /* value 0-9, '*' */
+ char * node; /* name of the node attached to this outlet */
+};
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+
+ int fd; /* FD open to the serial port */
+
+ char * device; /* Serial device name to use to communicate
+ to this RPS10
+ */
+
+#define WTI_NUM_CONTROLLERS 10
+ struct cntrlr_str
+ controllers[WTI_NUM_CONTROLLERS];
+ /* one master switch can address 10 controllers */
+
+ /* Number of actually configured units */
+ int unit_count;
+
+};
+
+/* This string is used to identify this type of object in the config file */
+static const char * pluginid = "WTI_RPS10";
+static const char * NOTwtiid = "OBJECT DESTROYED: (WTI RPS-10)";
+
+#include "stonith_config_xml.h"
+
+#define XML_RPS10_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ "Value in the format \"serial_device remotenode outlet [remotenode outlet]...\"" \
+ XML_PARM_SHORTDESC_END
+
+#define XML_RPS10_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The RPS-10 STONITH device configuration information in the format \"serial_device remotenode outlet [remotenode outlet]...\"" \
+ XML_PARM_LONGDESC_END
+
+#define XML_RPS10_PARM \
+ XML_PARAMETER_BEGIN(ST_RPS10, "string", "1", "1") \
+ XML_RPS10_SHORTDESC \
+ XML_RPS10_LONGDESC \
+ XML_PARAMETER_END
+
+static const char *rps10XML =
+ XML_PARAMETERS_BEGIN
+ XML_RPS10_PARM
+ XML_PARAMETERS_END;
+
+/* WTIpassword - The fixed string ^B^X^X^B^X^X */
+static const char WTIpassword[7] = {2,24,24,2,24,24,0};
+
+/*
+ * Different expect strings that we get from the WTI_RPS10
+ * Remote Power Controllers...
+ */
+
+static struct Etoken WTItokReady[] = { {"RPS-10 Ready", 0, 0}, {NULL,0,0}};
+static struct Etoken WTItokComplete[] = { {"Complete", 0, 0} ,{NULL,0,0}};
+static struct Etoken WTItokPlug[] = { {"Plug", 0, 0}, {NULL,0,0}};
+static struct Etoken WTItokOutlet[] = { {"0", 0, 0},
+ {"1", 0, 0},
+ {"2", 0, 0},
+ {"3", 0, 0},
+ {"4", 0, 0},
+ {"5", 0, 0},
+ {"6", 0, 0},
+ {"7", 0, 0},
+ {"8", 0, 0},
+ {"9", 0, 0},
+ {NULL,0,0}};
+
+static struct Etoken WTItokOff[] = { {"Off", 0, 0}, {NULL,0,0}};
+
+/*
+ * Tokens currently not used because they don't show up on all RPS10 units:
+ *
+ */
+static struct Etoken WTItokOn[] = { {"On", 0, 0}, {NULL,0,0}};
+
+/* Accept either a CR/NL or an NL/CR */
+static struct Etoken WTItokCRNL[] = { {"\n\r",0,0},{"\r\n",0,0},{NULL,0,0}};
+
+static int RPSConnect(struct pluginDevice * ctx);
+static int RPSDisconnect(struct pluginDevice * ctx);
+
+static int RPSReset(struct pluginDevice*, char unit_id, const char * rebootid);
+#if defined(ST_POWERON)
+static int RPSOn(struct pluginDevice*, char unit_id, const char * rebootid);
+#endif
+#if defined(ST_POWEROFF)
+static int RPSOff(struct pluginDevice*, char unit_id, const char * rebootid);
+#endif
+static signed char RPSNametoOutlet ( struct pluginDevice * ctx, const char * host );
+
+static int RPS_parse_config_info(struct pluginDevice* ctx, const char * info);
+
+#define SENDCMD(outlet, cmd, timeout) { \
+ int ret_val = RPSSendCommand(ctx, outlet, cmd, timeout);\
+ if (ret_val != S_OK) { \
+ return ret_val; \
+ } \
+ }
+
+/*
+ * RPSSendCommand - send a command to the specified outlet
+ */
+static int
+RPSSendCommand (struct pluginDevice *ctx, char outlet, char command, int timeout)
+{
+ char writebuf[10]; /* all commands are 9 chars long! */
+ int return_val; /* system call result */
+ fd_set rfds, wfds, xfds;
+ struct timeval tv; /* */
+
+ /* list of FDs for select() */
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ FD_ZERO(&xfds);
+
+ snprintf (writebuf, sizeof(writebuf), "%s%c%c\r",
+ WTIpassword, outlet, command);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Sending %s\n", writebuf);
+ }
+
+ /* Make sure the serial port won't block on us. use select() */
+ FD_SET(ctx->fd, &wfds);
+ FD_SET(ctx->fd, &xfds);
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ return_val = select(ctx->fd+1, NULL, &wfds,&xfds, &tv);
+ if (return_val == 0) {
+ /* timeout waiting on serial port */
+ LOG(PIL_CRIT, "%s: Timeout writing to %s",
+ pluginid, ctx->device);
+ return S_TIMEOUT;
+ } else if ((return_val == -1) || FD_ISSET(ctx->fd, &xfds)) {
+ /* an error occured */
+ LOG(PIL_CRIT, "%s: Error before writing to %s: %s",
+ pluginid, ctx->device, strerror(errno));
+ return S_OOPS;
+ }
+
+ /* send the command */
+ if (write(ctx->fd, writebuf, strlen(writebuf)) !=
+ (int)strlen(writebuf)) {
+ LOG(PIL_CRIT, "%s: Error writing to %s : %s",
+ pluginid, ctx->device, strerror(errno));
+ return S_OOPS;
+ }
+
+ /* suceeded! */
+ return S_OK;
+
+} /* end RPSSendCommand() */
+
+/*
+ * RPSReset - Reset (power-cycle) the given outlet id
+ */
+static int
+RPSReset(struct pluginDevice* ctx, char unit_id, const char * rebootid)
+{
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ if (ctx->fd < 0) {
+ LOG(PIL_CRIT, "%s: device %s is not open!", pluginid,
+ ctx->device);
+ return S_OOPS;
+ }
+
+ /* send the "toggle power" command */
+ SENDCMD(unit_id, 'T', 10);
+
+ /* Expect "Plug 0 Off" */
+ /* Note: If asked to control "*", the RPS10 will report all units it
+ * separately; however, we don't know how many, so we can only wait
+ * for the first unit to report something and then wait until the
+ * "Complete" */
+ EXPECT(ctx->fd, WTItokPlug, 5);
+ if (Debug) {
+ LOG(PIL_DEBUG, "Got Plug\n");
+ }
+ EXPECT(ctx->fd, WTItokOutlet, 2);
+ if (Debug) {
+ LOG(PIL_DEBUG, "Got Outlet #\n");
+ }
+ EXPECT(ctx->fd, WTItokOff, 2);
+ if (Debug) {
+ LOG(PIL_DEBUG, "Got Off\n");
+ }
+ EXPECT(ctx->fd, WTItokCRNL, 2);
+ LOG(PIL_INFO, "Host is being rebooted: %s", rebootid);
+
+ /* Expect "Complete" */
+ EXPECT(ctx->fd, WTItokComplete, 14);
+ if (Debug) {
+ LOG(PIL_DEBUG, "Got Complete\n");
+ }
+ EXPECT(ctx->fd, WTItokCRNL, 2);
+ if (Debug) {
+ LOG(PIL_DEBUG, "Got NL\n");
+ }
+
+ return(S_OK);
+
+} /* end RPSReset() */
+
+
+#if defined(ST_POWERON)
+/*
+ * RPSOn - Turn OFF the given outlet id
+ */
+static int
+RPSOn(struct pluginDevice* ctx, char unit_id, const char * host)
+{
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ if (ctx->fd < 0) {
+ LOG(PIL_CRIT, "%s: device %s is not open!", pluginid,
+ ctx->device);
+ return S_OOPS;
+ }
+
+ /* send the "On" command */
+ SENDCMD(unit_id, '1', 10);
+
+ /* Expect "Plug 0 On" */
+ EXPECT(ctx->fd, WTItokPlug, 5);
+ EXPECT(ctx->fd, WTItokOutlet, 2);
+ EXPECT(ctx->fd, WTItokOn, 2);
+ EXPECT(ctx->fd, WTItokCRNL, 2);
+ LOG(PIL_INFO, "Host is being turned on: %s", host);
+
+ /* Expect "Complete" */
+ EXPECT(ctx->fd, WTItokComplete, 5);
+ EXPECT(ctx->fd, WTItokCRNL, 2);
+
+ return(S_OK);
+
+} /* end RPSOn() */
+#endif
+
+
+#if defined(ST_POWEROFF)
+/*
+ * RPSOff - Turn Off the given outlet id
+ */
+static int
+RPSOff(struct pluginDevice* ctx, char unit_id, const char * host)
+{
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ if (ctx->fd < 0) {
+ LOG(PIL_CRIT, "%s: device %s is not open!", pluginid,
+ ctx->device);
+ return S_OOPS;
+ }
+
+ /* send the "Off" command */
+ SENDCMD(unit_id, '0', 10);
+
+ /* Expect "Plug 0 Off" */
+ EXPECT(ctx->fd, WTItokPlug, 5);
+ EXPECT(ctx->fd, WTItokOutlet, 2);
+ EXPECT(ctx->fd, WTItokOff, 2);
+ EXPECT(ctx->fd, WTItokCRNL, 2);
+ LOG(PIL_INFO, "Host is being turned on: %s", host);
+
+ /* Expect "Complete" */
+ EXPECT(ctx->fd, WTItokComplete, 5);
+ EXPECT(ctx->fd, WTItokCRNL, 2);
+
+ return(S_OK);
+
+} /* end RPSOff() */
+#endif
+
+
+/*
+ * rps10_status - API entry point to probe the status of the stonith device
+ * (basically just "is it reachable and functional?", not the
+ * status of the individual outlets)
+ *
+ * Returns:
+ * S_OOPS - some error occured
+ * S_OK - if the stonith device is reachable and online.
+ */
+static int
+rps10_status(StonithPlugin *s)
+{
+ struct pluginDevice* ctx;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ ctx = (struct pluginDevice*) s;
+ if (RPSConnect(ctx) != S_OK) {
+ return(S_OOPS);
+ }
+
+ /* The "connect" really does enough work to see if the
+ controller is alive... It verifies that it is returning
+ RPS-10 Ready
+ */
+
+ return(RPSDisconnect(ctx));
+}
+
+/*
+ * rps10_hostlist - API entry point to return the list of hosts
+ * for the devices on this WTI_RPS10 unit
+ *
+ * This type of device is configured from the config file,
+ * so we don't actually have to connect to figure this
+ * out, just peruse the 'ctx' structure.
+ * Returns:
+ * NULL on error
+ * a malloced array, terminated with a NULL,
+ * of null-terminated malloc'ed strings.
+ */
+static char **
+rps10_hostlist(StonithPlugin *s)
+{
+ char ** ret = NULL; /* list to return */
+ int i;
+ int j;
+ struct pluginDevice* ctx;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,NULL);
+
+ ctx = (struct pluginDevice*) s;
+
+ if (ctx->unit_count >= 1) {
+ ret = (char **)MALLOC((ctx->unit_count+1)*sizeof(char*));
+ if (ret == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return ret;
+ }
+ ret[ctx->unit_count]=NULL; /* null terminate the array */
+ for (i=0; i < ctx->unit_count; i++) {
+ ret[i] = STRDUP(ctx->controllers[i].node);
+ if (ret[i] == NULL) {
+ for(j=0; j<i; j++) {
+ FREE(ret[j]);
+ }
+ FREE(ret); ret = NULL;
+ break;
+ }
+ } /* end for each possible outlet */
+ } /* end if any outlets are configured */
+ return(ret);
+} /* end si_hostlist() */
+
+/*
+ * Parse the given configuration information, and stash
+ * it away...
+ *
+ * The format of <info> for this module is:
+ * <serial device> <remotenode> <outlet> [<remotenode> <outlet>] ...
+ *
+ * e.g. A machine named 'nodea' can kill a machine named 'nodeb' through
+ * a device attached to serial port /dev/ttyS0.
+ * A machine named 'nodeb' can kill machines 'nodea' and 'nodec'
+ * through a device attached to serial port /dev/ttyS1 (outlets 0
+ * and 1 respectively)
+ *
+ * <assuming this is the heartbeat configuration syntax:>
+ *
+ * stonith nodea rps10 /dev/ttyS0 nodeb 0
+ * stonith nodeb rps10 /dev/ttyS0 nodea 0 nodec 1
+ *
+ * Another possible configuration is for 2 stonith devices
+ * accessible through 2 different serial ports on nodeb:
+ *
+ * stonith nodeb rps10 /dev/ttyS0 nodea 0
+ * stonith nodeb rps10 /dev/ttyS1 nodec 0
+ */
+
+/*
+ * OOPS!
+ *
+ * Most of the large block of comments above is incorrect as far as this
+ * module is concerned. It is somewhat applicable to the heartbeat code,
+ * but not to this Stonith module.
+ *
+ * The format of parameter string for this module is:
+ * <serial device> <remotenode> <outlet> [<remotenode> <outlet>] ...
+ */
+
+static int
+RPS_parse_config_info(struct pluginDevice* ctx, const char * info)
+{
+ char *copy;
+ char *token;
+ char *outlet, *node;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ /* strtok() is nice to use to parse a string with
+ (other than it isn't threadsafe), but it is destructive, so
+ we're going to alloc our own private little copy for the
+ duration of this function.
+ */
+
+ copy = STRDUP(info);
+ if (!copy) {
+ LOG(PIL_CRIT, "out of memory");
+ return S_OOPS;
+ }
+
+ /* Grab the serial device */
+ token = strtok (copy, " \t");
+
+ if (!token) {
+ LOG(PIL_CRIT, "%s: Can't find serial device on config line '%s'",
+ pluginid, info);
+ goto token_error;
+ }
+
+ ctx->device = STRDUP(token);
+ if (!ctx->device) {
+ LOG(PIL_CRIT, "out of memory");
+ goto token_error;
+ }
+
+ /* Loop through the rest of the command line which should consist of */
+ /* <nodename> <outlet> pairs */
+ while ((node = strtok (NULL, " \t"))
+ && (outlet = strtok (NULL, " \t\n"))) {
+ char outlet_id;
+
+ /* validate the outlet token */
+ if ((sscanf (outlet, "%c", &outlet_id) != 1)
+ || !( ((outlet_id >= '0') && (outlet_id <= '9'))
+ || (outlet_id == '*') || (outlet_id == 'A') )
+ ) {
+ LOG(PIL_CRIT
+ , "%s: the outlet_id %s must be between"
+ " 0 and 9 or '*' / 'A'",
+ pluginid, outlet);
+ goto token_error;
+ }
+
+ if (outlet_id == 'A') {
+ /* Remap 'A' to '*'; in some configurations,
+ * a '*' can't be configured because it breaks
+ * scripts -- lmb */
+ outlet_id = '*';
+ }
+
+ if (ctx->unit_count >= WTI_NUM_CONTROLLERS) {
+ LOG(PIL_CRIT,
+ "%s: Tried to configure too many controllers",
+ pluginid);
+ goto token_error;
+ }
+
+ ctx->controllers[ctx->unit_count].node = STRDUP(node);
+ strdown(ctx->controllers[ctx->unit_count].node);
+ ctx->controllers[ctx->unit_count].outlet_id = outlet_id;
+ ctx->unit_count++;
+
+ }
+
+ /* free our private copy of the string we've been destructively
+ * parsing with strtok()
+ */
+ FREE(copy);
+ return ((ctx->unit_count > 0) ? S_OK : S_BADCONFIG);
+
+token_error:
+ FREE(copy);
+ if (ctx->device) {
+ FREE(ctx->device);
+ ctx->device = NULL;
+ }
+ return(S_BADCONFIG);
+}
+
+
+/*
+ * dtrtoggle - toggle DTR on the serial port
+ *
+ * snarfed from minicom, sysdep1.c, a well known POSIX trick.
+ *
+ */
+static void dtrtoggle(int fd) {
+ struct termios tty, old;
+ int sec = 2;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ tcgetattr(fd, &tty);
+ tcgetattr(fd, &old);
+ cfsetospeed(&tty, B0);
+ cfsetispeed(&tty, B0);
+ tcsetattr(fd, TCSANOW, &tty);
+ if (sec>0) {
+ sleep(sec);
+ tcsetattr(fd, TCSANOW, &old);
+ }
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "dtrtoggle Complete (%s)\n", pluginid);
+ }
+}
+
+/*
+ * RPSConnect -
+ *
+ * Connect to the given WTI_RPS10 device.
+ * Side Effects
+ * DTR on the serial port is toggled
+ * ctx->fd now contains a valid file descriptor to the serial port
+ * ??? LOCK THE SERIAL PORT ???
+ *
+ * Returns
+ * S_OK on success
+ * S_OOPS on error
+ * S_TIMEOUT if the device did not respond
+ *
+ */
+static int
+RPSConnect(struct pluginDevice * ctx)
+{
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ /* Open the serial port if it isn't already open */
+ if (ctx->fd < 0) {
+ struct termios tio;
+
+ if (OurImports->TtyLock(ctx->device) < 0) {
+ LOG(PIL_CRIT, "%s: TtyLock failed.", pluginid);
+ return S_OOPS;
+ }
+
+ ctx->fd = open (ctx->device, O_RDWR);
+ if (ctx->fd <0) {
+ LOG(PIL_CRIT, "%s: Can't open %s : %s",
+ pluginid, ctx->device, strerror(errno));
+ return S_OOPS;
+ }
+
+ /* set the baudrate to 9600 8 - N - 1 */
+ memset (&tio, 0, sizeof(tio));
+
+ /* ??? ALAN - the -tradtitional flag on gcc causes the
+ CRTSCTS constant to generate a warning, and warnings
+ are treated as errors, so I can't set this flag! - EZA ???
+
+ Hmmm. now that I look at the documentation, RTS
+ is just wired high on this device! we don't need it.
+ */
+ /* tio.c_cflag = B9600 | CS8 | CLOCAL | CREAD | CRTSCTS ;*/
+ tio.c_cflag = B9600 | CS8 | CLOCAL | CREAD ;
+ tio.c_lflag = ICANON;
+
+ if (tcsetattr (ctx->fd, TCSANOW, &tio) < 0) {
+ LOG(PIL_CRIT, "%s: Can't set attributes %s : %s",
+ pluginid, ctx->device, strerror(errno));
+ close (ctx->fd);
+ OurImports->TtyUnlock(ctx->device);
+ ctx->fd=-1;
+ return S_OOPS;
+ }
+ /* flush all data to and fro the serial port before we start */
+ if (tcflush (ctx->fd, TCIOFLUSH) < 0) {
+ LOG(PIL_CRIT, "%s: Can't flush %s : %s",
+ pluginid, ctx->device, strerror(errno));
+ close (ctx->fd);
+ OurImports->TtyUnlock(ctx->device);
+ ctx->fd=-1;
+ return S_OOPS;
+ }
+
+ }
+
+ /* Toggle DTR - this 'resets' the controller serial port interface
+ In minicom, try CTRL-A H to hangup and you can see this behavior.
+ */
+ dtrtoggle(ctx->fd);
+
+ /* Wait for the switch to respond with "RPS-10 Ready".
+ Emperically, this usually takes 5-10 seconds...
+ ... If this fails, this may be a hint that you got
+ a broken serial cable, which doesn't connect hardware
+ flow control.
+ */
+ if (Debug) {
+ LOG(PIL_DEBUG, "Waiting for READY\n");
+ }
+ EXPECT(ctx->fd, WTItokReady, 12);
+ if (Debug) {
+ LOG(PIL_DEBUG, "Got READY\n");
+ }
+ EXPECT(ctx->fd, WTItokCRNL, 2);
+ if (Debug) {
+ LOG(PIL_DEBUG, "Got NL\n");
+ }
+
+ return(S_OK);
+}
+
+static int
+RPSDisconnect(struct pluginDevice * ctx)
+{
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ if (ctx->fd >= 0) {
+ /* Flush the serial port, we don't care what happens to the
+ * characters and failing to do this can cause close to hang.
+ */
+ tcflush(ctx->fd, TCIOFLUSH);
+ close (ctx->fd);
+ if (ctx->device != NULL) {
+ OurImports->TtyUnlock(ctx->device);
+ }
+ }
+ ctx->fd = -1;
+
+ return S_OK;
+}
+
+/*
+ * RPSNametoOutlet - Map a hostname to an outlet on this stonith device.
+ *
+ * Returns:
+ * 0-9, * on success ( the outlet id on the RPS10 )
+ * -1 on failure (host not found in the config file)
+ *
+ */
+static signed char
+RPSNametoOutlet ( struct pluginDevice * ctx, const char * host )
+{
+ int i=0;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ /* scan the controllers[] array to see if this host is there */
+ for (i=0;i<ctx->unit_count;i++) {
+ /* return the outlet id */
+ if ( ctx->controllers[i].node
+ && !strcasecmp(host, ctx->controllers[i].node)) {
+ /* found it! */
+ break;
+ }
+ }
+
+ if (i == ctx->unit_count) {
+ return -1;
+ } else {
+ return ctx->controllers[i].outlet_id;
+ }
+}
+
+
+/*
+ * rps10_reset - API call to Reset (reboot) the given host on
+ * this Stonith device. This involves toggling the power off
+ * and then on again, OR just calling the builtin reset command
+ * on the stonith device.
+ */
+static int
+rps10_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ int rc = S_OK;
+ int lorc = S_OK;
+ signed char outlet_id = -1;
+ struct pluginDevice* ctx;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ ctx = (struct pluginDevice*) s;
+
+ if ((rc = RPSConnect(ctx)) != S_OK) {
+ return(rc);
+ }
+
+ outlet_id = RPSNametoOutlet(ctx, host);
+
+ if (outlet_id < 0) {
+ LOG(PIL_WARN, "%s: %s doesn't control host [%s]"
+ , pluginid, ctx->device, host );
+ RPSDisconnect(ctx);
+ return(S_BADHOST);
+ }
+
+ switch(request) {
+
+#if defined(ST_POWERON)
+ case ST_POWERON:
+ rc = RPSOn(ctx, outlet_id, host);
+ break;
+#endif
+#if defined(ST_POWEROFF)
+ case ST_POWEROFF:
+ rc = RPSOff(ctx, outlet_id, host);
+ break;
+#endif
+ case ST_GENERIC_RESET:
+ rc = RPSReset(ctx, outlet_id, host);
+ break;
+ default:
+ rc = S_INVAL;
+ break;
+ }
+
+ lorc = RPSDisconnect(ctx);
+
+ return(rc != S_OK ? rc : lorc);
+}
+
+/*
+ * Parse the information in the given string,
+ * and stash it away...
+ */
+static int
+rps10_set_config(StonithPlugin* s, StonithNVpair* list)
+{
+ struct pluginDevice* ctx;
+ StonithNamesToGet namestocopy [] =
+ { {ST_RPS10, NULL}
+ , {NULL, NULL}
+ };
+ int rc=0;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ if (s->isconfigured) {
+ /* The module is already configured. */
+ return(S_OOPS);
+ }
+
+ ctx = (struct pluginDevice*) s;
+
+ if((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK){
+ LOG(PIL_DEBUG , "get all calues failed");
+ return rc;
+ }
+
+ rc = RPS_parse_config_info(ctx, namestocopy[0].s_value);
+ FREE(namestocopy[0].s_value);
+ return rc;
+}
+
+/*
+ * Return the Stonith plugin configuration parameter
+ *
+ */
+static const char * const *
+rps10_get_confignames(StonithPlugin* p)
+{
+ static const char * Rps10Params[] = {ST_RPS10 ,NULL };
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ return Rps10Params;
+}
+
+/*
+ * rps10_getinfo - API entry point to retrieve something from the handle
+ */
+static const char *
+rps10_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* ctx;
+ const char * ret;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,NULL);
+
+ /*
+ * We look in the ST_TEXTDOMAIN catalog for our messages
+ */
+ ctx = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = ctx->idinfo;
+ break;
+ case ST_DEVICENAME:
+ ret = ctx->device;
+ break;
+ case ST_DEVICEDESCR:
+ ret = "Western Telematic Inc. (WTI) "
+ "Remote Power Switch - RPS-10M.\n";
+ break;
+ case ST_DEVICEURL:
+ ret = "http://www.wti.com/";
+ break;
+ case ST_CONF_XML: /* XML metadata */
+ ret = rps10XML;
+ break;
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * rps10_destroy - API entry point to destroy a WTI_RPS10 Stonith object.
+ */
+static void
+rps10_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* ctx;
+ int i;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ VOIDERRIFWRONGDEV(s);
+
+ ctx = (struct pluginDevice *)s;
+
+ ctx->pluginid = NOTwtiid;
+
+ /* close the fd if open and set ctx->fd to invalid */
+ RPSDisconnect(ctx);
+
+ if (ctx->device != NULL) {
+ FREE(ctx->device);
+ ctx->device = NULL;
+ }
+ if (ctx->unit_count > 0) {
+ for (i = 0; i < ctx->unit_count; i++) {
+ if (ctx->controllers[i].node != NULL) {
+ FREE(ctx->controllers[i].node);
+ ctx->controllers[i].node = NULL;
+ }
+ }
+ }
+ FREE(ctx);
+}
+
+/*
+ * rps10_new - API entry point called to create a new WTI_RPS10 Stonith device
+ * object.
+ */
+static StonithPlugin *
+rps10_new(const char *subplugin)
+{
+ struct pluginDevice* ctx = ST_MALLOCT(struct pluginDevice);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ if (ctx == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->pluginid = pluginid;
+ ctx->fd = -1;
+ ctx->unit_count = 0;
+ ctx->device = NULL;
+ ctx->idinfo = DEVICE;
+ ctx->sp.s_ops = &rps10Ops;
+
+ return &(ctx->sp);
+}
diff --git a/lib/plugins/stonith/ssh.c b/lib/plugins/stonith/ssh.c
new file mode 100644
index 0000000..e90c199
--- /dev/null
+++ b/lib/plugins/stonith/ssh.c
@@ -0,0 +1,351 @@
+/*
+ * Stonith module for SSH Stonith device
+ *
+ * Copyright (c) 2001 SuSE Linux AG
+ *
+ * Authors: Joachim Gleissner <jg@suse.de>, Lars Marowsky-Brée <lmb@suse.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <lha_internal.h>
+
+#include <config.h>
+
+#define DEVICE "SSH STONITH device"
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN ssh
+#define PIL_PLUGIN_S "ssh"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+static StonithPlugin * ssh_new(const char *);
+static void ssh_destroy(StonithPlugin *);
+static const char * const * ssh_get_confignames(StonithPlugin *);
+static int ssh_set_config(StonithPlugin *, StonithNVpair*);
+static const char * ssh_get_info(StonithPlugin * s, int InfoType);
+static int ssh_status(StonithPlugin * );
+static int ssh_reset_req(StonithPlugin * s, int request
+, const char * host);
+static char ** ssh_hostlist(StonithPlugin *);
+
+static struct stonith_ops sshOps ={
+ ssh_new, /* Create new STONITH object */
+ ssh_destroy, /* Destroy STONITH object */
+ ssh_get_info, /* Return STONITH info string */
+ ssh_get_confignames, /* Return configuration parameters */
+ ssh_set_config, /* set configuration */
+ ssh_status, /* Return STONITH device status */
+ ssh_reset_req, /* Request a reset */
+ ssh_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &sshOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/* uncomment this if you have an ssh that can do what it claims
+#define SSH_COMMAND "ssh -q -x -o PasswordAuthentication=no StrictHostKeyChecking=no"
+*/
+/* use this if you have the (broken) OpenSSH 2.1.1 */
+/* sunjd@cn.ibm.com added the option -f to temporily work around the block issue
+ * in which the child process always stay in 'system' call. Please FIX this.
+ * Additonally, this issue seems related to both of 2.6 kernel and stonithd.
+ */
+#define SSH_COMMAND "ssh -q -x -n -l root"
+
+/* We need to do a real hard reboot without syncing anything to simulate a
+ * power cut.
+ * We have to do it in the background, otherwise this command will not
+ * return.
+ */
+#define REBOOT_COMMAND "nohup sh -c '(sleep 2; nohup " REBOOT " " REBOOT_OPTIONS ") </dev/null >/dev/null 2>&1' &"
+#undef REBOOT_COMMAND
+#define REBOOT_COMMAND "echo 'sleep 2; " REBOOT " " REBOOT_OPTIONS "' | SHELL=/bin/sh at now >/dev/null 2>&1"
+#define POWEROFF_COMMAND "echo 'sleep 2; " POWEROFF_CMD " " POWEROFF_OPTIONS "' | SHELL=/bin/sh at now >/dev/null 2>&1"
+
+#define MAX_PING_ATTEMPTS 15
+
+/*
+ * SSH STONITH device
+ *
+ * I used the null device as template, so I guess there is missing
+ * some functionality.
+ *
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ char ** hostlist;
+ int hostcount;
+};
+
+static const char * pluginid = "SSHDevice-Stonith";
+static const char * NOTpluginid = "SSH device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+static const char *sshXML =
+ XML_PARAMETERS_BEGIN
+ XML_HOSTLIST_PARM
+ XML_PARAMETERS_END;
+
+static int
+ssh_status(StonithPlugin *s)
+{
+ ERRIFWRONGDEV(s, S_OOPS);
+
+ return system(NULL) ? S_OK : S_OOPS;
+}
+
+
+/*
+ * Return the list of hosts configured for this SSH device
+ */
+
+static char **
+ssh_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice* sd = (struct pluginDevice*)s;
+
+ ERRIFWRONGDEV(s, NULL);
+
+ if (sd->hostcount < 0) {
+ LOG(PIL_CRIT
+ , "unconfigured stonith object in %s", __FUNCTION__);
+ return(NULL);
+ }
+
+ return OurImports->CopyHostList((const char * const *)sd->hostlist);
+}
+
+
+/*
+ * Reset the given host on this Stonith device.
+ */
+static int
+ssh_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ char cmd[4096];
+ int i, status = -1;
+
+ ERRIFWRONGDEV(s, S_OOPS);
+
+ if (request == ST_POWERON) {
+ LOG(PIL_CRIT, "%s not capable of power-on operation", DEVICE);
+ return S_INVAL;
+ } else if (request != ST_POWEROFF && request != ST_GENERIC_RESET) {
+ return S_INVAL;
+ }
+
+ for (i = 0; i < sd->hostcount; i++) {
+ if (strcasecmp(host, sd->hostlist[i]) == 0) {
+ break;
+ }
+ }
+
+ if (i >= sd->hostcount) {
+ LOG(PIL_CRIT, "%s doesn't control host [%s]"
+ , sd->idinfo, host);
+ return(S_BADHOST);
+ }
+
+ LOG(PIL_INFO, "Initiating ssh-%s on host: %s"
+ , request == ST_POWEROFF ? "poweroff" : "reset", host);
+
+ snprintf(cmd, sizeof(cmd)-1, "%s \"%s\" \"%s\"", SSH_COMMAND
+ , host
+ , request == ST_POWEROFF ? POWEROFF_COMMAND : REBOOT_COMMAND);
+
+ status = system(cmd);
+ if (WIFEXITED(status) && 0 == WEXITSTATUS(status)) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "checking whether %s stonith'd", host);
+ }
+
+ snprintf(cmd, sizeof(cmd)-1
+ , "ping -w1 -c1 %s >/dev/null 2>&1", host);
+
+ for (i = 0; i < MAX_PING_ATTEMPTS; i++) {
+ status = system(cmd);
+ if (WIFEXITED(status) && 1 == WEXITSTATUS(status)) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "unable to ping %s"
+ " after %d tries, stonith did work"
+ , host, i);
+ }
+ return S_OK;
+ }
+ sleep(1);
+ }
+
+ LOG(PIL_CRIT, "still able to ping %s after %d tries, stonith"
+ " did not work", host, MAX_PING_ATTEMPTS);
+ return S_RESETFAIL;
+ }else{
+ LOG(PIL_CRIT, "command %s failed", cmd);
+ return S_RESETFAIL;
+ }
+}
+
+static const char * const *
+ssh_get_confignames(StonithPlugin* p)
+{
+ static const char * SshParams[] = {ST_HOSTLIST, NULL };
+ return SshParams;
+}
+
+/*
+ * Parse the config information in the given string, and stash it away...
+ */
+static int
+ssh_set_config(StonithPlugin* s, StonithNVpair* list)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ const char * hlist;
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ if ((hlist = OurImports->GetValue(list, ST_HOSTLIST)) == NULL) {
+ return S_OOPS;
+ }
+ sd->hostlist = OurImports->StringToHostList(hlist);
+ if (sd->hostlist == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ sd->hostcount = 0;
+ }else{
+ for (sd->hostcount = 0; sd->hostlist[sd->hostcount]
+ ; sd->hostcount++) {
+ strdown(sd->hostlist[sd->hostcount]);
+ }
+ }
+
+ return sd->hostcount ? S_OK : S_OOPS;
+}
+
+
+static const char *
+ssh_get_info(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ const char * ret;
+
+ ERRIFWRONGDEV(s, NULL);
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = sd->idinfo;
+ break;
+
+
+ case ST_DEVICENAME:
+ ret = "ssh STONITH device";
+ break;
+
+
+ case ST_DEVICEDESCR: /* Description of device type */
+ ret = "SSH-based host reset\n"
+ "Fine for testing, but not suitable for production!";
+ break;
+
+
+ case ST_DEVICEURL:
+ ret = "http://openssh.org";
+ break;
+
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = sshXML;
+ break;
+
+
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * SSH Stonith destructor...
+ */
+static void
+ssh_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+
+ VOIDERRIFWRONGDEV(s);
+
+ sd->pluginid = NOTpluginid;
+ if (sd->hostlist) {
+ stonith_free_hostlist(sd->hostlist);
+ sd->hostlist = NULL;
+ }
+ sd->hostcount = -1;
+ FREE(sd);
+}
+
+/* Create a new ssh Stonith device */
+static StonithPlugin*
+ssh_new(const char *subplugin)
+{
+ struct pluginDevice* sd = ST_MALLOCT(struct pluginDevice);
+
+ if (sd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(sd, 0, sizeof(*sd));
+ sd->pluginid = pluginid;
+ sd->hostlist = NULL;
+ sd->hostcount = -1;
+ sd->idinfo = DEVICE;
+ sd->sp.s_ops = &sshOps;
+ return &(sd->sp);
+}
diff --git a/lib/plugins/stonith/stonith_config_xml.h b/lib/plugins/stonith/stonith_config_xml.h
new file mode 100644
index 0000000..ff04ae9
--- /dev/null
+++ b/lib/plugins/stonith/stonith_config_xml.h
@@ -0,0 +1,157 @@
+/*
+ * stonith_config_xml.h: common macros easing the writing of config
+ * XML for STONITH plugins. Only a STONITH
+ * plugin should include this header!
+ *
+ * Copyright (C) International Business Machines Corp., 2005
+ * Author: Dave Blaschke <debltc@us.ibm.com>
+ * Support: linux-ha@lists.linux-ha.org
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef _STONITH_CONFIG_XML_H
+#define _STONITH_CONFIG_XML_H
+
+/*
+ * The generic constants for XML
+ */
+
+/* <parameters>?</parameters> */
+#define XML_PARAMETERS_BEGIN "<parameters>"
+#define XML_PARAMETERS_END "</parameters>"
+
+/* <parameter name="ipaddr" unique="?">?<content type="string" /></parameter> */
+#define XML_PARAMETER_BEGIN(name,type,req,uniq) \
+ "<parameter name=\"" name "\" unique=\"" uniq "\" required=\"" req "\">" \
+ "<content type=\"" type "\" />\n"
+#define XML_PARAMETER_END "</parameter>\n"
+
+/* <shortdesc lang="en">?</shortdesc> */
+#define XML_PARM_SHORTDESC_BEGIN(lang) \
+ "<shortdesc lang=\"" lang "\">\n"
+#define XML_PARM_SHORTDESC_END "</shortdesc>\n"
+
+/* <longdesc lang="en">?</longdesc> */
+#define XML_PARM_LONGDESC_BEGIN(lang) \
+ "<longdesc lang=\"" lang "\">\n"
+#define XML_PARM_LONGDESC_END "</longdesc>\n"
+
+/*
+ * The short and long descriptions for the few standardized parameter names;
+ * these can be translated by appending different languages to these constants
+ * (must include XML_PARM_****DESC_BEGIN(), the translated description, and
+ * XML_PARM_****DESC_END for each language)
+ */
+#define XML_HOSTLIST_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ "Hostlist" \
+ XML_PARM_SHORTDESC_END
+
+#define XML_HOSTLIST_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The list of hosts that the STONITH device controls" \
+ XML_PARM_LONGDESC_END
+
+#define XML_IPADDR_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ "IP Address" \
+ XML_PARM_SHORTDESC_END
+
+#define XML_IPADDR_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The IP address of the STONITH device" \
+ XML_PARM_LONGDESC_END
+
+#define XML_LOGIN_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ "Login" \
+ XML_PARM_SHORTDESC_END
+
+#define XML_LOGIN_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The username used for logging in to the STONITH device" \
+ XML_PARM_LONGDESC_END
+
+#define XML_PASSWD_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ "Password" \
+ XML_PARM_SHORTDESC_END
+
+#define XML_PASSWD_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The password used for logging in to the STONITH device" \
+ XML_PARM_LONGDESC_END
+
+#define XML_COMMUNITY_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ "SNMP Community" \
+ XML_PARM_SHORTDESC_END
+
+#define XML_COMMUNITY_LONGDESC "" \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The SNMP community string associated with the STONITH device" \
+ XML_PARM_LONGDESC_END
+
+#define XML_TTYDEV_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ "TTY Device" \
+ XML_PARM_SHORTDESC_END
+
+#define XML_TTYDEV_LONGDESC "" \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The TTY device used for connecting to the STONITH device" \
+ XML_PARM_LONGDESC_END
+
+/*
+ * Complete parameter descriptions for the few standardized parameter names
+ */
+#define XML_HOSTLIST_PARM \
+ XML_PARAMETER_BEGIN(ST_HOSTLIST, "string", "1", "0") \
+ XML_HOSTLIST_SHORTDESC \
+ XML_HOSTLIST_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_IPADDR_PARM \
+ XML_PARAMETER_BEGIN(ST_IPADDR, "string", "1", "0") \
+ XML_IPADDR_SHORTDESC \
+ XML_IPADDR_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_LOGIN_PARM \
+ XML_PARAMETER_BEGIN(ST_LOGIN, "string", "1", "0") \
+ XML_LOGIN_SHORTDESC \
+ XML_LOGIN_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_PASSWD_PARM \
+ XML_PARAMETER_BEGIN(ST_PASSWD, "string", "1", "0") \
+ XML_PASSWD_SHORTDESC \
+ XML_PASSWD_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_COMMUNITY_PARM \
+ XML_PARAMETER_BEGIN(ST_COMMUNITY, "string", "1", "0") \
+ XML_COMMUNITY_SHORTDESC \
+ XML_COMMUNITY_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_TTYDEV_PARM \
+ XML_PARAMETER_BEGIN(ST_TTYDEV, "string", "1", "0") \
+ XML_TTYDEV_SHORTDESC \
+ XML_TTYDEV_LONGDESC \
+ XML_PARAMETER_END
+
+#endif
diff --git a/lib/plugins/stonith/stonith_expect_helpers.h b/lib/plugins/stonith/stonith_expect_helpers.h
new file mode 100644
index 0000000..f9eaa19
--- /dev/null
+++ b/lib/plugins/stonith/stonith_expect_helpers.h
@@ -0,0 +1,120 @@
+/*
+ * stonith_expect_helpers.h: Some common expect defines.
+ *
+ * Copyright (C) 2004 Lars Marowsky-Bree <lmb@suse.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* This is still somewhat ugly. It needs to be included after the PILS
+ * definitions so that it can access them, but the code reduction seemed
+ * to justify this. Hopefully it can be made somewhat more elegant
+ * eventually. */
+
+/*
+ * Many expect/telnet plugins use these defines and functions.
+ */
+
+#define SEND(fd,s) { \
+ size_t slen = strlen(s); \
+ if (Debug) { \
+ LOG(PIL_DEBUG \
+ , "Sending [%s] (len %d)" \
+ , (s) \
+ , (int)slen); \
+ } \
+ if (write((fd), (s), slen) != slen) { \
+ LOG(PIL_CRIT \
+ , "%s: write failed" \
+ , __FUNCTION__); \
+ } \
+ }
+
+#define EXPECT(fd,p,t) { \
+ if (StonithLookFor(fd, p, t) < 0) \
+ return(errno == ETIMEDOUT \
+ ? S_TIMEOUT : S_OOPS); \
+ }
+
+#define NULLEXPECT(fd,p,t) { \
+ if (StonithLookFor(fd, p, t) < 0) \
+ return(NULL); \
+ }
+
+#define SNARF(fd,s, to) { \
+ if (StonithScanLine(fd,to,(s),sizeof(s))\
+ != S_OK){ \
+ return(S_OOPS); \
+ } \
+ }
+
+#define NULLSNARF(fd,s, to){ \
+ if (StonithScanLine(fd,to,(s),sizeof(s))\
+ != S_OK) { \
+ return(NULL); \
+ } \
+ }
+
+/* Look for any of the given patterns. We don't care which */
+static int
+StonithLookFor(int fd, struct Etoken * tlist, int timeout)
+{
+ int rc;
+ char savebuf[512];
+
+ if ((rc = EXPECT_TOK(fd, tlist, timeout, savebuf, sizeof(savebuf)
+ , Debug)) < 0) {
+ LOG(PIL_CRIT, "Did not find string %s from " DEVICE "."
+ , tlist[0].string);
+ LOG(PIL_CRIT, "Received [%s]", savebuf);
+ }
+ return(rc);
+}
+
+#ifndef DOESNT_USE_STONITHSCANLINE
+/* Accept either a CR/NL or an NL/CR */
+static struct Etoken CRNL[] = { {"\n\r",0,0},{"\r\n",0,0},{NULL,0,0}};
+
+static int
+StonithScanLine(int fd, int timeout, char * buf, int max)
+{
+ if (EXPECT_TOK(fd, CRNL, timeout, buf, max, Debug) < 0) {
+ LOG(PIL_CRIT, "Could not read line from" DEVICE ".");
+ return(S_OOPS);
+ }
+ return(S_OK);
+}
+#endif
+
+#ifndef DOESNT_USE_STONITHKILLCOMM
+static void
+Stonithkillcomm(int *rdfd, int *wrfd, int *pid)
+{
+ if ((rdfd != NULL) && (*rdfd >= 0)) {
+ close(*rdfd);
+ *rdfd = -1;
+ }
+ if ((wrfd != NULL) && (*wrfd >= 0)) {
+ close(*wrfd);
+ *wrfd = -1;
+ }
+ if ((pid != NULL) && (*pid > 0)) {
+ STONITH_KILL(*pid, SIGKILL);
+ (void)waitpid(*pid, NULL, 0);
+ *pid = -1;
+ }
+}
+#endif
diff --git a/lib/plugins/stonith/stonith_plugin_common.h b/lib/plugins/stonith/stonith_plugin_common.h
new file mode 100644
index 0000000..dcdd7c8
--- /dev/null
+++ b/lib/plugins/stonith/stonith_plugin_common.h
@@ -0,0 +1,127 @@
+/*
+ * stonith_plugin_common.h: common macros easing the writing of STONITH
+ * plugins. Only a STONITH plugin should
+ * include this header!
+ *
+ * Copyright (C) 2004 Lars Marowsky-Bree <lmb@suse.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef _STONITH_PLUGIN_COMMON_H
+#define _STONITH_PLUGIN_COMMON_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <libintl.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <netdb.h>
+#ifdef HAVE_TERMIO_H
+# include <termio.h>
+#endif
+#ifdef HAVE_SYS_TERMIOS_H
+#include <sys/termios.h>
+#else
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+#endif
+#include <glib.h>
+
+
+#include <stonith/stonith.h>
+#include <stonith/stonith_plugin.h>
+
+#define LOG(w...) PILCallLog(PluginImports->log, w)
+
+#define MALLOC PluginImports->alloc
+#define REALLOC PluginImports->mrealloc
+#define STRDUP PluginImports->mstrdup
+#define FREE PluginImports->mfree
+#define EXPECT_TOK OurImports->ExpectToken
+#define STARTPROC OurImports->StartProcess
+
+#ifdef MALLOCT
+# undef MALLOCT
+#endif
+#define ST_MALLOCT(t) ((t *)(MALLOC(sizeof(t))))
+
+#define N_(text) (text)
+#define _(text) dgettext(ST_TEXTDOMAIN, text)
+
+#define WHITESPACE " \t\n\r\f"
+
+#ifndef MIN
+/* some macros */
+# define MIN( i, j ) ( i > j ? j : i )
+#endif
+
+#define REPLSTR(s,v) { \
+ if ((s) != NULL) { \
+ FREE(s); \
+ (s)=NULL; \
+ } \
+ (s) = STRDUP(v); \
+ if ((s) == NULL) { \
+ PILCallLog(PluginImports->log, \
+ PIL_CRIT, "out of memory"); \
+ } \
+ }
+
+#ifndef DEVICE
+#define DEVICE "Dummy"
+#endif
+
+#define PIL_PLUGINTYPE STONITH_TYPE
+#define PIL_PLUGINTYPE_S STONITH_TYPE_S
+
+#define ISCORRECTDEV(i) ((i)!= NULL \
+ && ((struct pluginDevice *)(i))->pluginid == pluginid)
+
+#define ERRIFWRONGDEV(s, retval) if (!ISCORRECTDEV(s)) { \
+ LOG(PIL_CRIT, "%s: invalid argument", __FUNCTION__); \
+ return(retval); \
+ }
+
+#define VOIDERRIFWRONGDEV(s) if (!ISCORRECTDEV(s)) { \
+ LOG(PIL_CRIT, "%s: invalid argument", __FUNCTION__); \
+ return; \
+ }
+
+#define ISCONFIGED(i) (i->isconfigured)
+
+#define ERRIFNOTCONFIGED(s,retval) ERRIFWRONGDEV(s,retval); \
+ if (!ISCONFIGED(s)) { \
+ LOG(PIL_CRIT, "%s: not configured", __FUNCTION__); \
+ return(retval); \
+ }
+
+#define VOIDERRIFNOTCONFIGED(s) VOIDERRIFWRONGDEV(s); \
+ if (!ISCONFIGED(s)) { \
+ LOG(PIL_CRIT, "%s: not configured", __FUNCTION__); \
+ return; \
+ }
+
+#endif
+
diff --git a/lib/plugins/stonith/stonith_signal.h b/lib/plugins/stonith/stonith_signal.h
new file mode 100644
index 0000000..99513f5
--- /dev/null
+++ b/lib/plugins/stonith/stonith_signal.h
@@ -0,0 +1,68 @@
+/*
+ * stonith_signal.h: signal handling routines to be used by stonith
+ * plugin libraries
+ *
+ * Copyright (C) 2002 Horms <horms@verge.net.au>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef _STONITH_SIGNAL_H
+#define _STONITH_SIGNAL_H
+
+#include <signal.h>
+#include <sys/signal.h>
+
+int
+stonith_signal_set_simple_handler(int sig, void (*handler)(int)
+, struct sigaction *oldact);
+
+int
+stonith_signal_set_simple_handler(int sig, void (*handler)(int)
+, struct sigaction *oldact)
+{
+ struct sigaction sa;
+ sigset_t mask;
+
+ (void)stonith_signal_set_simple_handler;
+ if(sigemptyset(&mask) < 0) {
+ return(-1);
+ }
+
+ sa.sa_handler = handler;
+ sa.sa_mask = mask;
+ sa.sa_flags = 0;
+
+ if(sigaction(sig, &sa, oldact) < 0) {
+ return(-1);
+ }
+
+ return(0);
+}
+
+#define STONITH_SIGNAL(_sig, _handler) \
+ stonith_signal_set_simple_handler((_sig), (_handler), NULL)
+#ifdef HAVE_SIGIGNORE
+#define STONITH_IGNORE_SIG(_sig) \
+ sigignore((_sig))
+#else
+#define STONITH_IGNORE_SIG(_sig) \
+ STONITH_SIGNAL((_sig), SIG_IGN)
+#endif
+#define STONITH_DEFAULT_SIG(_sig) STONITH_SIGNAL((_sig), SIG_DFL)
+
+#define STONITH_KILL(_pid, _sig) kill((_pid), (_sig))
+
+#endif /* _STONITH_SIGNAL_H */
diff --git a/lib/plugins/stonith/suicide.c b/lib/plugins/stonith/suicide.c
new file mode 100644
index 0000000..b9d1db4
--- /dev/null
+++ b/lib/plugins/stonith/suicide.c
@@ -0,0 +1,274 @@
+/* File: suicide.c
+ * Description: Stonith module for suicide
+ *
+ * Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+ * Copyright (c) 2004 International Business Machines
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <lha_internal.h>
+#include <config.h>
+#include <sys/utsname.h>
+
+#define DEVICE "Suicide STONITH device"
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN suicide
+#define PIL_PLUGIN_S "suicide"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+static StonithPlugin * suicide_new(const char *);
+static void suicide_destroy(StonithPlugin *);
+static const char * const * suicide_get_confignames(StonithPlugin *);
+static int suicide_set_config(StonithPlugin *, StonithNVpair*);
+static const char * suicide_get_info(StonithPlugin * s, int InfoType);
+static int suicide_status(StonithPlugin * );
+static int suicide_reset_req(StonithPlugin * s, int request
+ , const char * host);
+static char ** suicide_hostlist(StonithPlugin *);
+
+static struct stonith_ops suicideOps ={
+ suicide_new, /* Create new STONITH object */
+ suicide_destroy, /* Destroy STONITH object */
+ suicide_get_info, /* Return STONITH info string */
+ suicide_get_confignames, /* Return configuration parameters */
+ suicide_set_config, /* Set configuration */
+ suicide_status, /* Return STONITH device status */
+ suicide_reset_req, /* Request a reset */
+ suicide_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &suicideOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+#define REBOOT_COMMAND "nohup sh -c 'sleep 2; " REBOOT " " REBOOT_OPTIONS " </dev/null >/dev/null 2>&1' &"
+#define POWEROFF_COMMAND "nohup sh -c 'sleep 2; " POWEROFF_CMD " " POWEROFF_OPTIONS " </dev/null >/dev/null 2>&1' &"
+/*
+#define REBOOT_COMMAND "echo 'sleep 2; " REBOOT " " REBOOT_OPTIONS "' | SHELL=/bin/sh at now >/dev/null 2>&1"
+#define POWEROFF_COMMAND "echo 'sleep 2; " POWEROFF_CMD " " POWEROFF_OPTIONS "' | SHELL=/bin/sh at now >/dev/null 2>&1"
+*/
+
+/*
+ * Suicide STONITH device
+ */
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+};
+
+static const char * pluginid = "SuicideDevice-Stonith";
+static const char * NOTpluginid = "Suicide device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+static const char *suicideXML =
+ XML_PARAMETERS_BEGIN
+ XML_PARAMETERS_END;
+
+static int
+suicide_status(StonithPlugin *s)
+{
+ ERRIFWRONGDEV(s, S_OOPS);
+
+ return S_OK;
+}
+
+/*
+ * Return the list of hosts configured for this Suicide device
+ */
+static char **
+suicide_hostlist(StonithPlugin *s)
+{
+ char** ret = NULL;
+ struct utsname name;
+
+ ERRIFWRONGDEV(s, NULL);
+
+ if (uname(&name) == -1) {
+ LOG(PIL_CRIT, "uname error %d", errno);
+ return ret;
+ }
+
+ ret = OurImports->StringToHostList(name.nodename);
+ if (ret == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return ret;
+ }
+ strdown(ret[0]);
+
+ return ret;
+}
+
+/*
+ * Suicide - reset or poweroff itself.
+ */
+static int
+suicide_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ int rc = -1;
+ struct utsname name;
+
+ ERRIFWRONGDEV(s, S_OOPS);
+
+ if (request == ST_POWERON) {
+ LOG(PIL_CRIT, "%s not capable of power-on operation", DEVICE);
+ return S_INVAL;
+ } else if (request != ST_POWEROFF && request != ST_GENERIC_RESET) {
+ LOG(PIL_CRIT, "As for suicide virtual stonith device, "
+ "reset request=%d is not supported", request);
+ return S_INVAL;
+ }
+
+ if (uname(&name) == -1) {
+ LOG(PIL_CRIT, "uname error %d", errno);
+ return S_RESETFAIL ;
+ }
+
+ if (strcmp(name.nodename, host)) {
+ LOG(PIL_CRIT, "%s doesn't control host [%s]"
+ , name.nodename, host);
+ return S_RESETFAIL ;
+ }
+
+ LOG(PIL_INFO, "Initiating suicide on host %s", host);
+
+ rc = system(
+ request == ST_GENERIC_RESET ? REBOOT_COMMAND : POWEROFF_COMMAND);
+
+ if (rc == 0) {
+ LOG(PIL_INFO, "Suicide stonith succeeded.");
+ return S_OK;
+ } else {
+ LOG(PIL_CRIT, "Suicide stonith failed.");
+ return S_RESETFAIL ;
+ }
+}
+
+static const char * const *
+suicide_get_confignames(StonithPlugin* p)
+{
+ /* Donnot need to initialize from external. */
+ static const char * SuicideParams[] = { NULL };
+ return SuicideParams;
+}
+
+/*
+ * Parse the config information in the given string, and stash it away...
+ */
+static int
+suicide_set_config(StonithPlugin* s, StonithNVpair* list)
+{
+ ERRIFWRONGDEV(s,S_OOPS);
+ return S_OK;
+}
+
+static const char *
+suicide_get_info(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ const char * ret;
+
+ ERRIFWRONGDEV(s, NULL);
+ sd = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = sd->idinfo;
+ break;
+
+ case ST_DEVICENAME:
+ ret = "suicide STONITH device";
+ break;
+
+ case ST_DEVICEDESCR: /* Description of device type */
+ ret = "Virtual device to reboot/powerdown itself.\n";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = suicideXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * Suicide Stonith destructor...
+ */
+static void
+suicide_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* sd;
+
+ VOIDERRIFWRONGDEV(s);
+
+ sd = (struct pluginDevice *)s;
+
+ sd->pluginid = NOTpluginid;
+ FREE(sd);
+}
+
+/* Create a new suicide Stonith device */
+static StonithPlugin*
+suicide_new(const char * subplugin)
+{
+ struct pluginDevice* sd = ST_MALLOCT(struct pluginDevice);
+
+ if (sd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(sd, 0, sizeof(*sd));
+ sd->pluginid = pluginid;
+ sd->idinfo = DEVICE;
+ sd->sp.s_ops = &suicideOps;
+ return &(sd->sp);
+}
diff --git a/lib/plugins/stonith/vacm.c b/lib/plugins/stonith/vacm.c
new file mode 100644
index 0000000..ce6d041
--- /dev/null
+++ b/lib/plugins/stonith/vacm.c
@@ -0,0 +1,485 @@
+
+/******************************************************************************
+*
+* Copyright 2000 Sistina Software, Inc.
+* Tiny bits Copyright 2000 Alan Robertson <alanr@unix.sh>
+* Tiny bits Copyright 2000 Zac Sprackett, VA Linux Systems
+* Tiny bits Copyright 2005 International Business Machines
+* Significantly Mangled by Sun Jiang Dong <sunjd@cn.ibm.com>, IBM, 2005
+*
+* This is free software released under the GNU General Public License.
+* There is no warranty for this software. See the file COPYING for
+* details.
+*
+* See the file CONTRIBUTORS for a list of contributors.
+*
+* This file is maintained by:
+* Michael C Tilstra <conrad@sistina.com>
+*
+* Becasue I have no device to test, now I just make it pass the compiling
+* with vacm-2.0.5a. Please review before using.
+* Sun Jiang Dong <sunjd@cn.ibm.com>, IBM, 2005
+*
+* This module provides a driver for the VA Linux Cluster Manager.
+* For more information on VACM, see http://vacm.sourceforge.net/
+*
+* This module is rather poorly commented. But if you've read the
+* VACM Manual, and looked at the code example they have, this
+* should make pretty clean sense. (You obiviously should have
+* looked at the other stonith source too)
+*
+*/
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define DEVICE "VA Linux Cluster Manager"
+
+#include "stonith_plugin_common.h"
+#include "vacmclient_api.h"
+
+#define PIL_PLUGIN vacm
+#define PIL_PLUGIN_S "vacm"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+static StonithPlugin * vacm_new(const char *);
+static void vacm_destroy(StonithPlugin *);
+static const char * const * vacm_get_confignames(StonithPlugin *);
+static int vacm_set_config(StonithPlugin *, StonithNVpair *);
+static const char * vacm_getinfo(StonithPlugin * s, int InfoType);
+static int vacm_status(StonithPlugin * );
+static int vacm_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** vacm_hostlist(StonithPlugin *);
+
+static struct stonith_ops vacmOps ={
+ vacm_new, /* Create new STONITH object */
+ vacm_destroy, /* Destroy STONITH object */
+ vacm_getinfo, /* Return STONITH info string */
+ vacm_get_confignames, /* Return configuration parameters */
+ vacm_set_config, /* Set configuration */
+ vacm_status, /* Return STONITH device status */
+ vacm_reset_req, /* Request a reset */
+ vacm_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug);
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &vacmOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*structs*/
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ void *h; /* a handle to the nexxus. */
+ char * nexxus;
+ char * user;
+ char * passwd;
+};
+
+#define ST_NEXXUS "nexxus"
+
+static const char * pluginid = "VACMDevice-Stonith";
+static const char * NOTpluginid = "VACM device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+#define XML_NEXXUS_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_NEXXUS \
+ XML_PARM_SHORTDESC_END
+
+#define XML_NEXXUS_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The Nexxus component of the VA Cluster Manager" \
+ XML_PARM_LONGDESC_END
+
+#define XML_NEXXUS_PARM \
+ XML_PARAMETER_BEGIN(ST_NEXXUS, "string", "1", "1") \
+ XML_NEXXUS_SHORTDESC \
+ XML_NEXXUS_LONGDESC \
+ XML_PARAMETER_END
+
+static const char *vacmXML =
+ XML_PARAMETERS_BEGIN
+ XML_NEXXUS_PARM
+ XML_LOGIN_PARM
+ XML_PASSWD_PARM
+ XML_PARAMETERS_END;
+
+/*funcs*/
+int
+vacm_status(StonithPlugin *s)
+{
+ struct pluginDevice *sd;
+ char snd[] = "NEXXUS:VERSION";
+ char *rcv, *tk;
+ int rcvlen;
+
+ ERRIFWRONGDEV(s,S_OOPS);
+ sd = (struct pluginDevice*)s;
+
+ /* If grabbing the nexxus version works, then the status must be ok.
+ * right?
+ */
+
+ api_nexxus_send_ipc(sd->h, snd, strlen(snd)+1);
+ while(1) {
+ if (api_nexxus_wait_for_data(sd->h, &rcv, &rcvlen, 20)<0) {
+ break;
+ }
+ if (!(tk = strtok(rcv,":"))) { /*NEXXUS*/
+ break;
+ }else if (!(tk=strtok(NULL,":"))) { /* Job ID */
+ break;
+ }else if (!(tk=strtok(NULL,":"))) { /* one of the below */
+ break;
+ } else if ( !strcmp(tk, "JOB_COMPLETED")) {
+ free(rcv);
+ return S_OK; /* YEAH!! */
+ }else if(!strcmp(tk, "JOB_STARTED")) {
+ free(rcv);
+ continue;
+ }else if(!strcmp(tk, "JOB_ERROR")) {
+ free(rcv);
+ break;
+ }else if(!strcmp(tk, "VERSION")) {
+ free(rcv);
+ continue;
+ } else {
+ LOG(PIL_CRIT, "Unexpected token \"%s\" in line \"%s\"\n"
+ , tk, rcv);
+ break;
+ }
+ }
+
+ return S_OOPS;
+}
+
+/* Better make sure the current group is correct.
+ * Can't think of a good way to do this.
+ */
+char **
+vacm_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice *sd;
+ char snd[] = "NEXXUS:NODE_LIST";
+ char *rcv,*tk;
+ int rcvlen;
+ char ** hlst=NULL;
+ int hacnt=0, hrcnt=0;
+#define MSTEP 20
+
+ ERRIFWRONGDEV(s, NULL);
+ sd = (struct pluginDevice*)s;
+
+ hlst = (char **)MALLOC(MSTEP * sizeof(char*));
+ if (hlst == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return NULL;
+ }
+ hacnt=MSTEP;
+
+ api_nexxus_send_ipc(sd->h, snd, strlen(snd)+1);
+ while(1) {
+ if(api_nexxus_wait_for_data(sd->h, &rcv, &rcvlen, 20)<0) {
+ goto HL_cleanup;
+ }
+ if(!(tk=strtok(rcv, ":"))) { /* NEXXUS */
+ goto HL_cleanup;
+ }else if(!(tk=strtok(NULL,":"))) { /* Job ID */
+ goto HL_cleanup;
+ }else if(!(tk=strtok(NULL,":"))) { /* JOB_* or NODELIST */
+ goto HL_cleanup;
+ }else if( !strcmp(tk, "JOB_STARTED")) {
+ free(rcv);
+ continue;
+ }else if( !strcmp(tk, "JOB_COMPLETED")) {
+ free(rcv);
+ return hlst;
+ }else if( !strcmp(tk, "JOB_ERROR")) {
+ free(rcv);
+ break;
+ }else if( !strcmp(tk, "NODELIST")) {
+ if(!(tk = strtok(NULL,":"))) { /* group */
+ goto HL_cleanup;
+ }else if((tk = strtok(NULL," \t\n\r"))) { /*Finally, a machine name.*/
+ if( hrcnt >= (hacnt-1)) { /* grow array. */
+ char **oldhlst = hlst;
+ hlst = (char **)REALLOC(hlst, (hacnt +MSTEP)*sizeof(char*));
+ if( !hlst ) {
+ stonith_free_hostlist(oldhlst);
+ return NULL;
+ }
+ hacnt += MSTEP;
+ }
+ hlst[hrcnt] = STRDUP(tk); /* stuff the name. */
+ hlst[hrcnt+1] = NULL; /* set next to NULL for looping */
+ if (hlst[hrcnt] == NULL) {
+ stonith_free_hostlist(hlst);
+ return NULL;
+ }
+ strdown(hlst[hrcnt]);
+ hrcnt++;
+ }
+ }else {
+ /* WTF?! */
+ LOG(PIL_CRIT, "Unexpected token \"%s\" in line \"%s\"\n",tk,rcv);
+ break;
+ }
+ }
+
+HL_cleanup:
+ stonith_free_hostlist(hlst); /* give the mem back */
+ return NULL;
+}
+
+#define SND_SIZE 256
+int
+vacm_reset_req(StonithPlugin *s, int request, const char *host)
+{
+ struct pluginDevice *sd;
+ char snd[SND_SIZE]; /* god forbid its bigger than this */
+ char *rcv, *tk;
+ int rcvlen;
+
+ ERRIFWRONGDEV(s,S_OOPS);
+ sd = (struct pluginDevice*)s;
+
+ switch(request) {
+#ifdef ST_POWERON
+ case ST_POWERON:
+ snprintf(snd, SND_SIZE, "EMP:POWER_ON:%s", host);
+ break;
+#endif /*ST_POWERON*/
+#ifdef ST_POWEROFF
+ case ST_POWEROFF:
+ snprintf(snd, SND_SIZE, "EMP:POWER_OFF:%s", host);
+ break;
+#endif /*ST_POWEROFF*/
+ case ST_GENERIC_RESET:
+ snprintf(snd, SND_SIZE, "EMP:POWER_CYCLE:%s", host);
+ break;
+ default:
+ return S_INVAL;
+ }
+
+ api_nexxus_send_ipc(sd->h, snd, strlen(snd)+1);
+ while(1) {
+ if (api_nexxus_wait_for_data(sd->h, &rcv, &rcvlen, 20)<0) {
+ return S_RESETFAIL;
+ }
+ if (!(tk = strtok(rcv,":"))) { /*EMP*/
+ break;
+ }else if (!(tk=strtok(NULL,":"))) { /* Job ID */
+ break;
+ }else if (!(tk=strtok(NULL,":"))) { /* one of teh below */
+ break;
+ } else if ( !strcmp(tk, "JOB_COMPLETED")) {
+ free(rcv);
+ return S_OK;
+ } else if(!strcmp(tk, "JOB_STARTED")) {
+ free(rcv);
+ continue;
+ } else if(!strcmp(tk, "JOB_ERROR")) {
+ free(rcv);
+ return S_RESETFAIL;
+ } else {
+ /* WTF?! */
+ LOG(PIL_CRIT, "Unexpected token \"%s\" in line \"%s\"\n"
+ , tk, rcv);
+ break;
+ }
+ }
+
+ return S_RESETFAIL;
+}
+
+/* list => "nexxus:username:password" */
+static const char * const *
+vacm_get_confignames(StonithPlugin * s)
+{
+ static const char * ret[] = {ST_NEXXUS, ST_LOGIN, ST_PASSWD, NULL};
+ return ret;
+}
+
+static int
+vacm_set_config(StonithPlugin *s, StonithNVpair * list)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ int rc;
+ StonithNamesToGet namestocopy [] =
+ { {ST_NEXXUS, NULL}
+ , {ST_LOGIN, NULL}
+ , {ST_PASSWD, NULL}
+ , {NULL, NULL}
+ };
+ char *rcv;
+ int rcvlen;
+
+ ERRIFWRONGDEV(s, S_OOPS);
+ if (sd->sp.isconfigured) {
+ return S_OOPS;
+ }
+
+ if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ sd->nexxus = namestocopy[0].s_value;
+ sd->user = namestocopy[1].s_value;
+ sd->passwd = namestocopy[2].s_value;
+ /* When to initialize the sd->h */
+
+ if (api_nexxus_connect(sd->nexxus, sd->user, sd->passwd, &sd->h)<0){
+ return S_OOPS;
+ }
+ if (api_nexxus_wait_for_data(sd->h, &rcv, &rcvlen, 20)<0) {
+ return S_OOPS;
+ }
+ if (strcmp(rcv, "NEXXUS_READY")) {
+ rc = S_BADCONFIG;
+ }else{
+ rc = S_OK;
+ }
+ free(rcv);
+
+ return(rc);
+}
+
+/*
+ * The "vacmconf:" is in the conffile so that one file could be used for
+ * multiple device configs. This module will only look at the first line
+ * that starts with this token. All other line are ignored. (and thus
+ * could contain configs for other modules.)
+ *
+ * I don't think any other stonith modules do this currently.
+ */
+const char *
+vacm_getinfo(StonithPlugin *s, int reqtype)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ const char * ret;
+
+ ERRIFWRONGDEV(s, NULL);
+ switch (reqtype) {
+
+ case ST_DEVICEID: /* What type of device? */
+ ret = sd->idinfo;
+ break;
+
+ case ST_DEVICENAME: /* Which particular device? */
+ ret = dgettext(ST_TEXTDOMAIN, "VACM");
+ break;
+
+ case ST_DEVICEDESCR: /* Description of dev type */
+ ret = "A driver for the VA Linux Cluster Manager.";
+ break;
+
+ case ST_DEVICEURL: /* VACM's web site */
+ ret = "http://vacm.sourceforge.net/";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = vacmXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+
+ return ret;
+}
+
+void
+vacm_destroy(StonithPlugin *s)
+{
+ struct pluginDevice *sd;
+
+ VOIDERRIFWRONGDEV(s);
+ sd = (struct pluginDevice*)s;
+
+ if( sd->h ) {
+ api_nexxus_disconnect(sd->h);
+ }
+
+ sd->pluginid = NOTpluginid;
+ if (sd->nexxus != NULL) {
+ FREE(sd->nexxus);
+ sd->nexxus = NULL;
+ }
+ if (sd->user != NULL) {
+ FREE(sd->user);
+ sd->user = NULL;
+ }
+ if (sd->passwd != NULL) {
+ FREE(sd->passwd);
+ sd->passwd = NULL;
+ }
+
+ FREE(sd);
+}
+
+static StonithPlugin *
+vacm_new(const char *subplugin)
+{
+ struct pluginDevice *sd;
+
+ sd = MALLOC(sizeof(struct pluginDevice));
+ if (sd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(sd, 0, sizeof(*sd));
+ sd->h = NULL;
+ sd->pluginid = pluginid;
+ sd->nexxus = NULL;
+ sd->user = NULL;
+ sd->passwd = NULL;
+ sd->idinfo = DEVICE;
+ sd->sp.s_ops = &vacmOps;
+ return &(sd->sp); /* same as "sd" */
+}
diff --git a/lib/plugins/stonith/wti_mpc.c b/lib/plugins/stonith/wti_mpc.c
new file mode 100644
index 0000000..548f91c
--- /dev/null
+++ b/lib/plugins/stonith/wti_mpc.c
@@ -0,0 +1,856 @@
+/*
+ * Stonith module for WTI MPC (SNMP)
+ * Copyright (c) 2001 Andreas Piesk <a.piesk@gmx.net>
+ * Mangled by Sun Jiang Dong <sunjd@cn.ibm.com>, IBM, 2005
+ *
+ * Modified for WTI MPC by Denis Chapligin <chollya@satgate.net>, SatGate, 2009
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.*
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <lha_internal.h>
+
+/* device ID */
+#define DEVICE "WTI MPC"
+
+#include "stonith_plugin_common.h"
+#undef FREE /* defined by snmp stuff */
+
+#ifdef PACKAGE_BUGREPORT
+#undef PACKAGE_BUGREPORT
+#endif
+#ifdef PACKAGE_NAME
+#undef PACKAGE_NAME
+#endif
+#ifdef PACKAGE_STRING
+#undef PACKAGE_STRING
+#endif
+#ifdef PACKAGE_TARNAME
+#undef PACKAGE_TARNAME
+#endif
+#ifdef PACKAGE_VERSION
+#undef PACKAGE_VERSION
+#endif
+
+#ifdef HAVE_NET_SNMP_NET_SNMP_CONFIG_H
+# include <net-snmp/net-snmp-config.h>
+# include <net-snmp/net-snmp-includes.h>
+# include <net-snmp/agent/net-snmp-agent-includes.h>
+# define INIT_AGENT() init_master_agent()
+#else
+# include <ucd-snmp/ucd-snmp-config.h>
+# include <ucd-snmp/ucd-snmp-includes.h>
+# include <ucd-snmp/ucd-snmp-agent-includes.h>
+# ifndef NETSNMP_DS_APPLICATION_ID
+# define NETSNMP_DS_APPLICATION_ID DS_APPLICATION_ID
+# endif
+# ifndef NETSNMP_DS_AGENT_ROLE
+# define NETSNMP_DS_AGENT_ROLE DS_AGENT_ROLE
+# endif
+# define netsnmp_ds_set_boolean ds_set_boolean
+# define INIT_AGENT() init_master_agent(161, NULL, NULL)
+#endif
+
+#define PIL_PLUGIN wti_mpc
+#define PIL_PLUGIN_S "wti_mpc"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+#define DEBUGCALL \
+ if (Debug) { \
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); \
+ }
+
+static StonithPlugin * wti_mpc_new(const char *);
+static void wti_mpc_destroy(StonithPlugin *);
+static const char * const * wti_mpc_get_confignames(StonithPlugin *);
+static int wti_mpc_set_config(StonithPlugin *, StonithNVpair *);
+static const char * wti_mpc_getinfo(StonithPlugin * s, int InfoType);
+static int wti_mpc_status(StonithPlugin * );
+static int wti_mpc_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** wti_mpc_hostlist(StonithPlugin *);
+
+static struct stonith_ops wti_mpcOps ={
+ wti_mpc_new, /* Create new STONITH object */
+ wti_mpc_destroy, /* Destroy STONITH object */
+ wti_mpc_getinfo, /* Return STONITH info string */
+ wti_mpc_get_confignames, /* Get configuration parameters */
+ wti_mpc_set_config, /* Set configuration */
+ wti_mpc_status, /* Return STONITH device status */
+ wti_mpc_reset_req, /* Request a reset */
+ wti_mpc_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+ DEBUGCALL;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &wti_mpcOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * APCMaster tested with APC Masterswitch 9212
+ */
+
+/* outlet commands / status codes */
+#define OUTLET_ON 5
+#define OUTLET_OFF 6
+#define OUTLET_REBOOT 7
+
+/* oids */
+#define OID_IDENT ".1.3.6.1.2.1.1.5.0"
+
+#define OID_GROUP_NAMES_V1 ".1.3.6.1.4.1.2634.3.1.3.1.2.%u"
+#define OID_GROUP_STATE_V1 ".1.3.6.1.4.1.2634.3.1.3.1.3.%i"
+
+#define OID_GROUP_NAMES_V3 ".1.3.6.1.4.1.2634.3.100.300.1.2.%u"
+#define OID_GROUP_STATE_V3 ".1.3.6.1.4.1.2634.3.100.300.1.3.%i"
+
+#define MAX_OUTLETS 128
+
+/*
+ snmpset -c private -v1 172.16.0.32:161
+ ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.1" i 1
+ The last octet in the OID is the plug number. The value can
+ be 1 thru 8 because there are 8 power plugs on this device.
+ The integer that can be set is as follows: 1=on, 2=off, and
+ 3=reset
+*/
+
+/* own defines */
+#define MAX_STRING 128
+#define ST_PORT "port"
+#define ST_MIBVERSION "mib-version"
+
+/* structur of stonith object */
+struct pluginDevice {
+ StonithPlugin sp; /* StonithPlugin object */
+ const char* pluginid; /* id of object */
+ const char* idinfo; /* type of device */
+ struct snmp_session* sptr; /* != NULL->session created */
+ char * hostname; /* masterswitch's hostname */
+ /* or ip addr */
+ int port; /* snmp port */
+ int mib_version; /* mib version to use */
+ char * community; /* snmp community (r/w) */
+ int num_outlets; /* number of outlets */
+};
+
+/* constant strings */
+static const char *pluginid = "WTI-MPC-Stonith";
+static const char *NOTpluginID = "WTI MPC device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+#define XML_PORT_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_PORT \
+ XML_PARM_SHORTDESC_END
+
+#define XML_PORT_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The port number on which the SNMP server is running on the STONITH device" \
+ XML_PARM_LONGDESC_END
+
+#define XML_PORT_PARM \
+ XML_PARAMETER_BEGIN(ST_PORT, "string", "1", "0") \
+ XML_PORT_SHORTDESC \
+ XML_PORT_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_MIBVERSION_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_MIBVERSION \
+ XML_PARM_SHORTDESC_END
+
+#define XML_MIBVERSION_LONGDESC \
+ XML_MIBVERSION_LONGDESC_BEGIN("en") \
+ "Version number of MPC MIB that we should use. Valid values are 1 (for 1.44 firmware) and 3 (for 1.62 firmware and later)" \
+ XML_PARM_LONGDESC_END
+
+#define XML_MIBVERSION_PARM \
+ XML_PARAMETER_BEGIN(ST_MIBVERSION, "string", "1", "0") \
+ XML_PORT_SHORTDESC \
+ XML_PORT_LONGDESC \
+ XML_PARAMETER_END
+
+static const char *apcmastersnmpXML =
+ XML_PARAMETERS_BEGIN
+ XML_IPADDR_PARM
+ XML_PORT_PARM
+ XML_COMMUNITY_PARM
+ XML_MIBVERSION_PARM
+ XML_PARAMETERS_END;
+
+/*
+ * own prototypes
+ */
+
+static void MPC_error(struct snmp_session *sptr, const char *fn
+, const char *msg);
+static struct snmp_session *MPC_open(char *hostname, int port
+, char *community);
+static void *MPC_read(struct snmp_session *sptr, const char *objname
+, int type);
+static int MPC_write(struct snmp_session *sptr, const char *objname
+, char type, char *value);
+
+static void
+MPC_error(struct snmp_session *sptr, const char *fn, const char *msg)
+{
+ int snmperr = 0;
+ int cliberr = 0;
+ char *errstr;
+
+ snmp_error(sptr, &cliberr, &snmperr, &errstr);
+ LOG(PIL_CRIT
+ , "%s: %s (cliberr: %i / snmperr: %i / error: %s)."
+ , fn, msg, cliberr, snmperr, errstr);
+ free(errstr);
+}
+
+
+/*
+ * creates a snmp session
+ */
+static struct snmp_session *
+MPC_open(char *hostname, int port, char *community)
+{
+ static struct snmp_session session;
+ struct snmp_session *sptr;
+
+ DEBUGCALL;
+
+ /* create session */
+ snmp_sess_init(&session);
+
+ /* fill session */
+ session.peername = hostname;
+ session.version = SNMP_VERSION_1;
+ session.remote_port = port;
+ session.community = (u_char *)community;
+ session.community_len = strlen(community);
+ session.retries = 5;
+ session.timeout = 1000000;
+
+ /* open session */
+ sptr = snmp_open(&session);
+
+ if (sptr == NULL) {
+ MPC_error(&session, __FUNCTION__, "cannot open snmp session");
+ }
+
+ /* return pointer to opened session */
+ return (sptr);
+}
+
+/*
+ * parse config
+ */
+
+/*
+ * read value of given oid and return it as string
+ */
+static void *
+MPC_read(struct snmp_session *sptr, const char *objname, int type)
+{
+ oid name[MAX_OID_LEN];
+ size_t namelen = MAX_OID_LEN;
+ struct variable_list *vars;
+ struct snmp_pdu *pdu;
+ struct snmp_pdu *resp;
+ static char response_str[MAX_STRING];
+ static int response_int;
+
+ DEBUGCALL;
+
+ /* convert objname into oid; return NULL if invalid */
+ if (!read_objid(objname, name, &namelen)) {
+ LOG(PIL_CRIT, "%s: cannot convert %s to oid.", __FUNCTION__, objname);
+ return (NULL);
+ }
+
+ /* create pdu */
+ if ((pdu = snmp_pdu_create(SNMP_MSG_GET)) != NULL) {
+
+ /* get-request have no values */
+ snmp_add_null_var(pdu, name, namelen);
+
+ /* send pdu and get response; return NULL if error */
+ if (snmp_synch_response(sptr, pdu, &resp) == SNMPERR_SUCCESS) {
+
+ /* request succeed, got valid response ? */
+ if (resp->errstat == SNMP_ERR_NOERROR) {
+
+ /* go through the returned vars */
+ for (vars = resp->variables; vars;
+ vars = vars->next_variable) {
+
+ /* return response as string */
+ if ((vars->type == type) && (type == ASN_OCTET_STR)) {
+ memset(response_str, 0, MAX_STRING);
+ strncpy(response_str, (char *)vars->val.string,
+ MIN(vars->val_len, MAX_STRING));
+ snmp_free_pdu(resp);
+ return ((void *) response_str);
+ }
+ /* return response as integer */
+ if ((vars->type == type) && (type == ASN_INTEGER)) {
+ response_int = *vars->val.integer;
+ snmp_free_pdu(resp);
+ return ((void *) &response_int);
+ }
+ }
+ }else{
+ LOG(PIL_CRIT, "%s: error in response packet, reason %ld [%s]."
+ , __FUNCTION__, resp->errstat, snmp_errstring(resp->errstat));
+ }
+ }else{
+ MPC_error(sptr, __FUNCTION__, "error sending/receiving pdu");
+ }
+ /* free repsonse pdu (necessary?) */
+ snmp_free_pdu(resp);
+ }else{
+ MPC_error(sptr, __FUNCTION__, "cannot create pdu");
+ }
+ /* error: return nothing */
+ return (NULL);
+}
+
+/*
+ * write value of given oid
+ */
+static int
+MPC_write(struct snmp_session *sptr, const char *objname, char type,
+ char *value)
+{
+ oid name[MAX_OID_LEN];
+ size_t namelen = MAX_OID_LEN;
+ struct snmp_pdu *pdu;
+ struct snmp_pdu *resp;
+
+ DEBUGCALL;
+
+ /* convert objname into oid; return FALSE if invalid */
+ if (!read_objid(objname, name, &namelen)) {
+ LOG(PIL_CRIT, "%s: cannot convert %s to oid.", __FUNCTION__, objname);
+ return (FALSE);
+ }
+
+ /* create pdu */
+ if ((pdu = snmp_pdu_create(SNMP_MSG_SET)) != NULL) {
+
+ /* add to be written value to pdu */
+ snmp_add_var(pdu, name, namelen, type, value);
+
+ /* send pdu and get response; return NULL if error */
+ if (snmp_synch_response(sptr, pdu, &resp) == STAT_SUCCESS) {
+
+ /* go through the returned vars */
+ if (resp->errstat == SNMP_ERR_NOERROR) {
+
+ /* request successful done */
+ snmp_free_pdu(resp);
+ return (TRUE);
+
+ }else{
+ LOG(PIL_CRIT, "%s: error in response packet, reason %ld [%s]."
+ , __FUNCTION__, resp->errstat, snmp_errstring(resp->errstat));
+ }
+ }else{
+ MPC_error(sptr, __FUNCTION__, "error sending/receiving pdu");
+ }
+ /* free pdu (again: necessary?) */
+ snmp_free_pdu(resp);
+ }else{
+ MPC_error(sptr, __FUNCTION__, "cannot create pdu");
+ }
+ /* error */
+ return (FALSE);
+}
+
+/*
+ * return the status for this device
+ */
+
+static int
+wti_mpc_status(StonithPlugin * s)
+{
+ struct pluginDevice *ad;
+ char *ident;
+
+ DEBUGCALL;
+
+ ERRIFNOTCONFIGED(s, S_OOPS);
+
+ ad = (struct pluginDevice *) s;
+
+ if ((ident = MPC_read(ad->sptr, OID_IDENT, ASN_OCTET_STR)) == NULL) {
+ LOG(PIL_CRIT, "%s: cannot read ident.", __FUNCTION__);
+ return (S_ACCESS);
+ }
+
+ /* status ok */
+ return (S_OK);
+}
+
+/*
+ * return the list of hosts configured for this device
+ */
+
+static char **
+wti_mpc_hostlist(StonithPlugin * s)
+{
+ char **hl;
+ struct pluginDevice *ad;
+ int j, h, num_outlets;
+ char *outlet_name;
+ char objname[MAX_STRING];
+
+ DEBUGCALL;
+
+ ERRIFNOTCONFIGED(s, NULL);
+
+ ad = (struct pluginDevice *) s;
+
+ /* allocate memory for array of up to NUM_OUTLETS strings */
+ if ((hl = (char **)MALLOC((ad->num_outlets+1) * sizeof(char *))) == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory.", __FUNCTION__);
+ return (NULL);
+ }
+ /* clear hostlist array */
+ memset(hl, 0, (ad->num_outlets + 1) * sizeof(char *));
+ num_outlets = 0;
+
+ /* read NUM_OUTLETS values and put them into hostlist array */
+ for (j = 0; j < ad->num_outlets; ++j) {
+
+ /* prepare objname */
+ switch (ad->mib_version) {
+ case 3:
+ snprintf(objname,MAX_STRING,OID_GROUP_NAMES_V3,j+1);
+ break;
+ case 1:
+ default:
+ snprintf(objname,MAX_STRING,OID_GROUP_NAMES_V1,j+1);
+ break;
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: using %s as group names oid", __FUNCTION__, objname);
+ }
+
+ /* read outlet name */
+ if ((outlet_name = MPC_read(ad->sptr, objname, ASN_OCTET_STR)) ==
+ NULL) {
+ LOG(PIL_CRIT, "%s: cannot read name for outlet %d."
+ , __FUNCTION__, j+1);
+ stonith_free_hostlist(hl);
+ hl = NULL;
+ return (hl);
+ }
+
+ /* Check whether the host is already listed */
+ for (h = 0; h < num_outlets; ++h) {
+ if (strcasecmp(hl[h],outlet_name) == 0)
+ break;
+ }
+
+ if (h >= num_outlets) {
+ /* put outletname in hostlist */
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: added %s to hostlist."
+ , __FUNCTION__, outlet_name);
+ }
+
+ if ((hl[num_outlets] = STRDUP(outlet_name)) == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory.", __FUNCTION__);
+ stonith_free_hostlist(hl);
+ hl = NULL;
+ return (hl);
+ }
+ strdown(hl[num_outlets]);
+ num_outlets++;
+ }
+ }
+
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: %d unique hosts connected to %d outlets."
+ , __FUNCTION__, num_outlets, j);
+ }
+ /* return list */
+ return (hl);
+}
+
+/*
+ * reset the host
+ */
+
+static int
+wti_mpc_reset_req(StonithPlugin * s, int request, const char *host)
+{
+ struct pluginDevice *ad;
+ char objname[MAX_STRING];
+ char value[MAX_STRING];
+ char *outlet_name;
+ int req_oid = OUTLET_REBOOT;
+ int outlet;
+ int found_outlet=-1;
+
+ DEBUGCALL;
+
+ ERRIFNOTCONFIGED(s, S_OOPS);
+
+ ad = (struct pluginDevice *) s;
+
+ /* read max. as->num_outlets values */
+ for (outlet = 1; outlet <= ad->num_outlets; outlet++) {
+
+ /* prepare objname */
+ switch (ad->mib_version) {
+ case 3:
+ snprintf(objname,MAX_STRING,OID_GROUP_NAMES_V3,outlet);
+ break;
+ case 1:
+ default:
+ snprintf(objname,MAX_STRING,OID_GROUP_NAMES_V1,outlet);
+ break;
+ }
+
+ /* read outlet name */
+ if ((outlet_name = MPC_read(ad->sptr, objname, ASN_OCTET_STR))
+ == NULL) {
+ LOG(PIL_CRIT, "%s: cannot read name for outlet %d."
+ , __FUNCTION__, outlet);
+ return (S_ACCESS);
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: found outlet: %s.", __FUNCTION__, outlet_name);
+ }
+
+ /* found one */
+ if (strcasecmp(outlet_name, host) == 0) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: found %s at outlet %d."
+ , __FUNCTION__, host, outlet);
+ }
+
+ /* Ok, stop iterating over host list */
+ found_outlet=outlet;
+ break;
+ }
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: outlet: %i.", __FUNCTION__, outlet);
+ }
+
+ /* host not found in outlet names */
+ if (found_outlet == -1) {
+ LOG(PIL_CRIT, "%s: no active outlet for '%s'.", __FUNCTION__, host);
+ return (S_BADHOST);
+ }
+
+
+ /* choose the OID for the stonith request */
+ switch (request) {
+ case ST_POWERON:
+ req_oid = OUTLET_ON;
+ break;
+ case ST_POWEROFF:
+ req_oid = OUTLET_OFF;
+ break;
+ case ST_GENERIC_RESET:
+ req_oid = OUTLET_REBOOT;
+ break;
+ default: break;
+ }
+
+ /* Turn them all off */
+
+ /* prepare objnames */
+
+ switch (ad->mib_version) {
+ case 3:
+ snprintf(objname,MAX_STRING,OID_GROUP_STATE_V3,found_outlet);
+ break;
+ case 1:
+ default:
+ snprintf(objname,MAX_STRING,OID_GROUP_STATE_V1,found_outlet);
+ break;
+ }
+
+ snprintf(value, MAX_STRING, "%i", req_oid);
+
+ /* send reboot cmd */
+ if (!MPC_write(ad->sptr, objname, 'i', value)) {
+ LOG(PIL_CRIT
+ , "%s: cannot send reboot command for outlet %d."
+ , __FUNCTION__, found_outlet);
+ return (S_RESETFAIL);
+ }
+
+ return (S_OK);
+}
+
+/*
+ * Get the configuration parameter names.
+ */
+
+static const char * const *
+wti_mpc_get_confignames(StonithPlugin * s)
+{
+ static const char * ret[] = {ST_IPADDR, ST_PORT, ST_COMMUNITY, ST_MIBVERSION, NULL};
+ return ret;
+}
+
+/*
+ * Set the configuration parameters.
+ */
+
+static int
+wti_mpc_set_config(StonithPlugin * s, StonithNVpair * list)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ int rc;
+ char * i;
+ int mo;
+ char objname[MAX_STRING];
+ StonithNamesToGet namestocopy [] =
+ { {ST_IPADDR, NULL}
+ , {ST_PORT, NULL}
+ , {ST_COMMUNITY, NULL}
+ , {ST_MIBVERSION, NULL}
+ , {NULL, NULL}
+ };
+
+ DEBUGCALL;
+ ERRIFWRONGDEV(s,S_INVAL);
+ if (sd->sp.isconfigured) {
+ return S_OOPS;
+ }
+
+ if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ sd->hostname = namestocopy[0].s_value;
+ sd->port = atoi(namestocopy[1].s_value);
+ PluginImports->mfree(namestocopy[1].s_value);
+ sd->community = namestocopy[2].s_value;
+ sd->mib_version = atoi(namestocopy[3].s_value);
+ PluginImports->mfree(namestocopy[3].s_value);
+
+ /* try to resolve the hostname/ip-address */
+ if (gethostbyname(sd->hostname) != NULL) {
+ /* init snmp library */
+ init_snmp("wti_mpc");
+
+ /* now try to get a snmp session */
+ if ((sd->sptr = MPC_open(sd->hostname, sd->port, sd->community)) != NULL) {
+
+ /* ok, get the number of groups from the mpc */
+ sd->num_outlets=0;
+ /* We scan goup names table starting from 1 to MAX_OUTLETS */
+ /* and increase num_outlet counter on every group entry with name */
+ /* first entry without name is the mark of the end of the group table */
+ for (mo=1;mo<MAX_OUTLETS;mo++) {
+ switch (sd->mib_version) {
+ case 3:
+ snprintf(objname,MAX_STRING,OID_GROUP_NAMES_V3,mo);
+ break;
+ case 1:
+ default:
+ snprintf(objname,MAX_STRING,OID_GROUP_NAMES_V1,mo);
+ break;
+ }
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: used for groupTable retrieval: %s."
+ , __FUNCTION__, objname);
+ }
+
+ if ((i = MPC_read(sd->sptr, objname, ASN_OCTET_STR)) == NULL) {
+ LOG(PIL_CRIT
+ , "%s: cannot read number of outlets."
+ , __FUNCTION__);
+ return (S_ACCESS);
+ }
+ if (strlen(i)) {
+ /* store the number of outlets */
+ sd->num_outlets++;
+ } else {
+ break;
+ }
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: number of outlets: %i."
+ , __FUNCTION__, sd->num_outlets );
+ }
+
+ /* Everything went well */
+ return (S_OK);
+ }else{
+ LOG(PIL_CRIT, "%s: cannot create snmp session."
+ , __FUNCTION__);
+ }
+ }else{
+ LOG(PIL_CRIT, "%s: cannot resolve hostname '%s', h_errno %d."
+ , __FUNCTION__, sd->hostname, h_errno);
+ }
+
+ /* not a valid config */
+ return (S_BADCONFIG);
+}
+
+/*
+ * get info about the stonith device
+ */
+
+static const char *
+wti_mpc_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice *ad;
+ const char *ret = NULL;
+
+ DEBUGCALL;
+
+ ERRIFWRONGDEV(s, NULL);
+
+ ad = (struct pluginDevice *) s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = ad->idinfo;
+ break;
+
+ case ST_DEVICENAME:
+ ret = ad->hostname;
+ break;
+
+ case ST_DEVICEDESCR:
+ ret = "WTI MPC (via SNMP)\n"
+ "The WTI MPC can accept multiple simultaneous SNMP clients";
+ break;
+
+ case ST_DEVICEURL:
+ ret = "http://www.wti.com/";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = apcmastersnmpXML;
+ break;
+
+ }
+ return ret;
+}
+
+
+/*
+ * APC StonithPlugin destructor...
+ */
+
+static void
+wti_mpc_destroy(StonithPlugin * s)
+{
+ struct pluginDevice *ad;
+
+ DEBUGCALL;
+
+ VOIDERRIFWRONGDEV(s);
+
+ ad = (struct pluginDevice *) s;
+
+ ad->pluginid = NOTpluginID;
+
+ /* release snmp session */
+ if (ad->sptr != NULL) {
+ snmp_close(ad->sptr);
+ ad->sptr = NULL;
+ }
+
+ /* reset defaults */
+ if (ad->hostname != NULL) {
+ PluginImports->mfree(ad->hostname);
+ ad->hostname = NULL;
+ }
+ if (ad->community != NULL) {
+ PluginImports->mfree(ad->community);
+ ad->community = NULL;
+ }
+ ad->num_outlets = 0;
+
+ PluginImports->mfree(ad);
+}
+
+/*
+ * Create a new APC StonithPlugin device. Too bad this function can't be
+ * static
+ */
+
+static StonithPlugin *
+wti_mpc_new(const char *subplugin)
+{
+ struct pluginDevice *ad = ST_MALLOCT(struct pluginDevice);
+
+ DEBUGCALL;
+
+ /* no memory for stonith-object */
+ if (ad == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory.", __FUNCTION__);
+ return (NULL);
+ }
+
+ /* clear stonith-object */
+ memset(ad, 0, sizeof(*ad));
+
+ /* set defaults */
+ ad->pluginid = pluginid;
+ ad->sptr = NULL;
+ ad->hostname = NULL;
+ ad->community = NULL;
+ ad->mib_version=1;
+ ad->idinfo = DEVICE;
+ ad->sp.s_ops = &wti_mpcOps;
+
+ /* return the object */
+ return (&(ad->sp));
+}
diff --git a/lib/plugins/stonith/wti_nps.c b/lib/plugins/stonith/wti_nps.c
new file mode 100644
index 0000000..f0b81f7
--- /dev/null
+++ b/lib/plugins/stonith/wti_nps.c
@@ -0,0 +1,813 @@
+/*
+ *
+ * Copyright 2001 Mission Critical Linux, Inc.
+ *
+ * All Rights Reserved.
+ */
+/*
+ * Stonith module for WTI Network Power Switch Devices (NPS-xxx)
+ * Also supports the WTI Telnet Power Switch Devices (TPS-xxx)
+ *
+ * Copyright 2001 Mission Critical Linux, Inc.
+ * author: mike ledoux <mwl@mclinux.com>
+ * author: Todd Wheeling <wheeling@mclinux.com>
+ * Mangled by Zhaokai <zhaokai@cn.ibm.com>, IBM, 2005
+ * Further hurt by Lon <lhh@redhat.com>, Red Hat, 2005
+ *
+ * Supported WTI devices:
+ * NPS-115
+ * NPS-230
+ * IPS-15
+ * IPS-800
+ * IPS-800-CE
+ * NBB-1600
+ * NBB-1600-CE
+ * TPS-2
+ *
+ * Based strongly on original code from baytech.c by Alan Robertson.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* Observations/Notes
+ *
+ * 1. The WTI Network Power Switch, unlike the BayTech network power switch,
+ * accpets only one (telnet) connection/session at a time. When one
+ * session is active, any subsequent attempt to connect to the NPS will
+ * result in a connection refused/closed failure. In a cluster environment
+ * or other environment utilizing polling/monitoring of the NPS
+ * (from multiple nodes), this can clearly cause problems. Obviously the
+ * more nodes and the shorter the polling interval, the more frequently such
+ * errors/collisions may occur.
+ *
+ * 2. We observed that on busy networks where there may be high occurances
+ * of broadcasts, the NPS became unresponsive. In some
+ * configurations this necessitated placing the power switch onto a
+ * private subnet.
+ */
+
+#include <lha_internal.h>
+#define DEVICE "WTI Network Power Switch"
+
+#define DOESNT_USE_STONITHKILLCOMM 1
+
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN wti_nps
+#define PIL_PLUGIN_S "wti_nps"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#define MAX_WTIPLUGINID 256
+
+#include <pils/plugin.h>
+
+#include "stonith_signal.h"
+
+static StonithPlugin * wti_nps_new(const char *);
+static void wti_nps_destroy(StonithPlugin *);
+static const char * const * wti_nps_get_confignames(StonithPlugin *);
+static int wti_nps_set_config(StonithPlugin * , StonithNVpair * );
+static const char * wti_nps_get_info(StonithPlugin * s, int InfoType);
+static int wti_nps_status(StonithPlugin * );
+static int wti_nps_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** wti_nps_hostlist(StonithPlugin *);
+
+static struct stonith_ops wti_npsOps ={
+ wti_nps_new, /* Create new STONITH object */
+ wti_nps_destroy, /* Destroy STONITH object */
+ wti_nps_get_info, /* Return STONITH info string */
+ wti_nps_get_confignames,/* Return configration parameters */
+ wti_nps_set_config, /* set configration */
+ wti_nps_status, /* Return STONITH device status */
+ wti_nps_reset_req, /* Request a reset */
+ wti_nps_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+#include "stonith_expect_helpers.h"
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &wti_npsOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * I have a NPS-110. This code has been tested with this switch.
+ * (Tested with NPS-230 and TPS-2 by lmb)
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ pid_t pid;
+ int rdfd;
+ int wrfd;
+ char * device;
+ char * passwd;
+};
+
+static const char * pluginid = "WTINPS-Stonith";
+static const char * NOTnpsid = "WTINPS device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+static const char *wti_npsXML =
+ XML_PARAMETERS_BEGIN
+ XML_IPADDR_PARM
+ XML_PASSWD_PARM
+ XML_PARAMETERS_END;
+
+
+/*
+ * Different expect strings that we get from the WTI
+ * Network Power Switch
+ */
+
+#define WTINPSSTR " Power Switch"
+#define WTINBBSTR "Boot Bar"
+
+static struct Etoken password[] = { {"Password:", 0, 0}, {NULL,0,0}};
+static struct Etoken Prompt[] = { {"PS>", 0, 0}
+ , {"IPS>", 0, 0}
+ , {"BB>", 0, 0}
+ , {NULL,0,0}};
+static struct Etoken LoginOK[] = { {WTINPSSTR, 0, 0}
+ , {WTINBBSTR, 0, 0}
+ , {"Invalid password", 1, 0}
+ , {NULL,0,0} };
+static struct Etoken Separator[] = { {"-----+", 0, 0} ,{NULL,0,0}};
+
+/* We may get a notice about rebooting, or a request for confirmation */
+static struct Etoken Processing[] = { {"rocessing - please wait", 0, 0}
+ , {"(Y/N):", 1, 0}
+ , {NULL,0,0}};
+
+static int NPS_connect_device(struct pluginDevice * nps);
+static int NPSLogin(struct pluginDevice * nps);
+static int NPSNametoOutlet(struct pluginDevice*, const char * name, char **outlets);
+static int NPSReset(struct pluginDevice*, char * outlets, const char * rebootid);
+static int NPSLogout(struct pluginDevice * nps);
+
+#if defined(ST_POWERON) && defined(ST_POWEROFF)
+static int NPS_onoff(struct pluginDevice*, const char * outlets, const char * unitid
+, int request);
+#endif
+
+/* Attempt to login up to 20 times... */
+static int
+NPSRobustLogin(struct pluginDevice * nps)
+{
+ int rc = S_OOPS;
+ int j = 0;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ for ( ; ; ) {
+ if (NPS_connect_device(nps) == S_OK) {
+ rc = NPSLogin(nps);
+ if (rc == S_OK) {
+ break;
+ }
+ }
+ if ((++j) == 20) {
+ break;
+ }
+ else {
+ sleep(1);
+ }
+ }
+
+ return rc;
+}
+
+/* Login to the WTI Network Power Switch (NPS) */
+static int
+NPSLogin(struct pluginDevice * nps)
+{
+ char IDinfo[128];
+ char * idptr = IDinfo;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ /* Look for the unit type info */
+ if (EXPECT_TOK(nps->rdfd, password, 2, IDinfo
+ , sizeof(IDinfo), Debug) < 0) {
+ LOG(PIL_CRIT, "No initial response from %s.", nps->idinfo);
+ return(errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS);
+ }
+ idptr += strspn(idptr, WHITESPACE);
+ /*
+ * We should be looking at something like this:
+ * Enter Password:
+ */
+
+ SEND(nps->wrfd, nps->passwd);
+ SEND(nps->wrfd, "\r");
+ /* Expect "Network Power Switch vX.YY" */
+
+ switch (StonithLookFor(nps->rdfd, LoginOK, 5)) {
+
+ case 0: /* Good! */
+ LOG(PIL_INFO, "Successful login to %s.", nps->idinfo);
+ break;
+
+ case 1: /* Uh-oh - bad password */
+ LOG(PIL_CRIT, "Invalid password for %s.", nps->idinfo);
+ return(S_ACCESS);
+
+ default:
+ return(errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS);
+ }
+ return(S_OK);
+}
+
+/* Log out of the WTI NPS */
+
+static int
+NPSLogout(struct pluginDevice* nps)
+{
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ /* Send "/h" help command and expect back prompt */
+ /*
+ SEND(nps->wrfd, "/h\r");
+ */
+ /* Expect "PS>" */
+ rc = StonithLookFor(nps->rdfd, Prompt, 5);
+
+ /* "/x" is Logout, "/x,y" auto-confirms */
+ SEND(nps->wrfd, "/x,y\r");
+
+ close(nps->wrfd);
+ close(nps->rdfd);
+ nps->wrfd = nps->rdfd = -1;
+
+ return(rc >= 0 ? S_OK : (errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS));
+}
+
+/* Reset (power-cycle) the given outlets */
+static int
+NPSReset(struct pluginDevice* nps, char * outlets, const char * rebootid)
+{
+ char unum[32];
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ /* Send "/h" help command and expect back prompt */
+ SEND(nps->wrfd, "/h\r");
+ /* Expect "PS>" */
+ EXPECT(nps->rdfd, Prompt, 5);
+
+ /* Send REBOOT command for given outlets */
+ snprintf(unum, sizeof(unum), "/BOOT %s,y\r", outlets);
+ SEND(nps->wrfd, unum);
+
+ /* Expect "Processing "... or "(Y/N)" (if confirmation turned on) */
+
+ retry:
+ switch (StonithLookFor(nps->rdfd, Processing, 5)) {
+ case 0: /* Got "Processing" Do nothing */
+ break;
+
+ case 1: /* Got that annoying command confirmation :-( */
+ SEND(nps->wrfd, "Y\r");
+ goto retry;
+
+ default:
+ return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS);
+ }
+ LOG(PIL_INFO, "Host is being rebooted: %s", rebootid);
+
+ /* Expect "PS>" */
+ if (StonithLookFor(nps->rdfd, Prompt, 60) < 0) {
+ return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS);
+ }
+
+ /* All Right! Power is back on. Life is Good! */
+
+ LOG(PIL_INFO, "Power restored to host: %s", rebootid);
+ SEND(nps->wrfd, "/h\r");
+ return(S_OK);
+}
+
+#if defined(ST_POWERON) && defined(ST_POWEROFF)
+static int
+NPS_onoff(struct pluginDevice* nps, const char * outlets, const char * unitid, int req)
+{
+ char unum[32];
+
+ const char * onoff = (req == ST_POWERON ? "/On" : "/Off");
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ /* Send "/h" help command and expect prompt back */
+ SEND(nps->wrfd, "/h\r");
+ /* Expect "PS>" */
+ EXPECT(nps->rdfd, Prompt, 5);
+
+ /* Send ON/OFF command for given outlet */
+ snprintf(unum, sizeof(unum), "%s %s,y\r", onoff, outlets);
+ SEND(nps->wrfd, unum);
+
+ /* Expect "Processing"... or "(Y/N)" (if confirmation turned on) */
+
+ if (StonithLookFor(nps->rdfd, Processing, 5) == 1) {
+ /* They've turned on that annoying command confirmation :-( */
+ SEND(nps->wrfd, "Y\r");
+ }
+ EXPECT(nps->rdfd, Prompt, 60);
+
+ /* All Right! Command done. Life is Good! */
+ LOG(PIL_INFO, "Power to NPS outlet(s) %s turned %s", outlets, onoff);
+
+ SEND(nps->wrfd, "/h\r");
+ return(S_OK);
+}
+#endif /* defined(ST_POWERON) && defined(ST_POWEROFF) */
+
+/*
+ * Map the given host name into an (AC) Outlet number on the power strip
+ */
+
+static int
+NPSNametoOutlet(struct pluginDevice* nps, const char * name, char **outlets)
+{
+ char NameMapping[128];
+ int sockno;
+ char sockname[32];
+ char buf[32];
+ int left = 17;
+ int ret = -1;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ if ((*outlets = (char *)MALLOC(left*sizeof(char))) == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(-1);
+ }
+
+ strncpy(*outlets, "", left);
+ left = left - 1; /* ensure terminating '\0' */
+ /* Expect "PS>" */
+ EXPECT(nps->rdfd, Prompt, 5);
+
+ /* The status command output contains mapping of hosts to outlets */
+ SEND(nps->wrfd, "/s\r");
+
+ /* Expect: "-----+" so we can skip over it... */
+ EXPECT(nps->rdfd, Separator, 5);
+
+ do {
+ NameMapping[0] = EOS;
+ SNARF(nps->rdfd, NameMapping, 5);
+
+ if (sscanf(NameMapping
+ , "%d | %16c",&sockno, sockname) == 2) {
+
+ char * last = sockname+16;
+ *last = EOS;
+ --last;
+ /* Strip off trailing blanks */
+ for(; last > sockname; --last) {
+ if (*last == ' ') {
+ *last = EOS;
+ }else{
+ break;
+ }
+ }
+ if (strncasecmp(name, sockname, 16) == 0) {
+ ret = sockno;
+ snprintf(buf, sizeof(buf), "%d ", sockno);
+ strncat(*outlets, buf, left);
+ left = left - strlen(buf);
+ }
+ }
+ } while (strlen(NameMapping) > 2 && left > 0);
+
+ return(ret);
+}
+
+static int
+wti_nps_status(StonithPlugin *s)
+{
+ struct pluginDevice* nps;
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ nps = (struct pluginDevice*) s;
+
+ if ((rc = NPSRobustLogin(nps) != S_OK)) {
+ LOG(PIL_CRIT, "Cannot log into %s.", nps->idinfo);
+ return(rc);
+ }
+
+ /* Send "/h" help command and expect back prompt */
+ SEND(nps->wrfd, "/h\r");
+ /* Expect "PS>" */
+ EXPECT(nps->rdfd, Prompt, 5);
+
+ return(NPSLogout(nps));
+}
+
+/*
+ * Return the list of hosts (outlet names) for the devices on this NPS unit
+ */
+
+static char **
+wti_nps_hostlist(StonithPlugin *s)
+{
+ char NameMapping[128];
+ char* NameList[64];
+ unsigned int numnames = 0;
+ char ** ret = NULL;
+ struct pluginDevice* nps;
+ unsigned int i;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,NULL);
+
+ nps = (struct pluginDevice*) s;
+ if (NPSRobustLogin(nps) != S_OK) {
+ LOG(PIL_CRIT, "Cannot log into %s.", nps->idinfo);
+ return(NULL);
+ }
+
+ /* Expect "PS>" */
+ NULLEXPECT(nps->rdfd, Prompt, 5);
+
+ /* The status command output contains mapping of hosts to outlets */
+ SEND(nps->wrfd, "/s\r");
+
+ /* Expect: "-----" so we can skip over it... */
+ NULLEXPECT(nps->rdfd, Separator, 5);
+ NULLEXPECT(nps->rdfd, CRNL, 5);
+
+ /* Looks Good! Parse the status output */
+
+ do {
+ int sockno;
+ char sockname[64];
+ NameMapping[0] = EOS;
+ NULLSNARF(nps->rdfd, NameMapping, 5);
+ if (sscanf(NameMapping
+ , "%d | %16c",&sockno, sockname) == 2) {
+
+ char * last = sockname+16;
+ char * nm;
+ *last = EOS;
+ --last;
+
+ /* Strip off trailing blanks */
+ for(; last > sockname; --last) {
+ if (*last == ' ') {
+ *last = EOS;
+ }else{
+ break;
+ }
+ }
+ if (numnames >= DIMOF(NameList)-1) {
+ break;
+ }
+ if (!strcmp(sockname,"(undefined)") ||
+ !strcmp(sockname,"---")) {
+ /* lhh - skip undefined */
+ continue;
+ }
+ if ((nm = STRDUP(sockname)) == NULL) {
+ goto out_of_memory;
+ }
+ strdown(nm);
+ NameList[numnames] = nm;
+ ++numnames;
+ NameList[numnames] = NULL;
+ }
+ } while (strlen(NameMapping) > 2);
+
+ if (numnames >= 1) {
+ ret = (char **)MALLOC((numnames+1)*sizeof(char*));
+ if (ret == NULL) {
+ goto out_of_memory;
+ }else{
+ memset(ret, 0, (numnames+1)*sizeof(char*));
+ memcpy(ret, NameList, (numnames+1)*sizeof(char*));
+ }
+ }
+ (void)NPSLogout(nps);
+
+ return(ret);
+
+out_of_memory:
+ LOG(PIL_CRIT, "out of memory");
+ for (i=0; i<numnames; i++) {
+ FREE(NameList[i]);
+ }
+
+ return (NULL);
+}
+
+/*
+ * Connect to the given NPS device. We should add serial support here
+ * eventually...
+ */
+static int
+NPS_connect_device(struct pluginDevice * nps)
+{
+ int fd = OurImports->OpenStreamSocket(nps->device
+ , TELNET_PORT, TELNET_SERVICE);
+
+ if (fd < 0) {
+ return(S_OOPS);
+ }
+ nps->rdfd = nps->wrfd = fd;
+ return(S_OK);
+}
+
+/*
+ * Reset the given host on this Stonith device.
+ */
+static int
+wti_nps_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ int rc = 0;
+ int lorc = 0;
+ struct pluginDevice* nps;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ nps = (struct pluginDevice*) s;
+
+ if ((rc = NPSRobustLogin(nps)) != S_OK) {
+ LOG(PIL_CRIT, "Cannot log into %s.", nps->idinfo);
+ }else{
+ char *outlets;
+ int noutlet;
+
+ outlets = NULL;
+ noutlet = NPSNametoOutlet(nps, host, &outlets);
+
+ if (noutlet < 1) {
+ LOG(PIL_WARN, "%s doesn't control host [%s]"
+ , nps->device, host);
+ if (outlets != NULL) {
+ FREE(outlets);
+ outlets = NULL;
+ }
+ return(S_BADHOST);
+ }
+ switch(request) {
+
+#if defined(ST_POWERON) && defined(ST_POWEROFF)
+ case ST_POWERON:
+ case ST_POWEROFF:
+ rc = NPS_onoff(nps, outlets, host, request);
+ if (outlets != NULL) {
+ FREE(outlets);
+ outlets = NULL;
+ }
+ break;
+#endif
+ case ST_GENERIC_RESET:
+ rc = NPSReset(nps, outlets, host);
+ if (outlets != NULL) {
+ FREE(outlets);
+ outlets = NULL;
+ }
+ break;
+ default:
+ rc = S_INVAL;
+ if (outlets != NULL) {
+ FREE(outlets);
+ outlets = NULL;
+ }
+ break;
+ }
+ }
+
+ lorc = NPSLogout(nps);
+ return(rc != S_OK ? rc : lorc);
+}
+
+/*
+ * Parse the information in the given string,
+ * and stash it away...
+ */
+static int
+wti_nps_set_config(StonithPlugin * s, StonithNVpair *list)
+{
+ struct pluginDevice* nps;
+ StonithNamesToGet namestocopy [] =
+ { {ST_IPADDR, NULL}
+ , {ST_PASSWD, NULL}
+ , {NULL, NULL}
+ };
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.\n", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ nps = (struct pluginDevice*) s;
+
+ if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ nps->device = namestocopy[0].s_value;
+ nps->passwd = namestocopy[1].s_value;
+ return S_OK;
+}
+
+
+/*
+ * Return the Stonith plugin configuration parameter
+ *
+ */
+static const char * const *
+wti_nps_get_confignames(StonithPlugin * p)
+{
+ static const char * names[] = { ST_IPADDR , ST_PASSWD , NULL};
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+ return names;
+}
+
+/*
+ * Get info about the stonith device
+ *
+ */
+static const char *
+wti_nps_get_info(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* nps;
+ const char * ret;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,NULL);
+
+ /*
+ * We look in the ST_TEXTDOMAIN catalog for our messages
+ */
+ nps = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+
+ case ST_DEVICEID:
+ ret = nps->idinfo;
+ break;
+ case ST_DEVICENAME:
+ ret = nps->device;
+ break;
+ case ST_DEVICEDESCR:
+ ret = "Western Telematic (WTI) Network Power Switch Devices (NPS-xxx)\n"
+ "Also supports the WTI Telnet Power Switch Devices (TPS-xxx)\n"
+ "NOTE: The WTI Network Power Switch, accepts only "
+ "one (telnet) connection/session at a time.";
+ break;
+ case ST_DEVICEURL:
+ ret = "http://www.wti.com/";
+ break;
+ case ST_CONF_XML: /* XML metadata */
+ ret = wti_npsXML;
+ break;
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * WTI NPS Stonith destructor...
+ */
+static void
+wti_nps_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* nps;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ VOIDERRIFWRONGDEV(s);
+
+ nps = (struct pluginDevice *)s;
+
+ nps->pluginid = NOTnpsid;
+ if (nps->rdfd >= 0) {
+ close(nps->rdfd);
+ nps->rdfd = -1;
+ }
+ if (nps->wrfd >= 0) {
+ close(nps->wrfd);
+ nps->wrfd = -1;
+ }
+ if (nps->device != NULL) {
+ FREE(nps->device);
+ nps->device = NULL;
+ }
+ if (nps->passwd != NULL) {
+ FREE(nps->passwd);
+ nps->passwd = NULL;
+ }
+ FREE(nps);
+}
+
+/* Create a new BayTech Stonith device. */
+
+static StonithPlugin *
+wti_nps_new(const char *subplugin)
+{
+ struct pluginDevice* nps = ST_MALLOCT(struct pluginDevice);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ if (nps == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(nps, 0, sizeof(*nps));
+ nps->pluginid = pluginid;
+ nps->pid = -1;
+ nps->rdfd = -1;
+ nps->wrfd = -1;
+ nps->device = NULL;
+ nps->passwd = NULL;
+ nps->idinfo = DEVICE;
+ nps->sp.s_ops = &wti_npsOps;
+
+ return &(nps->sp);
+}
+
diff --git a/lib/stonith/Makefile.am b/lib/stonith/Makefile.am
new file mode 100644
index 0000000..429e1d3
--- /dev/null
+++ b/lib/stonith/Makefile.am
@@ -0,0 +1,54 @@
+#
+# Stonith: Shoot The Node In The Head
+#
+# Copyright (C) 2001 Alan Robertson
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \
+ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \
+ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl
+
+## include files
+
+## binaries
+sbin_PROGRAMS = stonith meatclient
+
+stonith_SOURCES = main.c
+
+stonith_LDADD = libstonith.la $(top_builddir)/lib/pils/libpils.la $(GLIBLIB) \
+ $(top_builddir)/lib/clplumbing/libplumb.la \
+ $(top_builddir)/lib/clplumbing/libplumbgpl.la
+stonith_LDFLAGS = @LIBADD_DL@ @LIBLTDL@ -export-dynamic @DLOPEN_FORCE_FLAGS@ @LIBADD_INTL@
+
+meatclient_SOURCES = meatclient.c
+meatclient_LDADD = $(GLIBLIB) libstonith.la
+
+## libraries
+
+lib_LTLIBRARIES = libstonith.la
+
+libstonith_la_SOURCES = expect.c stonith.c st_ttylock.c
+libstonith_la_LDFLAGS = -version-info 1:0:0
+libstonith_la_LIBADD = $(top_builddir)/lib/pils/libpils.la \
+ $(top_builddir)/replace/libreplace.la \
+ $(GLIBLIB)
+
+helperdir = $(datadir)/$(PACKAGE_NAME)
+helper_SCRIPTS = ha_log.sh
+
+EXTRA_DIST = $(helper_SCRIPTS)
diff --git a/lib/stonith/README b/lib/stonith/README
new file mode 100644
index 0000000..6b98ef9
--- /dev/null
+++ b/lib/stonith/README
@@ -0,0 +1,31 @@
+The STONITH module (a.k.a. STOMITH) provides an extensible interface
+for remotely powering down a node in the cluster. The idea is quite simple:
+When the software running on one machine wants to make sure another
+machine in the cluster is not using a resource, pull the plug on the other
+machine. It's simple and reliable, albiet admittedly brutal.
+
+Here's an example command line invocation used to power off a machine
+named 'nodeb'. The parameters are dependent on the type of device you
+are using for this capability.
+
+stonith -t rps10 -p "/dev/ttyS5 nodeb 0 " nodeb
+
+Currently supported devices:
+
+ apcsmart: APCSmart (tested with 2 old 900XLI)
+ baytech: Baytech RPC5
+ meatware: Alerts an operator to manually turn off a device.
+ nw_rpc100s: Micro Energetics Night/Ware RPC100S
+ rps10: Western Telematics RPS10
+ vacm_stonith: VA Linux Cluster Manager (see README.vacm)
+
+
+To see the parameter syntax for a module, run the 'stonith' command and omit the
+-p parameter. For example:
+
+$ /usr/sbin/stonith -t rps10 test
+
+stonith: Invalid config file for rps10 device.
+stonith: Config file syntax: <serial_device> <node> <outlet> [ <node> <outlet> [...] ]
+All tokens are white-space delimited.
+Blank lines and lines beginning with # are ignored
diff --git a/lib/stonith/expect.c b/lib/stonith/expect.c
new file mode 100644
index 0000000..bb1f818
--- /dev/null
+++ b/lib/stonith/expect.c
@@ -0,0 +1,539 @@
+/*
+ * Simple expect module for the STONITH library
+ *
+ * Copyright (c) 2000 Alan Robertson <alanr@unix.sh>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <lha_internal.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <syslog.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/times.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stonith/st_ttylock.h>
+#include <clplumbing/longclock.h>
+#define ENABLE_PIL_DEFS_PRIVATE
+#include <pils/plugin.h>
+
+#ifdef _POSIX_PRIORITY_SCHEDULING
+# include <sched.h>
+#endif
+
+#include <stonith/stonith.h>
+#include <stonith/stonith_plugin.h>
+
+extern PILPluginUniv* StonithPIsys;
+
+#define LOG(args...) PILCallLog(StonithPIsys->imports->log, args)
+#define DEBUG(args...) LOG(PIL_DEBUG, args)
+#undef DEBUG
+#define DEBUG(args...) PILCallLog(StonithPIsys->imports->log, PIL_DEBUG, args)
+#define MALLOC StonithPIsys->imports->alloc
+#define REALLOC StonithPIsys->imports->mrealloc
+#define STRDUP StonithPIsys->imports->mstrdup
+#define FREE(p) {StonithPIsys->imports->mfree(p); (p) = NULL;}
+
+#ifdef TIMES_ALLOWS_NULL_PARAM
+# define TIMES_PARAM NULL
+#else
+ static struct tms dummy_longclock_tms_struct;
+# define TIMES_PARAM &dummy_longclock_tms_struct
+#endif
+
+static unsigned long
+our_times(void) /* Make times(2) behave rationally on Linux */
+{
+ clock_t ret;
+#ifndef DISABLE_TIMES_KLUDGE
+ int save_errno = errno;
+
+ /*
+ * This code copied from clplumbing/longclock.c to avoid
+ * making STONITH depend on clplumbing. See it for an explanation
+ */
+
+ errno = 0;
+#endif /* DISABLE_TIMES_KLUDGE */
+
+ ret = times(TIMES_PARAM);
+
+#ifndef DISABLE_TIMES_KLUDGE
+ if (errno != 0) {
+ ret = (clock_t) (-errno);
+ }
+ errno = save_errno;
+#endif /* DISABLE_TIMES_KLUDGE */
+ return (unsigned long)ret;
+}
+
+/*
+ * Look for ('expect') any of a series of tokens in the input
+ * Return the token type for the given token or -1 on error.
+ */
+
+static int
+ExpectToken(int fd, struct Etoken * toklist, int to_secs, char * savebuf
+, int maxline, int Debug)
+{
+ unsigned long starttime;
+ unsigned long endtime;
+ int wraparound=0;
+ unsigned Hertz = sysconf(_SC_CLK_TCK);
+ int tickstousec = (1000000/Hertz);
+ unsigned long now;
+ unsigned long ticks;
+ int nchars = 1; /* reserve space for an EOS */
+ struct timeval tv;
+ char * buf = savebuf;
+
+ struct Etoken * this;
+
+ /* Figure out when to give up. Handle lbolt wraparound */
+
+ starttime = our_times();
+ ticks = (to_secs*Hertz);
+ endtime = starttime + ticks;
+
+ if (endtime < starttime) {
+ wraparound = 1;
+ }
+
+ if (buf) {
+ *buf = EOS;
+ }
+
+ for (this=toklist; this->string; ++this) {
+ this->matchto = 0;
+ }
+
+
+ while (now = our_times(),
+ (wraparound && (now > starttime || now <= endtime))
+ || (!wraparound && now <= endtime)) {
+
+ fd_set infds;
+ char ch;
+ unsigned long timeleft;
+ int retval;
+
+ timeleft = endtime - now;
+
+ tv.tv_sec = timeleft / Hertz;
+ tv.tv_usec = (timeleft % Hertz) * tickstousec;
+
+ if (tv.tv_sec == 0 && tv.tv_usec < tickstousec) {
+ /* Give 'em a little chance */
+ tv.tv_usec = tickstousec;
+ }
+
+ /* Watch our FD to see when it has input. */
+ FD_ZERO(&infds);
+ FD_SET(fd, &infds);
+
+ retval = select(fd+1, &infds, NULL, NULL, &tv);
+ if (retval <= 0) {
+ errno = ETIMEDOUT;
+ return(-1);
+ }
+ /* Whew! All that work just to read one character! */
+
+ if (read(fd, &ch, sizeof(ch)) <= 0) {
+ return(-1);
+ }
+ /* Save the text, if we can */
+ if (buf && nchars < maxline-1) {
+ *buf = ch;
+ ++buf;
+ *buf = EOS;
+ ++nchars;
+ }
+ if (Debug > 1) {
+ DEBUG("Got '%c'", ch);
+ }
+
+ /* See how this character matches our expect strings */
+
+ for (this=toklist; this->string; ++this) {
+
+ if (ch == this->string[this->matchto]) {
+
+ /* It matches the current token */
+
+ ++this->matchto;
+ if (this->string[this->matchto] == EOS){
+ /* Hallelujah! We matched */
+ if (Debug) {
+ DEBUG("Matched [%s] [%d]"
+ , this->string
+ , this->toktype);
+ if (savebuf) {
+ DEBUG("Saved [%s]"
+ , savebuf);
+ }
+ }
+ return(this->toktype);
+ }
+ }else{
+
+ /* It doesn't appear to match this token */
+
+ int curlen;
+ int nomatch=1;
+ /*
+ * If we already had a match (matchto is
+ * greater than zero), we look for a match
+ * of the tail of the pattern matched so far
+ * (with the current character) against the
+ * head of the pattern.
+ */
+
+ /*
+ * This is to make the string "aab" match
+ * the pattern "ab" correctly
+ * Painful, but nice to do it right.
+ */
+
+ for (curlen = (this->matchto)
+ ; nomatch && curlen >= 0
+ ; --curlen) {
+ const char * tail;
+ tail=(this->string)
+ + this->matchto
+ - curlen;
+
+ if (strncmp(this->string, tail
+ , curlen) == 0
+ && this->string[curlen] == ch) {
+
+ if (this->string[curlen+1]==EOS){
+ /* We matched! */
+ /* (can't happen?) */
+ return(this->toktype);
+ }
+ this->matchto = curlen+1;
+ nomatch=0;
+ }
+ }
+ if (nomatch) {
+ this->matchto = 0;
+ }
+ }
+ }
+ }
+ errno = ETIMEDOUT;
+ return(-1);
+}
+
+/*
+ * Start a process with its stdin and stdout redirected to pipes
+ * so the parent process can talk to it.
+ */
+static int
+StartProcess(const char * cmd, int * readfd, int * writefd)
+{
+ pid_t pid;
+ int wrpipe[2]; /* The pipe the parent process writes to */
+ /* (which the child process reads from) */
+ int rdpipe[2]; /* The pipe the parent process reads from */
+ /* (which the child process writes to) */
+
+ if (pipe(wrpipe) < 0) {
+ perror("cannot create pipe\n");
+ return(-1);
+ }
+ if (pipe(rdpipe) < 0) {
+ perror("cannot create pipe\n");
+ close(wrpipe[0]);
+ close(wrpipe[1]);
+ return(-1);
+ }
+ switch(pid=fork()) {
+
+ case -1: perror("cannot StartProcess cmd");
+ close(rdpipe[0]);
+ close(wrpipe[1]);
+ close(wrpipe[0]);
+ close(rdpipe[1]);
+ return(-1);
+
+ case 0: /* We are the child */
+
+ /* Redirect stdin */
+ close(0);
+ dup2(wrpipe[0], 0);
+ close(wrpipe[0]);
+ close(wrpipe[1]);
+
+ /* Redirect stdout */
+ close(1);
+ dup2(rdpipe[1], 1);
+ close(rdpipe[0]);
+ close(rdpipe[1]);
+#if defined(SCHED_OTHER) && !defined(ON_DARWIN)
+ {
+ /*
+ * Try and (re)set our scheduling to "normal"
+ * Sometimes our callers run in soft
+ * real-time mode. The program we exec might
+ * not be very well behaved - this is bad for
+ * operation in high-priority (soft real-time)
+ * mode. In particular, telnet is prone to
+ * going into infinite loops when killed.
+ */
+ struct sched_param sp;
+ memset(&sp, 0, sizeof(sp));
+ sp.sched_priority = 0;
+ sched_setscheduler(0, SCHED_OTHER, &sp);
+ }
+#endif
+ execlp("/bin/sh", "sh", "-c", cmd, (const char *)NULL);
+ perror("cannot exec shell!");
+ exit(1);
+
+ default: /* We are the parent */
+ *readfd = rdpipe[0];
+ close(rdpipe[1]);
+
+ *writefd = wrpipe[1];
+ close(wrpipe[0]);
+ return(pid);
+ }
+ /*NOTREACHED*/
+ return(-1);
+}
+
+static char **
+stonith_copy_hostlist(const char * const * hostlist)
+{
+ int hlleng = 1;
+ const char * const * here = hostlist;
+ char ** hret;
+ char ** ret;
+
+ for (here = hostlist; *here; ++here) {
+ ++hlleng;
+ }
+ ret = (char**)MALLOC(hlleng * sizeof(char *));
+ if (ret == NULL) {
+ return ret;
+ }
+
+ hret = ret;
+ for (here = hostlist; *here; ++here,++hret) {
+ *hret = STRDUP(*here);
+ if (*hret == NULL) {
+ stonith_free_hostlist(ret);
+ return NULL;
+ }
+ }
+ *hret = NULL;
+ return ret;
+}
+
+static char **
+StringToHostList(const char * s)
+{
+ const char * here;
+ int hlleng = 0;
+ char ** ret;
+ char ** hret;
+ const char * delims = " \t\n\f\r,";
+
+ /* Count the number of strings (words) in the result */
+ here = s;
+ while (*here != EOS) {
+ /* skip delimiters */
+ here += strspn(here, delims);
+ if (*here == EOS) {
+ break;
+ }
+ /* skip over substring proper... */
+ here += strcspn(here, delims);
+ ++hlleng;
+ }
+
+
+ /* Malloc space for the result string pointers */
+ ret = (char**)MALLOC((hlleng+1) * sizeof(char *));
+ if (ret == NULL) {
+ return NULL;
+ }
+
+ hret = ret;
+ here = s;
+
+ /* Copy each substring into a separate string */
+ while (*here != EOS) {
+ int slen; /* substring length */
+
+ /* skip delimiters */
+ here += strspn(here, delims);
+ if (*here == EOS) {
+ break;
+ }
+ /* Compute substring length */
+ slen = strcspn(here, delims);
+ *hret = MALLOC((slen+1) * sizeof(char));
+ if (*hret == NULL) {
+ stonith_free_hostlist(hret);
+ return NULL;
+ }
+ /* Copy string (w/o EOS) */
+ memcpy(*hret, here, slen);
+ /* Add EOS to result string */
+ (*hret)[slen] = EOS;
+ strdown(*hret);
+ here += slen;
+ ++hret;
+ }
+ *hret = NULL;
+ return ret;
+}
+
+
+static const char *
+GetValue(StonithNVpair* parameters, const char * name)
+{
+ while (parameters->s_name) {
+ if (strcmp(name, parameters->s_name) == 0) {
+ return parameters->s_value;
+ }
+ ++parameters;
+ }
+ return NULL;
+}
+
+static int
+CopyAllValues(StonithNamesToGet* output, StonithNVpair * input)
+{
+ int j;
+ int rc;
+
+ for (j=0; output[j].s_name; ++j) {
+ const char * value = GetValue(input, output[j].s_name);
+ if (value == NULL) {
+ rc = S_INVAL;
+ output[j].s_value = NULL;
+ goto fail;
+ }
+ if ((output[j].s_value = STRDUP(value)) == NULL) {
+ rc = S_OOPS;
+ goto fail;
+ }
+ }
+ return S_OK;
+
+fail:
+ for (j=0; output[j].s_value; ++j) {
+ FREE(output[j].s_value);
+ }
+ return rc;
+}
+
+
+static int
+OpenStreamSocket(const char * host, int port, const char * service)
+{
+ union s_un {
+ struct sockaddr_in si4;
+ struct sockaddr_in6 si6;
+ }sockun;
+ int sock;
+ int addrlen = -1;
+
+
+ memset(&sockun, 0, sizeof(sockun));
+
+ if (inet_pton(AF_INET, host, (void*)&sockun.si4.sin_addr) < 0) {
+ sockun.si4.sin_family = AF_INET;
+ }else if (inet_pton(AF_INET6, host, (void*)&sockun.si6.sin6_addr)<0){
+ sockun.si6.sin6_family = AF_INET6;
+ }else{
+ struct hostent* hostp = gethostbyname(host);
+ if (hostp == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ sockun.si4.sin_family = hostp->h_addrtype;
+ memcpy(&sockun.si4.sin_addr, hostp->h_addr, hostp->h_length);
+ }
+ if ((sock = socket(sockun.si4.sin_family, SOCK_STREAM, 0)) < 0) {
+ return -1;
+ }
+ if (service != NULL) {
+ struct servent* se = getservbyname(service, "tcp");
+ if (se != NULL) {
+ /* We convert it back later... */
+ port = ntohs(se->s_port);
+ }
+ }
+ if (port <= 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ port = htons(port);
+ if (sockun.si6.sin6_family == AF_INET6) {
+ sockun.si6.sin6_port = port;
+ addrlen = sizeof(sockun.si6);
+ }else if (sockun.si4.sin_family == AF_INET) {
+ sockun.si4.sin_port = port;
+ addrlen = sizeof(sockun.si4);
+ }else{
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (connect(sock, (struct sockaddr*)(&sockun), addrlen)< 0){
+ int save = errno;
+ perror("connect() failed");
+ close(sock);
+ errno = save;
+ return -1;
+ }
+ return sock;
+}
+
+StonithImports stonithimports = {
+ ExpectToken,
+ StartProcess,
+ OpenStreamSocket,
+ GetValue,
+ CopyAllValues,
+ StringToHostList,
+ stonith_copy_hostlist,
+ stonith_free_hostlist,
+ st_ttylock,
+ st_ttyunlock
+};
diff --git a/lib/stonith/ha_log.sh b/lib/stonith/ha_log.sh
new file mode 100755
index 0000000..73093f0
--- /dev/null
+++ b/lib/stonith/ha_log.sh
@@ -0,0 +1,114 @@
+#!/bin/sh
+#
+#
+# ha_log.sh for stonith external plugins
+# (equivalent to ocf_log in ocf-shellfuncs in resource-agents)
+#
+# Copyright (c) 2004 SUSE LINUX AG, Lars Marowsky-Brée
+# All Rights Reserved.
+#
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+# Build version: @GLUE_BUILD_VERSION@
+
+PROG=`basename $0`
+
+: ${HA_DATEFMT=+"%b %d %T"}
+: ${HA_LOGD=yes}
+: ${HA_LOGTAG=""}
+: ${HA_LOGFACILITY=daemon}
+: ${HA_LOGFILE=""}
+: ${HA_DEBUGLOG=""}
+: ${HA_debug="0"}
+
+hadate() {
+ date "+$HA_DATEFMT"
+}
+
+level_pres() {
+ case "$1" in
+ crit) echo "CRIT";;
+ err|error) echo "ERROR";;
+ warn|warning) echo "WARN";;
+ notice) echo "notice";;
+ info) echo "info";;
+ debug) echo "debug";;
+ *)
+ ha_log err "$PROG: unrecognized loglevel: $1"
+ exit 1
+ ;;
+ esac
+}
+
+set_logtag() {
+ # add parent pid to the logtag
+ if [ "$HA_LOGTAG" ]; then
+ if [ -n "$CRM_meta_st_device_id" ]; then
+ HA_LOGTAG="$HA_LOGTAG($CRM_meta_st_device_id)[$PPID]"
+ else
+ HA_LOGTAG="$HA_LOGTAG[$PPID]"
+ fi
+ fi
+}
+
+ha_log() {
+ loglevel=$1
+ shift
+ prn_level=`level_pres $loglevel`
+ msg="$prn_level: $@"
+
+ if [ "x$HA_debug" = "x0" -a "x$loglevel" = xdebug ] ; then
+ return 0
+ fi
+
+ set_logtag
+
+ # if we're connected to a tty, then output to stderr
+ if tty >/dev/null; then
+ if [ "$HA_LOGTAG" ]; then
+ echo "$HA_LOGTAG: $msg"
+ else
+ echo "$msg"
+ fi >&2
+ return 0
+ fi
+
+ [ "x$HA_LOGD" = "xyes" ] &&
+ cat<<EOF | ha_logger -t "$HA_LOGTAG" && return 0
+$msg
+EOF
+
+ if [ -n "$HA_LOGFACILITY" -a "$HA_LOGFACILITY" != none ]; then
+ logger -t "$HA_LOGTAG" -p $HA_LOGFACILITY.$loglevel "$msg"
+ fi
+ dest=${HA_LOGFILE:-$HA_DEBUGLOG}
+ if [ -n "$dest" ]; then
+ msg="$prn_level: `hadate` $@"
+ echo "$HA_LOGTAG: $msg" >> $dest
+ fi
+}
+
+if [ $# -lt 2 ]; then
+ ha_log err "$PROG: not enough arguments [$#]"
+ exit 1
+fi
+
+loglevel="$1"
+shift 1
+msg="$*"
+
+ha_log "$loglevel" "$msg"
diff --git a/lib/stonith/main.c b/lib/stonith/main.c
new file mode 100644
index 0000000..44e099f
--- /dev/null
+++ b/lib/stonith/main.c
@@ -0,0 +1,727 @@
+/*
+ * Stonith: simple test program for exercising the Stonith API code
+ *
+ * Copyright (C) 2000 Alan Robertson <alanr@unix.sh>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <lha_internal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+#include <stonith/stonith.h>
+#include <pils/plugin.h>
+#include <clplumbing/cl_log.h>
+#include <glib.h>
+#include <libxml/entities.h>
+
+#define OPTIONS "c:F:p:t:T:EsnSlLmvhVd"
+#define EQUAL '='
+
+extern char * optarg;
+extern int optind, opterr, optopt;
+
+static int debug = 0;
+
+#define LOG_TERMINAL 0
+#define LOG_CLLOG 1
+static int log_destination = LOG_TERMINAL;
+
+static const char META_TEMPLATE[] =
+"<?xml version=\"1.0\"?>\n"
+"<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"
+"<resource-agent name=\"%s\">\n"
+"<version>1.0</version>\n"
+"<longdesc lang=\"en\">\n"
+"%s\n"
+"</longdesc>\n"
+"<shortdesc lang=\"en\">%s</shortdesc>\n"
+"%s\n"
+"<actions>\n"
+"<action name=\"start\" timeout=\"20\" />\n"
+"<action name=\"stop\" timeout=\"15\" />\n"
+"<action name=\"status\" timeout=\"20\" />\n"
+"<action name=\"monitor\" timeout=\"20\" interval=\"3600\" />\n"
+"<action name=\"meta-data\" timeout=\"15\" />\n"
+"</actions>\n"
+"<special tag=\"heartbeat\">\n"
+"<version>2.0</version>\n"
+"</special>\n"
+"</resource-agent>\n";
+
+void version(void);
+void usage(const char * cmd, int exit_status, const char * devtype);
+void confhelp(const char * cmd, FILE* stream, const char * devtype);
+void print_stonith_meta(Stonith * stonith_obj, const char *rsc_type);
+void print_types(void);
+void print_confignames(Stonith *s);
+
+void log_buf(int severity, char *buf);
+void log_msg(int severity, const char * fmt, ...)G_GNUC_PRINTF(2,3);
+void trans_log(int priority, const char * fmt, ...)G_GNUC_PRINTF(2,3);
+
+static int pil_loglevel_to_syslog_severity[] = {
+ /* Indices: <none>=0, PIL_FATAL=1, PIL_CRIT=2, PIL_WARN=3,
+ PIL_INFO=4, PIL_DEBUG=5
+ */
+ LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_WARNING, LOG_INFO, LOG_DEBUG
+ };
+
+/*
+ * Note that we don't use the cl_log logging code because the STONITH
+ * command is intended to be shipped without the clplumbing libraries.
+ *
+ * :-(
+ *
+ * The stonith command has so far always been shipped along with
+ * the clplumbing library, so we'll use cl_log
+ * If that ever changes, we'll use something else
+ */
+
+void
+version()
+{
+ printf("stonith: %s (%s)\n", GLUE_VERSION, GLUE_BUILD_VERSION);
+ exit(0);
+}
+
+void
+usage(const char * cmd, int exit_status, const char * devtype)
+{
+ FILE *stream;
+
+ stream = exit_status ? stderr : stdout;
+
+ /* non-NULL devtype indicates help for specific device, so no usage */
+ if (devtype == NULL) {
+ fprintf(stream, "usage:\n");
+ fprintf(stream, "\t %s [-svh] "
+ "-L\n"
+ , cmd);
+
+ fprintf(stream, "\t %s [-svh] "
+ "-t stonith-device-type "
+ "-n\n"
+ , cmd);
+
+ fprintf(stream, "\t %s [-svh] "
+ "-t stonith-device-type "
+ "-m\n"
+ , cmd);
+
+ fprintf(stream, "\t %s [-svh] "
+ "-t stonith-device-type "
+ "{-p stonith-device-parameters | "
+ "-F stonith-device-parameters-file | "
+ "-E | "
+ "name=value...} "
+ "[-c count] "
+ "-lS\n"
+ , cmd);
+
+ fprintf(stream, "\t %s [-svh] "
+ "-t stonith-device-type "
+ "{-p stonith-device-parameters | "
+ "-F stonith-device-parameters-file | "
+ "-E | "
+ "name=value...} "
+ "[-c count] "
+ "-T {reset|on|off} nodename\n"
+ , cmd);
+
+ fprintf(stream, "\nwhere:\n");
+ fprintf(stream, "\t-L\tlist supported stonith device types\n");
+ fprintf(stream, "\t-l\tlist hosts controlled by this stonith device\n");
+ fprintf(stream, "\t-S\treport stonith device status\n");
+ fprintf(stream, "\t-s\tsilent\n");
+ fprintf(stream, "\t-v\tverbose\n");
+ fprintf(stream, "\t-n\toutput the config names of stonith-device-parameters\n");
+ fprintf(stream, "\t-m\tdisplay meta-data of the stonith device type\n");
+ fprintf(stream, "\t-h\tdisplay detailed help message with stonith device description(s)\n");
+ }
+
+ if (exit_status == 0) {
+ confhelp(cmd, stream, devtype);
+ }
+
+ exit(exit_status);
+}
+
+/* Thanks to Lorn Kay <lorn_kay@hotmail.com> for the confhelp code */
+void
+confhelp(const char * cmd, FILE* stream, const char * devtype)
+{
+ char ** typelist;
+ char ** this;
+ Stonith * s;
+ int devfound = 0;
+
+
+ /* non-NULL devtype indicates help for specific device, so no header */
+ if (devtype == NULL) {
+ fprintf(stream
+ , "\nSTONITH -t device types and"
+ " associated configuration details:\n");
+ }
+
+ typelist = stonith_types();
+
+ if (typelist == NULL) {
+ fprintf(stderr,
+ "Failed to retrieve list of STONITH modules!\n");
+ return;
+ }
+ for(this=typelist; *this && !devfound; ++this) {
+ const char * SwitchType = *this;
+ const char * cres;
+ const char * const * pnames;
+
+
+ if ((s = stonith_new(SwitchType)) == NULL) {
+ fprintf(stderr, "Invalid STONITH type %s(!)\n"
+ , SwitchType);
+ continue;
+ }
+
+ if (devtype) {
+ if (strcmp(devtype, SwitchType)) {
+ continue;
+ } else {
+ devfound = 1;
+ }
+ }
+
+ fprintf(stream, "\n\nSTONITH Device: %s - ", SwitchType);
+
+ if ((cres = stonith_get_info(s, ST_DEVICEDESCR)) != NULL){
+ fprintf(stream, "%s\n"
+ , cres);
+ }
+
+ if ((cres = stonith_get_info(s, ST_DEVICEURL)) != NULL){
+ fprintf(stream
+ , "For more information see %s\n"
+ , cres);
+ }
+ if (NULL == (pnames = stonith_get_confignames(s))) {
+ continue;
+ }
+ fprintf(stream
+ , "List of valid parameter names for %s STONITH device:\n"
+ , SwitchType);
+ for (;*pnames; ++pnames) {
+ fprintf(stream
+ , "\t%s\n", *pnames);
+ }
+
+#ifdef ST_CONFI_INFO_SYNTAX
+ fprintf(stream, "\nConfig info [-p] syntax for %s:\n\t%s\n"
+ , SwitchType, stonith_get_info(s, ST_CONF_INFO_SYNTAX));
+#else
+ fprintf(stream, "For Config info [-p] syntax"
+ ", give each of the above parameters in order as"
+ "\nthe -p value.\n"
+ "Arguments are separated by white space.");
+#endif
+#ifdef ST_CONFI_FILE_SYNTAX
+ fprintf(stream, "\nConfig file [-F] syntax for %s:\n\t%s\n"
+ , SwitchType, stonith->get_info(s, ST_CONF_FILE_SYNTAX));
+#else
+ fprintf(stream
+ , "\nConfig file [-F] syntax is the same as -p"
+ ", except # at the start of a line"
+ "\ndenotes a comment\n");
+#endif
+
+ stonith_delete(s); s = NULL;
+ }
+ /* Note that the type list can't/shouldn't be freed */
+ if (devtype && !devfound) {
+ fprintf(stderr, "Invalid device type: '%s'\n", devtype);
+ }
+
+}
+
+void
+print_stonith_meta(Stonith * stonith_obj, const char *rsc_type)
+{
+ const char * meta_param = NULL;
+ const char * meta_longdesc = NULL;
+ const char * meta_shortdesc = NULL;
+ char *xml_meta_longdesc = NULL;
+ char *xml_meta_shortdesc = NULL;
+ static const char * no_parameter_info = "<!-- no value -->";
+
+ meta_longdesc = stonith_get_info(stonith_obj, ST_DEVICEDESCR);
+ if (meta_longdesc == NULL) {
+ fprintf(stderr, "stonithRA plugin: no long description");
+ meta_longdesc = no_parameter_info;
+ }
+ xml_meta_longdesc = (char *)xmlEncodeEntitiesReentrant(NULL, (const unsigned char *)meta_longdesc);
+
+ meta_shortdesc = stonith_get_info(stonith_obj, ST_DEVICEID);
+ if (meta_shortdesc == NULL) {
+ fprintf(stderr, "stonithRA plugin: no short description");
+ meta_shortdesc = no_parameter_info;
+ }
+ xml_meta_shortdesc = (char *)xmlEncodeEntitiesReentrant(NULL, (const unsigned char *)meta_shortdesc);
+
+ meta_param = stonith_get_info(stonith_obj, ST_CONF_XML);
+ if (meta_param == NULL) {
+ fprintf(stderr, "stonithRA plugin: no list of parameters");
+ meta_param = no_parameter_info;
+ }
+
+ printf(META_TEMPLATE,
+ rsc_type, xml_meta_longdesc, xml_meta_shortdesc, meta_param);
+
+ xmlFree(xml_meta_longdesc);
+ xmlFree(xml_meta_shortdesc);
+}
+
+#define MAXNVARG 50
+
+void
+print_types()
+{
+ char ** typelist;
+
+ typelist = stonith_types();
+ if (typelist == NULL) {
+ log_msg(LOG_ERR, "Could not list Stonith types.");
+ }else{
+ char ** this;
+
+ for(this=typelist; *this; ++this) {
+ printf("%s\n", *this);
+ }
+ }
+}
+
+void
+print_confignames(Stonith *s)
+{
+ const char * const * names;
+ int i;
+
+ names = stonith_get_confignames(s);
+
+ if (names != NULL) {
+ for (i=0; names[i]; ++i) {
+ printf("%s ", names[i]);
+ }
+ }
+ printf("\n");
+}
+
+void
+log_buf(int severity, char *buf)
+{
+ if (severity == LOG_DEBUG && !debug)
+ return;
+ if (log_destination == LOG_TERMINAL) {
+ fprintf(stderr, "%s: %s\n", prio2str(severity),buf);
+ } else {
+ cl_log(severity, "%s", buf);
+ }
+}
+
+void
+log_msg(int severity, const char * fmt, ...)
+{
+ va_list ap;
+ char buf[MAXLINE];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf)-1, fmt, ap);
+ va_end(ap);
+ log_buf(severity, buf);
+}
+
+void
+trans_log(int priority, const char * fmt, ...)
+{
+ int severity;
+ va_list ap;
+ char buf[MAXLINE];
+
+ severity = pil_loglevel_to_syslog_severity[ priority % sizeof
+ (pil_loglevel_to_syslog_severity) ];
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf)-1, fmt, ap);
+ va_end(ap);
+ log_buf(severity, buf);
+}
+
+int
+main(int argc, char** argv)
+{
+ char * cmdname;
+ int rc;
+ Stonith * s;
+ const char * SwitchType = NULL;
+ const char * optfile = NULL;
+ const char * parameters = NULL;
+ int reset_type = ST_GENERIC_RESET;
+ int verbose = 0;
+ int status = 0;
+ int silent = 0;
+ int listhosts = 0;
+ int listtypes = 0;
+ int listparanames = 0;
+ int params_from_env = 0;
+
+ int c;
+ int errors = 0;
+ int argcount;
+ StonithNVpair nvargs[MAXNVARG];
+ int nvcount=0;
+ int j;
+ int count = 1;
+ int help = 0;
+ int metadata = 0;
+
+ /* The bladehpi stonith plugin makes use of openhpi which is
+ * threaded. The mix of memory allocation without thread
+ * initialization followed by g_thread_init followed by
+ * deallocating that memory results in segfault. Hence the
+ * following G_SLICE setting; see
+ * http://library.gnome.org/devel/glib/stable/glib-Memory-Slices.html#g-slice-alloc
+ */
+
+ setenv("G_SLICE", "always-malloc", 1);
+
+ if ((cmdname = strrchr(argv[0], '/')) == NULL) {
+ cmdname = argv[0];
+ }else{
+ ++cmdname;
+ }
+
+
+ while ((c = getopt(argc, argv, OPTIONS)) != -1) {
+ switch(c) {
+
+ case 'c': count = atoi(optarg);
+ if (count < 1) {
+ fprintf(stderr
+ , "bad count [%s]\n"
+ , optarg);
+ usage(cmdname, 1, NULL);
+ }
+ break;
+
+ case 'd': debug++;
+ break;
+
+ case 'F': optfile = optarg;
+ break;
+
+ case 'E': params_from_env = 1;
+ break;
+
+ case 'h': help++;
+ break;
+
+ case 'm': metadata++;
+ break;
+
+ case 'l': ++listhosts;
+ break;
+
+ case 'L': ++listtypes;
+ break;
+
+ case 'p': parameters = optarg;
+ break;
+
+ case 's': ++silent;
+ break;
+
+ case 'S': ++status;
+ break;
+
+ case 't': SwitchType = optarg;
+ break;
+
+ case 'T': if (strcmp(optarg, "on")== 0) {
+ reset_type = ST_POWERON;
+ }else if (strcmp(optarg, "off")== 0) {
+ reset_type = ST_POWEROFF;
+ }else if (strcmp(optarg, "reset")== 0) {
+ reset_type = ST_GENERIC_RESET;
+ }else{
+ fprintf(stderr
+ , "bad reset type [%s]\n"
+ , optarg);
+ usage(cmdname, 1, NULL);
+ }
+ break;
+
+ case 'n': ++listparanames;
+ break;
+
+ case 'v': ++verbose;
+ break;
+
+ case 'V': version();
+ break;
+
+ default: ++errors;
+ break;
+ }
+ }
+
+ /* if we're invoked by stonithd, log through cl_log */
+ if (!isatty(fileno(stdin))) {
+ log_destination = LOG_CLLOG;
+ cl_log_set_entity("stonith");
+ cl_log_enable_stderr(debug?TRUE:FALSE);
+ cl_log_set_facility(HA_LOG_FACILITY);
+
+ /* Use logd if it's enabled by heartbeat */
+ cl_inherit_logging_environment(0);
+ }
+
+ if (help && !errors) {
+ usage(cmdname, 0, SwitchType);
+ }
+ if (debug) {
+ PILpisysSetDebugLevel(debug);
+ setenv("HA_debug","2",0);
+ }
+ if ((optfile && parameters) || (optfile && params_from_env)
+ || (params_from_env && parameters)) {
+ fprintf(stderr
+ , "Please use just one of -F, -p, and -E options\n");
+ usage(cmdname, 1, NULL);
+ }
+
+ /*
+ * Process name=value arguments on command line...
+ */
+ for (;optind < argc; ++optind) {
+ char * eqpos;
+ if ((eqpos=strchr(argv[optind], EQUAL)) == NULL) {
+ break;
+ }
+ if (parameters || optfile || params_from_env) {
+ fprintf(stderr
+ , "Cannot mix name=value and -p, -F, or -E "
+ "style arguments\n");
+ usage(cmdname, 1, NULL);
+ }
+ if (nvcount >= MAXNVARG) {
+ fprintf(stderr
+ , "Too many name=value style arguments\n");
+ exit(1);
+ }
+ nvargs[nvcount].s_name = argv[optind];
+ *eqpos = EOS;
+ nvargs[nvcount].s_value = eqpos+1;
+ nvcount++;
+ }
+ nvargs[nvcount].s_name = NULL;
+ nvargs[nvcount].s_value = NULL;
+
+ argcount = argc - optind;
+
+ if (!(argcount == 1 || (argcount < 1
+ && (status||listhosts||listtypes||listparanames||metadata)))) {
+ ++errors;
+ }
+
+ if (errors) {
+ usage(cmdname, 1, NULL);
+ }
+
+ if (listtypes) {
+ print_types();
+ exit(0);
+ }
+
+ if (SwitchType == NULL) {
+ log_msg(LOG_ERR,"Must specify device type (-t option)");
+ usage(cmdname, 1, NULL);
+ }
+ s = stonith_new(SwitchType);
+ if (s == NULL) {
+ log_msg(LOG_ERR,"Invalid device type: '%s'", SwitchType);
+ exit(S_OOPS);
+ }
+ if (debug) {
+ stonith_set_debug(s, debug);
+ }
+ stonith_set_log(s, (PILLogFun)trans_log);
+
+ if (!listparanames && !metadata && optfile == NULL &&
+ parameters == NULL && !params_from_env && nvcount == 0) {
+ const char * const * names;
+ int needs_parms = 1;
+
+ if (s != NULL && (names = stonith_get_confignames(s)) != NULL && names[0] == NULL) {
+ needs_parms = 0;
+ }
+
+ if (needs_parms) {
+ fprintf(stderr
+ , "Must specify either -p option, -F option, -E option, or "
+ "name=value style arguments\n");
+ if (s != NULL) {
+ stonith_delete(s);
+ }
+ usage(cmdname, 1, NULL);
+ }
+ }
+
+ if (listparanames) {
+ print_confignames(s);
+ stonith_delete(s);
+ s=NULL;
+ exit(0);
+ }
+
+ if (metadata) {
+ print_stonith_meta(s,SwitchType);
+ stonith_delete(s);
+ s=NULL;
+ exit(0);
+ }
+
+ /* Old STONITH version 1 stuff... */
+ if (optfile) {
+ /* Configure the Stonith object from a file */
+ if ((rc=stonith_set_config_file(s, optfile)) != S_OK) {
+ log_msg(LOG_ERR
+ , "Invalid config file for %s device."
+ , SwitchType);
+#if 0
+ log_msg(LOG_INFO, "Config file syntax: %s"
+ , s->s_ops->getinfo(s, ST_CONF_FILE_SYNTAX));
+#endif
+ stonith_delete(s); s=NULL;
+ exit(S_BADCONFIG);
+ }
+ }else if (params_from_env) {
+ /* Configure Stonith object from the environment */
+ StonithNVpair * pairs;
+ if ((pairs = stonith_env_to_NVpair(s)) == NULL) {
+ fprintf(stderr
+ , "Invalid config info for %s device.\n"
+ , SwitchType);
+ stonith_delete(s); s=NULL;
+ exit(1);
+ }
+ if ((rc = stonith_set_config(s, pairs)) != S_OK) {
+ fprintf(stderr
+ , "Invalid config info for %s device\n"
+ , SwitchType);
+ }
+ }else if (parameters) {
+ /* Configure Stonith object from the -p argument */
+ StonithNVpair * pairs;
+ if ((pairs = stonith1_compat_string_to_NVpair
+ ( s, parameters)) == NULL) {
+ fprintf(stderr
+ , "Invalid STONITH -p parameter [%s]\n"
+ , parameters);
+ stonith_delete(s); s=NULL;
+ exit(1);
+ }
+ if ((rc = stonith_set_config(s, pairs)) != S_OK) {
+ fprintf(stderr
+ , "Invalid config info for %s device\n"
+ , SwitchType);
+ }
+ }else{
+ /*
+ * Configure STONITH device using cmdline arguments...
+ */
+ if ((rc = stonith_set_config(s, nvargs)) != S_OK) {
+ const char * const * names;
+ int j;
+ fprintf(stderr
+ , "Invalid config info for %s device\n"
+ , SwitchType);
+
+ names = stonith_get_confignames(s);
+
+ if (names != NULL) {
+ fprintf(stderr
+ , "Valid config names are:\n");
+
+ for (j=0; names[j]; ++j) {
+ fprintf(stderr
+ , "\t%s\n", names[j]);
+ }
+ }
+ stonith_delete(s); s=NULL;
+ exit(rc);
+ }
+ }
+
+
+ for (j=0; j < count; ++j) {
+ rc = S_OK;
+
+ if (status) {
+ rc = stonith_get_status(s);
+
+ if (!silent) {
+ if (rc == S_OK) {
+ log_msg((log_destination == LOG_TERMINAL) ?
+ LOG_INFO : LOG_DEBUG,
+ "%s device OK.", SwitchType);
+ }else{
+ /* Uh-Oh */
+ log_msg(LOG_ERR, "%s device not accessible."
+ , SwitchType);
+ }
+ }
+ }
+
+ if (listhosts) {
+ char ** hostlist;
+
+ hostlist = stonith_get_hostlist(s);
+ if (hostlist == NULL) {
+ log_msg(LOG_ERR, "Could not list hosts for %s."
+ , SwitchType);
+ rc = -1;
+ }else{
+ char ** this;
+
+ for(this=hostlist; *this; ++this) {
+ printf("%s\n", *this);
+ }
+ stonith_free_hostlist(hostlist);
+ }
+ }
+
+ if (optind < argc) {
+ char *nodename;
+ nodename = g_strdup(argv[optind]);
+ strdown(nodename);
+ rc = stonith_req_reset(s, reset_type, nodename);
+ g_free(nodename);
+ }
+ }
+ stonith_delete(s); s = NULL;
+ return(rc);
+}
diff --git a/lib/stonith/meatclient.c b/lib/stonith/meatclient.c
new file mode 100644
index 0000000..e95dc0e
--- /dev/null
+++ b/lib/stonith/meatclient.c
@@ -0,0 +1,152 @@
+/*
+ * Stonith client for Human Operator Stonith device
+ *
+ * Copyright (c) 2001 Gregor Binder <gbinder@sysfive.com>
+ *
+ * This program is a rewrite of the "do_meatware" program by
+ * David C. Teigland <teigland@sistina.com> originally appeared
+ * in the GFS stomith meatware agent.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <lha_internal.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stonith/stonith.h>
+#include <glib.h>
+
+#define OPTIONS "c:w"
+
+void usage(const char * cmd);
+
+void
+usage(const char * cmd)
+{
+ fprintf(stderr, "usage: %s -c node [-w]\n", cmd);
+ exit(S_INVAL);
+}
+
+extern char * optarg;
+extern int optind, opterr, optopt;
+int
+main(int argc, char** argv)
+{
+ char * cmdname;
+ const char * meatpipe_pr = HA_VARRUNDIR "/meatware"; /* if you intend to
+ change this, modify
+ meatware.c as well */
+ char * opthost = NULL;
+ int clearhost = 0;
+
+ int c, argcount, waitmode = 0;
+ int errors = 0;
+
+ if ((cmdname = strrchr(argv[0], '/')) == NULL) {
+ cmdname = argv[0];
+ }else{
+ ++cmdname;
+ }
+
+ while ((c = getopt(argc, argv, OPTIONS)) != -1) {
+ switch(c) {
+ case 'c': opthost = optarg;
+ ++clearhost;
+ break;
+ case 'w': ++waitmode;
+ break;
+ default: ++errors;
+ break;
+ }
+ }
+ argcount = argc - optind;
+ if (!(argcount == 0) || !opthost) {
+ errors++;
+ }
+
+ if (errors) {
+ usage(cmdname);
+ }
+
+ strdown(opthost);
+
+ if (clearhost) {
+
+ int rc, fd;
+ char resp[3];
+
+ char line[256];
+ char meatpipe[256];
+
+ gboolean waited=FALSE;
+
+ snprintf(meatpipe, 256, "%s.%s", meatpipe_pr, opthost);
+
+ while(1) {
+ fd = open(meatpipe, O_WRONLY | O_NONBLOCK);
+ if (fd >= 0)
+ break;
+ if (!waitmode || (errno != ENOENT && errno != ENXIO)) {
+ if (waited) printf("\n");
+ snprintf(line, sizeof(line)
+ , "Meatware_IPC failed: %s", meatpipe);
+ perror(line);
+ exit(S_BADHOST);
+ }
+ printf("."); fflush(stdout); waited=TRUE;
+ sleep(1);
+ }
+ if (waited) printf("\n");
+
+ printf("\nWARNING!\n\n"
+ "If node \"%s\" has not been manually power-cycled or "
+ "disconnected from all shared resources and networks, "
+ "data on shared disks may become corrupted and "
+ "migrated services might not work as expected.\n"
+ "Please verify that the name or address above "
+ "corresponds to the node you just rebooted.\n\n"
+ "PROCEED? [yN] ", opthost);
+
+ rc = scanf("%s", resp);
+
+ if (rc == 0 || rc == EOF || tolower(resp[0] != 'y')) {
+ printf("Meatware_client: operation canceled.\n");
+ exit(S_INVAL);
+ }
+
+ sprintf(line, "meatware reply %s", opthost);
+
+ rc = write(fd, line, 256);
+
+ if (rc < 0) {
+ sprintf(line, "Meatware_IPC failed: %s", meatpipe);
+ perror(line);
+ exit(S_OOPS);
+ }
+
+ printf("Meatware_client: reset confirmed.\n");
+ }
+
+ exit(S_OK);
+}
diff --git a/lib/stonith/st_ttylock.c b/lib/stonith/st_ttylock.c
new file mode 100644
index 0000000..adc918d
--- /dev/null
+++ b/lib/stonith/st_ttylock.c
@@ -0,0 +1,225 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <lha_internal.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <clplumbing/cl_signal.h>
+#include <stonith/st_ttylock.h>
+
+/*
+ * The following information is from the Filesystem Hierarchy Standard
+ * version 2.1 dated 12 April, 2000.
+ *
+ * 5.6 /var/lock : Lock files
+ * Lock files should be stored within the /var/lock directory structure.
+ * Device lock files, such as the serial device lock files that were originally
+ * found in either /usr/spool/locks or /usr/spool/uucp, must now be stored in
+ * /var/lock. The naming convention which must be used is LCK.. followed by
+ * the base name of the device file. For example, to lock /dev/cua0 the file
+ * LCK..cua0 would be created.
+ *
+ * The format used for device lock files must be the HDB UUCP lock file format.
+ * The HDB format is to store the process identifier (PID) as a ten byte
+ * ASCII decimal number, with a trailing newline. For example, if process 1230
+ * holds a lock file, it would contain the eleven characters: space, space,
+ * space, space, space, space, one, two, three, zero, and newline.
+ * Then, anything wishing to use /dev/cua0 can read the lock file and act
+ * accordingly (all locks in /var/lock should be world-readable).
+ *
+ *
+ * PERMISSIONS NOTE:
+ * Different linux distributions set the mode of the lock directory differently
+ * Any process which wants to create lock files must have write permissions
+ * on HA_VARLOCKDIR (probably /var/lock). For things like the heartbeat API
+ * code, this may mean allowing the uid of the processes that use this API
+ * to join group uucp, or making the binaries setgid to uucp.
+ */
+
+#define DEVDIR "/dev/"
+#define DEVLEN (sizeof(DEVDIR)-1)
+
+static void raw_device (const char *dev, char *dest_name, size_t size);
+static int DoLock(const char * prefix, const char *lockname);
+static int DoUnlock(const char * prefix, const char *lockname);
+
+/* The code in this file originally written by Guenther Thomsen */
+/* Somewhat mangled by Alan Robertson */
+
+/*
+ * Lock a tty (using lock files, see linux `man 2 open` close to O_EXCL)
+ * serial_device has to be _the complete path_, i.e. including '/dev/' to the
+ * special file, which denotes the tty to lock -tho
+ * return 0 on success,
+ * -1 if device is locked (lockfile exists and isn't stale),
+ * -2 for temporarily failure, try again,
+ * other negative value, if something unexpected happend (failure anyway)
+ */
+
+static void
+raw_device (const char *serial_device, char *dest_name, size_t size)
+{
+ char* dp = dest_name;
+ const char* sp = serial_device+DEVLEN;
+ const char* dpend = dp + size - 1;
+
+ while (*sp != '\0' && dp < dpend) {
+ if (isalnum((unsigned int)*sp))
+ *dp++ = *sp;
+ sp++;
+ }
+ *dp = EOS;
+}
+
+int
+st_ttylock(const char *serial_device)
+{
+ char rawname[64];
+
+ if (serial_device == NULL) {
+ errno = EFAULT;
+ return -3;
+ }
+ raw_device (serial_device, rawname, sizeof(rawname));
+ return(DoLock("LCK..", rawname));
+}
+
+/*
+ * Unlock a tty (remove its lockfile)
+ * do we need to check, if its (still) ours? No, IMHO, if someone else
+ * locked our line, it's his fault -tho
+ * returns 0 on success
+ * <0 if some failure occured
+ */
+
+int
+st_ttyunlock(const char *serial_device)
+{
+ char rawname[64];
+
+ if (serial_device == NULL) {
+ errno = EFAULT;
+ return -3;
+ }
+
+ raw_device (serial_device, rawname, sizeof(rawname));
+ return(DoUnlock("LCK..", rawname));
+}
+
+/* This is what the FHS standard specifies for the size of our lock file */
+#define LOCKSTRLEN 11
+
+static int
+DoLock(const char * prefix, const char *lockname)
+{
+ char lf_name[256], tf_name[256], buf[LOCKSTRLEN+1];
+ int fd;
+ long pid, mypid;
+ int rc;
+ struct stat sbuf;
+
+ mypid = (unsigned long) getpid();
+
+ snprintf(lf_name, sizeof(lf_name), "%s/%s%s"
+ , HA_VARLOCKDIR, prefix, lockname);
+
+ snprintf(tf_name, sizeof(tf_name), "%s/tmp%lu-%s"
+ , HA_VARLOCKDIR, mypid, lockname);
+
+ if ((fd = open(lf_name, O_RDONLY)) >= 0) {
+ if (fstat(fd, &sbuf) >= 0 && sbuf.st_size < LOCKSTRLEN) {
+ sleep(1); /* if someone was about to create one,
+ * give'm a sec to do so
+ * Though if they follow our protocol,
+ * this won't happen. They should really
+ * put the pid in, then link, not the
+ * other way around.
+ */
+ }
+ if (read(fd, buf, sizeof(buf)) < 1) {
+ /* lockfile empty -> rm it and go on */;
+ } else {
+ if (sscanf(buf, "%lu", &pid) < 1) {
+ /* lockfile screwed up -> rm it and go on */
+ } else {
+ if (pid > 1 && ((long)getpid() != pid)
+ && ((CL_KILL((pid_t)pid, 0) >= 0)
+ || errno != ESRCH)) {
+ /* tty is locked by existing (not
+ * necessarily running) process
+ * -> give up */
+ close(fd);
+ return -1;
+ } else {
+ /* stale lockfile -> rm it and go on */
+ }
+ }
+ }
+ unlink(lf_name);
+ }
+ if ((fd = open(tf_name, O_CREAT | O_WRONLY | O_EXCL, 0644)) < 0) {
+ /* Hmmh, why did we fail? Anyway, nothing we can do about it */
+ return -3;
+ }
+
+ /* Slight overkill with the %*d format ;-) */
+ snprintf(buf, sizeof(buf), "%*lu\n", LOCKSTRLEN-1, mypid);
+
+ if (write(fd, buf, LOCKSTRLEN) != LOCKSTRLEN) {
+ /* Again, nothing we can do about this */
+ return -3;
+ }
+ close(fd);
+
+ switch (link(tf_name, lf_name)) {
+ case 0:
+ if (stat(tf_name, &sbuf) < 0) {
+ /* something weird happened */
+ rc = -3;
+ break;
+ }
+ if (sbuf.st_nlink < 2) {
+ /* somehow, it didn't get through - NFS trouble? */
+ rc = -2;
+ break;
+ }
+ rc = 0;
+ break;
+ case EEXIST:
+ rc = -1;
+ break;
+ default:
+ rc = -3;
+ }
+ unlink(tf_name);
+ return rc;
+}
+
+static int
+DoUnlock(const char * prefix, const char *lockname)
+{
+ char lf_name[256];
+
+ snprintf(lf_name, sizeof(lf_name), "%s/%s%s", HA_VARLOCKDIR
+ , prefix, lockname);
+ return unlink(lf_name);
+}
diff --git a/lib/stonith/stonith.c b/lib/stonith/stonith.c
new file mode 100644
index 0000000..4ced8c7
--- /dev/null
+++ b/lib/stonith/stonith.c
@@ -0,0 +1,636 @@
+/*
+ * Stonith API infrastructure.
+ *
+ * Copyright (c) 2000 Alan Robertson <alanr@unix.sh>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <lha_internal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <dlfcn.h>
+#include <dirent.h>
+#include <glib.h>
+#define ENABLE_PIL_DEFS_PRIVATE
+#include <pils/plugin.h>
+#include <pils/generic.h>
+#include <stonith/stonith.h>
+#include <stonith/stonith_plugin.h>
+
+
+#define MALLOC StonithPIsys->imports->alloc
+#ifdef MALLOCT
+# undef MALLOCT
+#endif
+#define MALLOCT(t) (t*)(MALLOC(sizeof(t)))
+#define REALLOC StonithPIsys->imports->mrealloc
+#define STRDUP StonithPIsys->imports->mstrdup
+#define FREE(p) {StonithPIsys->imports->mfree(p); (p) = NULL;}
+
+#define LOG(args...) PILCallLog(StonithPIsys->imports->log, args)
+
+#define EXTPINAME_S "external"
+#define RHCSPINAME_S "rhcs"
+
+PILPluginUniv* StonithPIsys = NULL;
+static GHashTable* Splugins = NULL;
+static int init_pluginsys(void);
+extern StonithImports stonithimports;
+
+static PILGenericIfMgmtRqst Reqs[] =
+{
+ {STONITH_TYPE_S, &Splugins, &stonithimports, NULL, NULL},
+ {NULL, NULL, NULL, NULL, NULL}
+};
+
+void PILpisysSetDebugLevel(int);
+/* Initialize the plugin system... */
+static int
+init_pluginsys(void) {
+
+ if (StonithPIsys) {
+ return TRUE;
+ }
+
+
+ /* PILpisysSetDebugLevel(10); */
+ StonithPIsys = NewPILPluginUniv(STONITH_MODULES);
+
+ if (StonithPIsys) {
+ int rc = PILLoadPlugin(StonithPIsys, PI_IFMANAGER, "generic", Reqs);
+ if (rc != PIL_OK) {
+ fprintf(stderr, "generic plugin load failed: %d\n", rc);
+ DelPILPluginUniv(StonithPIsys);
+ StonithPIsys = NULL;
+ }
+ /*PILSetDebugLevel(StonithPIsys, PI_IFMANAGER, "generic", 10);*/
+ }else{
+ fprintf(stderr, "pi univ creation failed\n");
+ }
+ return StonithPIsys != NULL;
+}
+
+/*
+ * Create a new Stonith object of the requested type.
+ */
+
+Stonith *
+stonith_new(const char * type)
+{
+ StonithPlugin * sp = NULL;
+ struct stonith_ops* ops = NULL;
+ char * key;
+ char * subplugin;
+ char * typecopy;
+
+
+ if (!init_pluginsys()) {
+ return NULL;
+ }
+
+ if ((typecopy = STRDUP(type)) == NULL) {
+ return NULL;
+ }
+
+ if (((subplugin = strchr(typecopy, '/')) != NULL) &&
+ (strncmp(EXTPINAME_S, typecopy, strlen(EXTPINAME_S)) == 0 ||
+ strncmp(RHCSPINAME_S, typecopy, strlen(RHCSPINAME_S)) == 0)) {
+ *subplugin++ = 0; /* make two strings */
+ }
+
+ /* Look and see if it's already loaded... */
+
+ if (g_hash_table_lookup_extended(Splugins, typecopy
+ , (gpointer)&key, (gpointer)&ops)) {
+ /* Yes! Increment reference count */
+ PILIncrIFRefCount(StonithPIsys, STONITH_TYPE_S, typecopy, 1);
+
+ }else{ /* No. Try and load it... */
+ if (PILLoadPlugin(StonithPIsys, STONITH_TYPE_S, typecopy, NULL)
+ != PIL_OK) {
+ FREE(typecopy);
+ return NULL;
+ }
+
+ /* Look up the plugin in the Splugins table */
+ if (!g_hash_table_lookup_extended(Splugins, typecopy
+ , (void*)&key, (void*)&ops)) {
+ /* OOPS! didn't find it(!?!)... */
+ PILIncrIFRefCount(StonithPIsys, STONITH_TYPE_S
+ , typecopy, -1);
+ FREE(typecopy);
+ return NULL;
+ }
+ }
+
+ if (ops != NULL) {
+ sp = ops->new((const char *)(subplugin));
+ if (sp != NULL) {
+ sp->s.stype = STRDUP(typecopy);
+ }
+ }
+
+ FREE(typecopy);
+ return sp ? (&sp->s) : NULL;
+}
+
+static int
+qsort_string_cmp(const void *a, const void *b)
+{
+ return(strcmp(*(const char * const *)a, *(const char * const *)b));
+}
+
+/*
+ * Return list of STONITH types valid in stonith_new()
+ */
+
+static char **
+get_plugin_list(const char *pltype)
+{
+ char ** typelist = NULL;
+ const char * const *extPI;
+ const char * const *p;
+ int numextPI, i;
+ Stonith * ext;
+
+ /* let the external plugin return a list */
+ if ((ext = stonith_new(pltype)) == NULL) {
+ LOG(PIL_CRIT, "Cannot create new external "
+ "plugin object");
+ return NULL;
+ }
+ if ((extPI = stonith_get_confignames(ext)) == NULL) {
+ /* don't complain if rhcs plugins are not installed */
+ if (strcmp(pltype, "rhcs"))
+ LOG(PIL_INFO, "Cannot get %s plugin subplugins", pltype);
+ stonith_delete(ext);
+ return NULL;
+ }
+
+ /* count the external plugins */
+ for (numextPI = 0, p = extPI; *p; p++, numextPI++);
+
+ typelist = (char **)
+ MALLOC((numextPI+1)*sizeof(char *));
+ if (typelist == NULL) {
+ LOG(PIL_CRIT, "Out of memory");
+ stonith_delete(ext);
+ return NULL;
+ }
+
+ memset(typelist, 0, (numextPI + 1)*sizeof(char *));
+
+ /* copy external plugins */
+ for (i = 0; i < numextPI; i++) {
+ int len = strlen(pltype) +
+ strlen(extPI[i]) + 2;
+ typelist[i] = MALLOC(len);
+ if (typelist[i] == NULL) {
+ LOG(PIL_CRIT, "Out of memory");
+ stonith_delete(ext);
+ goto err;
+ }
+ snprintf(typelist[i], len, "%s/%s"
+ , pltype, extPI[i]);
+ }
+
+ stonith_delete(ext);
+
+ /* sort the list of plugin names */
+ qsort(typelist, numextPI, sizeof(char *), qsort_string_cmp);
+
+ return typelist;
+err:
+ stonith_free_hostlist(typelist);
+ return NULL;
+}
+
+char **
+stonith_types(void)
+{
+ int i, j, cur=0, rl_size, sub_pl = 0;
+ static char ** rl = NULL;
+ char ** new_list, **sub_list = NULL;
+
+ if (!init_pluginsys()) {
+ return NULL;
+ }
+
+ new_list = PILListPlugins(StonithPIsys, STONITH_TYPE_S, NULL);
+ if (new_list == NULL) {
+ return NULL;
+ }
+ for (i=0; new_list[i]; ++i)
+ ; /* count */
+ rl_size = i+1;
+
+ rl = (char**)MALLOC(rl_size * sizeof(char *));
+ if (rl == NULL) {
+ LOG(PIL_CRIT, "Out of memory");
+ goto types_exit;
+ }
+
+ for (i=0; new_list[i]; ++i) {
+ /* look for 'external' and 'rhcs' plugins */
+ if (strcmp(new_list[i], EXTPINAME_S) == 0) {
+ sub_list = get_plugin_list(EXTPINAME_S);
+ sub_pl = 1;
+ } else if (strcmp(new_list[i], RHCSPINAME_S) == 0) {
+ sub_list = get_plugin_list(RHCSPINAME_S);
+ sub_pl = 1;
+ }
+ if (sub_pl) {
+ if (sub_list) {
+ for (j=0; sub_list[j]; ++j)
+ ; /* count */
+ rl_size += j;
+ rl = (char**)REALLOC(rl, rl_size*sizeof(char *));
+ for (j=0; sub_list[j]; ++j) {
+ rl[cur++] = sub_list[j];
+ }
+ FREE(sub_list);
+ sub_list = NULL;
+ }
+ sub_pl = 0;
+ } else {
+ rl[cur] = STRDUP(new_list[i]);
+ if (rl[cur] == NULL) {
+ LOG(PIL_CRIT, "Out of memory");
+ goto types_exit_mem;
+ }
+ cur++;
+ }
+ }
+
+ rl[cur] = NULL;
+ goto types_exit;
+
+types_exit_mem:
+ stonith_free_hostlist(rl);
+ rl = NULL;
+types_exit:
+ PILFreePluginList(new_list);
+ return rl;
+}
+
+/* Destroy the STONITH object... */
+
+void
+stonith_delete(Stonith *s)
+{
+ StonithPlugin* sp = (StonithPlugin*)s;
+
+ if (sp && sp->s_ops) {
+ char * st = sp->s.stype;
+ sp->s_ops->destroy(sp);
+ PILIncrIFRefCount(StonithPIsys, STONITH_TYPE_S, st, -1);
+ /* destroy should not free it */
+ FREE(st);
+ }
+}
+
+const char * const *
+stonith_get_confignames(Stonith* s)
+{
+ StonithPlugin* sp = (StonithPlugin*)s;
+
+ if (sp && sp->s_ops) {
+ return sp->s_ops->get_confignames(sp);
+ }
+ return NULL;
+}
+
+const char*
+stonith_get_info(Stonith* s, int infotype)
+{
+ StonithPlugin* sp = (StonithPlugin*)s;
+
+ if (sp && sp->s_ops) {
+ return sp->s_ops->get_info(sp, infotype);
+ }
+ return NULL;
+
+}
+
+void
+stonith_set_debug (Stonith* s, int debuglevel)
+{
+ StonithPlugin* sp = (StonithPlugin*)s;
+ if (StonithPIsys == NULL) {
+ return;
+ }
+ PILSetDebugLevel(StonithPIsys, STONITH_TYPE_S, sp->s.stype, debuglevel);
+}
+
+void
+stonith_set_log(Stonith* s, PILLogFun logfun)
+{
+ if (StonithPIsys == NULL) {
+ return;
+ }
+ PilPluginUnivSetLog(StonithPIsys, logfun);
+}
+
+int
+stonith_set_config(Stonith* s, StonithNVpair* list)
+{
+ StonithPlugin* sp = (StonithPlugin*)s;
+
+ if (sp && sp->s_ops) {
+ int rc = sp->s_ops->set_config(sp, list);
+ if (rc == S_OK) {
+ sp->isconfigured = TRUE;
+ }
+ return rc;
+ }
+ return S_INVAL;
+}
+
+/*
+ * FIXME: We really ought to support files with name=value type syntax
+ * on each line...
+ *
+ */
+int
+stonith_set_config_file(Stonith* s, const char * configname)
+{
+ FILE * cfgfile;
+
+ char line[1024];
+
+ if ((cfgfile = fopen(configname, "r")) == NULL) {
+ LOG(PIL_CRIT, "Cannot open %s", configname);
+ return(S_BADCONFIG);
+ }
+ while (fgets(line, sizeof(line), cfgfile) != NULL){
+ int len;
+
+ if (*line == '#' || *line == '\n' || *line == EOS) {
+ continue;
+ }
+
+ /*remove the new line in the end*/
+ len = strnlen(line, sizeof(line)-1);
+ if (line[len-1] == '\n'){
+ line[len-1] = '\0';
+ }else{
+ line[len] = '\0';
+ }
+
+ fclose(cfgfile);
+ return stonith_set_config_info(s, line);
+ }
+ fclose(cfgfile);
+ return S_BADCONFIG;
+}
+
+int
+stonith_set_config_info(Stonith* s, const char * info)
+{
+ StonithNVpair* cinfo;
+ int rc;
+ cinfo = stonith1_compat_string_to_NVpair(s, info);
+ if (cinfo == NULL) {
+ return S_BADCONFIG;
+ }
+ rc = stonith_set_config(s, cinfo);
+ free_NVpair(cinfo); cinfo = NULL;
+ return rc;
+}
+
+char**
+stonith_get_hostlist(Stonith* s)
+{
+ StonithPlugin* sp = (StonithPlugin*)s;
+ if (sp && sp->s_ops && sp->isconfigured) {
+ return sp->s_ops->get_hostlist(sp);
+ }
+ return NULL;
+}
+
+void
+stonith_free_hostlist(char** hostlist)
+{
+ char ** here;
+
+ for (here=hostlist; *here; ++here) {
+ FREE(*here);
+ }
+ FREE(hostlist);
+}
+
+int
+stonith_get_status(Stonith* s)
+{
+ StonithPlugin* sp = (StonithPlugin*)s;
+ if (sp && sp->s_ops && sp->isconfigured) {
+ return sp->s_ops->get_status(sp);
+ }
+ return S_INVAL;
+}
+
+void
+strdown(char *str)
+{
+ while( *str ) {
+ if( isupper(*str) )
+ *str = tolower(*str);
+ str++;
+ }
+}
+
+int
+stonith_req_reset(Stonith* s, int operation, const char* node)
+{
+ StonithPlugin* sp = (StonithPlugin*)s;
+ if (sp && sp->s_ops && sp->isconfigured) {
+ char* nodecopy = STRDUP(node);
+ int rc;
+ if (nodecopy == NULL) {
+ return S_OOPS;
+ }
+ strdown(nodecopy);
+
+ rc = sp->s_ops->req_reset(sp, operation, nodecopy);
+ FREE(nodecopy);
+ return rc;
+ }
+ return S_INVAL;
+}
+/* Stonith 1 compatibility: Convert a string to an NVpair set */
+StonithNVpair*
+stonith1_compat_string_to_NVpair(Stonith* s, const char * str)
+{
+ /* We make some assumptions that the order of parameters in the
+ * result from stonith_get_confignames() matches that which
+ * was required from a Stonith1 module.
+ * Everything after the last delimiter is passed along as part of
+ * the final argument - white space and all...
+ */
+ const char * const * config_names;
+ int n_names;
+ int j;
+ const char * delims = " \t\n\r\f";
+ StonithNVpair* ret;
+
+ if ((config_names = stonith_get_confignames(s)) == NULL) {
+ return NULL;
+ }
+ for (n_names=0; config_names[n_names] != NULL; ++n_names) {
+ /* Just count */;
+ }
+ ret = (StonithNVpair*) (MALLOC((n_names+1)*sizeof(StonithNVpair)));
+ if (ret == NULL) {
+ return NULL;
+ }
+ memset(ret, 0, (n_names+1)*sizeof(StonithNVpair));
+ for (j=0; j < n_names; ++j) {
+ size_t len;
+ if ((ret[j].s_name = STRDUP(config_names[j])) == NULL) {
+ goto freeandexit;
+ }
+ ret[j].s_value = NULL;
+ str += strspn(str, delims);
+ if (*str == EOS) {
+ goto freeandexit;
+ }
+ if (j == (n_names -1)) {
+ len = strlen(str);
+ }else{
+ len = strcspn(str, delims);
+ }
+ if ((ret[j].s_value = MALLOC((len+1)*sizeof(char))) == NULL) {
+ goto freeandexit;
+ }
+ memcpy(ret[j].s_value, str, len);
+ ret[j].s_value[len] = EOS;
+ str += len;
+ }
+ ret[j].s_name = NULL;
+ return ret;
+freeandexit:
+ free_NVpair(ret); ret = NULL;
+ return NULL;
+}
+
+StonithNVpair*
+stonith_env_to_NVpair(Stonith* s)
+{
+ /* Read the config names values from the environment */
+ const char * const * config_names;
+ int n_names;
+ int j;
+ StonithNVpair* ret;
+
+ if ((config_names = stonith_get_confignames(s)) == NULL) {
+ return NULL;
+ }
+ for (n_names=0; config_names[n_names] != NULL; ++n_names) {
+ /* Just count */;
+ }
+ ret = (StonithNVpair*) (MALLOC((n_names+1)*sizeof(StonithNVpair)));
+ if (ret == NULL) {
+ return NULL;
+ }
+ memset(ret, 0, (n_names+1)*sizeof(StonithNVpair));
+ for (j=0; j < n_names; ++j) {
+ char *env_value;
+ if ((ret[j].s_name = STRDUP(config_names[j])) == NULL) {
+ goto freeandexit;
+ }
+ env_value = getenv(config_names[j]);
+ if (env_value) {
+ if ((ret[j].s_value = STRDUP(env_value)) == NULL) {
+ goto freeandexit;
+ }
+ } else {
+ ret[j].s_value = NULL;
+ }
+ }
+ ret[j].s_name = NULL;
+ return ret;
+freeandexit:
+ free_NVpair(ret); ret = NULL;
+ return NULL;
+}
+
+static int NVcur = -1;
+static int NVmax = -1;
+static gboolean NVerr = FALSE;
+
+static void
+stonith_walk_ghash(gpointer key, gpointer value, gpointer user_data)
+{
+ StonithNVpair* u = user_data;
+
+ if (NVcur <= NVmax && !NVerr) {
+ u[NVcur].s_name = STRDUP(key);
+ u[NVcur].s_value = STRDUP(value);
+ if (u[NVcur].s_name == NULL || u[NVcur].s_value == NULL) {
+ /* Memory allocation error */
+ NVerr = TRUE;
+ return;
+ }
+ ++NVcur;
+ }else{
+ NVerr = TRUE;
+ }
+}
+
+
+StonithNVpair*
+stonith_ghash_to_NVpair(GHashTable* stringtable)
+{
+ int hsize = g_hash_table_size(stringtable);
+ StonithNVpair* ret;
+
+ if ((ret = (StonithNVpair*)MALLOC(sizeof(StonithNVpair)*(hsize+1))) == NULL) {
+ return NULL;
+ }
+ NVmax = hsize;
+ NVcur = 0;
+ ret[hsize].s_name = NULL;
+ ret[hsize].s_value = NULL;
+ g_hash_table_foreach(stringtable, stonith_walk_ghash, ret);
+ NVmax = NVcur = -1;
+ if (NVerr) {
+ free_NVpair(ret);
+ ret = NULL;
+ }
+ return ret;
+}
+
+void
+free_NVpair(StonithNVpair* nv)
+{
+ StonithNVpair* this;
+
+ if (nv == NULL) {
+ return;
+ }
+ for (this=nv; this->s_name; ++this) {
+ FREE(this->s_name);
+ if (this->s_value) {
+ FREE(this->s_value);
+ }
+ }
+ FREE(nv);
+}