/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef MAXLINE # define MAXLINE 512 #endif /* * 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 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); }