diff options
Diffstat (limited to '')
-rw-r--r-- | logd/Makefile.am | 56 | ||||
-rw-r--r-- | logd/ha_logd.c | 1085 | ||||
-rw-r--r-- | logd/ha_logger.1 | 38 | ||||
-rw-r--r-- | logd/ha_logger.c | 142 | ||||
-rw-r--r-- | logd/logd.cf | 66 | ||||
-rwxr-xr-x | logd/logd.in | 101 | ||||
-rw-r--r-- | logd/logd.service.in | 13 | ||||
-rw-r--r-- | logd/logtest.c | 129 |
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*)©.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); + +} + |