1 //
2 /**************************************************************************************************
3 * IOWOW library
4 *
5 * MIT License
6 *
7 * Copyright (c) 2012-2022 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 #include "iwcfg.h"
29 #include "iwp.h"
30 #include "iwlog.h"
31 #include "iwutils.h"
32
33 #include <assert.h>
34 #include <errno.h>
35 #include <inttypes.h>
36 #include <math.h>
37 #include <pthread.h>
38 #include <stdlib.h>
39 #include <time.h>
40 #include <limits.h>
41
42 #ifdef __ANDROID__
43 #define IW_ANDROID_LOG
44 #include <android/log.h>
45 #endif // __ANDROID__
46
47 static iwrc _default_logfn(
48 FILE *out, locale_t locale, iwlog_lvl lvl, iwrc ecode, int errno_code, int werror_code,
49 const char *file, int line, uint64_t ts, void *opts, const char *fmt,
50 va_list argp, bool no_va);
51
52 static const char* _ecode_explained(locale_t locale, uint32_t ecode);
53 static const char* _default_ecodefn(locale_t locale, uint32_t ecode);
54
55 static pthread_mutex_t _mtx = PTHREAD_MUTEX_INITIALIZER;
56 static IWLOG_FN _current_logfn = _default_logfn;
57 static void *_current_logfn_options = 0;
58 #define _IWLOG_MAX_ECODE_FUN 256
59 static IWLOG_ECODE_FN _ecode_functions[_IWLOG_MAX_ECODE_FUN] = { 0 };
60
iwlog(iwlog_lvl lvl,iwrc ecode,const char * file,int line,const char * fmt,...)61 iwrc iwlog(iwlog_lvl lvl, iwrc ecode, const char *file, int line, const char *fmt, ...) {
62 va_list argp;
63 iwrc rc;
64 va_start(argp, fmt);
65 rc = iwlog_va(stderr, lvl, ecode, file, line, fmt, argp, false);
66 va_end(argp);
67 return rc;
68 }
69
iwlog2(iwlog_lvl lvl,iwrc ecode,const char * file,int line,const char * fmt,...)70 void iwlog2(iwlog_lvl lvl, iwrc ecode, const char *file, int line, const char *fmt, ...) {
71 va_list argp;
72 va_start(argp, fmt);
73 iwlog_va(stderr, lvl, ecode, file, line, fmt, argp, false);
74 va_end(argp);
75 }
76
iwlog3(iwlog_lvl lvl,iwrc ecode,const char * file,int line,const char * data)77 void iwlog3(iwlog_lvl lvl, iwrc ecode, const char *file, int line, const char *data) {
78 va_list argp = { 0 };
79 iwlog_va(stderr, lvl, ecode, file, line, data, argp, true);
80 }
81
iwlog_va(FILE * out,iwlog_lvl lvl,iwrc ecode,const char * file,int line,const char * fmt,va_list argp,bool no_va)82 iwrc iwlog_va(
83 FILE *out,
84 iwlog_lvl lvl,
85 iwrc ecode,
86 const char *file,
87 int line,
88 const char *fmt,
89 va_list argp,
90 bool no_va
91 ) {
92 assert(_current_logfn);
93
94 #ifdef _WIN32
95 int werror_code = iwrc_strip_werror(&ecode);
96 locale_t locale = NULL;
97 #else
98 int werror_code = 0;
99 locale_t locale = uselocale(0);
100 #endif
101 int errno_code = iwrc_strip_errno(&ecode);
102 uint64_t ts;
103 iwrc rc = iwp_current_time_ms(&ts, false);
104 RCRET(rc);
105
106 IWLOG_FN logfn = _current_logfn;
107 void *opts = _current_logfn_options;
108
109 rc = logfn(out, locale, lvl, ecode, errno_code, werror_code, file, line, ts, opts, fmt, argp, no_va);
110 if (rc) {
111 fprintf(stderr, "Logging function returned with error: %" PRIu64 IW_LINE_SEP, rc);
112 }
113 return rc;
114 }
115
116 #define _IWLOG_ERRNO_RC_MASK 0x01U
117 #define _IWLOG_WERR_EC_MASK 0x02U
118
iwrc_set_errno(iwrc rc,int errno_code)119 iwrc iwrc_set_errno(iwrc rc, int errno_code) {
120 if (!errno_code) {
121 return rc;
122 }
123 uint64_t ret = _IWLOG_ERRNO_RC_MASK;
124 ret <<= 30;
125 ret |= (uint32_t) errno_code & 0x3fffffffU;
126 ret <<= 32;
127 ret |= (uint32_t) rc;
128 return ret;
129 }
130
iwrc_strip_errno(iwrc * rc)131 uint32_t iwrc_strip_errno(iwrc *rc) {
132 uint64_t rcv = *rc;
133 if (((rcv >> 62) & 0x03U) != _IWLOG_ERRNO_RC_MASK) {
134 return 0;
135 }
136 *rc = rcv & 0x00000000ffffffffULL;
137 return (uint32_t) (rcv >> 32) & 0x3fffffffU;
138 }
139
140 #ifdef _WIN32
141
iwrc_set_werror(iwrc rc,uint32_t werror)142 iwrc iwrc_set_werror(iwrc rc, uint32_t werror) {
143 if (!werror) {
144 return rc;
145 }
146 uint64_t ret = _IWLOG_WERR_EC_MASK;
147 ret <<= 30;
148 ret |= (uint32_t) werror & 0x3fffffffU;
149 ret <<= 32;
150 ret |= (uint32_t) rc;
151 return ret;
152 }
153
iwrc_strip_werror(iwrc * rc)154 uint32_t iwrc_strip_werror(iwrc *rc) {
155 uint64_t rcv = *rc;
156 if (((rcv >> 62) & 0x03U) != _IWLOG_WERR_EC_MASK) {
157 return 0;
158 }
159 *rc = rcv & 0x00000000ffffffffULL;
160 return (uint32_t) (rcv >> 32) & 0x3fffffffU;
161 }
162
163 #endif
164
iwrc_strip_code(iwrc * rc)165 void iwrc_strip_code(iwrc *rc) {
166 *rc = *rc & 0x00000000ffffffffULL;
167 }
168
iwlog_set_logfn(IWLOG_FN fp,void * opts)169 void iwlog_set_logfn(IWLOG_FN fp, void *opts) {
170 if (!fp) {
171 _current_logfn = _default_logfn;
172 } else {
173 _current_logfn = fp;
174 }
175 _current_logfn_options = opts;
176 }
177
iwlog_get_logfn(void)178 IWLOG_FN iwlog_get_logfn(void) {
179 return _current_logfn;
180 }
181
iwlog_ecode_explained(iwrc ecode)182 const char* iwlog_ecode_explained(iwrc ecode) {
183 iwrc_strip_errno(&ecode);
184 const char *res;
185 pthread_mutex_lock(&_mtx);
186 res = _ecode_explained(0, ecode);
187 pthread_mutex_unlock(&_mtx);
188 return res;
189 }
190
iwlog_register_ecodefn(IWLOG_ECODE_FN fp)191 iwrc iwlog_register_ecodefn(IWLOG_ECODE_FN fp) {
192 assert(fp);
193 int success = 0;
194 pthread_mutex_lock(&_mtx);
195 for (int i = 0; i < _IWLOG_MAX_ECODE_FUN; ++i) {
196 if (_ecode_functions[i] == 0) {
197 _ecode_functions[i] = fp;
198 success = 1;
199 break;
200 }
201 }
202 pthread_mutex_unlock(&_mtx);
203 return success ? 0 : IW_ERROR_FAIL;
204 }
205
iwlog_init(void)206 iwrc iwlog_init(void) {
207 static int _iwlog_initialized = 0;
208 iwrc rc;
209 if (!__sync_bool_compare_and_swap(&_iwlog_initialized, 0, 1)) {
210 return 0; // initialized already
211 }
212 rc = iwlog_register_ecodefn(_default_ecodefn);
213 return rc;
214 }
215
216 ////////////////////////////////////////////////////////////////////////////////////////////////////
217
218 // Assumed:
219 // 1. `_mtx` is locked.
_ecode_explained(locale_t locale,uint32_t ecode)220 static const char* _ecode_explained(locale_t locale, uint32_t ecode) {
221 const char *ret = 0;
222 for (int i = 0; i < _IWLOG_MAX_ECODE_FUN; ++i) {
223 if (_ecode_functions[i] == 0) {
224 break;
225 } else {
226 ret = _ecode_functions[i](locale, ecode);
227 if (ret) {
228 break;
229 }
230 }
231 }
232 return ret;
233 }
234
_default_ecodefn(locale_t locale,uint32_t ecode)235 static const char* _default_ecodefn(locale_t locale, uint32_t ecode) {
236 switch (ecode) {
237 case IW_ERROR_FAIL:
238 return "Unspecified error. (IW_ERROR_FAIL)";
239 case IW_ERROR_ERRNO:
240 return "Error with expected errno status set. (IW_ERROR_ERRNO)";
241 case IW_ERROR_IO_ERRNO:
242 return "IO error with expected errno status set. (IW_ERROR_IO_ERRNO)";
243 case IW_ERROR_NOT_EXISTS:
244 return "Resource is not exists. (IW_ERROR_NOT_EXISTS)";
245 case IW_ERROR_READONLY:
246 return "Resource is readonly. (IW_ERROR_READONLY)";
247 case IW_ERROR_ALREADY_OPENED:
248 return "Resource is already opened. (IW_ERROR_ALREADY_OPENED)";
249 case IW_ERROR_THREADING:
250 return "Threading error. (IW_ERROR_THREADING)";
251 case IW_ERROR_THREADING_ERRNO:
252 return "Threading error with errno status set. "
253 "(IW_ERROR_THREADING_ERRNO)";
254 case IW_ERROR_ASSERTION:
255 return "Generic assertion error. (IW_ERROR_ASSERTION)";
256 case IW_ERROR_INVALID_HANDLE:
257 return "Invalid HANDLE value. (IW_ERROR_INVALID_HANDLE)";
258 case IW_ERROR_OUT_OF_BOUNDS:
259 return "Argument/parameter/value is out of bounds. "
260 "(IW_ERROR_OUT_OF_BOUNDS)";
261 case IW_ERROR_NOT_IMPLEMENTED:
262 return "Method is not implemented. (IW_ERROR_NOT_IMPLEMENTED)";
263 case IW_ERROR_ALLOC:
264 return "Memory allocation failed. (IW_ERROR_ALLOC)";
265 case IW_ERROR_INVALID_STATE:
266 return "Illegal state error. (IW_ERROR_INVALID_STATE)";
267 case IW_ERROR_NOT_ALIGNED:
268 return "Argument is not aligned properly. (IW_ERROR_NOT_ALIGNED)";
269 case IW_ERROR_FALSE:
270 return "False response/rejection. (IW_ERROR_FALSE)";
271 case IW_ERROR_INVALID_ARGS:
272 return "Invalid function arguments. (IW_ERROR_INVALID_ARGS)";
273 case IW_ERROR_OVERFLOW:
274 return "Overflow. (IW_ERROR_OVERFLOW)";
275 case IW_ERROR_INVALID_VALUE:
276 return " Invalid value. (IW_ERROR_INVALID_VALUE)";
277 case IW_ERROR_UNEXPECTED_RESPONSE:
278 return "Unexpected response. (IW_ERROR_UNEXPECTED_RESPONSE)";
279 case IW_ERROR_NOT_ALLOWED:
280 return "Action is not allowed. (IW_ERROR_NOT_ALLOWED)";
281 case IW_ERROR_UNSUPPORTED:
282 return "Unsupported opration. (IW_ERROR_UNSUPPORTED)";
283 case IW_ERROR_EOF:
284 return "End of IO stream/file (IW_ERROR_EOF)";
285 case IW_ERROR_UNEXPECTED_INPUT:
286 return "Unexpected input/data (IW_ERROR_UNEXPECTED_INPUT)";
287 case IW_ERROR_IO:
288 return "IO error (IW_ERROR_IO)";
289 case IW_ERROR_INVALID_CONFIG:
290 return "Invalid configuration (IW_ERROR_INVALID_CONFIG)";
291 case IW_OK:
292 default:
293 return 0;
294 }
295 return 0;
296 }
297
_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,bool no_va)298 static iwrc _default_logfn(
299 FILE *out,
300 locale_t locale,
301 iwlog_lvl lvl,
302 iwrc ecode,
303 int errno_code,
304 int werror_code,
305 const char *file,
306 int line,
307 uint64_t ts,
308 void *opts,
309 const char *fmt,
310 va_list argp,
311 bool no_va
312 ) {
313 #define TBUF_SZ 96
314 #define EBUF_SZ 256
315
316 iwrc rc = 0;
317
318 #ifndef IW_ANDROID_LOG
319 time_t ts_sec = ((long double) ts / 1000);
320 struct tm timeinfo;
321 size_t sz, sz2;
322 char tbuf[TBUF_SZ];
323 char *cat;
324 #endif
325
326 char ebuf[EBUF_SZ];
327 char *errno_msg = 0, *werror_msg = 0;
328 const char *ecode_msg = 0;
329 char fnamebuf[MAXPATHLEN];
330 char *fnameptr = fnamebuf;
331 char *fname = 0;
332
333 if (opts) {
334 out = ((IWLOG_DEFAULT_OPTS*) opts)->out;
335 if (!out) {
336 goto finish;
337 }
338 }
339
340 if (errno_code) {
341 #if defined(_WIN32)
342 int rci = strerror_s(ebuf, EBUF_SZ, errno_code);
343 if (!rci) {
344 errno_msg = ebuf;
345 }
346 #elif defined(__GLIBC__) && defined(_GNU_SOURCE)
347 errno_msg = strerror_r(errno_code, ebuf, EBUF_SZ); // NOLINT
348 #else
349 int rci = strerror_r(errno_code, ebuf, EBUF_SZ);
350 if (!rci) {
351 errno_msg = ebuf;
352 }
353 #endif
354 }
355
356 #ifdef _WIN32
357 if (werror_code) {
358 LPTSTR msg = NULL;
359 DWORD ret = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, werror_code,
360 MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), (LPTSTR) &msg, 0, NULL);
361
362 if (ret == 0) {
363 if (msg) {
364 LocalFree(msg);
365 msg = NULL;
366 }
367 }
368 werror_msg = msg;
369 }
370 #endif
371
372
373 #ifndef IW_ANDROID_LOG
374 // cppcheck-suppress portability
375 localtime_r(&ts_sec, &timeinfo);
376
377 sz = strftime(tbuf, TBUF_SZ, "%d %b %H:%M:%S", &timeinfo);
378 if (sz == 0) {
379 tbuf[0] = '\0';
380 } else if (TBUF_SZ - sz > 4) { // .000 suffix
381 tbuf[sz] = '.';
382 sz2 = snprintf((char*) tbuf + sz + 1, 4, "%03d", (int) (ts % 1000));
383 if (sz2 > 3) {
384 tbuf[sz] = '\0';
385 }
386 }
387 #else
388 android_LogPriority alp = ANDROID_LOG_INFO;
389 #endif // IW_ANDROID_LOG
390
391 switch (lvl) {
392 case IWLOG_DEBUG:
393 #ifdef IW_ANDROID_LOG
394 alp = ANDROID_LOG_DEBUG;
395 #else
396 cat = "DEBUG";
397 #endif
398 break;
399
400 case IWLOG_INFO:
401 #ifdef IW_ANDROID_LOG
402 alp = ANDROID_LOG_INFO;
403 #else
404 cat = "INFO";
405 #endif
406 file = 0;
407 break;
408
409 case IWLOG_VERBOSE:
410 #ifdef IW_ANDROID_LOG
411 alp = ANDROID_LOG_INFO;
412 #else
413 cat = "VERBOSE";
414 #endif
415 file = 0;
416 break;
417
418
419 case IWLOG_WARN:
420 #ifdef IW_ANDROID_LOG
421 alp = ANDROID_LOG_WARN;
422 #else
423 cat = "WARN";
424 #endif
425 break;
426
427 case IWLOG_ERROR:
428 #ifdef IW_ANDROID_LOG
429 alp = ANDROID_LOG_ERROR;
430 #else
431 cat = "ERROR";
432 #endif
433 break;
434
435 default:
436 #ifndef IW_ANDROID_LOG
437 cat = "UNKNOW";
438 #endif
439 assert(0);
440 break;
441 }
442 if (ecode) {
443 ecode_msg = _ecode_explained(locale, ecode);
444 }
445 if (file && (line > 0)) {
446 size_t len = strlen(file);
447 if (len < sizeof(fnamebuf)) {
448 memcpy(fnameptr, file, len);
449 fnameptr[len] = '\0';
450 } else {
451 fnameptr = strdup(file);
452 RCA(fnameptr, finish);
453 }
454 fname = iwp_basename(fnameptr);
455 }
456
457 if (pthread_mutex_lock(&_mtx)) {
458 rc = IW_ERROR_THREADING_ERRNO;
459 goto finish;
460 }
461
462 #ifndef IW_ANDROID_LOG
463
464 if (ecode || errno_code || werror_code) {
465 if (fname && (line > 0)) {
466 fprintf(out, "%s %s %s:%d %" PRIu64 "|%d|%d|%s|%s|%s: ", tbuf, cat, fname, line, ecode, errno_code,
467 werror_code, (ecode_msg ? ecode_msg : ""), (errno_msg ? errno_msg : ""),
468 (werror_msg ? werror_msg : "")); // -V547
469 } else {
470 fprintf(out, "%s %s %" PRIu64 "|%d|%d|%s|%s|%s: ", tbuf, cat, ecode, errno_code, werror_code,
471 (ecode_msg ? ecode_msg : ""), (errno_msg ? errno_msg : ""), (werror_msg ? werror_msg : "")); // -V547
472 }
473 } else {
474 if (fname && (line > 0)) {
475 fprintf(out, "%s %s %s:%d: ", tbuf, cat, fname, line);
476 } else {
477 fprintf(out, "%s %s: ", tbuf, cat);
478 }
479 }
480 if (fmt) {
481 if (no_va) {
482 fwrite(fmt, strlen(fmt), 1, out);
483 } else {
484 vfprintf(out, fmt, argp);
485 }
486 }
487 fwrite(IW_LINE_SEP, sizeof(IW_LINE_SEP) - 1, 1, out);
488 fflush(out);
489
490 #else
491
492 if (ecode || errno_code || werror_code) {
493 if (fname && (line > 0)) {
494 __android_log_print(alp, "IWLOG", "%s:%d %" PRIu64 "|%d|%d|%s|%s|%s: ", fname, line, ecode, errno_code,
495 werror_code, (ecode_msg ? ecode_msg : ""), (errno_msg ? errno_msg : ""),
496 (werror_msg ? werror_msg : ""));
497 } else {
498 __android_log_print(alp, "IWLOG", "%" PRIu64 "|%d|%d|%s|%s|%s: ", ecode, errno_code, werror_code,
499 (ecode_msg ? ecode_msg : ""), (errno_msg ? errno_msg : ""), (werror_msg ? werror_msg : ""));
500 }
501 } else {
502 if (fname && (line > 0)) {
503 __android_log_print(alp, "IWLOG", "%s:%d: ", fname, line);
504 }
505 }
506 if (fmt) {
507 if (no_va) {
508 __android_log_write(alp, "IWLOG", fmt);
509 } else {
510 __android_log_vprint(alp, "IWLOG", fmt, argp);
511 }
512 }
513
514 #endif
515
516 pthread_mutex_unlock(&_mtx);
517
518 finish:
519 if (fnameptr != fnamebuf) {
520 free(fnameptr);
521 }
522
523 #ifdef _WIN32
524 if (werror_msg) {
525 LocalFree(werror_msg);
526 }
527 #endif
528
529 #undef TBUF_SZ
530 #undef EBUF_SZ
531 return rc;
532 }
533