• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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