File Coverage

LogSyslogFast.c
Criterion Covered Total %
statement 160 194 82.4
branch 60 88 68.1
condition n/a
subroutine n/a
pod n/a
total 220 282 78.0


line stmt bran cond sub pod time code
1             #include "LogSyslogFast.h"
2              
3             #include
4             #include
5             #include
6             #include
7             #include
8             #include
9             #include
10             #include
11             #include
12             #include
13             #include
14              
15             #define INITIAL_BUFSIZE 2048
16              
17             static
18             void
19 200           update_prefix(LogSyslogFast* logger, time_t t)
20             {
21 200 100         if (!logger->sender || !logger->name)
    100          
22 74           return; /* still initializing */
23              
24 126           logger->last_time = t;
25              
26             /* LOG_RFC3164 time string tops out at 15 chars, LOG_RFC5424 at 25 */
27             char timestr[26];
28 126           strftime(timestr, 26, logger->time_format, localtime(&t));
29              
30             /* %z in strftime returns 4DIGIT, but we need 2DIGIT ":" 2DIGIT */
31 126 100         if (logger->format == LOG_RFC5424) {
32 4           timestr[25] = 0;
33 4           timestr[24] = timestr[23];
34 4           timestr[23] = timestr[22];
35 4           timestr[22] = ':';
36             }
37              
38 126 100         if (logger->format == LOG_RFC3164 || logger->format == LOG_RFC5424) {
    100          
39 118           logger->prefix_len = snprintf(
40 118           logger->linebuf, logger->bufsize, logger->msg_format,
41             logger->priority, timestr, logger->sender, logger->name, logger->pid
42             );
43 8 50         } else if (logger->format == LOG_RFC3164_LOCAL) { /* without sender */
44 8           logger->prefix_len = snprintf(
45 8           logger->linebuf, logger->bufsize, logger->msg_format,
46             logger->priority, timestr, logger->name, logger->pid
47             );
48             }
49              
50 126 50         if (logger->prefix_len > logger->bufsize - 1)
51 0           logger->prefix_len = logger->bufsize - 1;
52              
53             /* cache the location in linebuf where msg should be pasted in */
54 126           logger->msg_start = logger->linebuf + logger->prefix_len;
55             }
56              
57             LogSyslogFast*
58 37           LSF_alloc()
59             {
60 37           return malloc(sizeof(LogSyslogFast));
61             }
62              
63             int
64 37           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 37 50         if (!logger)
69 0           return -1;
70              
71 37           logger->sock = -1;
72              
73 37           logger->pid = getpid();
74              
75 37           logger->linebuf = malloc(logger->bufsize = INITIAL_BUFSIZE);
76 37 50         if (!logger->linebuf) {
77 0           logger->err = strerror(errno);
78 0           return -1;
79             }
80              
81 37           logger->sender = NULL;
82 37           logger->name = NULL;
83 37           LSF_set_format(logger, LOG_RFC3164);
84 37           LSF_set_sender(logger, sender);
85 37           LSF_set_name(logger, name);
86              
87 37           logger->priority = (facility << 3) | severity;
88 37           update_prefix(logger, time(0));
89              
90 37           return LSF_set_receiver(logger, proto, hostname, port);
91             }
92              
93             int
94 28           LSF_destroy(LogSyslogFast* logger)
95             {
96 28           int ret = close(logger->sock);
97 28 50         if (ret)
98 0           logger->err = strerror(errno);
99 28           free(logger->sender);
100 28           free(logger->name);
101 28           free(logger->linebuf);
102 28           free(logger);
103 28           return ret;
104             }
105              
106             void
107 11           LSF_set_priority(LogSyslogFast* logger, int facility, int severity)
108             {
109 11           logger->priority = (facility << 3) | severity;
110 11           update_prefix(logger, time(0));
111 11           }
112              
113             void
114 1           LSF_set_facility(LogSyslogFast* logger, int facility)
115             {
116 1           LSF_set_priority(logger, facility, LSF_get_severity(logger));
117 1           }
118              
119             void
120 1           LSF_set_severity(LogSyslogFast* logger, int severity)
121             {
122 1           LSF_set_priority(logger, LSF_get_facility(logger), severity);
123 1           }
124              
125             int
126 46           LSF_set_sender(LogSyslogFast* logger, const char* sender)
127             {
128 46           free(logger->sender);
129 46           logger->sender = strdup(sender);
130 46 50         if (!logger->sender) {
131 0           logger->err = "strdup failure in set_sender";
132 0           return -1;
133             }
134 46           update_prefix(logger, time(0));
135 46           return 0;
136             }
137              
138             int
139 46           LSF_set_name(LogSyslogFast* logger, const char* name)
140             {
141 46           free(logger->name);
142 46           logger->name = strdup(name);
143 46 50         if (!logger->name) {
144 0           logger->err = "strdup failure in set_name";
145 0           return -1;
146             }
147 46           update_prefix(logger, time(0));
148 46           return 0;
149             }
150              
151             void
152 9           LSF_set_pid(LogSyslogFast* logger, int pid)
153             {
154 9           logger->pid = pid;
155 9           update_prefix(logger, time(0));
156 9           }
157              
158             int
159 51           LSF_set_format(LogSyslogFast* logger, int format)
160             {
161 51           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 51 100         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 39           logger->time_format = "%h %e %H:%M:%S";
178 39           logger->msg_format = "<%d>%s %s %s[%d]: ";
179             }
180 12 100         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 4           logger->time_format = "%Y-%m-%dT%H:%M:%S%z";
201              
202             /* STRUCTURED-DATA and MSGID fields are omitted */
203 4           logger->msg_format = "<%d>1 %s %s %s %d - - ";
204             }
205 8 50         else if (logger->format == LOG_RFC3164_LOCAL) {
206             /* Same as LOG_RFC3164 but without HOSTNAME */
207 8           logger->time_format = "%h %e %H:%M:%S";
208 8           logger->msg_format = "<%d>%s %s[%d]: ";
209             }
210             else {
211 0           logger->err = "invalid format constant";
212 0           return -1;
213             }
214 51           update_prefix(logger, time(0));
215 51           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
230 145           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 145           struct addrinfo* results = NULL;
236             #endif
237              
238 145 100         if (logger->sock >= 0) {
239 108           int ret = close(logger->sock);
240 108 50         if (ret) {
241 0           logger->err = strerror(errno);
242 0           return -1;
243             }
244             }
245              
246             /* set up a socket, letting kernel assign local port */
247 268 100         if (proto == LOG_UDP || proto == LOG_TCP) {
    100          
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 125           snprintf(portstr, sizeof(portstr), "%d", port);
267 125           memset(&hints, 0, sizeof(hints));
268 125           hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
269 125           hints.ai_family = AF_UNSPEC;
270 125 100         if (proto == LOG_TCP) {
271 110           hints.ai_socktype = SOCK_STREAM;
272             } else {
273 15           hints.ai_socktype = SOCK_DGRAM;
274             }
275 125           hints.ai_protocol = 0;
276 125           hints.ai_addrlen = 0;
277 125           hints.ai_addr = NULL;
278 125           hints.ai_canonname = NULL;
279 125           hints.ai_next = NULL;
280              
281 125           r = getaddrinfo(hostname, portstr, &hints, &results);
282 125 100         if (r < 0) {
283 2           logger->err = gai_strerror(r);
284 2           return -1;
285             }
286 123 50         else if (!results) {
287 0           logger->err = "no results from getaddrinfo";
288 0           return -1;
289             }
290 123 50         for (rp = results; rp != NULL; rp = rp->ai_next) {
291 123           logger->sock = socket(rp->ai_family, rp->ai_socktype, 0);
292 123 50         if (logger->sock == -1) {
293 0           r = errno;
294 0           continue;
295             }
296 123           p_address = rp->ai_addr;
297 123           address_len = rp->ai_addrlen;
298 123           break;
299             }
300 123 50         if (logger->sock == -1) {
301 0           logger->err = "socket failure";
302 0 0         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 20 50         else if (proto == LOG_UNIX) {
342              
343             /* create the log device's address */
344             struct sockaddr_un raddress;
345 20           raddress.sun_family = AF_UNIX;
346 20           strncpy(raddress.sun_path, hostname, sizeof(raddress.sun_path) - 1);
347 20           p_address = (const struct sockaddr*) &raddress;
348 20           address_len = sizeof(raddress);
349              
350             /* construct socket */
351 20           logger->sock = socket(AF_UNIX, SOCK_STREAM, 0);
352             }
353             else {
354 0           logger->err = "bad protocol";
355 0           return -1;
356             }
357              
358 143 50         if (logger->sock < 0) {
359 0           logger->err = strerror(errno);
360 0 0         clean_return(-1);
361             }
362              
363             /* close the socket after exec to match normal Perl behavior for sockets */
364 143           fcntl(logger->sock, F_SETFD, FD_CLOEXEC);
365              
366             /* connect the socket */
367 143 100         if (connect(logger->sock, p_address, address_len) != 0) {
368             /* some servers (rsyslog) may use SOCK_DGRAM for unix domain sockets */
369 14 100         if (proto == LOG_UNIX && errno == EPROTOTYPE) {
    100          
370             /* clean up existing bad socket */
371 7           close(logger->sock);
372 7 50         if (logger->sock < 0) {
373 0           logger->err = strerror(errno);
374 0 0         clean_return(-1);
375             }
376              
377 7           logger->sock = socket(AF_UNIX, SOCK_DGRAM, 0);
378 7 50         if (connect(logger->sock, p_address, address_len) != 0) {
379 0           logger->err = strerror(errno);
380 0 0         clean_return(-1);
381             }
382             }
383             else {
384 7           logger->err = strerror(errno);
385 7 100         clean_return(-1);
386             }
387             }
388              
389 145 100         clean_return(0);
390             }
391              
392             int
393 35           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 35 50         if (t != logger->last_time)
397 0           update_prefix(logger, t);
398              
399 35           int line_len = logger->prefix_len + msg_len;
400             /* ensure there's space in the buffer for total length including a trailing NULL */
401 35 100         if (logger->bufsize < line_len + 1) {
402             /* try to increase buffer */
403 1           int new_bufsize = 2 * logger->bufsize;
404 2 100         while (new_bufsize < line_len + 1)
405 1           new_bufsize *= 2;
406 1 50         if (new_bufsize < 0) {
407             /* overflow */
408 0           logger->err = "message too large";
409 0           return -1;
410             }
411              
412 1           char* new_buf = realloc(logger->linebuf, new_bufsize);
413 1 50         if (!new_buf) {
414 0           logger->err = strerror(errno);
415 0           return -1;
416             }
417              
418 1           logger->linebuf = new_buf;
419 1           logger->bufsize = new_bufsize;
420 1           logger->msg_start = logger->linebuf + logger->prefix_len;
421             }
422              
423             /* paste the message into linebuf just past where the prefix was placed */
424 35           memcpy(logger->msg_start, msg_str, msg_len + 1); /* include perl-added null */
425              
426 35           int ret = send(logger->sock, logger->linebuf, line_len, 0);
427              
428 35 100         if (ret < 0)
429 5           logger->err = strerror(errno);
430 35           return ret;
431             }
432              
433             int
434 3           LSF_get_priority(LogSyslogFast* logger)
435             {
436 3           return logger->priority;
437             }
438              
439             int
440 3           LSF_get_facility(LogSyslogFast* logger)
441             {
442 3           return logger->priority >> 3;
443             }
444              
445             int
446 3           LSF_get_severity(LogSyslogFast* logger)
447             {
448 3           return logger->priority & 7;
449             }
450              
451             const char*
452 2           LSF_get_sender(LogSyslogFast* logger)
453             {
454 2           return logger->sender;
455             }
456              
457             const char*
458 2           LSF_get_name(LogSyslogFast* logger)
459             {
460 2           return logger->name;
461             }
462              
463             int
464 2           LSF_get_pid(LogSyslogFast* logger)
465             {
466 2           return logger->pid;
467             }
468              
469             int
470 2           LSF_get_sock(LogSyslogFast* logger)
471             {
472 2           return logger->sock;
473             }
474              
475             int
476 0           LSF_get_format(LogSyslogFast* logger)
477             {
478 0           return logger->format;
479             }