summaryrefslogtreecommitdiffstats
path: root/lib/clplumbing
diff options
context:
space:
mode:
Diffstat (limited to 'lib/clplumbing')
-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
39 files changed, 21215 insertions, 0 deletions
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