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