1 //
2 /**************************************************************************************************
3 * IOWOW library
4 *
5 * MIT License
6 *
7 * Copyright (c) 2012-2020 Softmotions Ltd <info@softmotions.com>
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a copy
10 * of this software and associated documentation files (the "Software"), to deal
11 * in the Software without restriction, including without limitation the rights
12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in all
17 * copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * SOFTWARE.
26 *************************************************************************************************/
27
28
29 #include "iwcfg.h"
30 #include "iwp.h"
31 #include "iwlog.h"
32 #include "iwutils.h"
33
34 #include <assert.h>
35 #include <errno.h>
36 #include <inttypes.h>
37 #include <math.h>
38 #include <pthread.h>
39 #include <stdlib.h>
40 #include <time.h>
41 #include <limits.h>
42
43 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__ANDROID__)
44 #include <libgen.h>
45 #elif defined(_WIN32)
46 #include <libiberty/libiberty.h>
47 #else
48 #include <string.h>
49 #endif
50
51
52 #ifdef __ANDROID__
53 #define IW_ANDROID_LOG
54 #include <android/log.h>
55 #endif // __ANDROID__
56
57 static iwrc _default_logfn(FILE *out, locale_t locale, iwlog_lvl lvl, iwrc ecode, int errno_code, int werror_code,
58 const char *file, int line, uint64_t ts, void *opts, const char *fmt,
59 va_list argp);
60
61 static const char *_ecode_explained(locale_t locale, uint32_t ecode);
62 static const char *_default_ecodefn(locale_t locale, uint32_t ecode);
63
64 static IWLOG_FN _current_logfn;
65 static pthread_mutex_t _mtx = PTHREAD_MUTEX_INITIALIZER;
66 static IWLOG_FN _current_logfn = _default_logfn;
67 static void *_current_logfn_options = 0;
68 #define _IWLOG_MAX_ECODE_FUN 256
69 static IWLOG_ECODE_FN _ecode_functions[_IWLOG_MAX_ECODE_FUN] = {0};
70
iwlog(iwlog_lvl lvl,iwrc ecode,const char * file,int line,const char * fmt,...)71 iwrc iwlog(iwlog_lvl lvl, iwrc ecode, const char *file, int line, const char *fmt, ...) {
72 va_list argp;
73 iwrc rc;
74 va_start(argp, fmt);
75 rc = iwlog_va(stderr, lvl, ecode, file, line, fmt, argp);
76 va_end(argp);
77 return rc;
78 }
79
iwlog2(iwlog_lvl lvl,iwrc ecode,const char * file,int line,const char * fmt,...)80 void iwlog2(iwlog_lvl lvl, iwrc ecode, const char *file, int line, const char *fmt, ...) {
81 va_list argp;
82 va_start(argp, fmt);
83 iwlog_va(stderr, lvl, ecode, file, line, fmt, argp);
84 va_end(argp);
85 }
86
iwlog_va(FILE * out,iwlog_lvl lvl,iwrc ecode,const char * file,int line,const char * fmt,va_list argp)87 iwrc iwlog_va(FILE *out, iwlog_lvl lvl, iwrc ecode, const char *file, int line, const char *fmt, va_list argp) {
88 assert(_current_logfn);
89
90 #ifdef _WIN32
91 int werror_code = iwrc_strip_werror(&ecode);
92 locale_t locale = NULL;
93 #else
94 int werror_code = 0;
95 locale_t locale = uselocale(0);
96 #endif
97 int errno_code = iwrc_strip_errno(&ecode);
98 uint64_t ts;
99 iwrc rc = iwp_current_time_ms(&ts, false);
100 RCRET(rc);
101
102 pthread_mutex_lock(&_mtx);
103 IWLOG_FN logfn = _current_logfn;
104 void *opts = _current_logfn_options;
105 pthread_mutex_unlock(&_mtx);
106
107 rc = logfn(out, locale, lvl, ecode, errno_code, werror_code, file, line, ts, opts, fmt, argp);
108 if (rc) {
109 fprintf(stderr, "Logging function returned with error: %" PRIu64 IW_LINE_SEP, rc);
110 }
111 return rc;
112 }
113
114 #define _IWLOG_ERRNO_RC_MASK 0x01U
115 #define _IWLOG_WERR_EC_MASK 0x02U
116
iwrc_set_errno(iwrc rc,int errno_code)117 iwrc iwrc_set_errno(iwrc rc, int errno_code) {
118 if (!errno_code) {
119 return rc;
120 }
121 uint64_t ret = _IWLOG_ERRNO_RC_MASK;
122 ret <<= 30;
123 ret |= (uint32_t) errno_code & 0x3fffffffU;
124 ret <<= 32;
125 ret |= (uint32_t) rc;
126 return ret;
127 }
128
iwrc_strip_errno(iwrc * rc)129 uint32_t iwrc_strip_errno(iwrc *rc) {
130 uint64_t rcv = *rc;
131 if (((rcv >> 62) & 0x03U) != _IWLOG_ERRNO_RC_MASK) {
132 return 0;
133 }
134 *rc = rcv & 0x00000000ffffffffULL;
135 return (uint32_t)(rcv >> 32) & 0x3fffffffU;
136 }
137
138 #ifdef _WIN32
139
iwrc_set_werror(iwrc rc,uint32_t werror)140 iwrc iwrc_set_werror(iwrc rc, uint32_t werror) {
141 if (!werror) {
142 return rc;
143 }
144 uint64_t ret = _IWLOG_WERR_EC_MASK;
145 ret <<= 30;
146 ret |= (uint32_t) werror & 0x3fffffffU;
147 ret <<= 32;
148 ret |= (uint32_t) rc;
149 return ret;
150 }
151
iwrc_strip_werror(iwrc * rc)152 uint32_t iwrc_strip_werror(iwrc *rc) {
153 uint64_t rcv = *rc;
154 if (((rcv >> 62) & 0x03U) != _IWLOG_WERR_EC_MASK) {
155 return 0;
156 }
157 *rc = rcv & 0x00000000ffffffffULL;
158 return (uint32_t)(rcv >> 32) & 0x3fffffffU;
159 }
160 #endif
161
iwrc_strip_code(iwrc * rc)162 void iwrc_strip_code(iwrc *rc) {
163 *rc = *rc & 0x00000000ffffffffULL;
164 }
165
iwlog_set_logfn(IWLOG_FN fp)166 void iwlog_set_logfn(IWLOG_FN fp) {
167 pthread_mutex_lock(&_mtx);
168 if (!fp) {
169 _current_logfn = _default_logfn;
170 } else {
171 _current_logfn = fp;
172 }
173 pthread_mutex_unlock(&_mtx);
174 }
175
iwlog_get_logfn(void)176 IWLOG_FN iwlog_get_logfn(void) {
177 IWLOG_FN res;
178 pthread_mutex_lock(&_mtx);
179 res = _current_logfn;
180 pthread_mutex_unlock(&_mtx);
181 return res;
182 }
183
iwlog_set_logfn_opts(void * opts)184 void iwlog_set_logfn_opts(void *opts) {
185 pthread_mutex_lock(&_mtx);
186 _current_logfn_options = opts;
187 pthread_mutex_unlock(&_mtx);
188 }
189
iwlog_ecode_explained(iwrc ecode)190 const char *iwlog_ecode_explained(iwrc ecode) {
191 iwrc_strip_errno(&ecode);
192 const char *res;
193 pthread_mutex_lock(&_mtx);
194 res = _ecode_explained(0, ecode);
195 pthread_mutex_unlock(&_mtx);
196 return res;
197 }
198
iwlog_register_ecodefn(IWLOG_ECODE_FN fp)199 iwrc iwlog_register_ecodefn(IWLOG_ECODE_FN fp) {
200 assert(fp);
201 int success = 0;
202 pthread_mutex_lock(&_mtx);
203 for (int i = 0; i < _IWLOG_MAX_ECODE_FUN; ++i) {
204 if (_ecode_functions[i] == 0) {
205 _ecode_functions[i] = fp;
206 success = 1;
207 break;
208 }
209 }
210 pthread_mutex_unlock(&_mtx);
211 return success ? 0 : IW_ERROR_FAIL;
212 }
213
iwlog_init(void)214 iwrc iwlog_init(void) {
215 static int _iwlog_initialized = 0;
216 iwrc rc;
217 if (!__sync_bool_compare_and_swap(&_iwlog_initialized, 0, 1)) {
218 return 0; // initialized already
219 }
220 rc = iwlog_register_ecodefn(_default_ecodefn);
221 return rc;
222 }
223
224 ////////////////////////////////////////////////////////////////////////////////////////////////////
225
226 // Assumed:
227 // 1. `_mtx` is locked.
_ecode_explained(locale_t locale,uint32_t ecode)228 static const char *_ecode_explained(locale_t locale, uint32_t ecode) {
229 const char *ret = 0;
230 for (int i = 0; i < _IWLOG_MAX_ECODE_FUN; ++i) {
231 if (_ecode_functions[i] == 0) {
232 break;
233 } else {
234 ret = _ecode_functions[i](locale, ecode);
235 if (ret) {
236 break;
237 }
238 }
239 }
240 return ret;
241 }
242
_default_ecodefn(locale_t locale,uint32_t ecode)243 static const char *_default_ecodefn(locale_t locale, uint32_t ecode) {
244 switch (ecode) {
245 case IW_ERROR_FAIL:
246 return "Unspecified error. (IW_ERROR_FAIL)";
247 case IW_ERROR_ERRNO:
248 return "Error with expected errno status set. (IW_ERROR_ERRNO)";
249 case IW_ERROR_IO_ERRNO:
250 return "IO error with expected errno status set. (IW_ERROR_IO_ERRNO)";
251 case IW_ERROR_NOT_EXISTS:
252 return "Resource is not exists. (IW_ERROR_NOT_EXISTS)";
253 case IW_ERROR_READONLY:
254 return "Resource is readonly. (IW_ERROR_READONLY)";
255 case IW_ERROR_ALREADY_OPENED:
256 return "Resource is already opened. (IW_ERROR_ALREADY_OPENED)";
257 case IW_ERROR_THREADING:
258 return "Threading error. (IW_ERROR_THREADING)";
259 case IW_ERROR_THREADING_ERRNO:
260 return "Threading error with errno status set. "
261 "(IW_ERROR_THREADING_ERRNO)";
262 case IW_ERROR_ASSERTION:
263 return "Generic assertion error. (IW_ERROR_ASSERTION)";
264 case IW_ERROR_INVALID_HANDLE:
265 return "Invalid HANDLE value. (IW_ERROR_INVALID_HANDLE)";
266 case IW_ERROR_OUT_OF_BOUNDS:
267 return "Argument/parameter/value is out of bounds. "
268 "(IW_ERROR_OUT_OF_BOUNDS)";
269 case IW_ERROR_NOT_IMPLEMENTED:
270 return "Method is not implemented. (IW_ERROR_NOT_IMPLEMENTED)";
271 case IW_ERROR_ALLOC:
272 return "Memory allocation failed. (IW_ERROR_ALLOC)";
273 case IW_ERROR_INVALID_STATE:
274 return "Illegal state error. (IW_ERROR_INVALID_STATE)";
275 case IW_ERROR_NOT_ALIGNED:
276 return "Argument is not aligned properly. (IW_ERROR_NOT_ALIGNED)";
277 case IW_ERROR_FALSE:
278 return "False response/rejection. (IW_ERROR_FALSE)";
279 case IW_ERROR_INVALID_ARGS:
280 return "Invalid function arguments. (IW_ERROR_INVALID_ARGS)";
281 case IW_ERROR_OVERFLOW:
282 return "Overflow. (IW_ERROR_OVERFLOW)";
283 case IW_ERROR_INVALID_VALUE:
284 return " Invalid value. (IW_ERROR_INVALID_VALUE)";
285 case IW_OK:
286 default:
287 return 0;
288 }
289 return 0;
290 }
291
_default_logfn(FILE * out,locale_t locale,iwlog_lvl lvl,iwrc ecode,int errno_code,int werror_code,const char * file,int line,uint64_t ts,void * opts,const char * fmt,va_list argp)292 static iwrc _default_logfn(FILE *out,
293 locale_t locale,
294 iwlog_lvl lvl,
295 iwrc ecode,
296 int errno_code,
297 int werror_code,
298 const char *file,
299 int line,
300 uint64_t ts,
301 void *opts,
302 const char *fmt,
303 va_list argp) {
304 #define TBUF_SZ 96
305 #define EBUF_SZ 128
306
307 iwrc rc = 0;
308 IWLOG_DEFAULT_OPTS myopts = {0};
309
310 #ifndef IW_ANDROID_LOG
311 time_t ts_sec = ((long double) ts / 1000);
312 struct tm timeinfo;
313 size_t sz, sz2;
314 char tbuf[TBUF_SZ];
315 char *cat;
316 #endif
317
318 char ebuf[EBUF_SZ];
319 char *errno_msg = 0, *werror_msg = 0;
320 const char *ecode_msg = 0;
321 char fnamebuf[MAXPATHLEN];
322 char *fnameptr = fnamebuf;
323 char *fname = 0;
324
325 if (errno_code) {
326 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__ANDROID__)
327 int rci = strerror_r(errno_code, ebuf, EBUF_SZ);
328 if (!rci) {
329 errno_msg = ebuf;
330 }
331 #elif defined(_WIN32)
332 int rci = strerror_s(ebuf, EBUF_SZ, errno_code);
333 if (!rci) {
334 errno_msg = ebuf;
335 }
336 #else
337 errno_msg = strerror_r(errno_code, ebuf, EBUF_SZ); // NOLINT
338 #endif
339 }
340
341 #ifdef _WIN32
342 if (werror_code) {
343 LPTSTR msg = NULL;
344 DWORD ret = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, werror_code,
345 MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), (LPTSTR) &msg, 0, NULL);
346
347 if (ret == 0) {
348 if (msg) {
349 LocalFree(msg);
350 msg = NULL;
351 }
352 }
353 werror_msg = msg;
354 }
355 #endif
356
357 if (opts) {
358 myopts = *(IWLOG_DEFAULT_OPTS *) opts;
359 if (myopts.out) {
360 out = myopts.out;
361 }
362 }
363
364 #ifndef IW_ANDROID_LOG
365 // cppcheck-suppress portability
366 localtime_r(&ts_sec, &timeinfo);
367
368 sz = strftime(tbuf, TBUF_SZ, "%d %b %H:%M:%S", &timeinfo);
369 if (sz == 0) {
370 tbuf[0] = '\0';
371 } else if (TBUF_SZ - sz > 4) { // .000 suffix
372 tbuf[sz] = '.';
373 sz2 = snprintf((char *) tbuf + sz + 1, 4, "%03d", (int)(ts % 1000));
374 if (sz2 > 3) {
375 tbuf[sz] = '\0';
376 }
377 }
378 #else
379 android_LogPriority alp = ANDROID_LOG_INFO;
380 #endif // IW_ANDROID_LOG
381
382 switch (lvl) {
383 case IWLOG_DEBUG:
384 #ifdef IW_ANDROID_LOG
385 alp = ANDROID_LOG_DEBUG;
386 #else
387 cat = "DEBUG";
388 #endif
389 break;
390 case IWLOG_INFO:
391 #ifdef IW_ANDROID_LOG
392 alp = ANDROID_LOG_INFO;
393 #else
394 cat = "INFO";
395 #endif
396 file = 0;
397 break;
398 case IWLOG_WARN:
399 #ifdef IW_ANDROID_LOG
400 alp = ANDROID_LOG_WARN;
401 #else
402 cat = "WARN";
403 #endif
404 break;
405 case IWLOG_ERROR:
406 #ifdef IW_ANDROID_LOG
407 alp = ANDROID_LOG_ERROR;
408 #else
409 cat = "ERROR";
410 #endif
411 break;
412 default:
413 #ifndef IW_ANDROID_LOG
414 cat = "UNKNOW";
415 #endif
416 assert(0);
417 break;
418 }
419 if (ecode) {
420 ecode_msg = _ecode_explained(locale, ecode);
421 }
422 if (file && line > 0) {
423 size_t len = strlen(file);
424 if (len < sizeof(fnamebuf)) {
425 memcpy(fnameptr, file, len);
426 fnameptr[len] = '\0';
427 } else {
428 fnameptr = strdup(file);
429 RCA(fnameptr, finish);
430 }
431 #ifdef IW_HAVE_BASENAME_R
432 fname = basename_r(file, fnameptr);
433 #else
434 fname = basename(fnameptr); // NOLINT
435 #endif
436 }
437
438 if (pthread_mutex_lock(&_mtx)) {
439 rc = IW_ERROR_THREADING_ERRNO;
440 goto finish;
441 }
442
443 #ifndef IW_ANDROID_LOG
444
445 if (ecode || errno_code || werror_code) {
446 if (fname && line > 0) {
447 fprintf(out, "%s %s %s:%d %" PRIu64 "|%d|%d|%s|%s|%s: ", tbuf, cat, fname, line, ecode, errno_code,
448 werror_code, (ecode_msg ? ecode_msg : ""), (errno_msg ? errno_msg : ""),
449 (werror_msg ? werror_msg : "")); // -V547
450 } else {
451 fprintf(out, "%s %s %" PRIu64 "|%d|%d|%s|%s|%s: ", tbuf, cat, ecode, errno_code, werror_code,
452 (ecode_msg ? ecode_msg : ""), (errno_msg ? errno_msg : ""), (werror_msg ? werror_msg : "")); // -V547
453 }
454 } else {
455 if (fname && line > 0) {
456 fprintf(out, "%s %s %s:%d: ", tbuf, cat, fname, line);
457 } else {
458 fprintf(out, "%s %s: ", tbuf, cat);
459 }
460 }
461 if (fmt) {
462 vfprintf(out, fmt, argp);
463 }
464 fprintf(out, IW_LINE_SEP);
465 fflush(out);
466
467 #else
468
469 if (ecode || errno_code || werror_code) {
470 if (fname && line > 0) {
471 __android_log_print(alp, "IWLOG", "%s:%d %" PRIu64 "|%d|%d|%s|%s|%s: ", fname, line, ecode, errno_code,
472 werror_code, (ecode_msg ? ecode_msg : ""), (errno_msg ? errno_msg : ""),
473 (werror_msg ? werror_msg : ""));
474 } else {
475 __android_log_print(alp, "IWLOG", "%" PRIu64 "|%d|%d|%s|%s|%s: ", ecode, errno_code, werror_code,
476 (ecode_msg ? ecode_msg : ""), (errno_msg ? errno_msg : ""), (werror_msg ? werror_msg : ""));
477 }
478 } else {
479 if (fname && line > 0) {
480 __android_log_print(alp, "IWLOG", "%s:%d: ", fname, line);
481 }
482 }
483 if (fmt) {
484 __android_log_vprint(alp, "IWLOG", fmt, argp);
485 }
486
487 #endif
488
489 pthread_mutex_unlock(&_mtx);
490
491 finish:
492 if (fnameptr != fnamebuf) {
493 free(fnameptr);
494 }
495
496 #ifdef _WIN32
497 if (werror_msg) {
498 LocalFree(werror_msg);
499 }
500 #endif
501
502 #undef TBUF_SZ
503 #undef EBUF_SZ
504 return rc;
505 }
506