1 #include "LogSyslogFast.h"
2
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <netdb.h>
6 #include <netinet/in.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <sys/socket.h>
11 #include <sys/types.h>
12 #include <sys/un.h>
13 #include <unistd.h>
14
15 #define INITIAL_BUFSIZE 2048
16
17 static
18 void
update_prefix(LogSyslogFast * logger,time_t t)19 update_prefix(LogSyslogFast* logger, time_t t)
20 {
21 if (!logger->sender || !logger->name)
22 return; /* still initializing */
23
24 logger->last_time = t;
25
26 /* LOG_RFC3164 time string tops out at 15 chars, LOG_RFC5424 at 25 */
27 char timestr[26];
28 strftime(timestr, 26, logger->time_format, localtime(&t));
29
30 /* %z in strftime returns 4DIGIT, but we need 2DIGIT ":" 2DIGIT */
31 if (logger->format == LOG_RFC5424) {
32 timestr[25] = 0;
33 timestr[24] = timestr[23];
34 timestr[23] = timestr[22];
35 timestr[22] = ':';
36 }
37
38 if (logger->format == LOG_RFC3164 || logger->format == LOG_RFC5424) {
39 logger->prefix_len = snprintf(
40 logger->linebuf, logger->bufsize, logger->msg_format,
41 logger->priority, timestr, logger->sender, logger->name, logger->pid
42 );
43 } else if (logger->format == LOG_RFC3164_LOCAL) { /* without sender */
44 logger->prefix_len = snprintf(
45 logger->linebuf, logger->bufsize, logger->msg_format,
46 logger->priority, timestr, logger->name, logger->pid
47 );
48 }
49
50 if (logger->prefix_len > logger->bufsize - 1)
51 logger->prefix_len = logger->bufsize - 1;
52
53 /* cache the location in linebuf where msg should be pasted in */
54 logger->msg_start = logger->linebuf + logger->prefix_len;
55 }
56
57 LogSyslogFast*
LSF_alloc()58 LSF_alloc()
59 {
60 return malloc(sizeof(LogSyslogFast));
61 }
62
63 int
LSF_init(LogSyslogFast * logger,int proto,const char * hostname,int port,int facility,int severity,const char * sender,const char * name)64 LSF_init(
65 LogSyslogFast* logger, int proto, const char* hostname, int port,
66 int facility, int severity, const char* sender, const char* name)
67 {
68 if (!logger)
69 return -1;
70
71 logger->sock = -1;
72
73 logger->pid = getpid();
74
75 logger->linebuf = malloc(logger->bufsize = INITIAL_BUFSIZE);
76 if (!logger->linebuf) {
77 logger->err = strerror(errno);
78 return -1;
79 }
80
81 logger->sender = NULL;
82 logger->name = NULL;
83 LSF_set_format(logger, LOG_RFC3164);
84 LSF_set_sender(logger, sender);
85 LSF_set_name(logger, name);
86
87 logger->priority = (facility << 3) | severity;
88 update_prefix(logger, time(0));
89
90 return LSF_set_receiver(logger, proto, hostname, port);
91 }
92
93 int
LSF_destroy(LogSyslogFast * logger)94 LSF_destroy(LogSyslogFast* logger)
95 {
96 int ret = close(logger->sock);
97 if (ret)
98 logger->err = strerror(errno);
99 free(logger->sender);
100 free(logger->name);
101 free(logger->linebuf);
102 free(logger);
103 return ret;
104 }
105
106 void
LSF_set_priority(LogSyslogFast * logger,int facility,int severity)107 LSF_set_priority(LogSyslogFast* logger, int facility, int severity)
108 {
109 logger->priority = (facility << 3) | severity;
110 update_prefix(logger, time(0));
111 }
112
113 void
LSF_set_facility(LogSyslogFast * logger,int facility)114 LSF_set_facility(LogSyslogFast* logger, int facility)
115 {
116 LSF_set_priority(logger, facility, LSF_get_severity(logger));
117 }
118
119 void
LSF_set_severity(LogSyslogFast * logger,int severity)120 LSF_set_severity(LogSyslogFast* logger, int severity)
121 {
122 LSF_set_priority(logger, LSF_get_facility(logger), severity);
123 }
124
125 int
LSF_set_sender(LogSyslogFast * logger,const char * sender)126 LSF_set_sender(LogSyslogFast* logger, const char* sender)
127 {
128 free(logger->sender);
129 logger->sender = strdup(sender);
130 if (!logger->sender) {
131 logger->err = "strdup failure in set_sender";
132 return -1;
133 }
134 update_prefix(logger, time(0));
135 return 0;
136 }
137
138 int
LSF_set_name(LogSyslogFast * logger,const char * name)139 LSF_set_name(LogSyslogFast* logger, const char* name)
140 {
141 free(logger->name);
142 logger->name = strdup(name);
143 if (!logger->name) {
144 logger->err = "strdup failure in set_name";
145 return -1;
146 }
147 update_prefix(logger, time(0));
148 return 0;
149 }
150
151 void
LSF_set_pid(LogSyslogFast * logger,int pid)152 LSF_set_pid(LogSyslogFast* logger, int pid)
153 {
154 logger->pid = pid;
155 update_prefix(logger, time(0));
156 }
157
158 int
LSF_set_format(LogSyslogFast * logger,int format)159 LSF_set_format(LogSyslogFast* logger, int format)
160 {
161 logger->format = format;
162
163 /* msg_format must contain format strings for:
164 1) priority (int)
165 2) timestamp (string)
166 3) hostname (string)
167 4) app name (string)
168 5) pid (int)
169 */
170 if (logger->format == LOG_RFC3164) {
171 /*
172 'The TIMESTAMP field is the local time and is in the format of
173 "Mmm dd hh:mm:ss"'
174
175 Example: "Jan 4 11:22:33"
176 */
177 logger->time_format = "%h %e %H:%M:%S";
178 logger->msg_format = "<%d>%s %s %s[%d]: ";
179 }
180 else if (logger->format == LOG_RFC5424) {
181 /*
182 TIMESTAMP = NILVALUE / FULL-DATE "T" FULL-TIME
183 FULL-DATE = DATE-FULLYEAR "-" DATE-MONTH "-" DATE-MDAY
184 DATE-FULLYEAR = 4DIGIT
185 DATE-MONTH = 2DIGIT ; 01-12
186 DATE-MDAY = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on
187 ; month/year
188 FULL-TIME = PARTIAL-TIME TIME-OFFSET
189 PARTIAL-TIME = TIME-HOUR ":" TIME-MINUTE ":" TIME-SECOND
190 [TIME-SECFRAC]
191 TIME-HOUR = 2DIGIT ; 00-23
192 TIME-MINUTE = 2DIGIT ; 00-59
193 TIME-SECOND = 2DIGIT ; 00-59
194 TIME-SECFRAC = "." 1*6DIGIT
195 TIME-OFFSET = "Z" / TIME-NUMOFFSET
196 TIME-NUMOFFSET = ("+" / "-") TIME-HOUR ":" TIME-MINUTE
197
198 Example: "2012-01-04T11:22:33-0800"
199 */
200 logger->time_format = "%Y-%m-%dT%H:%M:%S%z";
201
202 /* STRUCTURED-DATA and MSGID fields are omitted */
203 logger->msg_format = "<%d>1 %s %s %s %d - - ";
204 }
205 else if (logger->format == LOG_RFC3164_LOCAL) {
206 /* Same as LOG_RFC3164 but without HOSTNAME */
207 logger->time_format = "%h %e %H:%M:%S";
208 logger->msg_format = "<%d>%s %s[%d]: ";
209 }
210 else {
211 logger->err = "invalid format constant";
212 return -1;
213 }
214 update_prefix(logger, time(0));
215 return 0;
216 }
217
218 #ifdef AF_INET6
219 #define clean_return(x) if (results) freeaddrinfo(results); return x;
220 #else
221 #define clean_return(x) return x;
222 #endif
223
224 /* must match constants in LogSyslogFast.pm */
225 #define LOG_UDP 0
226 #define LOG_TCP 1
227 #define LOG_UNIX 2
228
229 int
LSF_set_receiver(LogSyslogFast * logger,int proto,const char * hostname,int port)230 LSF_set_receiver(LogSyslogFast* logger, int proto, const char* hostname, int port)
231 {
232 const struct sockaddr* p_address;
233 int address_len;
234 #ifdef AF_INET6
235 struct addrinfo* results = NULL;
236 #endif
237
238 if (logger->sock >= 0) {
239 int ret = close(logger->sock);
240 if (ret) {
241 logger->err = strerror(errno);
242 return -1;
243 }
244 }
245
246 /* set up a socket, letting kernel assign local port */
247 if (proto == LOG_UDP || proto == LOG_TCP) {
248
249 #ifdef AF_INET6
250
251 /* For NetBSD: http://www.mail-archive.com/bug-gnulib@gnu.org/msg17067.html */
252 #ifndef AI_ADDRCONFIG
253 #define AI_ADDRCONFIG 0
254 #endif
255
256 /* For MacOS: http://mailman.videolan.org/pipermail/vlc-devel/2008-May/044005.html */
257 #ifndef AI_NUMERICSERV
258 #define AI_NUMERICSERV 0
259 #endif
260
261 struct addrinfo *rp;
262 struct addrinfo hints;
263 char portstr[32];
264 int r;
265
266 snprintf(portstr, sizeof(portstr), "%d", port);
267 memset(&hints, 0, sizeof(hints));
268 hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
269 hints.ai_family = AF_UNSPEC;
270 if (proto == LOG_TCP) {
271 hints.ai_socktype = SOCK_STREAM;
272 } else {
273 hints.ai_socktype = SOCK_DGRAM;
274 }
275 hints.ai_protocol = 0;
276 hints.ai_addrlen = 0;
277 hints.ai_addr = NULL;
278 hints.ai_canonname = NULL;
279 hints.ai_next = NULL;
280
281 r = getaddrinfo(hostname, portstr, &hints, &results);
282 if (r < 0) {
283 logger->err = gai_strerror(r);
284 return -1;
285 }
286 else if (!results) {
287 logger->err = "no results from getaddrinfo";
288 return -1;
289 }
290 for (rp = results; rp != NULL; rp = rp->ai_next) {
291 logger->sock = socket(rp->ai_family, rp->ai_socktype, 0);
292 if (logger->sock == -1) {
293 r = errno;
294 continue;
295 }
296 p_address = rp->ai_addr;
297 address_len = rp->ai_addrlen;
298 break;
299 }
300 if (logger->sock == -1) {
301 logger->err = "socket failure";
302 clean_return(-1);
303 }
304
305 #else /* !AF_INET6 */
306
307 /* resolve the remote host */
308 struct hostent* host = gethostbyname(hostname);
309 if (!host || !host->h_addr_list || !host->h_addr_list[0]) {
310 logger->err = "resolve failure";
311 return -1;
312 }
313
314 /* create the remote host's address */
315 struct sockaddr_in raddress;
316 raddress.sin_family = AF_INET;
317 memcpy(&raddress.sin_addr, host->h_addr_list[0], sizeof(raddress.sin_addr));
318 raddress.sin_port = htons(port);
319 p_address = (const struct sockaddr*) &raddress;
320 address_len = sizeof(raddress);
321
322 /* construct socket */
323 if (proto == LOG_UDP) {
324 logger->sock = socket(AF_INET, SOCK_DGRAM, 0);
325
326 /* make the socket non-blocking */
327 int flags = fcntl(logger->sock, F_GETFL, 0);
328 fcntl(logger->sock, F_SETFL, flags | O_NONBLOCK);
329 flags = fcntl(logger->sock, F_GETFL, 0);
330 if (!(flags & O_NONBLOCK)) {
331 logger->err = "nonblock failure";
332 return -1;
333 }
334 }
335 else if (proto == LOG_TCP) {
336 logger->sock = socket(AF_INET, SOCK_STREAM, 0);
337 }
338
339 #endif /* AF_INET6 */
340 }
341 else if (proto == LOG_UNIX) {
342
343 /* create the log device's address */
344 struct sockaddr_un raddress;
345 raddress.sun_family = AF_UNIX;
346 strncpy(raddress.sun_path, hostname, sizeof(raddress.sun_path) - 1);
347 p_address = (const struct sockaddr*) &raddress;
348 address_len = sizeof(raddress);
349
350 /* construct socket */
351 logger->sock = socket(AF_UNIX, SOCK_STREAM, 0);
352 }
353 else {
354 logger->err = "bad protocol";
355 return -1;
356 }
357
358 if (logger->sock < 0) {
359 logger->err = strerror(errno);
360 clean_return(-1);
361 }
362
363 /* close the socket after exec to match normal Perl behavior for sockets */
364 fcntl(logger->sock, F_SETFD, FD_CLOEXEC);
365
366 /* connect the socket */
367 if (connect(logger->sock, p_address, address_len) != 0) {
368 /* some servers (rsyslog) may use SOCK_DGRAM for unix domain sockets */
369 if (proto == LOG_UNIX && errno == EPROTOTYPE) {
370 /* clean up existing bad socket */
371 close(logger->sock);
372 if (logger->sock < 0) {
373 logger->err = strerror(errno);
374 clean_return(-1);
375 }
376
377 logger->sock = socket(AF_UNIX, SOCK_DGRAM, 0);
378 if (connect(logger->sock, p_address, address_len) != 0) {
379 logger->err = strerror(errno);
380 clean_return(-1);
381 }
382 }
383 else {
384 logger->err = strerror(errno);
385 clean_return(-1);
386 }
387 }
388
389 clean_return(0);
390 }
391
392 int
LSF_send(LogSyslogFast * logger,const char * msg_str,int msg_len,time_t t)393 LSF_send(LogSyslogFast* logger, const char* msg_str, int msg_len, time_t t)
394 {
395 /* update the prefix if seconds have rolled over */
396 if (t != logger->last_time)
397 update_prefix(logger, t);
398
399 int line_len = logger->prefix_len + msg_len;
400 /* ensure there's space in the buffer for total length including a trailing NULL */
401 if (logger->bufsize < line_len + 1) {
402 /* try to increase buffer */
403 int new_bufsize = 2 * logger->bufsize;
404 while (new_bufsize < line_len + 1)
405 new_bufsize *= 2;
406 if (new_bufsize < 0) {
407 /* overflow */
408 logger->err = "message too large";
409 return -1;
410 }
411
412 char* new_buf = realloc(logger->linebuf, new_bufsize);
413 if (!new_buf) {
414 logger->err = strerror(errno);
415 return -1;
416 }
417
418 logger->linebuf = new_buf;
419 logger->bufsize = new_bufsize;
420 logger->msg_start = logger->linebuf + logger->prefix_len;
421 }
422
423 /* paste the message into linebuf just past where the prefix was placed */
424 memcpy(logger->msg_start, msg_str, msg_len + 1); /* include perl-added null */
425
426 int ret = send(logger->sock, logger->linebuf, line_len, 0);
427
428 if (ret < 0)
429 logger->err = strerror(errno);
430 return ret;
431 }
432
433 int
LSF_get_priority(LogSyslogFast * logger)434 LSF_get_priority(LogSyslogFast* logger)
435 {
436 return logger->priority;
437 }
438
439 int
LSF_get_facility(LogSyslogFast * logger)440 LSF_get_facility(LogSyslogFast* logger)
441 {
442 return logger->priority >> 3;
443 }
444
445 int
LSF_get_severity(LogSyslogFast * logger)446 LSF_get_severity(LogSyslogFast* logger)
447 {
448 return logger->priority & 7;
449 }
450
451 const char*
LSF_get_sender(LogSyslogFast * logger)452 LSF_get_sender(LogSyslogFast* logger)
453 {
454 return logger->sender;
455 }
456
457 const char*
LSF_get_name(LogSyslogFast * logger)458 LSF_get_name(LogSyslogFast* logger)
459 {
460 return logger->name;
461 }
462
463 int
LSF_get_pid(LogSyslogFast * logger)464 LSF_get_pid(LogSyslogFast* logger)
465 {
466 return logger->pid;
467 }
468
469 int
LSF_get_sock(LogSyslogFast * logger)470 LSF_get_sock(LogSyslogFast* logger)
471 {
472 return logger->sock;
473 }
474
475 int
LSF_get_format(LogSyslogFast * logger)476 LSF_get_format(LogSyslogFast* logger)
477 {
478 return logger->format;
479 }
480