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