MediaProcessors
log.c
1 /*
2  * Copyright (c) 2017, 2018 Rafael Antoniello
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of copyright holders nor the names of its
14  * contributors may be used to endorse or promote products derived
15  * from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS OR CONTRIBUTORS
21  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 
31 #include "log.h"
32 
33 #include <stdlib.h>
34 #include <pthread.h>
35 #include <unistd.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <sys/types.h>
39 #include <fcntl.h>
40 #include <stdarg.h>
41 
42 #include "stat_codes.h"
43 #include "check_utils.h"
44 #include "llist.h"
45 
46 /* **** Definitions **** */
47 
51 #define LOG_MAX_FILESIZE (256* 1024)
52 
53 #ifndef _INSTALL_DIR //HACK
54 #define _INSTALL_DIR "./"
55 #endif
56 #ifndef _PROCNAME //HACK
57 #define _PROCNAME "stdout"
58 #endif
59 #define LOG_FILEPATH _INSTALL_DIR"/var/log/"_PROCNAME
60 #define LOG_FILE LOG_FILEPATH"/"_PROCNAME".log"
61 #define LOG_FILE_OLD LOG_FILEPATH"/"_PROCNAME".old.log"
62 
64 #ifdef LOG_FORCE_USING_STDOUT
65 
66  #define CNRM "\x1B[0m"
67  #define CRED "\033[1;31m" // Bold-red
68  #define CGRN "\x1B[32m"
69  #define CYEL "\x1B[33m"
70  #define CMAG "\x1B[35m"
71  #define CBLU "\x1B[34m"
72 
73  #define LOG_VERBOSE_HIGHLIGHT CNRM
74  #define LOG_DEBUG_HIGHLIGHT CNRM
75  #define LOG_WARNING_HIGHLIGHT CYEL
76  #define LOG_ERROR_HIGHLIGHT CRED
77  #define LOG_RAW_HIGHLIGHT CNRM
78  #define LOG_EVENT_HIGHLIGHT CGRN
79 
80  #define OPEN_FILE() \
81  fileno(stdout)
82  #define CLOSE_FILE(FD)
83  #define UPDATE_USE_STDOUT_FLAG() \
84  flag_use_stdout= 1
85 #else
86  #define LOG_VERBOSE_HIGHLIGHT "VERBOSE: "
87  #define LOG_DEBUG_HIGHLIGHT "DEBUG: "
88  #define LOG_WARNING_HIGHLIGHT "WARNING: "
89  #define LOG_ERROR_HIGHLIGHT "ERROR: "
90  #define LOG_RAW_HIGHLIGHT ""
91  #define LOG_EVENT_HIGHLIGHT "EVENT: "
92 
93  #define OPEN_FILE() \
94  open(LOG_FILE, O_RDWR| O_APPEND| O_TRUNC| O_CREAT, S_IRUSR| S_IWUSR)
95  #define CLOSE_FILE(FD) \
96  close(FD)
97  #define UPDATE_USE_STDOUT_FLAG() \
98  flag_use_stdout= 0
99 #endif
100 
102 typedef struct log_ctx_s {
108  int id;
112  pthread_mutex_t mutex;
130 } log_ctx_t;
131 
132 /* **** Prototypes **** */
133 
134 static void log_trace_fd(log_level_t type, const char *filename, int line,
135  const char *format, va_list arg);
136 static void log_trace_buf(log_level_t type, log_ctx_t *log_ctx,
137  const char *filename, int line, const char *format, va_list arg);
138 static void log_module_fflush();
139 
140 /* **** Implementations **** */
141 
143 static int logfile_fd= -1;
144 static pthread_mutex_t logfile_mutex= PTHREAD_MUTEX_INITIALIZER;
145 static int flag_use_stdout= 0;
146 
147 int log_module_open()
148 {
149  int ret_code, end_code= STAT_ERROR;
150  LOG_CTX_INIT(NULL);
151 
152  /* Open log-file */
153  logfile_fd= OPEN_FILE();
154  if(logfile_fd< 0) {
155  printf("Could not open LOG file '%s'\n", LOG_FILE);
156  goto end;
157  }
158 
159  /* Initialize module MUTEX for log-file */
160  ret_code= pthread_mutex_init(&logfile_mutex, NULL);
161  CHECK_DO(ret_code== 0, goto end);
162 
163  /* Update "use standard out" flag */
164  UPDATE_USE_STDOUT_FLAG();
165 
166  end_code= STAT_SUCCESS;
167 end:
168  if(end_code!= STAT_SUCCESS)
169  log_module_close();
170  return end_code;
171 }
172 
173 void log_module_close()
174 {
175  int ret_code;
176  LOG_CTX_INIT(NULL);
177 
178  /* Close log-file */
179  CLOSE_FILE(logfile_fd);
180  logfile_fd= -1;
181 
182  /* Release module MUTEX for log-file */
183  ret_code= pthread_mutex_destroy(&logfile_mutex);
184  ASSERT(ret_code== 0);
185 
186  /* Erase "use standard out" flag */
187  flag_use_stdout= 0;
188 }
189 
190 log_ctx_t* log_open(int id)
191 {
192  int ret_code, end_code= STAT_ERROR;
193  log_ctx_t *log_ctx= NULL;
194  LOG_CTX_INIT(NULL);
195 
196  /* Allocate LOG module instance context structure */
197  log_ctx= (log_ctx_t*)calloc(1, sizeof(log_ctx_t));
198  CHECK_DO(log_ctx!= NULL, goto end);
199 
200  /* Initialize context structure */
201  log_ctx->id= id;
202  ret_code= pthread_mutex_init(&log_ctx->mutex, NULL);
203  CHECK_DO(ret_code== 0, goto end);
204  log_ctx->log_line_ctx_llist= NULL;
207  log_ctx->log_line_ctx_llist_len= 0;
208 
209  end_code= STAT_SUCCESS;
210 end:
211  if(end_code!= STAT_SUCCESS)
212  log_close(&log_ctx);
213  return log_ctx;
214 }
215 
216 void log_close(log_ctx_t **ref_log_ctx)
217 {
218  log_ctx_t *log_ctx;
219 
220  if(ref_log_ctx== NULL)
221  return;
222 
223  if((log_ctx= *ref_log_ctx)!= NULL) {
224  int ret_code;
225  LOG_CTX_INIT(NULL);
226 
227  /* Release instance MUTEX */
228  ret_code= pthread_mutex_destroy(&log_ctx->mutex);
229  ASSERT(ret_code== 0);
230 
231  /* Release list of log-line context structures */
232  while(log_ctx->log_line_ctx_llist!= NULL) {
233  log_line_ctx_t *log_line_ctx= llist_pop(
234  &log_ctx->log_line_ctx_llist);
235  log_line_ctx_release(&log_line_ctx);
236  }
237 
238  free(log_ctx);
239  *ref_log_ctx= NULL;
240  }
241 }
242 
243 void log_trace(log_level_t type, log_ctx_t *log_ctx, const char *filename,
244  int line, const char *format, ...)
245 {
246  va_list arg, arg_cpy;
247 
248  /* Check arguments */
249  if(type>= LOG_TYPE_MAX) return;
250  if(format== NULL) return;
251 
252  /* Check module initialization: file-descriptor for file or STDOUT */
253  if(logfile_fd< 0) {
254  printf("'LOG' module should be initialized previously\n");
255  return;
256  }
257 
258  va_start(arg, format);
259  va_copy(arg_cpy, arg);
260 
261  if(flag_use_stdout== 0 && log_ctx!= NULL) {
262  /* Print to instance-specific trace function */
263  log_trace_buf(type, log_ctx, filename, line, format, arg_cpy);
264  } else {
265  log_trace_fd(type, filename, line, format, arg_cpy);
266  }
267 
268  va_end(arg);
269  return;
270 }
271 
272 const llist_t* log_get(log_ctx_t *log_ctx)
273 {
274  llist_t *curr_node, *prev_node;
275  llist_t *log_line_ctx_llist= NULL; //LOG-traces list replica to be returned
276 
277  /* Check arguments */
278  if(log_ctx== NULL)
279  return NULL;
280 
281  pthread_mutex_lock(&log_ctx->mutex);
282 
283  /* Duplicate LOG-traces list */
284  curr_node= log_ctx->log_line_ctx_llist;
285  prev_node= NULL;
286  while(curr_node!= NULL) {
287  log_line_ctx_t *log_line_ctx2;
288  llist_t *new_node;
289 
290  /* Get a copy of LOG-line context structure */
291  log_line_ctx2= log_line_ctx_dup((const log_line_ctx_t*)curr_node->data);
292  if(log_line_ctx2== NULL)
293  continue;
294 
295  /* Push copy of LOG-line structure to list replica */
296  new_node= (llist_t*)malloc(sizeof(llist_t));
297  if(new_node== NULL)
298  continue;
299  new_node->data= (void*)log_line_ctx2;
300  new_node->next= NULL;
301 
302  /* Link current node to previous node if applicable */
303  if(prev_node!= NULL)
304  prev_node->next= new_node;
305  else
306  log_line_ctx_llist= new_node;
307 
308  /* Update for next iteration */
309  prev_node= new_node;
310  curr_node= curr_node->next;
311  }
312 
313  pthread_mutex_unlock(&log_ctx->mutex);
314  return log_line_ctx_llist;
315 }
316 
317 void log_clear(log_ctx_t *log_ctx)
318 {
319  /* Check arguments */
320  if(log_ctx== NULL)
321  return;
322 
323  pthread_mutex_lock(&log_ctx->mutex);
324 
325  /* Release list of log-line context structures */
326  while(log_ctx->log_line_ctx_llist!= NULL) {
327  log_line_ctx_t *log_line_ctx= llist_pop(
328  &log_ctx->log_line_ctx_llist);
329  log_line_ctx_release(&log_line_ctx);
330  }
333  log_ctx->log_line_ctx_llist_len= 0;
334 
335  pthread_mutex_unlock(&log_ctx->mutex);
336 }
337 
338 log_line_ctx_t* log_line_ctx_allocate()
339 {
340  return (log_line_ctx_t*)calloc(1, sizeof(log_line_ctx_t));
341 }
342 
343 log_line_ctx_t* log_line_ctx_dup(const log_line_ctx_t* log_line_ctx_arg)
344 {
345  log_line_ctx_t *log_line_ctx= NULL;
346 
347  /* Check arguments */
348  if(log_line_ctx_arg== NULL)
349  return NULL;
350 
351  /* Allocate LOG-line context structure */
352  log_line_ctx= log_line_ctx_allocate();
353  if(log_line_ctx== NULL)
354  return NULL;
355 
356  /* Copy structure */
357  strncpy(log_line_ctx->code, log_line_ctx_arg->code, LOG_LINE_SIZE);
358  strncpy(log_line_ctx->desc, log_line_ctx_arg->desc, LOG_LINE_SIZE);
359  strncpy(log_line_ctx->date, log_line_ctx_arg->date, LOG_DATE_SIZE);
360  log_line_ctx->ts= log_line_ctx_arg->ts;
361  log_line_ctx->count= log_line_ctx_arg->count;
362 
363  return log_line_ctx;
364 }
365 
366 void log_line_ctx_release(log_line_ctx_t **ref_log_line_ctx)
367 {
368  log_line_ctx_t *log_line_ctx;
369 
370  if(ref_log_line_ctx== NULL)
371  return;
372 
373  if((log_line_ctx= *ref_log_line_ctx)!= NULL) {
374  free(log_line_ctx);
375  *ref_log_line_ctx= NULL;
376  }
377 }
378 
379 void log_trace_byte_table(const char *label, const char *file, int line,
380  uint8_t *data, size_t len, size_t xsize)
381 {
382  int i, j;
383  uint8_t *p= data;
384 
385  /* Check arguments */
386  if(file== NULL || data== NULL) return;
387  if(xsize& 3) {
388  log_trace(LOG_ERROR, NULL, __FILENAME__, __LINE__,
389  "Parameter 'xsize' MUST be a multiple of 4.\n");
390  return;
391  }
392 
393  log_trace(LOG_RAW, NULL, __FILENAME__, __LINE__,
394  "%s %d: \n> ======== %s ========\n", file, line,
395  label!= NULL? label: "");
396  for(i= 0; i< len; i+= xsize) {
397  log_trace(LOG_RAW, NULL, __FILENAME__, __LINE__, "> ");
398  for(j= 0; j< xsize; j++) {
399  if(i+ j>= len)
400  break;
401  log_trace(LOG_RAW, NULL, __FILENAME__, __LINE__, "%02x", p[i+j]);
402  if((j& 3)== 0)
403  log_trace(LOG_RAW, NULL, __FILENAME__, __LINE__, " ");
404  }
405  log_trace(LOG_RAW, NULL, __FILENAME__, __LINE__, "\n");
406  }
407  log_trace(LOG_RAW, NULL, __FILENAME__, __LINE__, ">\n\n");
408  log_module_fflush();
409 }
410 
411 static void log_trace_fd(log_level_t type, const char *filename, int line,
412  const char *format, va_list arg)
413 {
414  char str[LOG_LINE_SIZE], *highlight_prefix;
415  size_t str_size= 0;
416  ssize_t written= -1;
417 
418  pthread_mutex_lock(&logfile_mutex);
419 
420  /* Print color code to terminal or prefix for LOG-file */
421  switch(type) {
422  case LOG_VERBOSE:
423  highlight_prefix= LOG_VERBOSE_HIGHLIGHT;
424  break;
425  case LOG_DEBUG:
426  highlight_prefix= LOG_DEBUG_HIGHLIGHT;
427  break;
428  case LOG_WARNING:
429  highlight_prefix= LOG_WARNING_HIGHLIGHT;
430  break;
431  case LOG_ERROR:
432  highlight_prefix= LOG_ERROR_HIGHLIGHT;
433  break;
434  case LOG_RAW:
435  highlight_prefix= LOG_RAW_HIGHLIGHT;
436  break;
437  case LOG_EVENT:
438  highlight_prefix= LOG_EVENT_HIGHLIGHT;
439  break;
440  default:
441  highlight_prefix= "";
442  break;
443  }
444  str_size+= snprintf(&str[str_size], sizeof(str)- str_size, "%s",
445  highlight_prefix);
446 
447  /* Print source-code file-name and file-line */
448  if(filename!= NULL && type!= LOG_RAW) {
449  if(str_size>= sizeof(str)) goto end;
450  str_size+= snprintf(&str[str_size], sizeof(str)- str_size, "%s %d ",
451  filename, line);
452  }
453 
454  /* Print rest of the formatted string */
455  if(str_size>= sizeof(str)) goto end;
456  str_size+= vsnprintf(&str[str_size], sizeof(str)- str_size, format, arg);
457 
458  /* Write (create/truncate) to LOG-file */
459  if(str_size>= sizeof(str)) str_size= sizeof(str);
460  written= write(logfile_fd, str, str_size);
461  // Hack just to ignore compilation warnings
462  if(written!= str_size) written= -1;
463 
464  /* Flush file traces */
465  log_module_fflush();
466 
467 end:
468  pthread_mutex_unlock(&logfile_mutex);
469  va_end(arg);
470  return;
471 }
472 
473 static void log_trace_buf(log_level_t type, log_ctx_t *log_ctx,
474  const char *filename, int line, const char *format, va_list arg)
475 {
476  char code[LOG_LINE_SIZE], *extp;
477  llist_t **ref_curr_node= NULL;
478  log_line_ctx_t *new_log_line_ctx= NULL;
479  llist_t *new_node= NULL;
480  uint64_t oldest_ts= 0;
481  struct timespec monotime_curr= {0};
482  time_t t= time(NULL);
483  struct tm *tm= localtime(&t);
484 
485  /* Check arguments */
486  if(log_ctx== NULL)
487  return;
488 
489  /* Get current time */
490  if(clock_gettime(CLOCK_MONOTONIC, &monotime_curr)!= 0)
491  goto end;
492 
493  /* Print log-line */
494  snprintf(code, LOG_LINE_SIZE, "%d%s", line, filename);
495  extp= strstr(code, ".c");
496  if(extp!= NULL)
497  *extp= 0; // erase extension '.c*' from code nomenclature
498 
499  pthread_mutex_lock(&log_ctx->mutex);
500 
501  /* Check if this specific log trace (identified by unambiguous code) is
502  * available in log-list.
503  * Recycle list if code is already listed; otherwise add new trace.
504  */
505  ref_curr_node= &log_ctx->log_line_ctx_llist;
507  while(*ref_curr_node!= NULL) {
508  uint64_t ts_ith;
509  log_line_ctx_t *log_line_ctx_ith;
510  llist_t *curr_node= *ref_curr_node;
511 
512  /* Get node data */
513  log_line_ctx_ith= (log_line_ctx_t*)curr_node->data;
514  if(log_line_ctx_ith== NULL)
515  continue;
516 
517  /* Check if log-trace code is available */
518  if(strncmp(code, log_line_ctx_ith->code, strlen(code))== 0) {
519  /* Update node data */
520  vsnprintf(log_line_ctx_ith->desc, LOG_LINE_SIZE, format, arg);
521  log_line_ctx_ith->count++;
522  log_line_ctx_ith->ts= (uint64_t)monotime_curr.tv_sec;
523  if(tm!= NULL) {
524  snprintf(log_line_ctx_ith->date, LOG_DATE_SIZE,
525  "%d:%d:%d %d-%d-%d", tm->tm_hour, tm->tm_min,
526  tm->tm_sec, tm->tm_year + 1900, tm->tm_mon + 1,
527  tm->tm_mday);
528  } else {
529  memset(log_line_ctx_ith->date, 0, LOG_DATE_SIZE);
530  }
531  goto end;
532  }
533 
534  /* Register the reference of the oldest node of the list */
535  if((ts_ith= log_line_ctx_ith->ts)< oldest_ts) {
536  log_ctx->log_line_ctx_llist_oldest_ref= ref_curr_node;
537  oldest_ts= ts_ith;
538  }
539 
540  /* Register the reference of the last node of the list */
541  if((*ref_curr_node)->next== NULL)
542  log_ctx->log_line_ctx_llist_tail_ref= ref_curr_node;
543 
544  /* Update for next iteration */
545  ref_curr_node= &(*ref_curr_node)->next;
546  }
547 
548  /* **** If this point is reached, log-trace is new and should be added to
549  * the list **** */
550 
551  /* Create new LOG-line structure instance */
552  new_log_line_ctx= log_line_ctx_allocate();
553  if(new_log_line_ctx== NULL)
554  goto end; // Error
555  strncpy(new_log_line_ctx->code, code, LOG_LINE_SIZE);
556  vsnprintf(new_log_line_ctx->desc, LOG_LINE_SIZE, format, arg);
557  new_log_line_ctx->count= 1;
558  new_log_line_ctx->ts= (uint64_t)monotime_curr.tv_sec;
559  if(tm!= NULL) {
560  snprintf(new_log_line_ctx->date, LOG_DATE_SIZE, "%d:%d:%d %d-%d-%d",
561  tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_year + 1900,
562  tm->tm_mon + 1, tm->tm_mday);
563  } else {
564  memset(new_log_line_ctx->date, 0, LOG_DATE_SIZE);
565  }
566 
567  /* Create new LOG-list node */
568  new_node= (llist_t*)malloc(sizeof(llist_t));
569  if(new_node== NULL)
570  goto end; // Error
571  new_node->data= (void*)new_log_line_ctx;
572  new_log_line_ctx= NULL; // avoid freeing at the end of function
573  new_node->next= NULL; // set node 'next'
574 
575  /* Link new node to tail */
576  if(*log_ctx->log_line_ctx_llist_tail_ref== NULL)
577  *log_ctx->log_line_ctx_llist_tail_ref= new_node;
578  else
579  (*log_ctx->log_line_ctx_llist_tail_ref)->next= new_node;
580  new_node= NULL; // avoid freeing at the end of function
581 
582  /* Control log-list maximum size (truncate list if applicable) */
583  if(log_ctx->log_line_ctx_llist_len>= LOG_BUF_LINES_NUM) {
584  llist_t *log_line_ctx_llist_oldest=
586  log_line_ctx_t *log_line_ctx_oldest= (log_line_ctx_t*)
587  log_line_ctx_llist_oldest->data;
588  if(log_line_ctx_oldest!= NULL)
589  log_line_ctx_release(&log_line_ctx_oldest);
590 
591  /* Unlink and release least recent log-trace in list */
593  log_line_ctx_llist_oldest->next;
594  free(log_line_ctx_llist_oldest);
595  } else
596  log_ctx->log_line_ctx_llist_len++;
597 
598 end:
599 #if 0 //RAL: comment-me
600  do {
601  llist_t *log_line_ctx_llist= log_ctx->log_line_ctx_llist;
602  printf("LOG-traces list length (id= %d): %d; "
603  "last log-trace code: %s\n",
604  log_ctx->id, log_ctx->log_line_ctx_llist_len,
605  log_line_ctx_llist!= NULL?
606  ((log_line_ctx_t*)(log_line_ctx_llist->data))->code:
607  "null");
608  fflush(stdout);
609  } while(0);
610 #endif
611  pthread_mutex_unlock(&log_ctx->mutex);
612  if(new_log_line_ctx!= NULL)
613  log_line_ctx_release(&new_log_line_ctx);
614  if(new_node!= NULL)
615  free(new_node);
616  va_end(arg);
617  return;
618 }
619 
620 #ifdef LOG_FORCE_USING_STDOUT
621 
622 static void log_module_fflush()
623 {
624  /* Flush to standard-out */
625  fflush(stdout);
626 }
627 
628 #else
629 
630 static void log_module_fflush()
631 {
632  off_t logfile_off;
633 
634  /* Check LOG-file size and limit and flush if applicable */
635  logfile_off= lseek(logfile_fd, 0, SEEK_CUR);
636  if(logfile_off> LOG_MAX_FILESIZE) {
637  CLOSE_FILE(logfile_fd);
638  unlink(LOG_FILE_OLD);
639  rename(LOG_FILE, LOG_FILE_OLD);
640  logfile_fd= OPEN_FILE();
641  if(logfile_fd< 0)
642  printf("Error: Unable to create log-file '%s'.\n", LOG_FILE);
643  }
644 }
645 
646 #endif
int log_line_ctx_llist_len
Definition: log.c:129
pthread_mutex_t mutex
Definition: log.c:112
Definition: llist.h:49
int id
Definition: log.c:108
General status codes enumeration.
#define CHECK_DO(COND, ACTION)
Definition: check_utils.h:57
llist_t * log_line_ctx_llist
Definition: log.c:116
Simple linked-list utility implementation.
#define ASSERT(COND)
Definition: check_utils.h:51
llist_t ** log_line_ctx_llist_tail_ref
Definition: log.c:121
void * llist_pop(llist_t **ref_llist_head)
Definition: llist.c:75
Definition: log.c:102
llist_t ** log_line_ctx_llist_oldest_ref
Definition: log.c:125