1 /*
2  * DNS Reply Tool (drool)
3  *
4  * Copyright (c) 2017-2018, OARC, Inc.
5  * Copyright (c) 2017, Comcast Corporation
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  *
20  * 3. Neither the name of the copyright holder nor the names of its
21  *    contributors may be used to endorse or promote products derived
22  *    from this software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35  * POSSIBILITY OF SUCH DAMAGE.
36  */
37 
38 #include "config.h"
39 
40 #include "log.h"
41 #include "assert.h"
42 
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <string.h>
46 #include <pthread.h>
47 #include <time.h>
48 
get_facility(const drool_log_t * log,const drool_log_facility_t facility)49 static const drool_log_settings_t* get_facility(const drool_log_t* log, const drool_log_facility_t facility)
50 {
51     drool_assert(log);
52     switch (facility) {
53     case LOG_FACILITY_CORE:
54         return &(log->core);
55     case LOG_FACILITY_NETWORK:
56         return &(log->network);
57     default:
58         break;
59     }
60     return &(log->none);
61 }
62 
log_level_enable(drool_log_settings_t * settings,const drool_log_level_t level)63 inline int log_level_enable(drool_log_settings_t* settings, const drool_log_level_t level)
64 {
65     drool_assert(settings);
66 
67     switch (level) {
68     case LOG_LEVEL_DEBUG:
69         settings->debug = 1;
70         return 0;
71     case LOG_LEVEL_INFO:
72         settings->info = 1;
73         return 0;
74     case LOG_LEVEL_NOTICE:
75         settings->notice = 1;
76         return 0;
77     case LOG_LEVEL_WARNING:
78         settings->warning = 1;
79         return 0;
80     case LOG_LEVEL_ERROR:
81         settings->error = 1;
82         return 0;
83     case LOG_LEVEL_CRITICAL:
84         settings->critical = 1;
85         return 0;
86     case LOG_LEVEL_ALL:
87         settings->debug    = 1;
88         settings->info     = 1;
89         settings->notice   = 1;
90         settings->warning  = 1;
91         settings->error    = 1;
92         settings->critical = 1;
93         return 0;
94     default:
95         break;
96     }
97 
98     return -1;
99 }
100 
log_level_disable(drool_log_settings_t * settings,const drool_log_level_t level)101 inline int log_level_disable(drool_log_settings_t* settings, const drool_log_level_t level)
102 {
103     drool_assert(settings);
104 
105     switch (level) {
106     case LOG_LEVEL_DEBUG:
107         settings->debug = 0;
108         return 0;
109     case LOG_LEVEL_INFO:
110         settings->info = 0;
111         return 0;
112     case LOG_LEVEL_NOTICE:
113         settings->notice = 0;
114         return 0;
115     case LOG_LEVEL_WARNING:
116         settings->warning = 0;
117         return 0;
118     case LOG_LEVEL_ERROR:
119         settings->error = 0;
120         return 0;
121     case LOG_LEVEL_CRITICAL:
122         settings->critical = 0;
123         return 0;
124     case LOG_LEVEL_ALL:
125         settings->debug    = 0;
126         settings->info     = 0;
127         settings->notice   = 0;
128         settings->warning  = 0;
129         settings->error    = 0;
130         settings->critical = 0;
131         return 0;
132     default:
133         break;
134     }
135 
136     return -1;
137 }
138 
log_level_name(const drool_log_level_t level)139 const char* log_level_name(const drool_log_level_t level)
140 {
141     switch (level) {
142     case LOG_LEVEL_DEBUG:
143         return LOG_LEVEL_DEBUG_STR;
144     case LOG_LEVEL_INFO:
145         return LOG_LEVEL_INFO_STR;
146     case LOG_LEVEL_NOTICE:
147         return LOG_LEVEL_NOTICE_STR;
148     case LOG_LEVEL_WARNING:
149         return LOG_LEVEL_WARNING_STR;
150     case LOG_LEVEL_ERROR:
151         return LOG_LEVEL_ERROR_STR;
152     case LOG_LEVEL_CRITICAL:
153         return LOG_LEVEL_CRITICAL_STR;
154     case LOG_LEVEL_ALL:
155         return LOG_LEVEL_ALL_STR;
156     }
157     return LOG_LEVEL_UNKNOWN_STR;
158 }
159 
log_facility_name(const drool_log_facility_t facility)160 const char* log_facility_name(const drool_log_facility_t facility)
161 {
162     switch (facility) {
163     case LOG_FACILITY_CORE:
164         return LOG_FACILITY_CORE_STR;
165     case LOG_FACILITY_NETWORK:
166         return LOG_FACILITY_NETWORK_STR;
167     case LOG_FACILITY_NONE:
168         return LOG_FACILITY_NONE_STR;
169     }
170     return LOG_FACILITY_UNKNOWN_STR;
171 }
172 
log_is_enabled(const drool_log_t * log,const drool_log_facility_t facility,const drool_log_level_t level)173 int log_is_enabled(const drool_log_t* log, const drool_log_facility_t facility, const drool_log_level_t level)
174 {
175     const drool_log_settings_t* settings;
176 
177     drool_assert(log);
178     if (!log) {
179         return 0;
180     }
181 
182     settings = get_facility(log, facility);
183     drool_assert(settings);
184     if (!settings) {
185         return 0;
186     }
187 
188     switch (level) {
189     case LOG_LEVEL_DEBUG:
190         if (settings->debug)
191             return 1;
192         break;
193 
194     case LOG_LEVEL_INFO:
195         if (settings->info)
196             return 1;
197         break;
198 
199     case LOG_LEVEL_NOTICE:
200         if (settings->notice)
201             return 1;
202         break;
203 
204     case LOG_LEVEL_WARNING:
205         if (settings->warning)
206             return 1;
207         break;
208 
209     case LOG_LEVEL_ERROR:
210         if (settings->error)
211             return 1;
212         break;
213 
214     case LOG_LEVEL_CRITICAL:
215         if (settings->critical)
216             return 1;
217         break;
218 
219     default:
220         break;
221     }
222 
223     return 0;
224 }
225 
log_enable(drool_log_t * log,const drool_log_facility_t facility,const drool_log_level_t level)226 inline int log_enable(drool_log_t* log, const drool_log_facility_t facility, const drool_log_level_t level)
227 {
228     drool_assert(log);
229 
230     switch (facility) {
231     case LOG_FACILITY_CORE:
232         return log_level_enable(&(log->core), level);
233     case LOG_FACILITY_NETWORK:
234         return log_level_enable(&(log->network), level);
235     default:
236         break;
237     }
238 
239     return -1;
240 }
241 
log_disable(drool_log_t * log,const drool_log_facility_t facility,const drool_log_level_t level)242 inline int log_disable(drool_log_t* log, const drool_log_facility_t facility, const drool_log_level_t level)
243 {
244     drool_assert(log);
245 
246     switch (facility) {
247     case LOG_FACILITY_CORE:
248         return log_level_disable(&(log->core), level);
249     case LOG_FACILITY_NETWORK:
250         return log_level_disable(&(log->network), level);
251     default:
252         break;
253     }
254 
255     return -1;
256 }
257 
log_printf_fileline(const drool_log_t * log,const drool_log_facility_t facility,const drool_log_level_t level,const char * file,size_t line,const char * format,...)258 void log_printf_fileline(const drool_log_t* log, const drool_log_facility_t facility, const drool_log_level_t level, const char* file, size_t line, const char* format, ...)
259 {
260     va_list ap;
261     char    buf[1024];
262     size_t  s;
263     int     n, n2;
264 #if LOG_FILENAME_LINE && LOG_SHORT_FILENAME
265     char* filep;
266 #endif
267 #if LOG_DATETIME
268     struct tm tm;
269     time_t    t;
270 #endif
271 
272     drool_assert(log);
273     if (!log) {
274         return;
275     }
276     drool_assert(format);
277     if (!format) {
278         return;
279     }
280 
281     if (!log_is_enabled(log, facility, level)) {
282         return;
283     }
284 
285 #if LOG_DATETIME
286     memset(&tm, 0, sizeof(tm));
287     time(&t);
288     localtime_r(&t, &tm);
289 #endif
290 
291     s = sizeof(buf) - 2;
292     n = snprintf(buf, s,
293 #if LOG_DATETIME
294         "%d-%02d-%02d %02d:%02d:%02d "
295 #endif
296 #if LOG_FILENAME_LINE
297         "%s:%04lu "
298 #endif
299 #if LOG_THREAD_ID
300         "t:%lu "
301 #endif
302         "%s %s: ",
303 #if LOG_DATETIME
304         1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
305 #endif
306 #if LOG_FILENAME_LINE
307 #if LOG_SHORT_FILENAME
308         (filep = strrchr(file, '/')) ? (filep + 1) : file,
309 #else
310         file,
311 #endif
312         line,
313 #endif
314 #if LOG_THREAD_ID
315         pthread_self(),
316 #endif
317         log_facility_name(facility), log_level_name(level));
318     if (n < 1) {
319         printf("log_printf_fileline(): snprintf() failed with %d\n", n);
320         return;
321     }
322     if (n < s) {
323         va_start(ap, format);
324         n2 = vsnprintf(&buf[n], s - n, format, ap);
325         va_end(ap);
326         if (n2 < 1) {
327             printf("log_printf_fileline(): vsnprintf() failed with %d\n", n2);
328             return;
329         }
330     }
331     fprintf(stdout, "%s\n", buf);
332     fflush(stdout);
333 }
334 
log_errnumf_fileline(const drool_log_t * log,const drool_log_facility_t facility,const drool_log_level_t level,const char * file,size_t line,int errnum,const char * format,...)335 void log_errnumf_fileline(const drool_log_t* log, const drool_log_facility_t facility, const drool_log_level_t level, const char* file, size_t line, int errnum, const char* format, ...)
336 {
337     va_list ap;
338     char    errbuf[512];
339     char    buf[1024];
340     size_t  s;
341     int     n, n2;
342 #if LOG_FILENAME_LINE && LOG_SHORT_FILENAME
343     char* filep;
344 #endif
345 #if LOG_DATETIME
346     struct tm tm;
347     time_t    t;
348 #endif
349 
350     drool_assert(log);
351     if (!log) {
352         return;
353     }
354     drool_assert(format);
355     if (!format) {
356         return;
357     }
358 
359     if (!log_is_enabled(log, facility, level)) {
360         return;
361     }
362 
363     memset(errbuf, 0, sizeof(errbuf));
364 
365 #if ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && !_GNU_SOURCE) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__DragonFly__)
366     /* XSI-compliant version */
367     {
368         int ret = strerror_r(errnum, errbuf, sizeof(errbuf));
369         if (ret > 0) {
370             (void)strerror_r(ret, errbuf, sizeof(errbuf));
371         } else {
372             (void)strerror_r(errno, errbuf, sizeof(errbuf));
373         }
374     }
375 #else
376     /* GNU-specific version */
377     errbuf = strerror_r(errnum, errbuf, sizeof(errbuf));
378 #endif
379 
380 #if LOG_DATETIME
381     memset(&tm, 0, sizeof(tm));
382     time(&t);
383     localtime_r(&t, &tm);
384 #endif
385 
386     s = sizeof(buf) - 2;
387     n = snprintf(buf, s,
388 #if LOG_DATETIME
389         "%d-%02d-%02d %02d:%02d:%02d "
390 #endif
391 #if LOG_FILENAME_LINE
392         "%s:%04lu "
393 #endif
394 #if LOG_THREAD_ID
395         "t:%lu "
396 #endif
397         "%s %s: ",
398 #if LOG_DATETIME
399         1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
400 #endif
401 #if LOG_FILENAME_LINE
402 #if LOG_SHORT_FILENAME
403         (filep = strrchr(file, '/')) ? (filep + 1) : file,
404 #else
405         file,
406 #endif
407         line,
408 #endif
409 #if LOG_THREAD_ID
410         pthread_self(),
411 #endif
412         log_facility_name(facility), log_level_name(level));
413     if (n < 1) {
414         printf("log_printf_fileline(): snprintf() failed with %d\n", n);
415         return;
416     }
417     if (n < s) {
418         va_start(ap, format);
419         n2 = vsnprintf(&buf[n], s - n, format, ap);
420         va_end(ap);
421         if (n2 < 1) {
422             printf("log_printf_fileline(): vsnprintf() failed with %d\n", n2);
423             return;
424         }
425     }
426     fprintf(stdout, "%s: %s\n", buf, errbuf);
427     fflush(stdout);
428 };
429