summaryrefslogtreecommitdiffstats
path: root/logd/ha_logd.c
diff options
context:
space:
mode:
Diffstat (limited to 'logd/ha_logd.c')
-rw-r--r--logd/ha_logd.c1085
1 files changed, 1085 insertions, 0 deletions
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;
+}
+
+
+
+