summaryrefslogtreecommitdiffstats
path: root/logd
diff options
context:
space:
mode:
Diffstat (limited to 'logd')
-rw-r--r--logd/Makefile.am56
-rw-r--r--logd/ha_logd.c1085
-rw-r--r--logd/ha_logger.138
-rw-r--r--logd/ha_logger.c142
-rw-r--r--logd/logd.cf66
-rwxr-xr-xlogd/logd.in101
-rw-r--r--logd/logd.service.in13
-rw-r--r--logd/logtest.c129
8 files changed, 1630 insertions, 0 deletions
diff --git a/logd/Makefile.am b/logd/Makefile.am
new file mode 100644
index 0000000..7ac75e2
--- /dev/null
+++ b/logd/Makefile.am
@@ -0,0 +1,56 @@
+#
+# hbclient library: Linux-HA heartbeat code
+#
+# Copyright (C) 2001 Michael Moerz
+# Copyright (C) 2004 International Business Machines
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \
+ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \
+ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl
+
+halibdir = $(libdir)/@HB_PKG@
+ha_sbindir = $(sbindir)
+
+LIBRT = @LIBRT@
+AM_CFLAGS = @CFLAGS@
+
+initddir = @INITDIR@
+
+## binary progs
+ha_sbin_PROGRAMS = ha_logger
+halib_PROGRAMS = ha_logd logtest
+
+ha_logd_SOURCES = ha_logd.c
+ha_logd_LDADD = $(top_builddir)/lib/clplumbing/libplumb.la \
+ $(top_builddir)/lib/clplumbing/libplumbgpl.la
+
+# $(top_builddir)/lib/apphb/libapphb.la
+
+ha_logger_SOURCES = ha_logger.c
+ha_logger_LDADD = $(top_builddir)/lib/clplumbing/libplumb.la
+
+logtest_SOURCES = logtest.c
+logtest_LDADD = $(top_builddir)/lib/clplumbing/libplumb.la
+
+if HAVE_SYSTEMD
+systemdsystemunit_DATA = \
+ logd.service
+else
+initd_SCRIPTS = logd
+endif
diff --git a/logd/ha_logd.c b/logd/ha_logd.c
new file mode 100644
index 0000000..c98b9d7
--- /dev/null
+++ b/logd/ha_logd.c
@@ -0,0 +1,1085 @@
+/*
+ * ha_logd.c logging daemon
+ *
+ * Copyright (C) 2004 Guochun Shi <gshi@ncsa.uiuc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#include <lha_internal.h>
+#include <glib.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/ipc.h>
+#include <clplumbing/GSource.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <clplumbing/loggingdaemon.h>
+#include <netinet/in.h>
+#include <clplumbing/lsb_exitcodes.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdarg.h>
+#include <clplumbing/Gmain_timeout.h>
+#include <clplumbing/coredumps.h>
+#include <clplumbing/setproctitle.h>
+#include <clplumbing/cl_signal.h>
+#include <clplumbing/cl_misc.h>
+#include <sys/wait.h>
+#include <clplumbing/cl_pidfile.h>
+#include <clplumbing/cl_syslog.h>
+
+/*two processes involved
+ 1. parent process which reads messages from all client channels
+ and writes them to the child process
+
+ 2. the child process which reads messages from the parent process through IPC
+ and writes them to syslog/disk
+
+ I call the parent process READ process, and the child process WRITE one,
+ for convenience.
+
+*/
+
+
+
+#define DEFAULT_CFG_FILE HA_SYSCONFDIR "/logd.cf"
+#define LOGD_PIDFILE HA_VARRUNDIR "/logd.pid"
+
+#define FD_STDIN 0
+#define FD_STDOUT 1
+
+#define FD_STDERR 2
+
+
+#define WRITE_PROC_CHAN 0
+#define READ_PROC_CHAN 1
+#define LOGD_QUEUE_LEN 128
+
+#define EOS '\0'
+#define nullchk(a) ((a) ? (a) : "<null>")
+
+static const int logd_keepalive_ms = 1000;
+static const int logd_warntime_ms = 5000;
+static const int logd_deadtime_ms = 10000;
+static gboolean verbose = FALSE;
+static pid_t write_process_pid;
+static IPC_Channel *chanspair[2];
+static gboolean stop_reading = FALSE;
+static gboolean needs_shutdown = FALSE;
+
+static struct {
+ char debugfile[MAXLINE];
+ char logfile[MAXLINE];
+ char entity[MAXENTITY];
+ char syslogprefix[MAXENTITY];
+ int log_facility;
+ mode_t logmode;
+ gboolean syslogfmtmsgs;
+} logd_config =
+ {
+ .debugfile = "",
+ .logfile = "",
+ .entity = "logd",
+ .syslogprefix = "",
+ .log_facility = HA_LOG_FACILITY,
+ .logmode = 0644,
+ .syslogfmtmsgs = FALSE
+ };
+
+static void logd_log(const char * fmt, ...) G_GNUC_PRINTF(1,2);
+static int set_debugfile(const char* option);
+static int set_logfile(const char* option);
+static int set_facility(const char * value);
+static int set_entity(const char * option);
+static int set_syslogprefix(const char * option);
+static int set_sendqlen(const char * option);
+static int set_recvqlen(const char * option);
+static int set_logmode(const char * option);
+static int set_syslogfmtmsgs(const char * option);
+
+
+static char* cmdname = NULL;
+
+
+static struct directive {
+ const char* name;
+ int (*add_func)(const char*);
+} Directives[] = {
+ {"debugfile", set_debugfile},
+ {"logfile", set_logfile},
+ {"logfacility", set_facility},
+ {"entity", set_entity},
+ {"syslogprefix",set_syslogprefix},
+ {"sendqlen", set_sendqlen},
+ {"recvqlen", set_recvqlen},
+ {"logmode", set_logmode},
+ {"syslogmsgfmt",set_syslogfmtmsgs}
+};
+
+static void
+logd_log( const char * fmt, ...)
+{
+ char buf[MAXLINE];
+ va_list ap;
+
+ buf[MAXLINE-1] = EOS;
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf)-1, fmt, ap);
+ va_end(ap);
+
+ fprintf(stderr, "%s", buf);
+
+ return;
+}
+
+static int
+set_debugfile(const char* option)
+{
+ if (!option){
+ logd_config.debugfile[0] = EOS;
+ return FALSE;
+ }
+
+ cl_log(LOG_INFO, "setting debug file to %s", option);
+ strncpy(logd_config.debugfile, option, MAXLINE);
+ return TRUE;
+}
+static int
+set_logfile(const char* option)
+{
+ if (!option){
+ logd_config.logfile[0] = EOS;
+ return FALSE;
+ }
+ cl_log(LOG_INFO, "setting log file to %s", option);
+ strncpy(logd_config.logfile, option, MAXLINE);
+ return TRUE;
+}
+
+/* set syslog facility config variable */
+static int
+set_facility(const char * value)
+{
+ int i;
+
+ i = cl_syslogfac_str2int(value);
+ if (i >= 0) {
+ cl_log(LOG_INFO, "setting log facility to %s", value);
+ logd_config.log_facility = i;
+ return(TRUE);
+ }
+ else {
+ return(FALSE);
+ }
+}
+
+static int
+set_entity(const char * option)
+{
+ if (!option){
+ logd_config.entity[0] = EOS;
+ return FALSE;
+ }
+ strncpy(logd_config.entity, option, MAXENTITY);
+ logd_config.entity[MAXENTITY-1] = '\0';
+ if (strlen(option) >= MAXENTITY)
+ cl_log(LOG_WARNING, "setting entity to %s (truncated from %s)",
+ logd_config.entity, option);
+ else
+ cl_log(LOG_INFO, "setting entity to %s", logd_config.entity);
+ return TRUE;
+
+}
+
+static int
+set_syslogprefix(const char * option)
+{
+ if (!option){
+ logd_config.syslogprefix[0] = EOS;
+ return FALSE;
+ }
+ strncpy(logd_config.syslogprefix, option, MAXENTITY);
+ logd_config.syslogprefix[MAXENTITY-1] = '\0';
+ if (strlen(option) >= MAXENTITY)
+ cl_log(LOG_WARNING,
+ "setting syslogprefix to %s (truncated from %s)",
+ logd_config.syslogprefix, option);
+ else
+ cl_log(LOG_INFO,
+ "setting syslogprefix to %s",
+ logd_config.syslogprefix);
+ return TRUE;
+
+}
+
+static int
+set_sendqlen(const char * option)
+{
+ int length;
+
+ if (!option){
+ cl_log(LOG_ERR, "NULL send queue length");
+ return FALSE;
+ }
+
+ length = atoi(option);
+ if (length < 0){
+ cl_log(LOG_ERR, "negative send queue length");
+ return FALSE;
+ }
+
+ cl_log(LOG_INFO, "setting send queue length to %d", length);
+ chanspair[READ_PROC_CHAN]->ops->set_send_qlen(chanspair[READ_PROC_CHAN],
+ length);
+
+ return TRUE;
+
+}
+
+static int
+set_recvqlen(const char * option)
+{
+ int length;
+
+ if (!option){
+ cl_log(LOG_ERR, "NULL recv queue length");
+ return FALSE;
+ }
+
+ length = atoi(option);
+ if (length < 0){
+ cl_log(LOG_ERR, "negative recv queue length");
+ return FALSE;
+ }
+
+ cl_log(LOG_INFO, "setting recv queue length to %d", length);
+ chanspair[WRITE_PROC_CHAN]->ops->set_recv_qlen(chanspair[WRITE_PROC_CHAN],
+ length);
+
+ return TRUE;
+
+}
+
+static int
+set_logmode(const char * option)
+{
+ unsigned long mode;
+ char * endptr;
+ if (!option){
+ cl_log(LOG_ERR, "NULL logmode parameter");
+ return FALSE;
+ }
+ mode = strtoul(option, &endptr, 8);
+ if (*endptr != EOS) {
+ cl_log(LOG_ERR, "Invalid log mode [%s]", option);
+ return FALSE;
+ }
+ if (*option != '0') {
+ /* Whine if mode doesn't start with '0' */
+ cl_log(LOG_WARNING, "Log mode [%s] assumed to be octal"
+ , option);
+ }
+ logd_config.logmode = (mode_t)mode;
+ return TRUE;
+}
+static int
+set_syslogfmtmsgs(const char * option)
+{
+ gboolean dosyslogfmt;
+
+ if (cl_str_to_boolean(option, &dosyslogfmt) == HA_OK) {
+ cl_log_enable_syslog_filefmt(dosyslogfmt);
+ }else{
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+typedef struct {
+ char app_name[MAXENTITY];
+ pid_t pid;
+ gid_t gid;
+ uid_t uid;
+
+ IPC_Channel* chan;
+ IPC_Channel* logchan;
+ GCHSource* g_src;
+}ha_logd_client_t;
+
+static GList* logd_client_list = NULL;
+
+static IPC_Message*
+getIPCmsg(IPC_Channel* ch)
+{
+
+ int rc;
+ IPC_Message* ipcmsg;
+
+ /* FIXME: Should we block here?? */
+ rc = ch->ops->waitin(ch);
+
+ switch(rc) {
+ default:
+ case IPC_FAIL:
+ cl_log(LOG_ERR, "getIPCmsg: waitin failure\n");
+ return NULL;
+
+ case IPC_BROKEN:
+ sleep(1);
+ return NULL;
+
+ case IPC_INTR:
+ return NULL;
+
+ case IPC_OK:
+ break;
+ }
+
+ ipcmsg = NULL;
+ rc = ch->ops->recv(ch, &ipcmsg);
+ if (rc != IPC_OK) {
+ return NULL;
+ }
+
+ return ipcmsg;
+
+}
+
+/* Flow control all clients off */
+static void
+logd_suspend_clients(IPC_Channel* notused1, gpointer notused2)
+{
+ GList * gl;
+
+ stop_reading = TRUE;
+ for (gl=g_list_first(logd_client_list); gl != NULL
+ ; gl = g_list_next(gl)) {
+ ha_logd_client_t* client = gl->data;
+ if (client && client->g_src) {
+ G_main_IPC_Channel_pause(client->g_src);
+ }else if (client) {
+ cl_log(LOG_ERR, "Could not suspend client [%s] pid %d"
+ , nullchk(client->app_name), client->pid);
+ }else{
+ cl_log(LOG_ERR, "%s: Could not suspend NULL client",
+ __FUNCTION__);
+ }
+ }
+}
+
+/* Resume input from clients - Flow control all clients back on */
+static void
+logd_resume_clients(IPC_Channel* notused1, gpointer notused2)
+{
+ GList * gl;
+
+ stop_reading = FALSE;
+ for (gl=g_list_first(logd_client_list); gl != NULL
+ ; gl = g_list_next(gl)) {
+ ha_logd_client_t* client = gl->data;
+ if (client && client->g_src) {
+ G_main_IPC_Channel_resume(client->g_src);
+ }else if (client) {
+ cl_log(LOG_ERR, "Could not resume client [%s] pid %d"
+ , nullchk(client->app_name), client->pid);
+ }else{
+ cl_log(LOG_ERR, "%s: Could not suspend NULL client",
+ __FUNCTION__);
+ }
+ }
+}
+
+static gboolean
+on_receive_cmd (IPC_Channel* ch, gpointer user_data)
+{
+ IPC_Message* ipcmsg;
+ ha_logd_client_t* client = (ha_logd_client_t*)user_data;
+ IPC_Channel* logchan= client->logchan;
+
+
+ if (!ch->ops->is_message_pending(ch)) {
+ goto getout;
+ }
+
+ ipcmsg = getIPCmsg(ch);
+ if (ipcmsg == NULL){
+ if (IPC_ISRCONN(ch)) {
+ cl_log(LOG_ERR, "%s: read error on connected channel [%s:%d]"
+ , __FUNCTION__, client->app_name, client->pid);
+ }
+ return FALSE;
+ }
+
+ if( ipcmsg->msg_body && ipcmsg->msg_len > 0 ){
+
+ if (client->app_name[0] == '\0'){
+ LogDaemonMsgHdr* logmsghdr;
+ logmsghdr = (LogDaemonMsgHdr*) ipcmsg->msg_body;
+ strncpy(client->app_name, logmsghdr->entity, MAXENTITY);
+ }
+
+ if (!IPC_ISWCONN(logchan)){
+ cl_log(LOG_ERR
+ , "%s: channel to write process disconnected"
+ , __FUNCTION__);
+ return FALSE;
+ }
+ if (logchan->ops->send(logchan, ipcmsg) != IPC_OK){
+ cl_log(LOG_ERR
+ , "%s: forwarding msg from [%s:%d] to"
+ " write process failed"
+ , __FUNCTION__
+ , client->app_name, client->pid);
+ cl_log(LOG_ERR, "queue too small? (max=%ld, current len =%ld)",
+ (long)logchan->send_queue->max_qlen,
+ (long)logchan->send_queue->current_qlen);
+ return TRUE;
+ }
+
+ }else {
+ cl_log(LOG_ERR, "on_receive_cmd:"
+ " invalid ipcmsg\n");
+ }
+
+ getout:
+ return TRUE;
+}
+
+static void
+on_remove_client (gpointer user_data)
+{
+
+ logd_client_list = g_list_remove(logd_client_list, user_data);
+ if (user_data){
+ free(user_data);
+ }
+ return;
+}
+
+
+
+/*
+ *GLoop Message Handlers
+ */
+static gboolean
+on_connect_cmd (IPC_Channel* ch, gpointer user_data)
+{
+ ha_logd_client_t* client = NULL;
+
+ /* check paremeters */
+ if (NULL == ch) {
+ cl_log(LOG_ERR, "on_connect_cmd: channel is null");
+ return TRUE;
+ }
+ /* create new client */
+ if (NULL == (client = malloc(sizeof(ha_logd_client_t)))) {
+ return FALSE;
+ }
+ memset(client, 0, sizeof(ha_logd_client_t));
+ client->pid = ch->farside_pid;
+ client->chan = ch;
+ client->logchan = (IPC_Channel*)user_data;
+ client->g_src = G_main_add_IPC_Channel(G_PRIORITY_DEFAULT,
+ ch, FALSE, on_receive_cmd,
+ (gpointer)client,
+ on_remove_client);
+ if (client->g_src == NULL){
+ cl_log(LOG_ERR, "add the client to main loop failed");
+ free(client);
+ return TRUE;
+ }
+ if (stop_reading){
+ G_main_IPC_Channel_pause(client->g_src);
+ }
+
+ logd_client_list = g_list_append(logd_client_list, client);
+
+
+ return TRUE;
+}
+
+
+
+static void
+logd_make_daemon(gboolean daemonize)
+{
+ long pid;
+
+ if (daemonize) {
+ if (daemon(0,0)) {
+ fprintf(stderr, "%s: could not start daemon\n"
+ , cmdname);
+ perror("fork");
+ exit(LSB_EXIT_GENERIC);
+ }
+ }
+
+ if (cl_lock_pidfile(LOGD_PIDFILE) < 0 ){
+ pid = cl_read_pidfile(LOGD_PIDFILE);
+ if (pid > 0)
+ fprintf(stderr, "%s: already running [pid %ld].\n",
+ cmdname, pid);
+ else
+ fprintf(stderr, "%s: problem creating pid file %s\n",
+ cmdname, LOGD_PIDFILE);
+ exit(LSB_EXIT_OK);
+ }
+
+ if (daemonize || !verbose){
+ cl_log_enable_stderr(FALSE);
+ }
+
+ if (daemonize){
+ mode_t mask;
+ /*
+ * Some sample umask calculations:
+ *
+ * logmode = 0644
+ *
+ * (~0644)&0777 = 0133
+ * (0133 & ~0111) = 0022
+ * => umask will be 022 (the expected result)
+ *
+ * logmode = 0600
+ * (~0600)&0777 = 0177
+ * (0177 & ~0111) = 0066
+ */
+ mask = (mode_t)(((~logd_config.logmode) & 0777) & (~0111));
+ umask(mask);
+ }
+}
+
+
+
+static void
+logd_stop(void)
+{
+
+ long running_logd_pid = cl_read_pidfile(LOGD_PIDFILE);
+ int err;
+
+ if (running_logd_pid < 0) {
+ fprintf(stderr, "ha_logd already stopped.\n");
+ cl_log(LOG_INFO, "ha_logd already stopped.");
+ exit(LSB_EXIT_OK);
+ }
+
+ cl_log(LOG_DEBUG, "Stopping ha_logd with pid %ld", running_logd_pid);
+ if (kill((pid_t)running_logd_pid, SIGTERM) >= 0) {
+ /* Wait for the running logd to die */
+ cl_log(LOG_INFO, "Waiting for pid=%ld to exit",
+ running_logd_pid);
+ alarm(0);
+ do {
+ sleep(1);
+ }while (kill((pid_t)running_logd_pid, 0) >= 0);
+ }
+ err = errno;
+
+ if(errno == ESRCH) {
+ cl_log(LOG_INFO, "Pid %ld exited", running_logd_pid);
+ exit(LSB_EXIT_OK);
+ } else {
+ cl_perror("Pid %ld not killed", running_logd_pid);
+ exit((err == EPERM || err == EACCES)
+ ? LSB_EXIT_EPERM
+ : LSB_EXIT_GENERIC);
+ }
+
+}
+
+
+static int
+get_dir_index(const char* directive)
+{
+ int j;
+ for(j=0; j < DIMOF(Directives); j++){
+ if (0 == strcasecmp(directive, Directives[j].name)){
+ return j;
+ }
+ }
+ return -1;
+}
+
+
+/* Adapted from parse_config in config.c */
+static gboolean
+parse_config(const char* cfgfile)
+{
+ FILE* f;
+ char buf[MAXLINE];
+ char* bp;
+ char* cp;
+ char directive[MAXLINE];
+ int dirlength;
+ int optionlength;
+ char option[MAXLINE];
+ int dir_index;
+
+ gboolean ret = TRUE;
+
+ if ((f = fopen(cfgfile, "r")) == NULL){
+ cl_log(LOG_WARNING, "Cannot open config file [%s]", cfgfile);
+ return(FALSE);
+ }
+
+ while(fgets(buf, MAXLINE, f) != NULL){
+ bp = buf;
+ /* Skip over white space*/
+ bp += strspn(bp, " \t\n\r\f");
+
+ /* comments */
+ if ((cp = strchr(bp, '#')) != NULL){
+ *cp = EOS;
+ }
+
+ if (*bp == EOS){
+ continue;
+ }
+
+ dirlength = strcspn(bp, " \t\n\f\r");
+ strncpy(directive, bp, dirlength);
+ directive[dirlength] = EOS;
+
+ if ((dir_index = get_dir_index(directive)) == -1){
+ fprintf(stderr, "Illegal directive [%s] in %s\n"
+ , directive, cfgfile);
+ ret = FALSE;
+ continue;
+ }
+
+ bp += dirlength;
+
+ /* skip delimiters */
+ bp += strspn(bp, " ,\t\n\f\r");
+
+ /* Set option */
+ optionlength = strcspn(bp, " ,\t\n\f\r");
+ strncpy(option, bp, optionlength);
+ option[optionlength] = EOS;
+ if (!(*Directives[dir_index].add_func)(option)) {
+ ret = FALSE;
+ }
+ }/*while*/
+ fclose(f);
+ return ret;
+}
+
+static gboolean
+logd_term_action(int sig, gpointer userdata)
+{
+ GList *log_iter = logd_client_list;
+ GMainLoop *mainloop = (GMainLoop*)userdata;
+ ha_logd_client_t *client = NULL;
+
+ cl_log(LOG_DEBUG, "logd_term_action: received SIGTERM");
+ if (mainloop == NULL){
+ cl_log(LOG_ERR, "logd_term_action: invalid arguments");
+ return FALSE;
+ }
+
+ stop_reading = TRUE;
+
+ while(log_iter != NULL) {
+ client = log_iter->data;
+ log_iter = log_iter->next;
+
+ cl_log(LOG_DEBUG, "logd_term_action:"
+ " waiting for %d messages to be read for process %s",
+ (int)client->logchan->send_queue->current_qlen,
+ client->app_name);
+
+ client->logchan->ops->waitout(client->logchan);
+ }
+
+ cl_log(LOG_DEBUG, "logd_term_action"
+ ": waiting for %d messages to be read by write process"
+ , (int)chanspair[WRITE_PROC_CHAN]->send_queue->current_qlen);
+ chanspair[WRITE_PROC_CHAN]->ops->waitout(chanspair[WRITE_PROC_CHAN]);
+
+ cl_log(LOG_DEBUG, "logd_term_action: sending SIGTERM to write process");
+ if (CL_KILL(write_process_pid, SIGTERM) >= 0){
+
+ pid_t pid;
+ pid = wait4(write_process_pid, NULL, 0, NULL);
+ if (pid < 0){
+ cl_log(LOG_ERR, "wait4 for write process failed");
+ }
+
+ }
+
+ g_main_quit(mainloop);
+
+ return TRUE;
+}
+
+/*
+ * Handle SIGHUP to re-open log files
+ */
+static gboolean
+logd_hup_action(int sig, gpointer userdata)
+{
+ cl_log_close_log_files();
+ if (write_process_pid)
+ /* do we want to propagate the HUP,
+ * or do we assume that it was a killall anyways? */
+ CL_KILL(write_process_pid, SIGHUP);
+ else
+ cl_log(LOG_INFO, "SIGHUP received, re-opened log files");
+ return TRUE;
+}
+
+static void
+read_msg_process(IPC_Channel* chan)
+{
+ GHashTable* conn_cmd_attrs;
+ IPC_WaitConnection* conn_cmd = NULL;
+ char path[] = "path";
+ char socketpath[] = HA_LOGDAEMON_IPC;
+ GMainLoop* mainloop;
+
+
+
+ mainloop = g_main_new(FALSE);
+
+ G_main_add_SignalHandler(G_PRIORITY_HIGH, SIGTERM,
+ logd_term_action,mainloop, NULL);
+
+ conn_cmd_attrs = g_hash_table_new(g_str_hash, g_str_equal);
+
+ g_hash_table_insert(conn_cmd_attrs, path, socketpath);
+
+ conn_cmd = ipc_wait_conn_constructor(IPC_ANYTYPE, conn_cmd_attrs);
+ g_hash_table_destroy(conn_cmd_attrs);
+
+ if (conn_cmd == NULL){
+ fprintf(stderr, "ERROR: create waiting connection failed");
+ exit(1);
+ }
+
+ /*Create a source to handle new connect rquests for command*/
+ G_main_add_IPC_WaitConnection( G_PRIORITY_HIGH, conn_cmd, NULL, FALSE
+ , on_connect_cmd, chan, NULL);
+ chan->ops->set_high_flow_callback(chan, logd_suspend_clients, NULL);
+ chan->ops->set_low_flow_callback(chan, logd_resume_clients, NULL);
+ chan->high_flow_mark = chan->send_queue->max_qlen;
+ chan->low_flow_mark = (chan->send_queue->max_qlen*3)/4;
+
+ G_main_add_IPC_Channel(G_PRIORITY_DEFAULT, chan, FALSE,NULL,NULL,NULL);
+
+ G_main_add_SignalHandler(G_PRIORITY_DEFAULT, SIGHUP,
+ logd_hup_action, mainloop, NULL);
+ g_main_run(mainloop);
+
+ return;
+}
+
+static gboolean
+direct_log(IPC_Channel* ch, gpointer user_data)
+{
+ IPC_Message* ipcmsg;
+ GMainLoop* loop;
+ int pri = LOG_DEBUG + 1;
+
+ loop =(GMainLoop*)user_data;
+
+ while(ch->ops->is_message_pending(ch)){
+ if (ch->ch_status == IPC_DISCONNECT){
+ cl_log(LOG_ERR, "read channel is disconnected:"
+ "something very wrong happened");
+ return FALSE;
+ }
+
+ ipcmsg = getIPCmsg(ch);
+ if (ipcmsg == NULL){
+ return TRUE;
+ }
+
+ if( ipcmsg->msg_body
+ && ipcmsg->msg_len > 0 ){
+ LogDaemonMsgHdr *logmsghdr;
+ LogDaemonMsgHdr copy;
+ char *msgtext;
+
+ logmsghdr = (LogDaemonMsgHdr*) ipcmsg->msg_body;
+ /* this copy nonsense is here because apparently ia64
+ * complained about "unaligned memory access. */
+#define COPYFIELD(copy, msg, field) memcpy(((u_char*)&copy.field), ((u_char*)&msg->field), sizeof(copy.field))
+ COPYFIELD(copy, logmsghdr, use_pri_str);
+ COPYFIELD(copy, logmsghdr, entity);
+ COPYFIELD(copy, logmsghdr, entity_pid);
+ COPYFIELD(copy, logmsghdr, timestamp);
+ COPYFIELD(copy, logmsghdr, priority);
+ /* Don't want to copy the following message text */
+
+ msgtext = (char *)logmsghdr + sizeof(LogDaemonMsgHdr);
+ cl_direct_log(copy.priority, msgtext
+ , copy.use_pri_str
+ , copy.entity, copy.entity_pid
+ , copy.timestamp);
+
+ if (copy.priority < pri)
+ pri = copy.priority;
+
+ (void)logd_log;
+/*
+ if (verbose){
+ logd_log("%s[%d]: %s %s\n",
+ logmsg->entity[0]=='\0'?
+ "unknown": copy.entity,
+ copy.entity_pid,
+ ha_timestamp(copy.timestamp),
+ msgtext);
+ }
+ */
+ if (ipcmsg->msg_done){
+ ipcmsg->msg_done(ipcmsg);
+ }
+ }
+ }
+ /* current message backlog processed,
+ * about to return to mainloop,
+ * fflush and potentially fsync stuff */
+ cl_log_do_fflush(pri <= LOG_ERR);
+
+ if(needs_shutdown) {
+ cl_log(LOG_INFO, "Exiting write process");
+ g_main_quit(loop);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+logd_term_write_action(int sig, gpointer userdata)
+{
+ /* as a side-effect, the log message makes sure we enter direct_log()
+ * one last time (so we always exit)
+ */
+ needs_shutdown = TRUE;
+ cl_log(LOG_INFO, "logd_term_write_action: received SIGTERM");
+ cl_log(LOG_DEBUG, "Writing out %d messages then quitting",
+ (int)chanspair[WRITE_PROC_CHAN]->recv_queue->current_qlen);
+
+ direct_log(chanspair[WRITE_PROC_CHAN], userdata);
+
+ return TRUE;
+}
+
+static void
+write_msg_process(IPC_Channel* readchan)
+{
+
+ GMainLoop* mainloop;
+ IPC_Channel* ch = readchan;
+
+
+ mainloop = g_main_new(FALSE);
+
+ G_main_add_IPC_Channel(G_PRIORITY_DEFAULT,
+ ch, FALSE,
+ direct_log, mainloop, NULL);
+
+ G_main_add_SignalHandler(G_PRIORITY_HIGH, SIGTERM,
+ logd_term_write_action, mainloop, NULL);
+
+ G_main_add_SignalHandler(G_PRIORITY_DEFAULT, SIGHUP,
+ logd_hup_action, mainloop, NULL);
+
+ g_main_run(mainloop);
+
+}
+
+
+
+
+
+
+static void
+usage(void)
+{
+ printf("usage: \n"
+ "%s [options]\n\n"
+ "options: \n"
+ "-d make the program a daemon\n"
+ "-k stop the logging daemon if it is already running\n"
+ "-s return logging daemon status \n"
+ "-c use this config file\n"
+ "-v verbosely print debug messages"
+ "-h print out this message\n\n",
+ cmdname);
+
+ return;
+}
+int
+main(int argc, char** argv, char** envp)
+{
+
+ int c;
+ gboolean daemonize = FALSE;
+ gboolean stop_logd = FALSE;
+ gboolean ask_status= FALSE;
+ const char* cfgfile = NULL;
+ pid_t pid;
+
+ cmdname = argv[0];
+ while ((c = getopt(argc, argv, "c:dksvh")) != -1){
+
+ switch(c){
+
+ case 'd': /* daemonize */
+ daemonize = TRUE;
+ break;
+ case 'k': /* stop */
+ stop_logd = TRUE;
+ break;
+ case 's': /* status */
+ ask_status = TRUE;
+ break;
+ case 'c': /* config file*/
+ cfgfile = optarg;
+ break;
+ case 'v':
+ verbose = TRUE;
+ break;
+ case 'h': /*help message */
+ default:
+ usage();
+ exit(1);
+ }
+
+ }
+
+ set_ipc_time_debug_flag(FALSE);
+ cl_log_set_uselogd(FALSE);
+
+ if (!cfgfile && access(DEFAULT_CFG_FILE, F_OK) == 0) {
+ cfgfile = DEFAULT_CFG_FILE;
+ }
+
+
+ /* default one set to "logd"
+ * by setting facility, we enable syslog
+ */
+ cl_log_enable_stderr(TRUE);
+ cl_log_set_entity(logd_config.entity);
+ cl_log_set_facility(logd_config.log_facility);
+
+
+ if (ask_status){
+ long pid;
+
+ if( (pid = cl_read_pidfile(LOGD_PIDFILE)) > 0 ){
+ printf("logging daemon is running [pid = %ld].\n", pid);
+ exit(LSB_EXIT_OK);
+ }else{
+ if (pid == - LSB_STATUS_VAR_PID) {
+ printf("logging daemon is stopped: %s exists.\n"
+ , LOGD_PIDFILE);
+ }else{
+ printf("logging daemon is stopped.\n");
+ }
+ }
+ exit(-pid);
+
+ }
+ if (stop_logd){
+ logd_stop();
+ exit(LSB_EXIT_OK);
+ }
+
+ logd_make_daemon(daemonize);
+
+
+ if (ipc_channel_pair(chanspair) != IPC_OK){
+ cl_perror("cannot create channel pair IPC");
+ return -1;
+ }
+
+
+ if (cfgfile && !parse_config(cfgfile)) {
+ FILE* f;
+ if ((f = fopen(cfgfile, "r")) != NULL){
+ fclose(f);
+ cl_log(LOG_ERR, "Config file [%s] is incorrect."
+ , cfgfile);
+ exit(LSB_EXIT_NOTCONFIGED);
+ }
+ }
+
+ if (strlen(logd_config.debugfile) > 0) {
+ cl_log_set_debugfile(logd_config.debugfile);
+ }
+ if (strlen(logd_config.logfile) > 0) {
+ cl_log_set_logfile(logd_config.logfile);
+ }
+ cl_log_set_syslogprefix(logd_config.syslogprefix);
+ cl_log_set_entity(logd_config.entity);
+ cl_log_set_facility(logd_config.log_facility);
+
+ cl_log(LOG_INFO, "logd started with %s.",
+ cfgfile ? cfgfile : "default configuration");
+
+ if (cl_enable_coredumps(TRUE) < 0){
+ cl_log(LOG_ERR, "enabling core dump failed");
+ }
+ cl_cdtocoredir();
+
+
+
+
+ chanspair[WRITE_PROC_CHAN]->ops->set_recv_qlen(chanspair[WRITE_PROC_CHAN],
+ LOGD_QUEUE_LEN);
+
+ chanspair[READ_PROC_CHAN]->ops->set_send_qlen(chanspair[READ_PROC_CHAN],
+ LOGD_QUEUE_LEN);
+
+ if (init_set_proc_title(argc, argv, envp) < 0) {
+ cl_log(LOG_ERR, "Allocation of proc title failed.");
+ return -1;
+ }
+
+ switch(pid = fork()){
+ case -1:
+ cl_perror("Can't fork child process!");
+ return -1;
+ case 0:
+ /*child*/
+ cl_log_use_buffered_io(1);
+ set_proc_title("ha_logd: write process");
+ write_msg_process(chanspair[WRITE_PROC_CHAN]);
+ break;
+ default:
+ /*parent*/
+ set_proc_title("ha_logd: read process");
+ write_process_pid = pid;
+ /* we don't expect to log anything in the parent. */
+ cl_log_close_log_files();
+
+ read_msg_process(chanspair[READ_PROC_CHAN]);
+ break;
+ }
+ return 0;
+}
+
+
+
+
diff --git a/logd/ha_logger.1 b/logd/ha_logger.1
new file mode 100644
index 0000000..db7f939
--- /dev/null
+++ b/logd/ha_logger.1
@@ -0,0 +1,38 @@
+.TH HA_LOGGER 1 "5th Nov 2004"
+.SH NAME
+.B ha_logger
+\- Log a message to files/syslog through the HA Logging Daemon
+.SH SYNOPSIS
+.B ha_logger
+.RI "[-D ha-log/ha-debug] [-t tag] [message ...]"
+.P
+.SH DESCRIPTION
+.B ha_logger
+is used to log a message to files/syslog through the HA Logging Daemon
+.PP
+.IP "\fB-D\fP \fIha-log/ha-debug\fP"
+Log the message to different files. Ha-log will log the message to
+the log file and the debug file, while ha-debug will log the message
+to the debug file only.
+.IP "\fB-t\fP \fItag\fP"
+Mark every line in the log with the specified
+.IP \fBmessage\fP
+The message you want to log on.
+.PP
+.SH SEE ALSO
+ha_logd(8) heartbeat(8)
+
+.SH DOCUMENTATION
+More information may be found at
+.UR http://linux-ha.org/wiki
+http://linux-ha.org/wiki
+.UE
+
+.SH AUTHORS
+.nf
+ Guochun Shi <gshi@ncsa.uiuc.edu>
+ Alan Robertson <alanr@unix.sh>
+ Lars Marowsky-Bree <lmb@suse.de>
+.fi
+
+
diff --git a/logd/ha_logger.c b/logd/ha_logger.c
new file mode 100644
index 0000000..5e2f9ef
--- /dev/null
+++ b/logd/ha_logger.c
@@ -0,0 +1,142 @@
+/*
+ * ha_logger.c utility to log a message to the logging daemon
+ *
+ * Copyright (C) 2004 Guochun Shi <gshi@ncsa.uiuc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#include <lha_internal.h>
+#include <glib.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/ipc.h>
+#include <clplumbing/GSource.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <clplumbing/loggingdaemon.h>
+#include <syslog.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <netinet/in.h>
+
+#define EXIT_OK 0
+#define EXIT_FAIL 1
+
+int LogToDaemon(int priority, const char * buf, int bstrlen, gboolean use_pri_str);
+void cl_log(int priority, const char * fmt, ...) G_GNUC_PRINTF(2,3);
+static void
+usage(void)
+{
+ printf("usage: "
+ "ha_logger [-t tag] [-D <ha-log/ha-debug>] [message]\n");
+ return;
+}
+#define BUFSIZE 1024
+int
+main(int argc, char** argv)
+{
+ int priority;
+ char* entity = NULL;
+ int c;
+ char buf[BUFSIZE];
+ const char* logtype = "ha-log";
+
+
+ while (( c =getopt(argc, argv,"t:D:h")) != -1){
+ switch(c){
+
+ case 't':
+ entity = optarg;
+ break;
+ case 'D':
+ logtype=optarg;
+ break;
+ case 'h':
+ usage();
+ exit(1);
+ default:
+ usage();
+ exit(1);
+ }
+
+ }
+
+ if(!cl_log_test_logd()){
+ fprintf(stderr, "logd is not running");
+ return EXIT_FAIL;
+ }
+
+ argc -=optind;
+ argv += optind;
+
+ if (entity != NULL){
+ cl_log_set_entity(entity);
+ }
+
+ if (strcmp(logtype, "ha-log") == 0){
+ priority = LOG_INFO;
+ } else if (strcmp(logtype, "ha-debug") == 0){
+ priority = LOG_DEBUG;
+ }else{
+ goto err_exit;
+ }
+
+ if (argc > 0){
+ register char *p;
+
+ for (p = *argv; *argv; argv++, p = *argv) {
+ while (strlen(p) > BUFSIZE-1) {
+ memcpy(buf, p, BUFSIZE-1);
+ *(buf+BUFSIZE-1) = '\0';
+ if (LogToDaemon(priority,buf,
+ BUFSIZE,FALSE) != HA_OK){
+ return EXIT_FAIL;
+ }
+ p += BUFSIZE-1;
+ }
+ if (LogToDaemon(priority,p,
+ strnlen(p, BUFSIZE),FALSE) != HA_OK){
+ return EXIT_FAIL;
+ }
+ }
+ return EXIT_OK;
+ }else {
+ while (fgets(buf, sizeof(buf), stdin) != NULL) {
+ /* glibc is buggy and adds an additional newline,
+ so we have to remove it here until glibc is fixed */
+ int len = strlen(buf);
+
+ if (len > 0 && buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+
+ if (LogToDaemon(priority, buf,strlen(buf), FALSE) == HA_OK){
+ continue;
+ }else {
+ return EXIT_FAIL;
+ }
+ }
+
+ return EXIT_OK;
+ }
+
+ err_exit:
+ usage();
+ return(1);
+
+}
+
diff --git a/logd/logd.cf b/logd/logd.cf
new file mode 100644
index 0000000..cd8ccc3
--- /dev/null
+++ b/logd/logd.cf
@@ -0,0 +1,66 @@
+# File to write debug messages to
+# Default: /var/log/ha-debug
+#debugfile /var/log/ha-debug
+
+#
+#
+# File to write other messages to
+# Default: /var/log/ha-log
+#logfile /var/log/ha-log
+
+#
+#
+# Octal file permission to create the log files with
+# Default: 0644
+#logmode 0640
+
+
+#
+#
+# Facility to use for syslog()/logger
+# (set to 'none' to disable syslog logging)
+# Default: daemon
+#logfacility daemon
+
+
+# Entity to be shown at beginning of a message
+# generated by the logging daemon itself
+# Default: "logd"
+#entity logd
+
+
+# Entity to be shown at beginning of _every_ message
+# passed to syslog (not to log files).
+#
+# Intended for easier filtering, or safe blacklisting.
+# You can filter on logfacility and this prefix.
+#
+# Message format changes like this:
+# -Nov 18 11:30:31 soda logtest: [21366]: info: total message dropped: 0
+# +Nov 18 11:30:31 soda common-prefix: logtest[21366]: info: total message dropped: 0
+#
+# Default: none (disabled)
+#syslogprefix linux-ha
+
+
+# Do we register to apphbd
+# Default: no
+#useapphbd no
+
+# There are two processes running for logging daemon
+# 1. parent process which reads messages from all client channels
+# and writes them to the child process
+#
+# 2. the child process which reads messages from the parent process through IPC
+# and writes them to syslog/disk
+
+
+# set the send queue length from the parent process to the child process
+#
+#sendqlen 256
+
+# set the recv queue length in child process
+#
+#recvqlen 256
+
+
diff --git a/logd/logd.in b/logd/logd.in
new file mode 100755
index 0000000..41d60c5
--- /dev/null
+++ b/logd/logd.in
@@ -0,0 +1,101 @@
+#!/bin/sh
+#
+#
+# logd Start logd (non-blocking log service)
+#
+# Author: Dejan Muhamedagic <dmuhamedagic@suse.de>
+# (After the heartbeat init script)
+# License: GNU General Public License (GPL)
+#
+# This script works correctly under SuSE, Debian,
+# Conectiva, Red Hat and a few others. Please let me know if it
+# doesn't work under your distribution, and we'll fix it.
+# We don't hate anyone, and like for everyone to use
+# our software, no matter what OS or distribution you're using.
+#
+# chkconfig: 2345 19 21
+# description: Startup script logd service.
+# processname: ha_logd
+# pidfile: @localstatedir@/run/logd.pid
+# config: @sysconfdir@/logd.cf
+#
+### BEGIN INIT INFO
+# Description: ha_logd is a non-blocking logging daemon.
+# It can log messages either to a file or through syslog
+# daemon.
+# Short-Description: ha_logd logging daemon
+# Provides: ha_logd
+# Required-Start: $network $syslog $remote_fs
+# Required-Stop: $network $syslog $remote_fs
+# X-Start-Before: heartbeat openais corosync
+# Default-Start: 3 5
+# Default-Stop: 0 1 6
+### END INIT INFO
+
+LOGD_CFG=@sysconfdir@/logd.cf
+LOGD_OPT=""
+[ -f "$LOGD_CFG" ] && LOGD_OPT="-c $LOGD_CFG"
+LOGD_BIN="@libdir@/@HB_PKG@/ha_logd"
+
+if [ ! -f $LOGD_BIN ]; then
+ echo -n "ha_logd not installed."
+ exit 5
+fi
+
+StartLogd() {
+ echo -n "Starting ha_logd: "
+ $LOGD_BIN -s >/dev/null 2>&1
+ if [ $? -eq 0 ]; then
+ echo "logd is already running"
+ return 0
+ fi
+
+ $LOGD_BIN -d $LOGD_OPT >/dev/null 2>&1
+ if [ $? -ne 0 ]; then
+ echo "starting logd failed"
+ exit 1
+ fi
+ echo "ok"
+ exit 0
+}
+
+StopLogd() {
+ echo -n "Stopping ha_logd: "
+
+ $LOGD_BIN -s >/dev/null 2>&1
+ if [ $? -ne 0 ]; then
+ echo "logd is already stopped"
+ return 0
+ fi
+
+ $LOGD_BIN -k >/dev/null 2>&1
+ if [ $? -ne 0 ]; then
+ echo "stopping logd failed"
+ exit 1
+ fi
+ echo "stopped"
+ exit 0
+}
+
+StatusLogd() {
+ $LOGD_BIN -s
+ exit $?
+}
+
+case "$1" in
+ start) StartLogd ;;
+ status) StatusLogd ;;
+ stop) StopLogd ;;
+ restart)
+ sleeptime=1
+ $0 stop && sleep $sleeptime && $0 start
+ echo
+ ;;
+ try-restart)
+ $0 status && $0 restart
+ ;;
+ *)
+ echo "Usage: $0 {start|stop|status|restart}"
+ exit 1
+esac
+
diff --git a/logd/logd.service.in b/logd/logd.service.in
new file mode 100644
index 0000000..9783547
--- /dev/null
+++ b/logd/logd.service.in
@@ -0,0 +1,13 @@
+[Unit]
+Description=ha_logd logging daemon
+Before=pacemaker.service
+PartOf=pacemaker.service
+
+[Service]
+ExecStart=@libdir@/@HB_PKG@/ha_logd -c @sysconfdir@/logd.cf
+ExecStartPre=/bin/rm -f @HA_VARRUNDIR@/logd.pid
+ExecStopPost=/bin/rm -f @HA_VARRUNDIR@/logd.pid
+PIDFile=@HA_VARRUNDIR@/logd.pid
+
+[Install]
+WantedBy=multi-user.target
diff --git a/logd/logtest.c b/logd/logtest.c
new file mode 100644
index 0000000..11e4014
--- /dev/null
+++ b/logd/logtest.c
@@ -0,0 +1,129 @@
+/*
+ * ha_logger.c utility to log a message to the logging daemon
+ *
+ * Copyright (C) 2004 Guochun Shi <gshi@ncsa.uiuc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#include <lha_internal.h>
+#include <glib.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/ipc.h>
+#include <clplumbing/GSource.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <clplumbing/loggingdaemon.h>
+#include <syslog.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <netinet/in.h>
+
+#define EXIT_OK 0
+#define EXIT_FAIL 1
+#define MAXMSGSIZE (1048*4)
+
+int LogToDaemon(int priority, const char * buf, int bstrlen, gboolean use_pri_str);
+extern IPC_Channel * get_log_chan(void);
+
+static GMainLoop* loop;
+
+static gboolean
+send_log_msg(gpointer data)
+{
+ static int count = 0;
+ char msgstring[MAXMSGSIZE];
+ int priority;
+ static int dropmsg = 0;
+ long maxcount = (long) data;
+ IPC_Channel* chan = get_log_chan();
+
+
+ if (chan == NULL){
+ cl_log(LOG_ERR, "logging channel is NULL");
+ g_main_quit(loop);
+ return FALSE;
+
+ }
+ if (count >= maxcount){
+ cl_log(LOG_INFO, "total message dropped: %d", dropmsg);
+ g_main_quit(loop);
+ return FALSE;
+ }
+
+ if (chan->send_queue->current_qlen
+ == chan->send_queue->max_qlen){
+ return TRUE;
+ }
+
+
+ priority = LOG_INFO;
+ msgstring[0]=0;
+
+ snprintf(msgstring, sizeof(msgstring),"Message %d", count++);
+ fprintf(stderr, "sending %s\n", msgstring);
+ if (LogToDaemon(priority, msgstring,MAXMSGSIZE, FALSE) != HA_OK){
+ printf("sending out messge %d failed\n", count);
+ dropmsg++;
+ }
+
+
+
+ return TRUE;
+}
+
+
+static void
+usage(char* prog)
+{
+ printf("Usage:%s <num_of_messages>\n", prog);
+ return;
+}
+
+int
+main(int argc, char** argv)
+{
+
+ long maxcount;
+
+ if (argc < 2){
+ usage(argv[0]);
+ return 1;
+ }
+
+ maxcount = atoi(argv[1]);
+
+ cl_log_set_entity("logtest");
+ cl_log_set_facility(HA_LOG_FACILITY);
+ cl_log_set_uselogd(TRUE);
+
+ if(!cl_log_test_logd()){
+ return EXIT_FAIL;
+ }
+
+ cl_log_set_logd_channel_source(NULL, NULL);
+
+ g_idle_add(send_log_msg, (gpointer)maxcount);
+
+
+ loop = g_main_loop_new(NULL, FALSE);
+ g_main_run(loop);
+ return(1);
+
+}
+