1 /*
2 **  OSSP l2 - Flexible Logging
3 **  Copyright (c) 2001-2005 Cable & Wireless <http://www.cw.com/>
4 **  Copyright (c) 2001-2005 The OSSP Project <http://www.ossp.org/>
5 **  Copyright (c) 2001-2005 Ralf S. Engelschall <rse@engelschall.com>
6 **
7 **  This file is part of OSSP l2, a flexible logging library which
8 **  can be found at http://www.ossp.org/pkg/lib/l2/.
9 **
10 **  Permission to use, copy, modify, and distribute this software for
11 **  any purpose with or without fee is hereby granted, provided that
12 **  the above copyright notice and this permission notice appear in all
13 **  copies.
14 **
15 **  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
16 **  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17 **  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 **  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
19 **  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 **  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 **  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
22 **  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 **  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 **  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 **  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 **  SUCH DAMAGE.
27 **
28 **  l2_ch_syslog.c: syslog(3) channel implementation
29 */
30 
31 #include "l2.h"
32 #include "l2_p.h"
33 
34 #include <syslog.h>
35 #include <time.h>
36 #include <sys/types.h>
37 #include <sys/utsname.h>
38 #include <unistd.h>
39 
40 /* declare private channel configuration */
41 typedef struct {
42     char      *szTarget;
43     char      *szRemoteHost;
44     int        nRemotePort;
45     char      *szLocalHost;
46     char      *szFacility;
47     int        nFacility;
48     char      *szIdent;
49     int        bLogPid;
50     sa_t      *saRemoteSock;
51     sa_addr_t *saaRemoteAddr;
52 } l2_ch_syslog_t;
53 
54 /* mapping from L2 log levels to syslog(3) log levels */
55 static struct {
56     int levelL2;
57     int levelSL;
58 } l2_ch_syslog_L2toSL[] = {
59     { L2_LEVEL_PANIC,    LOG_EMERG   },
60     { L2_LEVEL_CRITICAL, LOG_CRIT    },
61     { L2_LEVEL_ERROR,    LOG_ERR	 },
62     { L2_LEVEL_WARNING,  LOG_WARNING },
63     { L2_LEVEL_NOTICE,   LOG_NOTICE  },
64     { L2_LEVEL_INFO,     LOG_INFO	 },
65     { L2_LEVEL_TRACE,    LOG_INFO    },
66     { L2_LEVEL_DEBUG,    LOG_DEBUG   },
67     { -1,                -1          }
68 };
69 
70 /* Syslog Facility Table */
71 static struct {
72     char *name;      /* the common name */
73     int   numREMOTE; /* number according to RFC3164 */
74     int   numLOCAL;  /* local encoding */
75 } l2_ch_syslog_SLfac[] = {
76     { "kern",      0,      LOG_KERN     },
77     { "user",      1,      LOG_USER     },
78     { "mail",      2,      LOG_MAIL     },
79     { "daemon",    3,      LOG_DAEMON   },
80     { "auth",      4,      LOG_AUTH     },
81 #ifndef LOG_SYSLOG
82 #define LOG_SYSLOG   (5 << 3)
83 #endif
84     { "syslog",    5,      LOG_SYSLOG   },
85     { "lpr",       6,      LOG_LPR      },
86     { "news",      7,      LOG_NEWS     },
87     { "uucp",      8,      LOG_UUCP     },
88     { "cron",      9,      LOG_CRON     },
89 #ifndef LOG_AUTHPRIV
90 #define LOG_AUTHPRIV (10 << 3)
91 #endif
92     { "authpriv", 10,      LOG_AUTHPRIV },
93 #ifndef LOG_FTP
94 #define LOG_FTP      (11 << 3)
95 #endif
96     { "ftp",      11,      LOG_FTP      },
97 #ifndef LOG_NTP
98 #define LOG_NTP      (12 << 3)
99 #endif
100     { "ntp",      12,      LOG_NTP      },
101 #ifndef LOG_SECURITY
102 #define LOG_SECURITY (13 << 3)
103 #endif
104     { "security", 13,      LOG_SECURITY },
105 #ifndef LOG_CONSOLE
106 #define LOG_CONSOLE  (14 << 3)
107 #endif
108     { "console",  14,      LOG_CONSOLE  },
109 #ifndef LOG_CLOCK
110 #define LOG_CLOCK    (15 << 3)
111 #endif
112     { "clock",    15,      LOG_CLOCK    },
113     { "local0",   16,      LOG_LOCAL0   },
114     { "local1",   17,      LOG_LOCAL1   },
115     { "local2",   18,      LOG_LOCAL2   },
116     { "local3",   19,      LOG_LOCAL3   },
117     { "local4",   20,      LOG_LOCAL4   },
118     { "local5",   21,      LOG_LOCAL5   },
119     { "local6",   22,      LOG_LOCAL6   },
120     { "local7",   23,      LOG_LOCAL7   },
121     { NULL,       0,       0            }
122 };
123 
124 /* create channel */
hook_create(l2_context_t * ctx,l2_channel_t * ch)125 static l2_result_t hook_create(l2_context_t *ctx, l2_channel_t *ch)
126 {
127     l2_ch_syslog_t *cfg;
128     struct utsname uts;
129     char *cp;
130 
131     /* allocate private channel configuration */
132     if ((cfg = (l2_ch_syslog_t *)malloc(sizeof(l2_ch_syslog_t))) == NULL)
133         return L2_ERR_MEM;
134 
135     /* initialize configuration with reasonable defaults */
136     cfg->szTarget      = strdup("local");
137     cfg->szRemoteHost  = NULL;
138     cfg->nRemotePort   = 514; /*FIXME[thl] better use getservbyname()*/
139     if (uname(&uts) == 0) {
140         cfg->szLocalHost = strdup(uts.nodename);
141         if ((cp = strchr(cfg->szLocalHost, '.')) != NULL)
142             *cp = '\0';
143     }
144     else
145         cfg->szLocalHost = strdup("localhost");
146     cfg->szFacility    = strdup("user");
147     cfg->nFacility     = LOG_USER;
148     cfg->szIdent       = NULL;
149     cfg->bLogPid       = FALSE;
150     cfg->saRemoteSock  = NULL;
151     cfg->saaRemoteAddr = NULL;
152 
153     /* link private channel configuration into channel context */
154     ctx->vp = cfg;
155 
156     return L2_OK;
157 }
158 
159 /* configure channel */
hook_configure(l2_context_t * ctx,l2_channel_t * ch,const char * fmt,va_list * ap)160 static l2_result_t hook_configure(l2_context_t *ctx, l2_channel_t *ch, const char *fmt, va_list *ap)
161 {
162     l2_ch_syslog_t *cfg = (l2_ch_syslog_t *)ctx->vp;
163     l2_param_t pa[8];
164     l2_result_t rv;
165     l2_env_t *env;
166     int i;
167 
168     /* feed and call generic parameter parsing engine */
169     L2_PARAM_SET(pa[0], target,     STR, &cfg->szTarget);
170     L2_PARAM_SET(pa[1], remotehost, STR, &cfg->szRemoteHost);
171     L2_PARAM_SET(pa[2], remoteport, INT, &cfg->nRemotePort);
172     L2_PARAM_SET(pa[3], localhost,  STR, &cfg->szLocalHost);
173     L2_PARAM_SET(pa[4], facility,   STR, &cfg->szFacility);
174     L2_PARAM_SET(pa[5], ident,      STR, &cfg->szIdent);
175     L2_PARAM_SET(pa[6], logpid,     INT, &cfg->bLogPid);
176     L2_PARAM_END(pa[7]);
177 
178     /* sanity checking & post-processing */
179     l2_channel_env(ch, &env);
180     rv = l2_util_setparams(env, pa, fmt, ap);
181     if (cfg->szTarget == NULL || cfg->szFacility == NULL)
182         return L2_ERR_USE;
183     if (!(   strcmp(cfg->szTarget, "local") == 0
184           || strcmp(cfg->szTarget, "remote") == 0))
185         return L2_ERR_USE;
186     for (i = 0; l2_ch_syslog_SLfac[i].name != NULL; i++)
187         if (strcmp(l2_ch_syslog_SLfac[i].name, cfg->szFacility) == 0)
188             break;
189     if (l2_ch_syslog_SLfac[i].name == NULL)
190         return L2_ERR_USE;
191     if (strcmp(cfg->szTarget, "local") == 0)
192         cfg->nFacility = l2_ch_syslog_SLfac[i].numLOCAL;
193     else
194         cfg->nFacility = (l2_ch_syslog_SLfac[i].numREMOTE << 3);
195     if (   strcmp(cfg->szTarget, "remote") == 0
196         && (cfg->szRemoteHost == NULL
197             || (cfg->nRemotePort <= 0 || cfg->nRemotePort >= 65536)))
198         return L2_ERR_USE;
199     if (   cfg->szLocalHost == NULL
200         || strchr(cfg->szLocalHost, '.') != NULL)
201         return L2_ERR_USE;
202     if (cfg->szIdent != NULL && strlen(cfg->szIdent) > (32-(1+5+1)))
203         return L2_ERR_USE;
204     return rv;
205 }
206 
207 /* open channel */
hook_open(l2_context_t * ctx,l2_channel_t * ch)208 static l2_result_t hook_open(l2_context_t *ctx, l2_channel_t *ch)
209 {
210     l2_ch_syslog_t *cfg = (l2_ch_syslog_t *)ctx->vp;
211     int opt;
212     sa_rc_t rc;
213     sa_addr_t *la;
214 
215     if (cfg->szIdent == NULL)
216         return L2_ERR_USE;
217     if (strcmp(cfg->szTarget, "local") == 0) {
218         /* open local syslog connection via syslog(3) */
219         opt = 0;
220         if (cfg->bLogPid)
221             opt |= LOG_PID;
222         openlog(cfg->szIdent, opt, cfg->nFacility);
223         /* setlogmask(0); */
224     }
225     else {
226         /* open remote syslog connection via UDP socket */
227         if (cfg->szRemoteHost == NULL)
228             return L2_ERR_USE;
229         if ((rc = sa_addr_create(&cfg->saaRemoteAddr)) != SA_OK)
230             return (rc == SA_ERR_SYS ? L2_ERR_SYS : L2_ERR_INT);
231         if ((rc = sa_addr_u2a(cfg->saaRemoteAddr, "inet://%s:%d",
232                               cfg->szRemoteHost, cfg->nRemotePort)) != SA_OK)
233             return (rc == SA_ERR_SYS ? L2_ERR_SYS : L2_ERR_INT);
234         if ((rc = sa_create(&cfg->saRemoteSock)) != SA_OK)
235             return (rc == SA_ERR_SYS ? L2_ERR_SYS : L2_ERR_INT);
236         sa_type(cfg->saRemoteSock, SA_TYPE_DATAGRAM);
237         sa_timeout(cfg->saRemoteSock, SA_TIMEOUT_ALL, 10, 0);
238         if ((rc = sa_addr_create(&la)) != SA_OK)
239             return (rc == SA_ERR_SYS ? L2_ERR_SYS : L2_ERR_INT);
240         /* FIXME: if uid == 0 -> use port 514 */
241         if ((rc = sa_addr_u2a(la, "inet://0.0.0.0:0")) != SA_OK)
242             return (rc == SA_ERR_SYS ? L2_ERR_SYS : L2_ERR_INT);
243         if ((rc = sa_bind(cfg->saRemoteSock, la)) != SA_OK)
244             return (rc == SA_ERR_SYS ? L2_ERR_SYS : L2_ERR_INT);
245         sa_addr_destroy(la);
246     }
247     return L2_OK;
248 }
249 
250 /* write to channel */
hook_write(l2_context_t * ctx,l2_channel_t * ch,l2_level_t level,const char * buf,size_t buf_size)251 static l2_result_t hook_write(l2_context_t *ctx, l2_channel_t *ch,
252                               l2_level_t level, const char *buf, size_t buf_size)
253 {
254     l2_ch_syslog_t *cfg = (l2_ch_syslog_t *)ctx->vp;
255     int prio;
256     int i;
257     char caTime[15+1];
258     char caBuf[2048];
259     time_t t;
260     struct tm *tm;
261     size_t n;
262     sa_rc_t rc;
263 
264     /* determine syslog priority */
265     prio = 0;
266     for (i = 0; l2_ch_syslog_L2toSL[i].levelL2 != -1; i++) {
267         if (l2_ch_syslog_L2toSL[i].levelL2 == level) {
268             prio = l2_ch_syslog_L2toSL[i].levelSL;
269             break;
270         }
271     }
272     if (l2_ch_syslog_L2toSL[i].levelL2 == -1)
273         return L2_ERR_USE;
274 
275     /* FIXME: nul-terminate buf? */
276 
277     if (strcmp(cfg->szTarget, "local") == 0) {
278         /* send to local syslogd via syslog(3) */
279         syslog(prio, "%s", buf);
280     }
281     else {
282         /* send to remote syslogd via UDP */
283         if (strlen(buf) > 1024)
284             return L2_ERR_MEM;
285         prio += cfg->nFacility;
286         t = time(NULL);
287         tm = localtime(&t);
288         strftime(caTime, sizeof(caTime), "%b %d %H:%M:%S", tm);
289         if (caTime[4] == '0')
290             caTime[4] = ' ';
291         if (cfg->bLogPid)
292             n = l2_util_sprintf(caBuf, sizeof(caBuf), "<%d>%s %s %s[%lu]: %s",
293                                 prio, caTime, cfg->szLocalHost,
294                                 cfg->szIdent, (unsigned long)getpid(), buf);
295         else
296             n = l2_util_sprintf(caBuf, sizeof(caBuf), "<%d>%s %s %s: %s",
297                                 prio, caTime, cfg->szLocalHost,
298                                 cfg->szIdent, buf);
299         if ((n = strlen(caBuf)) > 1024)
300             return L2_ERR_IO;
301         if ((rc = sa_send(cfg->saRemoteSock, cfg->saaRemoteAddr,
302                           caBuf, n, NULL)) != SA_OK)
303             return (rc == SA_ERR_SYS ? L2_ERR_SYS : L2_ERR_IO);
304     }
305 
306     return L2_OK;
307 }
308 
309 /* close channel */
hook_close(l2_context_t * ctx,l2_channel_t * ch)310 static l2_result_t hook_close(l2_context_t *ctx, l2_channel_t *ch)
311 {
312     l2_ch_syslog_t *cfg = (l2_ch_syslog_t *)ctx->vp;
313 
314     if (strcmp(cfg->szTarget, "local") == 0) {
315         closelog();
316     }
317     else {
318         if (cfg->saRemoteSock != NULL) {
319             sa_destroy(cfg->saRemoteSock);
320             cfg->saRemoteSock = NULL;
321         }
322         if (cfg->saaRemoteAddr != NULL) {
323             sa_addr_destroy(cfg->saaRemoteAddr);
324             cfg->saaRemoteAddr = NULL;
325         }
326     }
327     return L2_OK;
328 }
329 
330 /* destroy channel */
hook_destroy(l2_context_t * ctx,l2_channel_t * ch)331 static l2_result_t hook_destroy(l2_context_t *ctx, l2_channel_t *ch)
332 {
333     l2_ch_syslog_t *cfg = (l2_ch_syslog_t *)ctx->vp;
334 
335     /* destroy channel configuration */
336     if (cfg->szTarget != NULL)
337         free(cfg->szTarget);
338     if (cfg->szRemoteHost != NULL)
339         free(cfg->szRemoteHost);
340     if (cfg->szLocalHost != NULL)
341         free(cfg->szLocalHost);
342     if (cfg->szFacility != NULL)
343         free(cfg->szFacility);
344     if (cfg->szIdent != NULL)
345         free(cfg->szIdent);
346     if (cfg->saRemoteSock != NULL)
347         sa_destroy(cfg->saRemoteSock);
348     if (cfg->saaRemoteAddr != NULL)
349         sa_addr_destroy(cfg->saaRemoteAddr);
350     free(cfg);
351 
352     return L2_OK;
353 }
354 
355 /* exported channel handler structure */
356 l2_handler_t l2_handler_syslog = {
357     "syslog",
358     L2_CHANNEL_OUTPUT,
359     hook_create,
360     hook_configure,
361     hook_open,
362     hook_write,
363     NULL,
364     hook_close,
365     hook_destroy
366 };
367 
368