/* ** OSSP l2 - Flexible Logging ** Copyright (c) 2001-2005 Cable & Wireless ** Copyright (c) 2001-2005 The OSSP Project ** Copyright (c) 2001-2005 Ralf S. Engelschall ** ** This file is part of OSSP l2, a flexible logging library which ** can be found at http://www.ossp.org/pkg/lib/l2/. ** ** Permission to use, copy, modify, and distribute this software for ** any purpose with or without fee is hereby granted, provided that ** the above copyright notice and this permission notice appear in all ** copies. ** ** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED ** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF ** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, ** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT ** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. ** ** l2_ch_syslog.c: syslog(3) channel implementation */ #include "l2.h" #include "l2_p.h" #include #include #include #include #include /* declare private channel configuration */ typedef struct { char *szTarget; char *szRemoteHost; int nRemotePort; char *szLocalHost; char *szFacility; int nFacility; char *szIdent; int bLogPid; sa_t *saRemoteSock; sa_addr_t *saaRemoteAddr; } l2_ch_syslog_t; /* mapping from L2 log levels to syslog(3) log levels */ static struct { int levelL2; int levelSL; } l2_ch_syslog_L2toSL[] = { { L2_LEVEL_PANIC, LOG_EMERG }, { L2_LEVEL_CRITICAL, LOG_CRIT }, { L2_LEVEL_ERROR, LOG_ERR }, { L2_LEVEL_WARNING, LOG_WARNING }, { L2_LEVEL_NOTICE, LOG_NOTICE }, { L2_LEVEL_INFO, LOG_INFO }, { L2_LEVEL_TRACE, LOG_INFO }, { L2_LEVEL_DEBUG, LOG_DEBUG }, { -1, -1 } }; /* Syslog Facility Table */ static struct { char *name; /* the common name */ int numREMOTE; /* number according to RFC3164 */ int numLOCAL; /* local encoding */ } l2_ch_syslog_SLfac[] = { { "kern", 0, LOG_KERN }, { "user", 1, LOG_USER }, { "mail", 2, LOG_MAIL }, { "daemon", 3, LOG_DAEMON }, { "auth", 4, LOG_AUTH }, #ifndef LOG_SYSLOG #define LOG_SYSLOG (5 << 3) #endif { "syslog", 5, LOG_SYSLOG }, { "lpr", 6, LOG_LPR }, { "news", 7, LOG_NEWS }, { "uucp", 8, LOG_UUCP }, { "cron", 9, LOG_CRON }, #ifndef LOG_AUTHPRIV #define LOG_AUTHPRIV (10 << 3) #endif { "authpriv", 10, LOG_AUTHPRIV }, #ifndef LOG_FTP #define LOG_FTP (11 << 3) #endif { "ftp", 11, LOG_FTP }, #ifndef LOG_NTP #define LOG_NTP (12 << 3) #endif { "ntp", 12, LOG_NTP }, #ifndef LOG_SECURITY #define LOG_SECURITY (13 << 3) #endif { "security", 13, LOG_SECURITY }, #ifndef LOG_CONSOLE #define LOG_CONSOLE (14 << 3) #endif { "console", 14, LOG_CONSOLE }, #ifndef LOG_CLOCK #define LOG_CLOCK (15 << 3) #endif { "clock", 15, LOG_CLOCK }, { "local0", 16, LOG_LOCAL0 }, { "local1", 17, LOG_LOCAL1 }, { "local2", 18, LOG_LOCAL2 }, { "local3", 19, LOG_LOCAL3 }, { "local4", 20, LOG_LOCAL4 }, { "local5", 21, LOG_LOCAL5 }, { "local6", 22, LOG_LOCAL6 }, { "local7", 23, LOG_LOCAL7 }, { NULL, 0, 0 } }; /* create channel */ static l2_result_t hook_create(l2_context_t *ctx, l2_channel_t *ch) { l2_ch_syslog_t *cfg; struct utsname uts; char *cp; /* allocate private channel configuration */ if ((cfg = (l2_ch_syslog_t *)malloc(sizeof(l2_ch_syslog_t))) == NULL) return L2_ERR_MEM; /* initialize configuration with reasonable defaults */ cfg->szTarget = strdup("local"); cfg->szRemoteHost = NULL; cfg->nRemotePort = 514; /*FIXME[thl] better use getservbyname()*/ if (uname(&uts) == 0) { cfg->szLocalHost = strdup(uts.nodename); if ((cp = strchr(cfg->szLocalHost, '.')) != NULL) *cp = '\0'; } else cfg->szLocalHost = strdup("localhost"); cfg->szFacility = strdup("user"); cfg->nFacility = LOG_USER; cfg->szIdent = NULL; cfg->bLogPid = FALSE; cfg->saRemoteSock = NULL; cfg->saaRemoteAddr = NULL; /* link private channel configuration into channel context */ ctx->vp = cfg; return L2_OK; } /* configure channel */ static l2_result_t hook_configure(l2_context_t *ctx, l2_channel_t *ch, const char *fmt, va_list ap) { l2_ch_syslog_t *cfg = (l2_ch_syslog_t *)ctx->vp; l2_param_t pa[8]; l2_result_t rv; l2_env_t *env; int i; /* feed and call generic parameter parsing engine */ L2_PARAM_SET(pa[0], target, STR, &cfg->szTarget); L2_PARAM_SET(pa[1], remotehost, STR, &cfg->szRemoteHost); L2_PARAM_SET(pa[2], remoteport, INT, &cfg->nRemotePort); L2_PARAM_SET(pa[3], localhost, STR, &cfg->szLocalHost); L2_PARAM_SET(pa[4], facility, STR, &cfg->szFacility); L2_PARAM_SET(pa[5], ident, STR, &cfg->szIdent); L2_PARAM_SET(pa[6], logpid, INT, &cfg->bLogPid); L2_PARAM_END(pa[7]); /* sanity checking & post-processing */ l2_channel_env(ch, &env); rv = l2_util_setparams(env, pa, fmt, ap); if (cfg->szTarget == NULL || cfg->szFacility == NULL) return L2_ERR_USE; if (!( strcmp(cfg->szTarget, "local") == 0 || strcmp(cfg->szTarget, "remote") == 0)) return L2_ERR_USE; for (i = 0; l2_ch_syslog_SLfac[i].name != NULL; i++) if (strcmp(l2_ch_syslog_SLfac[i].name, cfg->szFacility) == 0) break; if (l2_ch_syslog_SLfac[i].name == NULL) return L2_ERR_USE; if (strcmp(cfg->szTarget, "local") == 0) cfg->nFacility = l2_ch_syslog_SLfac[i].numLOCAL; else cfg->nFacility = (l2_ch_syslog_SLfac[i].numREMOTE << 3); if ( strcmp(cfg->szTarget, "remote") == 0 && (cfg->szRemoteHost == NULL || (cfg->nRemotePort <= 0 || cfg->nRemotePort >= 65536))) return L2_ERR_USE; if ( cfg->szLocalHost == NULL || strchr(cfg->szLocalHost, '.') != NULL) return L2_ERR_USE; if (cfg->szIdent != NULL && strlen(cfg->szIdent) > (32-(1+5+1))) return L2_ERR_USE; return rv; } /* open channel */ static l2_result_t hook_open(l2_context_t *ctx, l2_channel_t *ch) { l2_ch_syslog_t *cfg = (l2_ch_syslog_t *)ctx->vp; int opt; sa_rc_t rc; sa_addr_t *la; if (cfg->szIdent == NULL) return L2_ERR_USE; if (strcmp(cfg->szTarget, "local") == 0) { /* open local syslog connection via syslog(3) */ opt = 0; if (cfg->bLogPid) opt |= LOG_PID; openlog(cfg->szIdent, opt, cfg->nFacility); /* setlogmask(0); */ } else { /* open remote syslog connection via UDP socket */ if (cfg->szRemoteHost == NULL) return L2_ERR_USE; if ((rc = sa_addr_create(&cfg->saaRemoteAddr)) != SA_OK) return (rc == SA_ERR_SYS ? L2_ERR_SYS : L2_ERR_INT); if ((rc = sa_addr_u2a(cfg->saaRemoteAddr, "inet://%s:%d", cfg->szRemoteHost, cfg->nRemotePort)) != SA_OK) return (rc == SA_ERR_SYS ? L2_ERR_SYS : L2_ERR_INT); if ((rc = sa_create(&cfg->saRemoteSock)) != SA_OK) return (rc == SA_ERR_SYS ? L2_ERR_SYS : L2_ERR_INT); sa_type(cfg->saRemoteSock, SA_TYPE_DATAGRAM); sa_timeout(cfg->saRemoteSock, SA_TIMEOUT_ALL, 10, 0); if ((rc = sa_addr_create(&la)) != SA_OK) return (rc == SA_ERR_SYS ? L2_ERR_SYS : L2_ERR_INT); /* FIXME: if uid == 0 -> use port 514 */ if ((rc = sa_addr_u2a(la, "inet://0.0.0.0:0")) != SA_OK) return (rc == SA_ERR_SYS ? L2_ERR_SYS : L2_ERR_INT); if ((rc = sa_bind(cfg->saRemoteSock, la)) != SA_OK) return (rc == SA_ERR_SYS ? L2_ERR_SYS : L2_ERR_INT); sa_addr_destroy(la); } return L2_OK; } /* write to channel */ static l2_result_t hook_write(l2_context_t *ctx, l2_channel_t *ch, l2_level_t level, const char *buf, size_t buf_size) { l2_ch_syslog_t *cfg = (l2_ch_syslog_t *)ctx->vp; int prio; int i; char caTime[15+1]; char caBuf[2048]; time_t t; struct tm *tm; size_t n; sa_rc_t rc; /* determine syslog priority */ prio = 0; for (i = 0; l2_ch_syslog_L2toSL[i].levelL2 != -1; i++) { if (l2_ch_syslog_L2toSL[i].levelL2 == level) { prio = l2_ch_syslog_L2toSL[i].levelSL; break; } } if (l2_ch_syslog_L2toSL[i].levelL2 == -1) return L2_ERR_USE; /* FIXME: nul-terminate buf? */ if (strcmp(cfg->szTarget, "local") == 0) { /* send to local syslogd via syslog(3) */ syslog(prio, "%s", buf); } else { /* send to remote syslogd via UDP */ if (strlen(buf) > 1024) return L2_ERR_MEM; prio += cfg->nFacility; t = time(NULL); tm = localtime(&t); strftime(caTime, sizeof(caTime), "%b %d %H:%M:%S", tm); if (caTime[4] == '0') caTime[4] = ' '; if (cfg->bLogPid) n = l2_util_sprintf(caBuf, sizeof(caBuf), "<%d>%s %s %s[%lu]: %s", prio, caTime, cfg->szLocalHost, cfg->szIdent, (unsigned long)getpid(), buf); else n = l2_util_sprintf(caBuf, sizeof(caBuf), "<%d>%s %s %s: %s", prio, caTime, cfg->szLocalHost, cfg->szIdent, buf); if ((n = strlen(caBuf)) > 1024) return L2_ERR_IO; if ((rc = sa_send(cfg->saRemoteSock, cfg->saaRemoteAddr, caBuf, n, NULL)) != SA_OK) return (rc == SA_ERR_SYS ? L2_ERR_SYS : L2_ERR_IO); } return L2_OK; } /* close channel */ static l2_result_t hook_close(l2_context_t *ctx, l2_channel_t *ch) { l2_ch_syslog_t *cfg = (l2_ch_syslog_t *)ctx->vp; if (strcmp(cfg->szTarget, "local") == 0) { closelog(); } else { if (cfg->saRemoteSock != NULL) { sa_destroy(cfg->saRemoteSock); cfg->saRemoteSock = NULL; } if (cfg->saaRemoteAddr != NULL) { sa_addr_destroy(cfg->saaRemoteAddr); cfg->saaRemoteAddr = NULL; } } return L2_OK; } /* destroy channel */ static l2_result_t hook_destroy(l2_context_t *ctx, l2_channel_t *ch) { l2_ch_syslog_t *cfg = (l2_ch_syslog_t *)ctx->vp; /* destroy channel configuration */ if (cfg->szTarget != NULL) free(cfg->szTarget); if (cfg->szRemoteHost != NULL) free(cfg->szRemoteHost); if (cfg->szLocalHost != NULL) free(cfg->szLocalHost); if (cfg->szFacility != NULL) free(cfg->szFacility); if (cfg->szIdent != NULL) free(cfg->szIdent); if (cfg->saRemoteSock != NULL) sa_destroy(cfg->saRemoteSock); if (cfg->saaRemoteAddr != NULL) sa_addr_destroy(cfg->saaRemoteAddr); free(cfg); return L2_OK; } /* exported channel handler structure */ l2_handler_t l2_handler_syslog = { "syslog", L2_CHANNEL_OUTPUT, hook_create, hook_configure, hook_open, hook_write, NULL, hook_close, hook_destroy };