summaryrefslogtreecommitdiffstats
path: root/lib/clplumbing/GSource.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/clplumbing/GSource.c1864
1 files changed, 1864 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;
+}