summaryrefslogtreecommitdiffstats
path: root/lib/clplumbing/cl_log.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/clplumbing/cl_log.c')
-rw-r--r--lib/clplumbing/cl_log.c1261
1 files changed, 1261 insertions, 0 deletions
diff --git a/lib/clplumbing/cl_log.c b/lib/clplumbing/cl_log.c
new file mode 100644
index 0000000..213e760
--- /dev/null
+++ b/lib/clplumbing/cl_log.c
@@ -0,0 +1,1261 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <lha_internal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <syslog.h>
+#include <time.h>
+#include <sys/utsname.h>
+#include <clplumbing/ipc.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/loggingdaemon.h>
+#include <clplumbing/longclock.h>
+#include <clplumbing/uids.h>
+#include <glib.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <clplumbing/GSource.h>
+#include <clplumbing/cl_misc.h>
+#include <clplumbing/cl_syslog.h>
+#include <ha_msg.h>
+
+#ifndef MAXLINE
+# define MAXLINE 512
+#endif
+/*
+ * <syslog.h> might not contain LOG_PRI...
+ * So, we define it ourselves, or error out if we can't...
+ */
+
+#ifndef LOG_PRI
+# ifdef LOG_PRIMASK
+ /* David Lee <T.D.Lee@durham.ac.uk> reports this works on Solaris */
+# define LOG_PRI(p) ((p) & LOG_PRIMASK)
+# else
+# error "Syslog.h does not define either LOG_PRI or LOG_PRIMASK."
+# endif
+#endif
+
+#define DFLT_ENTITY "cluster"
+#define DFLT_PREFIX ""
+#define NULLTIME 0
+#define QUEUE_SATURATION_FUZZ 10
+
+static IPC_Channel* logging_daemon_chan = NULL;
+static gboolean syslogformatfile = TRUE;
+/*
+ * If true, then output messages more or less like this...
+ * Jul 14 21:45:18 beam logd: [1056]: info: setting log file to /dev/null
+ */
+
+int LogToDaemon(int priority, const char * buf, int bstrlen, gboolean use_pri_str);
+
+static int LogToLoggingDaemon(int priority, const char * buf, int bstrlen, gboolean use_pri_str);
+static IPC_Message* ChildLogIPCMessage(int priority, const char *buf, int bstrlen,
+ gboolean use_priority_str, IPC_Channel* ch);
+static void FreeChildLogIPCMessage(IPC_Message* msg);
+static gboolean send_dropped_message(gboolean use_pri_str, IPC_Channel *chan);
+static int cl_set_logging_wqueue_maxlen(int qlen);
+
+static int use_logging_daemon = FALSE;
+static int conn_logd_time = 0;
+static char cl_log_entity[MAXENTITY]= DFLT_ENTITY;
+static char cl_log_syslogprefix[MAXENTITY] = DFLT_PREFIX;
+static char common_log_entity[MAXENTITY]= DFLT_ENTITY;
+static int cl_log_facility = LOG_USER;
+static int use_buffered_io = 0;
+
+static void cl_opensyslog(void);
+static int syslog_enabled = 0;
+static int stderr_enabled = 0;
+static int stdout_enabled = 0;
+static const char* logfile_name = NULL;
+static const char* debugfile_name = NULL;
+static int cl_process_pid = -1;
+int debug_level = 0;
+static GDestroyNotify destroy_logging_channel_callback;
+static void (*create_logging_channel_callback)(IPC_Channel* chan);
+static gboolean logging_chan_in_main_loop = FALSE;
+
+/***********************
+ *debug use only, do not use this function in your program
+ */
+IPC_Channel * get_log_chan(void);
+
+IPC_Channel* get_log_chan(void){
+ return logging_daemon_chan;
+}
+/*************************/
+
+/**************************
+ * check if the fd is in use for logging
+ **************************/
+int
+cl_log_is_logd_fd(int fd)
+{
+ return logging_daemon_chan && (
+ fd == logging_daemon_chan->ops->get_send_select_fd(logging_daemon_chan)
+ ||
+ fd == logging_daemon_chan->ops->get_recv_select_fd(logging_daemon_chan)
+ );
+}
+
+void
+cl_log_enable_stderr(int truefalse)
+{
+ stderr_enabled = truefalse;
+}
+
+void
+cl_log_enable_stdout(int truefalse)
+{
+ stdout_enabled = truefalse;
+}
+
+void
+cl_log_set_uselogd(int truefalse)
+{
+ use_logging_daemon = truefalse;
+}
+void
+cl_log_enable_syslog_filefmt(int truefalse)
+{
+ syslogformatfile = (gboolean)truefalse;
+}
+
+gboolean
+cl_log_get_uselogd(void)
+{
+ return use_logging_daemon;
+}
+
+
+int
+cl_log_get_logdtime(void)
+{
+ return conn_logd_time;
+
+}
+
+void
+cl_log_set_logdtime(int logdtime)
+{
+ conn_logd_time = logdtime;
+ return;
+}
+
+void
+cl_log_use_buffered_io(int truefalse)
+{
+ use_buffered_io = truefalse;
+ cl_log_close_log_files();
+}
+
+#define ENVPRE "HA_"
+
+#define ENV_HADEBUGVAL "HA_debug"
+#define ENV_LOGFENV "HA_logfile" /* well-formed log file :-) */
+#define ENV_DEBUGFENV "HA_debugfile" /* Debug log file */
+#define ENV_LOGFACILITY "HA_logfacility"/* Facility to use for logger */
+#define ENV_SYSLOGFMT "HA_syslogmsgfmt"/* TRUE if we should use syslog message formatting */
+#define ENV_LOGDAEMON "HA_use_logd"
+#define ENV_CONNINTVAL "HA_conn_logd_time"
+#define TRADITIONAL_COMPRESSION "HA_traditional_compression"
+#define COMPRESSION "HA_compression"
+
+static void
+inherit_compress(void)
+{
+ char* inherit_env = NULL;
+
+ inherit_env = getenv(TRADITIONAL_COMPRESSION);
+ if (inherit_env != NULL && *inherit_env != EOS) {
+ gboolean value;
+
+ if (cl_str_to_boolean(inherit_env, &value)!= HA_OK){
+ cl_log(LOG_ERR, "inherit traditional_compression failed");
+ }else{
+ cl_set_traditional_compression(value);
+ }
+ }
+
+}
+
+void
+cl_inherit_logging_environment(int logqueuemax)
+{
+ char * inherit_env = NULL;
+
+ /* Donnot need to free the return pointer from getenv */
+ inherit_env = getenv(ENV_HADEBUGVAL);
+ if (inherit_env != NULL && atoi(inherit_env) != 0 ) {
+ debug_level = atoi(inherit_env);
+ inherit_env = NULL;
+ }
+
+ inherit_env = getenv(ENV_LOGFENV);
+ if (inherit_env != NULL && *inherit_env != EOS) {
+ cl_log_set_logfile(inherit_env);
+ inherit_env = NULL;
+ }
+
+ inherit_env = getenv(ENV_DEBUGFENV);
+ if (inherit_env != NULL && *inherit_env != EOS) {
+ cl_log_set_debugfile(inherit_env);
+ inherit_env = NULL;
+ }
+
+ inherit_env = getenv(ENV_LOGFACILITY);
+ if (inherit_env != NULL && *inherit_env != EOS) {
+ int facility = -1;
+ facility = cl_syslogfac_str2int(inherit_env);
+ if ( facility >= 0 ) {
+ cl_log_set_facility(facility);
+ }
+ inherit_env = NULL;
+ }
+
+ inherit_env = getenv(ENV_SYSLOGFMT);
+ if (inherit_env != NULL && *inherit_env != EOS) {
+ gboolean truefalse;
+ if (cl_str_to_boolean(inherit_env, &truefalse) == HA_OK) {
+ cl_log_enable_syslog_filefmt(truefalse);
+ }
+ }
+
+ inherit_env = getenv(ENV_LOGDAEMON);
+ if (inherit_env != NULL && *inherit_env != EOS) {
+ gboolean uselogd;
+ cl_str_to_boolean(inherit_env, &uselogd);
+ cl_log_set_uselogd(uselogd);
+ if (uselogd) {
+ if (logqueuemax > 0) {
+ cl_set_logging_wqueue_maxlen(logqueuemax);
+ }
+ }
+ }
+
+ inherit_env = getenv(ENV_CONNINTVAL);
+ if (inherit_env != NULL && *inherit_env != EOS) {
+ int logdtime;
+ logdtime = cl_get_msec(inherit_env);
+ cl_log_set_logdtime(logdtime);
+ }
+
+ inherit_compress();
+ return;
+}
+
+
+static void
+add_logging_channel_mainloop(IPC_Channel* chan)
+{
+ GCHSource* chp=
+ G_main_add_IPC_Channel( G_PRIORITY_DEFAULT,
+ chan,
+ FALSE,
+ NULL,
+ NULL,
+ destroy_logging_channel_callback);
+
+ if (chp == NULL){
+ cl_log(LOG_INFO, "adding logging channel to mainloop failed");
+ }
+
+ logging_chan_in_main_loop = TRUE;
+
+
+ return;
+}
+
+static void
+remove_logging_channel_mainloop(gpointer userdata)
+{
+ logging_chan_in_main_loop = FALSE;
+
+ return;
+}
+
+
+static IPC_Channel*
+create_logging_channel(void)
+{
+ GHashTable* attrs;
+ char path[] = IPC_PATH_ATTR;
+ char sockpath[] = HA_LOGDAEMON_IPC;
+ IPC_Channel* chan;
+ static gboolean complained_yet = FALSE;
+
+ attrs = g_hash_table_new(g_str_hash, g_str_equal);
+ g_hash_table_insert(attrs, path, sockpath);
+
+ chan =ipc_channel_constructor(IPC_ANYTYPE, attrs);
+
+ g_hash_table_destroy(attrs);
+
+ if (chan == NULL) {
+ cl_log(LOG_ERR, "create_logging_channel:"
+ "contructing ipc channel failed");
+ return NULL;
+ }
+
+ if (chan->ops->initiate_connection(chan) != IPC_OK) {
+ if (!complained_yet) {
+ complained_yet = TRUE;
+ cl_log(LOG_WARNING, "Initializing connection"
+ " to logging daemon failed."
+ " Logging daemon may not be running");
+ }
+ if (!logging_chan_in_main_loop){
+ chan->ops->destroy(chan);
+ }
+
+ return NULL;
+ }
+ complained_yet = FALSE;
+
+ if (create_logging_channel_callback){
+ create_logging_channel_callback(chan);
+ }
+
+
+ return chan;
+
+}
+
+gboolean
+cl_log_test_logd(void)
+{
+ IPC_Channel* chan = logging_daemon_chan;
+
+ if (chan && chan->ops->get_chan_status(chan) == IPC_CONNECT){
+ return TRUE;
+ }
+ if (chan ){
+ if (!logging_chan_in_main_loop){
+ chan->ops->destroy(chan);
+ }
+ logging_daemon_chan = chan = NULL;
+ }
+
+ logging_daemon_chan = chan = create_logging_channel();
+
+ if (chan == NULL){
+ return FALSE;
+ }
+
+ if(chan->ops->get_chan_status(chan) != IPC_CONNECT){
+ if (!logging_chan_in_main_loop){
+ chan->ops->destroy(chan);
+ }
+ logging_daemon_chan = chan = NULL;
+ return FALSE;
+ }
+
+ return TRUE;
+
+}
+
+/* FIXME: This is way too ugly to bear */
+
+void
+cl_log_set_facility(int facility)
+{
+ if (syslog_enabled && facility == cl_log_facility) {
+ return;
+ }
+ cl_log_facility = facility;
+ closelog();
+ syslog_enabled = 0;
+ if (facility > 0) {
+ cl_opensyslog();
+ }
+}
+
+void
+cl_log_set_entity(const char * entity)
+{
+ if (entity == NULL) {
+ entity = DFLT_ENTITY;
+ }
+ strncpy(cl_log_entity, entity, MAXENTITY);
+ cl_log_entity[MAXENTITY-1] = '\0';
+ if (syslog_enabled) {
+ syslog_enabled = 0;
+ cl_opensyslog();
+ }
+}
+
+void
+cl_log_set_syslogprefix(const char *prefix)
+{
+ if (prefix == NULL) {
+ prefix = DFLT_PREFIX;
+ }
+ strncpy(cl_log_syslogprefix, prefix, MAXENTITY);
+ cl_log_syslogprefix[MAXENTITY-1] = '\0';
+ if (syslog_enabled) {
+ syslog_enabled = 0;
+ cl_opensyslog();
+ }
+}
+
+void
+cl_log_set_logfile(const char * path)
+{
+ if(path != NULL && strcasecmp("/dev/null", path) == 0) {
+ path = NULL;
+ }
+ logfile_name = path;
+ cl_log_close_log_files();
+}
+void
+cl_log_set_debugfile(const char * path)
+{
+ if(path != NULL && strcasecmp("/dev/null", path) == 0) {
+ path = NULL;
+ }
+ debugfile_name = path;
+ cl_log_close_log_files();
+}
+
+
+/*
+ * This function sets two callback functions.
+ * One for creating a channel and
+ * the other for destroying a channel*
+ */
+int
+cl_log_set_logd_channel_source( void (*create_callback)(IPC_Channel* chan),
+ GDestroyNotify destroy_callback)
+{
+ IPC_Channel* chan = logging_daemon_chan ;
+
+ if (destroy_callback == NULL){
+ destroy_logging_channel_callback = remove_logging_channel_mainloop;
+ }else{
+ destroy_logging_channel_callback = destroy_callback;
+ }
+
+ if (create_callback == NULL){
+ create_logging_channel_callback = add_logging_channel_mainloop;
+ }else{
+ create_logging_channel_callback = create_callback;
+ }
+
+ if (chan != NULL
+ && chan->ops->get_chan_status(chan) == IPC_CONNECT){
+ add_logging_channel_mainloop(chan);
+ }
+
+ return 0;
+}
+
+const char *
+prio2str(int priority)
+{
+ static const char *log_prio[8] = {
+ "EMERG",
+ "ALERT",
+ "CRIT",
+ "ERROR",
+ "WARN",
+ "notice",
+ "info",
+ "debug"
+ };
+ int logpri;
+
+ logpri = LOG_PRI(priority);
+
+ return (logpri < 0 || logpri >= DIMOF(log_prio)) ?
+ "(undef)" : log_prio[logpri];
+}
+
+/* print log line to a FILE *f */
+#define print_logline(fp,entity,entity_pid,ts,pristr,buf) { \
+ fprintf(fp, "%s[%d]: %s ",entity,entity_pid,ha_timestamp(ts)); \
+ if (pristr) \
+ fprintf(fp,"%s: %s\n",pristr,buf); \
+ else \
+ fprintf(fp,"%s\n",buf); \
+ }
+
+static char * syslog_timestamp(TIME_T t);
+static void cl_limit_log_update(struct msg_ctrl *ml, time_t ts);
+
+static void
+append_log(FILE * fp, const char * entity, int entity_pid
+, TIME_T timestamp, const char * pristr, const char * msg)
+{
+ static int got_uname = FALSE;
+ static struct utsname un;
+
+ if (!syslogformatfile) {
+ print_logline(fp, entity, entity_pid, timestamp, pristr, msg);
+ return;
+ }
+ if (!got_uname) {
+ uname(&un);
+ }
+ /*
+ * Jul 14 21:45:18 beam logd: [1056]: info: setting log file to /dev/null
+ */
+ fprintf(fp, "%s %s %s: [%d]: %s%s%s\n"
+ , syslog_timestamp(timestamp)
+ , un.nodename, entity, entity_pid
+ , (pristr ? pristr : "")
+ , (pristr ? ": " : "")
+ , msg);
+}
+
+/* As performance optimization we try to keep the file descriptor
+ * open all the time, but as logrotation needs to work, the calling
+ * program actually needs a signal handler.
+ *
+ * To be able to keep files open even without signal handler,
+ * we remember the stat info, and close/reopen if the inode changed.
+ * We keep the number of stat() calls to one per file per minute.
+ * logrotate should be configured for delayed compression, if any.
+ */
+
+struct log_file_context {
+ FILE *fp;
+ struct stat stat_buf;
+};
+
+static struct log_file_context log_file, debug_file;
+
+static void close_log_file(struct log_file_context *lfc)
+{
+ /* ignore errors, we cannot do anything about them anyways */
+ fflush(lfc->fp);
+ fsync(fileno(lfc->fp));
+ fclose(lfc->fp);
+ lfc->fp = NULL;
+}
+
+void cl_log_close_log_files(void)
+{
+ if (log_file.fp)
+ close_log_file(&log_file);
+ if (debug_file.fp)
+ close_log_file(&debug_file);
+}
+
+static void maybe_close_log_file(const char *fname, struct log_file_context *lfc)
+{
+ struct stat buf;
+ if (!lfc->fp)
+ return;
+ if (stat(fname, &buf) || buf.st_ino != lfc->stat_buf.st_ino) {
+ close_log_file(lfc);
+ cl_log(LOG_INFO, "log-rotate detected on logfile %s", fname);
+ }
+}
+
+/* Default to unbuffered IO. logd or others can use cl_log_use_buffered_io(1)
+ * to enable fully buffered mode, and then use fflush appropriately.
+ */
+static void open_log_file(const char *fname, struct log_file_context *lfc)
+{
+ lfc->fp = fopen(fname ,"a");
+ if (!lfc->fp) {
+ syslog(LOG_ERR, "Failed to open log file %s: %s\n" ,
+ fname, strerror(errno));
+ } else {
+ setvbuf(lfc->fp, NULL,
+ use_buffered_io ? _IOFBF : _IONBF,
+ BUFSIZ);
+ fstat(fileno(lfc->fp), &lfc->stat_buf);
+ }
+}
+
+static void maybe_reopen_log_files(const char *log_fname, const char *debug_fname)
+{
+ static TIME_T last_stat_time;
+
+ if (log_file.fp || debug_file.fp) {
+ TIME_T now = time(NULL);
+ if (now - last_stat_time > 59) {
+ /* Don't use an exact minute, have it jitter around a
+ * bit against cron or others. Note that, if there
+ * is no new log message, it can take much longer
+ * than this to notice logrotation and actually close
+ * our file handle on the possibly already rotated,
+ * or even deleted.
+ *
+ * As long as at least one minute pases between
+ * renaming the log file, and further processing,
+ * no message will be lost, so this should do fine:
+ * (mv ha-log ha-log.1; sleep 60; gzip ha-log.1)
+ */
+ maybe_close_log_file(log_fname, &log_file);
+ maybe_close_log_file(debug_fname, &debug_file);
+ last_stat_time = now;
+ }
+ }
+
+ if (log_fname && !log_file.fp)
+ open_log_file(log_fname, &log_file);
+
+ if (debug_fname && !debug_file.fp)
+ open_log_file(debug_fname, &debug_file);
+}
+
+/*
+ * This function can cost us realtime unless use_logging_daemon
+ * is enabled. Then we log everything through a child process using
+ * non-blocking IPC.
+ */
+
+/* Cluster logging function */
+void
+cl_direct_log(int priority, const char* buf, gboolean use_priority_str,
+ const char* entity, int entity_pid, TIME_T ts)
+{
+ const char * pristr;
+ int needprivs = !cl_have_full_privs();
+
+ pristr = use_priority_str ? prio2str(priority) : NULL;
+
+ if (!entity)
+ entity = *cl_log_entity ? cl_log_entity : DFLT_ENTITY;
+
+ if (needprivs) {
+ return_to_orig_privs();
+ }
+
+ if (syslog_enabled) {
+ snprintf(common_log_entity, MAXENTITY, "%s",
+ *cl_log_syslogprefix ? cl_log_syslogprefix : entity);
+
+ /* The extra trailing '\0' is supposed to work around some
+ * "known syslog bug that ends up concatinating entries".
+ * Knowledge about which syslog package, version, platform and
+ * what exactly the bug was has been lost, but leaving it in
+ * won't do any harm either. */
+ syslog(priority, "%s[%d]: %s%s%s%c",
+ *cl_log_syslogprefix ? entity : "",
+ entity_pid,
+ pristr ?: "", pristr ? ": " : "",
+ buf, 0);
+ }
+
+ maybe_reopen_log_files(logfile_name, debugfile_name);
+
+ if (debug_file.fp)
+ append_log(debug_file.fp, entity, entity_pid, ts, pristr, buf);
+
+ if (priority != LOG_DEBUG && log_file.fp)
+ append_log(log_file.fp, entity, entity_pid, ts, pristr, buf);
+
+ if (needprivs) {
+ return_to_dropped_privs();
+ }
+ return;
+}
+
+void cl_log_do_fflush(int do_fsync)
+{
+ if (log_file.fp) {
+ fflush(log_file.fp);
+ if (do_fsync)
+ fsync(fileno(log_file.fp));
+ }
+ if (debug_file.fp) {
+ fflush(debug_file.fp);
+ if (do_fsync)
+ fsync(fileno(debug_file.fp));
+ }
+}
+
+/*
+ * This function can cost us realtime unless use_logging_daemon
+ * is enabled. Then we log everything through a child process using
+ * non-blocking IPC.
+ */
+
+static int cl_log_depth = 0;
+
+/* Cluster logging function */
+void
+cl_log(int priority, const char * fmt, ...)
+{
+ va_list ap;
+ char buf[MAXLINE];
+ ssize_t nbytes;
+
+ cl_process_pid = (int)getpid();
+
+ cl_log_depth++;
+
+ buf[MAXLINE-1] = EOS;
+ va_start(ap, fmt);
+ nbytes=vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ if (nbytes >= (ssize_t)sizeof(buf)){
+ nbytes = sizeof(buf) -1 ;
+ }
+
+ if (stderr_enabled) {
+ append_log(stderr, cl_log_entity,cl_process_pid,
+ NULLTIME, prio2str(priority), buf);
+ }
+
+ if (stdout_enabled) {
+ append_log(stdout, cl_log_entity,cl_process_pid,
+ NULLTIME, prio2str(priority), buf);
+ }
+
+ if (use_logging_daemon && cl_log_depth <= 1) {
+ LogToLoggingDaemon(priority, buf, nbytes, TRUE);
+ }else{
+ /* this may cause blocking... maybe should make it optional? */
+ cl_direct_log(priority, buf, TRUE, NULL, cl_process_pid, NULLTIME);
+ }
+
+ cl_log_depth--;
+ return;
+}
+
+/*
+ * Log a message only if there were not too many messages of this
+ * kind recently. This is too prevent log spamming in case a
+ * condition persists over a long period of time. The maximum
+ * number of messages for the timeframe and other details are
+ * provided in struct logspam (see cl_log.h).
+ *
+ * Implementation details:
+ * - max number of time_t slots is allocated; slots keep time
+ * stamps of previous max number of messages
+ * - we check if the difference between now (i.e. new message just
+ * arrived) and the oldest message is _less_ than the window
+ * timeframe
+ * - it's up to the user to do cl_limit_log_new and afterwards
+ * cl_limit_log_destroy, though the latter is usually not
+ * necessary; the memory allocated with cl_limit_log_new stays
+ * constant during the lifetime of the process
+ *
+ * NB on Thu Aug 4 15:26:49 CEST 2011:
+ * This interface is very new, use with caution and report bugs.
+ */
+
+struct msg_ctrl *
+cl_limit_log_new(struct logspam *lspam)
+{
+ struct msg_ctrl *ml;
+
+ ml = (struct msg_ctrl *)malloc(sizeof(struct msg_ctrl));
+ if (!ml) {
+ cl_log(LOG_ERR, "%s:%d: out of memory"
+ , __FUNCTION__, __LINE__);
+ return NULL;
+ }
+ ml->msg_slots = (time_t *)calloc(lspam->max, sizeof(time_t));
+ if (!ml->msg_slots) {
+ cl_log(LOG_ERR, "%s:%d: out of memory"
+ , __FUNCTION__, __LINE__);
+ return NULL;
+ }
+ ml->lspam = lspam;
+ cl_limit_log_reset(ml);
+ return ml; /* to be passed later to cl_limit_log() */
+}
+
+void
+cl_limit_log_destroy(struct msg_ctrl *ml)
+{
+ if (!ml)
+ return;
+ g_free(ml->msg_slots);
+ g_free(ml);
+}
+
+void
+cl_limit_log_reset(struct msg_ctrl *ml)
+{
+ ml->last = -1;
+ ml->cnt = 0;
+ ml->suppress_t = (time_t)0;
+ memset(ml->msg_slots, 0, ml->lspam->max * sizeof(time_t));
+}
+
+static void
+cl_limit_log_update(struct msg_ctrl *ml, time_t ts)
+{
+ ml->last = (ml->last + 1) % ml->lspam->max;
+ *(ml->msg_slots + ml->last) = ts;
+ if (ml->cnt < ml->lspam->max)
+ ml->cnt++;
+}
+
+void
+cl_limit_log(struct msg_ctrl *ml, int priority, const char * fmt, ...)
+{
+ va_list ap;
+ char buf[MAXLINE];
+ time_t last_ts, now = time(NULL);
+
+ if (!ml)
+ goto log_msg;
+ if (ml->suppress_t) {
+ if ((now - ml->suppress_t) < ml->lspam->reset_time)
+ return;
+ /* message blocking expired */
+ cl_limit_log_reset(ml);
+ }
+ last_ts = ml->last != -1 ? *(ml->msg_slots + ml->last) : (time_t)0;
+ if (
+ ml->cnt < ml->lspam->max || /* not so many messages logged */
+ (now - last_ts) > ml->lspam->window /* messages far apart */
+ ) {
+ cl_limit_log_update(ml, now);
+ goto log_msg;
+ } else {
+ cl_log(LOG_INFO
+ , "'%s' messages logged too often, "
+ "suppressing messages of this kind for %ld seconds"
+ , ml->lspam->id, ml->lspam->reset_time);
+ cl_log(priority, "%s", ml->lspam->advice);
+ ml->suppress_t = now;
+ return;
+ }
+
+log_msg:
+ va_start(ap, fmt);
+ vsnprintf(buf, MAXLINE, fmt, ap);
+ va_end(ap);
+ cl_log(priority, "%s", buf);
+}
+
+void
+cl_perror(const char * fmt, ...)
+{
+ const char * err;
+
+ va_list ap;
+ char buf[MAXLINE];
+
+ err = strerror(errno);
+ va_start(ap, fmt);
+ vsnprintf(buf, MAXLINE, fmt, ap);
+ va_end(ap);
+
+ cl_log(LOG_ERR, "%s: %s", buf, err);
+
+}
+void
+cl_glib_msg_handler(const gchar *log_domain, GLogLevelFlags log_level
+, const gchar *message, gpointer user_data)
+{
+ GLogLevelFlags level = (log_level & G_LOG_LEVEL_MASK);
+ int ha_level;
+
+ switch(level) {
+ case G_LOG_LEVEL_ERROR: ha_level = LOG_ERR; break;
+ case G_LOG_LEVEL_CRITICAL: ha_level = LOG_ERR; break;
+ case G_LOG_LEVEL_WARNING: ha_level = LOG_WARNING; break;
+ case G_LOG_LEVEL_MESSAGE: ha_level = LOG_NOTICE; break;
+ case G_LOG_LEVEL_INFO: ha_level = LOG_INFO; break;
+ case G_LOG_LEVEL_DEBUG: ha_level = LOG_DEBUG; break;
+
+ default: ha_level = LOG_WARNING; break;
+ }
+
+
+ cl_log(ha_level, "glib: %s", message);
+}
+static char *
+syslog_timestamp(TIME_T t)
+{
+ static char ts[64];
+ struct tm* ttm;
+ TIME_T now;
+ time_t nowtt;
+ static const char* monthstr [12] = {
+ "Jan", "Feb", "Mar",
+ "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep",
+ "Oct", "Nov", "Dec"
+ };
+
+ /* Work around various weridnesses in different OSes and time_t definitions */
+ if (t == 0){
+ now = time(NULL);
+ }else{
+ now = t;
+ }
+
+ nowtt = (time_t)now;
+ ttm = localtime(&nowtt);
+
+ snprintf(ts, sizeof(ts), "%3s %02d %02d:%02d:%02d"
+ , monthstr[ttm->tm_mon], ttm->tm_mday
+ , ttm->tm_hour, ttm->tm_min, ttm->tm_sec);
+ return(ts);
+}
+
+
+
+char *
+ha_timestamp(TIME_T t)
+{
+ static char ts[64];
+ struct tm* ttm;
+ TIME_T now;
+ time_t nowtt;
+
+ /* Work around various weridnesses in different OSes and time_t definitions */
+ if (t == 0){
+ now = time(NULL);
+ }else{
+ now = t;
+ }
+
+ nowtt = (time_t)now;
+ ttm = localtime(&nowtt);
+
+ snprintf(ts, sizeof(ts), "%04d/%02d/%02d_%02d:%02d:%02d"
+ , ttm->tm_year+1900, ttm->tm_mon+1, ttm->tm_mday
+ , ttm->tm_hour, ttm->tm_min, ttm->tm_sec);
+ return(ts);
+}
+
+
+static int
+cl_set_logging_wqueue_maxlen(int qlen)
+{
+ int sendrc;
+ IPC_Channel* chan = logging_daemon_chan;
+
+ if (chan == NULL){
+ chan = logging_daemon_chan = create_logging_channel();
+ }
+
+ if (chan == NULL){
+ return HA_FAIL;
+ }
+
+ if (chan->ch_status != IPC_CONNECT){
+ cl_log(LOG_ERR, "cl_set_logging_wqueue_maxle:"
+ "channel is not connected");
+ if (!logging_chan_in_main_loop){
+ chan->ops->destroy(chan);
+ }
+ logging_daemon_chan = NULL;
+ return HA_FAIL;
+ }
+
+ sendrc = chan->ops->set_send_qlen(logging_daemon_chan, qlen);
+
+ if (sendrc == IPC_OK) {
+ return HA_OK;
+ }else {
+ return HA_FAIL;
+ }
+}
+
+
+
+int
+LogToDaemon(int priority, const char * buf,
+ int bufstrlen, gboolean use_pri_str)
+{
+ int rc;
+
+ cl_log_depth++;
+
+ rc= LogToLoggingDaemon(priority, buf, bufstrlen, use_pri_str);
+
+ cl_log_depth--;
+
+ return rc;
+}
+
+static int drop_msg_num = 0;
+
+void
+cl_flush_logs(void)
+{
+ if(logging_daemon_chan == NULL) {
+ return;
+ }
+ logging_daemon_chan->ops->waitout(logging_daemon_chan);
+}
+
+static int
+LogToLoggingDaemon(int priority, const char * buf,
+ int bufstrlen, gboolean use_pri_str)
+{
+ IPC_Channel* chan = logging_daemon_chan;
+ static longclock_t nexttime = 0;
+ IPC_Message* msg;
+ int sendrc = IPC_FAIL;
+ int intval = conn_logd_time;
+
+ /* make sure we don't hold file descriptors open
+ * we don't intend to use again */
+ cl_log_close_log_files();
+
+ if (chan == NULL) {
+ longclock_t lnow = time_longclock();
+
+ if (cmp_longclock(lnow, nexttime) >= 0){
+ nexttime = add_longclock(
+ lnow, msto_longclock(intval));
+
+ logging_daemon_chan = chan = create_logging_channel();
+ }
+ }
+
+ if (chan == NULL){
+ cl_direct_log(
+ priority, buf, TRUE, NULL, cl_process_pid, NULLTIME);
+ return HA_FAIL;
+ }
+
+ msg = ChildLogIPCMessage(priority, buf, bufstrlen, use_pri_str, chan);
+ if (msg == NULL) {
+ drop_msg_num++;
+ return HA_FAIL;
+ }
+
+ if (chan->ch_status == IPC_CONNECT){
+
+ if (chan->ops->is_sending_blocked(chan)) {
+ chan->ops->resume_io(chan);
+ }
+ /* Make sure there is room for the drop message _and_ the
+ * one we wish to log. Otherwise there is no point.
+ *
+ * Try to avoid bouncing on the limit by additionally
+ * waiting until there is room for QUEUE_SATURATION_FUZZ
+ * messages.
+ */
+ if (drop_msg_num > 0
+ && chan->send_queue->current_qlen
+ < (chan->send_queue->max_qlen -1 -QUEUE_SATURATION_FUZZ))
+ {
+ /* have to send it this way so the order is correct */
+ send_dropped_message(use_pri_str, chan);
+ }
+
+ /* Don't log a debug message if we're
+ * approaching the queue limit and already
+ * dropped a message
+ */
+ if (drop_msg_num == 0
+ || chan->send_queue->current_qlen <
+ (chan->send_queue->max_qlen -1 -QUEUE_SATURATION_FUZZ)
+ || priority != LOG_DEBUG )
+ {
+ sendrc = chan->ops->send(chan, msg);
+ }
+ }
+
+ if (sendrc == IPC_OK) {
+ return HA_OK;
+
+ } else {
+
+ if (chan->ops->get_chan_status(chan) != IPC_CONNECT) {
+ if (!logging_chan_in_main_loop){
+ chan->ops->destroy(chan);
+ }
+ logging_daemon_chan = NULL;
+ cl_direct_log(priority, buf, TRUE, NULL, cl_process_pid, NULLTIME);
+
+ if (drop_msg_num > 0){
+ /* Direct logging here is ok since we're
+ * switching to that for everything
+ * "for a while"
+ */
+ cl_log(LOG_ERR,
+ "cl_log: %d messages were dropped"
+ " : channel destroyed", drop_msg_num);
+ }
+
+ drop_msg_num=0;
+ FreeChildLogIPCMessage(msg);
+ return HA_FAIL;
+ }
+
+ drop_msg_num++;
+
+ }
+
+ FreeChildLogIPCMessage(msg);
+ return HA_FAIL;
+}
+
+
+static gboolean
+send_dropped_message(gboolean use_pri_str, IPC_Channel *chan)
+{
+ int sendrc;
+ char buf[64];
+ int buf_len = 0;
+ IPC_Message *drop_msg = NULL;
+
+ memset(buf, 0, 64);
+ snprintf(buf, 64, "cl_log: %d messages were dropped", drop_msg_num);
+ buf_len = strlen(buf)+1;
+ drop_msg = ChildLogIPCMessage(LOG_ERR, buf, buf_len, use_pri_str, chan);
+
+ if(drop_msg == NULL || drop_msg->msg_len == 0) {
+ return FALSE;
+ }
+
+ sendrc = chan->ops->send(chan, drop_msg);
+
+ if(sendrc == IPC_OK) {
+ drop_msg_num = 0;
+ }else{
+ FreeChildLogIPCMessage(drop_msg);
+ }
+ return sendrc == IPC_OK;
+}
+
+
+static IPC_Message*
+ChildLogIPCMessage(int priority, const char *buf, int bufstrlen,
+ gboolean use_prio_str, IPC_Channel* ch)
+{
+ IPC_Message* ret;
+ LogDaemonMsgHdr logbuf;
+ int msglen;
+ char* bodybuf;
+
+
+ if (ch->msgpad > MAX_MSGPAD){
+ cl_log(LOG_ERR, "ChildLogIPCMessage: invalid msgpad(%d)",
+ ch->msgpad);
+ return NULL;
+ }
+
+
+ ret = (IPC_Message*)malloc(sizeof(IPC_Message));
+
+ if (ret == NULL) {
+ return ret;
+ }
+
+ memset(ret, 0, sizeof(IPC_Message));
+
+ /* Compute msg len: including room for the EOS byte */
+ msglen = sizeof(LogDaemonMsgHdr)+bufstrlen + 1;
+ bodybuf = malloc(msglen + ch->msgpad);
+ if (bodybuf == NULL) {
+ free(ret);
+ return NULL;
+ }
+
+ memset(bodybuf, 0, msglen + ch->msgpad);
+ memset(&logbuf, 0, sizeof(logbuf));
+ logbuf.msgtype = LD_LOGIT;
+ logbuf.facility = cl_log_facility;
+ logbuf.priority = priority;
+ logbuf.use_pri_str = use_prio_str;
+ logbuf.entity_pid = getpid();
+ logbuf.timestamp = time(NULL);
+ if (*cl_log_entity){
+ strncpy(logbuf.entity,cl_log_entity,MAXENTITY);
+ }else {
+ strncpy(logbuf.entity,DFLT_ENTITY,MAXENTITY);
+ }
+
+ logbuf.msglen = bufstrlen + 1;
+ memcpy(bodybuf + ch->msgpad, &logbuf, sizeof(logbuf));
+ memcpy(bodybuf + ch->msgpad + sizeof(logbuf),
+ buf,
+ bufstrlen);
+
+ ret->msg_len = msglen;
+ ret->msg_buf = bodybuf;
+ ret->msg_body = bodybuf + ch->msgpad;
+ ret->msg_done = FreeChildLogIPCMessage;
+ ret->msg_ch = ch;
+
+ return ret;
+}
+
+
+static void
+FreeChildLogIPCMessage(IPC_Message* msg)
+{
+ if (msg == NULL) {
+ return;
+ }
+ memset(msg->msg_body, 0, msg->msg_len);
+ free(msg->msg_buf);
+
+ memset(msg, 0, sizeof (*msg));
+ free(msg);
+
+ return;
+
+}
+
+
+
+static void
+cl_opensyslog(void)
+{
+ if (*cl_log_entity == '\0' || cl_log_facility < 0) {
+ return;
+ }
+ syslog_enabled = 1;
+ strncpy(common_log_entity, cl_log_entity, MAXENTITY);
+ openlog(common_log_entity, LOG_CONS, cl_log_facility);
+}
+
+
+void
+cl_log_args(int argc, char **argv)
+{
+ int lpc = 0;
+ int len = 0;
+ int existing_len = 0;
+ char *arg_string = NULL;
+
+ if(argc == 0 || argv == NULL) {
+ return;
+ }
+
+ for(;lpc < argc; lpc++) {
+ if(argv[lpc] == NULL) {
+ break;
+ }
+
+ len = 2 + strlen(argv[lpc]); /* +1 space, +1 EOS */
+ if(arg_string) {
+ existing_len = strlen(arg_string);
+ }
+
+ arg_string = realloc(arg_string, len + existing_len);
+ sprintf(arg_string + existing_len, "%s ", argv[lpc]);
+ }
+ cl_log(LOG_INFO, "Invoked: %s", arg_string);
+ free(arg_string);
+}