1 /*
2  * Copyright (c) 2008 Sippy Software, Inc., http://www.sippysoft.com
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  */
27 
28 #include <sys/types.h>
29 #include <errno.h>
30 #include <math.h>
31 #include <syslog.h>
32 #include <stdatomic.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 
39 #include "rtpp_log.h"
40 #include "rtpp_cfg_stable.h"
41 #ifdef RTPP_LOG_ADVANCED
42 #include "rtpp_syslog_async.h"
43 #endif
44 #include "rtpp_time.h"
45 #include "rtpp_mallocs.h"
46 
47 #ifdef RTPP_LOG_ADVANCED
48 static int syslog_async_opened = 0;
49 #endif
50 static double iitime = 0.0;
51 
52 #define CALL_ID_NONE "GLOBAL"
53 
54 static struct {
55     atomic_uint next_ticket;
56     atomic_uint now_serving;
57 } _log_lock = {ATOMIC_VAR_INIT(0), ATOMIC_VAR_INIT(0)};
58 
59 struct rtpp_log_inst {
60     char *call_id;
61     int level;
62     const char *format_sl[2];
63     const char *eformat_sl[2];
64     const char *format_se[2];
65     const char *eformat_se[2];
66     double itime;
67 };
68 
69 struct rtpp_log_inst *
_rtpp_log_open(struct rtpp_cfg_stable * cf,const char * app,const char * call_id)70 _rtpp_log_open(struct rtpp_cfg_stable *cf, const char *app, const char *call_id)
71 {
72     const char *stritime;
73     const char *tform;
74     char *se;
75     struct rtpp_log_inst *rli;
76 #ifdef RTPP_LOG_ADVANCED
77     int facility;
78 
79     facility = cf->log_facility;
80     if (facility == -1)
81 	facility = LOG_DAEMON;
82 
83     if (cf->nodaemon == 0 && syslog_async_opened == 0) {
84 	if (syslog_async_init(app, facility) == 0)
85 	    syslog_async_opened = 1;
86     }
87 #endif
88     rli = rtpp_zmalloc(sizeof(struct rtpp_log_inst));
89     if (rli == NULL) {
90         return (NULL);
91     }
92     tform = getenv("RTPP_LOG_TFORM");
93     if (tform != NULL && strcmp(tform, "rel") == 0) {
94         stritime = getenv("RTPP_LOG_TSTART");
95         if (stritime != NULL) {
96             rli->itime = strtod(stritime, &se);
97         } else {
98             if (iitime == 0.0) {
99                 iitime = getdtime();
100             }
101             rli->itime = iitime;
102         }
103     }
104     if (call_id != NULL) {
105         rli->call_id = strdup(call_id);
106     }
107     if (cf->log_level == -1) {
108 	rli->level = (cf->nodaemon != 0) ? RTPP_LOG_DBUG : RTPP_LOG_WARN;
109     } else {
110         rli->level = cf->log_level;
111     }
112     rli->format_se[0] = "%s%s:%s:%s: ";
113     rli->format_se[1] = "\n";
114     rli->eformat_se[0] = "%s%s:%s:%s: ";
115     rli->eformat_se[1] = ": %s (%d)\n";
116     rli->format_sl[0] = "%s:%s:%s: ";
117     rli->format_sl[1] = NULL;
118     rli->eformat_sl[0] = "%s:%s:%s: ";
119     rli->eformat_sl[1] = ": %s (%d)";
120     return (rli);
121 }
122 
123 void
_rtpp_log_close(struct rtpp_log_inst * rli)124 _rtpp_log_close(struct rtpp_log_inst *rli)
125 {
126     if (rli->call_id != NULL) {
127         free(rli->call_id);
128     }
129     free(rli);
130     return;
131 }
132 
133 static const char *
strlvl(int level)134 strlvl(int level)
135 {
136 
137     switch(level) {
138     case RTPP_LOG_DBUG:
139 	return "DBUG";
140 
141     case RTPP_LOG_INFO:
142 	return "INFO";
143 
144     case RTPP_LOG_WARN:
145 	return "WARN";
146 
147     case RTPP_LOG_ERR:
148 	return "ERR";
149 
150     case RTPP_LOG_CRIT:
151 	return "CRIT";
152 
153     default:
154 	break;
155     }
156 
157     abort();
158 
159     return NULL;
160 }
161 
162 int
rtpp_log_str2lvl(const char * strl)163 rtpp_log_str2lvl(const char *strl)
164 {
165 
166     if (strcasecmp(strl, "DBUG") == 0)
167 	return RTPP_LOG_DBUG;
168 
169     if (strcasecmp(strl, "INFO") == 0)
170 	return RTPP_LOG_INFO;
171 
172     if (strcasecmp(strl, "WARN") == 0)
173 	return RTPP_LOG_WARN;
174 
175     if (strcasecmp(strl, "ERR") == 0)
176 	return RTPP_LOG_ERR;
177 
178     if (strcasecmp(strl, "CRIT") == 0)
179 	return RTPP_LOG_CRIT;
180 
181     return -1;
182 }
183 
184 static int
check_level(struct rtpp_log_inst * rli,int level)185 check_level(struct rtpp_log_inst *rli, int level)
186 {
187 
188     return (level <= rli->level);
189 }
190 
191 void
rtpp_log_setlevel(struct rtpp_log_inst * rli,int level)192 rtpp_log_setlevel(struct rtpp_log_inst *rli, int level)
193 {
194 
195     rli->level = level;
196 }
197 
198 static void
ftime(struct rtpp_log_inst * rli,double ltime,char * buf,int buflen)199 ftime(struct rtpp_log_inst *rli, double ltime, char *buf, int buflen)
200 {
201     int hrs, mins, secs, msec;
202 
203     if (rli->itime != 0.0) {
204         ltime -= rli->itime;
205         msec = modf(ltime, &ltime) * 1000;
206         hrs = (int)(ltime / (60 * 60));
207         ltime -= (hrs * 60 * 60);
208         mins = (int)(ltime / 60);
209         ltime -= (mins * 60);
210         secs = (int)(ltime);
211         snprintf(buf, buflen, "%.2d:%.2d:%.2d.%.3d/", hrs, mins, secs, msec);
212     } else {
213         buf[0] = '\0';
214     }
215 }
216 
217 static void
_rtpp_log_lock(void)218 _rtpp_log_lock(void)
219 {
220     unsigned int my_ticket;
221 
222     my_ticket = atomic_fetch_add_explicit(&(_log_lock.next_ticket), 1,
223       memory_order_acq_rel);
224     while (atomic_load_explicit(&(_log_lock.now_serving),
225       memory_order_acquire) != my_ticket) {
226         usleep(1);
227     }
228 }
229 
230 static void
_rtpp_log_unlock(void)231 _rtpp_log_unlock(void)
232 {
233 
234     atomic_fetch_add_explicit(&(_log_lock.now_serving), 1,
235       memory_order_release);
236 }
237 
238 void
_rtpp_log_write_va(struct rtpp_log_inst * rli,int level,const char * function,const char * format,va_list ap)239 _rtpp_log_write_va(struct rtpp_log_inst *rli, int level, const char *function,
240   const char *format, va_list ap)
241 {
242     char rtpp_time_buff[32];
243     const char *call_id;
244 #ifdef RTPP_LOG_ADVANCED
245     char rtpp_log_buff[2048];
246     va_list apc;
247 #endif
248 
249     if (check_level(rli, level) == 0)
250 	return;
251 
252     if (rli->call_id != NULL) {
253         call_id = rli->call_id;
254     } else {
255         call_id = CALL_ID_NONE;
256     }
257 
258 #ifdef RTPP_LOG_ADVANCED
259     if (syslog_async_opened != 0) {
260         snprintf(rtpp_log_buff, sizeof(rtpp_log_buff), rli->format_sl[0],
261           strlvl(level), call_id, function);
262         va_copy(apc, ap);
263 	vsyslog_async(level, rtpp_log_buff, rli->format_sl[1], format, apc);
264         va_end(apc);
265 #if !defined(RTPP_DEBUG)
266         return;
267 #endif
268     }
269 #endif
270 
271     ftime(rli, getdtime(), rtpp_time_buff, sizeof(rtpp_time_buff));
272     _rtpp_log_lock();
273     fprintf(stderr, rli->format_se[0], rtpp_time_buff, strlvl(level),
274       call_id, function);
275     vfprintf(stderr, format, ap);
276     fprintf(stderr, rli->format_se[1]);
277     fflush(stderr);
278     _rtpp_log_unlock();
279 }
280 
281 void
_rtpp_log_write(struct rtpp_log_inst * rli,int level,const char * function,const char * format,...)282 _rtpp_log_write(struct rtpp_log_inst *rli, int level, const char *function, const char *format, ...)
283 {
284     va_list ap;
285 
286     va_start(ap, format);
287     _rtpp_log_write_va(rli, level, function, format, ap);
288     va_end(ap);
289 }
290 
291 void
_rtpp_log_ewrite_va(struct rtpp_log_inst * rli,int level,const char * function,const char * format,va_list ap)292 _rtpp_log_ewrite_va(struct rtpp_log_inst *rli, int level, const char *function,
293   const char *format, va_list ap)
294 {
295     char rtpp_time_buff[32];
296     const char *call_id;
297 #ifdef RTPP_LOG_ADVANCED
298     char rtpp_log_buff[2048];
299     va_list apc;
300     char *post;
301 #endif
302 
303     if (check_level(rli, level) == 0)
304 	return;
305 
306     if (rli->call_id != NULL) {
307         call_id = rli->call_id;
308     } else {
309         call_id = CALL_ID_NONE;
310     }
311 
312 #ifdef RTPP_LOG_ADVANCED
313     if (syslog_async_opened != 0) {
314         int nch, m;
315 
316         m = sizeof(rtpp_log_buff);
317         nch = snprintf(rtpp_log_buff, m, rli->eformat_sl[0],
318           strlvl(level), call_id, function);
319         nch += 1;
320         post = " TRUNCATED! ";
321         if (nch < m) {
322             post = rtpp_log_buff + nch;
323             snprintf(post, m - nch, rli->eformat_sl[1],
324               strerror(errno), errno);
325         }
326         va_copy(apc, ap);
327 	vsyslog_async(level, rtpp_log_buff, post, format, apc);
328         va_end(apc);
329 #if !defined(RTPP_DEBUG)
330 	return;
331 #endif
332     }
333 #endif
334     ftime(rli, getdtime(), rtpp_time_buff, sizeof(rtpp_time_buff));
335     _rtpp_log_lock();
336     fprintf(stderr, rli->eformat_se[0], rtpp_time_buff, strlvl(level), call_id,
337       function);
338     vfprintf(stderr, format, ap);
339     fprintf(stderr, rli->eformat_se[1], strerror(errno), errno);
340     fflush(stderr);
341     _rtpp_log_unlock();
342 }
343 
344 void
_rtpp_log_ewrite(struct rtpp_log_inst * rli,int level,const char * function,const char * format,...)345 _rtpp_log_ewrite(struct rtpp_log_inst *rli, int level, const char *function, const char *format, ...)
346 {
347     va_list ap;
348 
349     va_start(ap, format);
350     _rtpp_log_ewrite_va(rli, level, function, format, ap);
351     va_end(ap);
352 }
353 
354 static struct {
355     const char *str_fac;
356     int int_fac;
357 } str2fac[] = {
358     {"LOG_AUTH",     LOG_AUTH},
359     {"LOG_CRON",     LOG_CRON},
360     {"LOG_DAEMON",   LOG_DAEMON},
361     {"LOG_KERN",     LOG_KERN},
362     {"LOG_LOCAL0",   LOG_LOCAL0},
363     {"LOG_LOCAL1",   LOG_LOCAL1},
364     {"LOG_LOCAL2",   LOG_LOCAL2},
365     {"LOG_LOCAL3",   LOG_LOCAL3},
366     {"LOG_LOCAL4",   LOG_LOCAL4},
367     {"LOG_LOCAL5",   LOG_LOCAL5},
368     {"LOG_LOCAL6",   LOG_LOCAL6},
369     {"LOG_LOCAL7",   LOG_LOCAL7},
370     {"LOG_LPR",      LOG_LPR},
371     {"LOG_MAIL",     LOG_MAIL},
372     {"LOG_NEWS",     LOG_NEWS},
373     {"LOG_USER",     LOG_USER},
374     {"LOG_UUCP",     LOG_UUCP},
375 #if !defined(__solaris__) && !defined(__sun) && !defined(__svr4__)
376     {"LOG_AUTHPRIV", LOG_AUTHPRIV},
377     {"LOG_FTP",      LOG_FTP},
378     {"LOG_SYSLOG",   LOG_SYSLOG},
379 #endif
380     {NULL,           0}
381 };
382 
383 int
rtpp_log_str2fac(const char * s)384 rtpp_log_str2fac(const char *s)
385 {
386     int i;
387 
388     for (i=0; str2fac[i].str_fac != NULL; i++) {
389         if (strcasecmp(s, str2fac[i].str_fac) == 0 || \
390 	  strcasecmp(s, str2fac[i].str_fac + 4) == 0)
391             return str2fac[i].int_fac;
392     }
393     return -1;
394 }
395