1 /* radare - LGPL - Copyright 2007-2018 - pancake, ret2libc */
2
3 #define LOG_CONFIGSTR_SIZE 512
4 #define LOG_OUTPUTBUF_SIZE 512
5
6 #include <r_core.h>
7 #include <stdarg.h>
8
9 // TODO: Use thread-local storage to make these variables thread-safe
10 static RList *log_cbs = NULL; // Functions to call when outputting log string
11 static int cfg_loglvl = R_LOGLVL_ERROR; // Log level output
12 static int cfg_logtraplvl = R_LOGLVL_FATAL; // Log trap level
13 static bool cfg_logsrcinfo = false; // Print out debug source info with the output
14 static bool cfg_logcolors = false; // Output colored log text based on level
15 static char cfg_logfile[LOG_CONFIGSTR_SIZE] = ""; // Output text to filename
16 static const char *level_tags[] = { // Log level to tag string lookup array
17 [R_LOGLVL_SILLY] = "SILLY",
18 [R_LOGLVL_VERBOSE] = "VERBOSE",
19 [R_LOGLVL_DEBUG] = "DEBUG",
20 [R_LOGLVL_INFO] = "INFO",
21 [R_LOGLVL_WARN] = "WARNING",
22 [R_LOGLVL_ERROR] = "ERROR",
23 [R_LOGLVL_FATAL] = "FATAL"
24 };
25
26 // cconfig.c configuration callback functions below
r_log_set_level(RLogLevel level)27 R_API void r_log_set_level(RLogLevel level) {
28 cfg_loglvl = level;
29 }
30
r_log_set_traplevel(RLogLevel level)31 R_API void r_log_set_traplevel(RLogLevel level) {
32 cfg_logtraplvl = level;
33 }
34
r_log_set_file(const char * filename)35 R_API void r_log_set_file(const char *filename) {
36 int value_len = r_str_nlen (filename, LOG_CONFIGSTR_SIZE) + 1;
37 strncpy (cfg_logfile, filename, value_len);
38 }
39
r_log_set_srcinfo(bool show_info)40 R_API void r_log_set_srcinfo(bool show_info) {
41 cfg_logsrcinfo = show_info;
42 }
43
r_log_set_colors(bool show_info)44 R_API void r_log_set_colors(bool show_info) {
45 cfg_logcolors = show_info;
46 }
47
48 /**
49 * \brief Add a logging callback
50 * \param cbfunc RLogCallback style function to be called
51 */
r_log_add_callback(RLogCallback cbfunc)52 R_API void r_log_add_callback(RLogCallback cbfunc) {
53 if (!log_cbs) {
54 log_cbs = r_list_new ();
55 }
56 if (!r_list_contains (log_cbs, cbfunc)) {
57 r_list_append (log_cbs, cbfunc);
58 }
59 }
60
61 /**
62 * \brief Remove a logging callback
63 * \param cbfunc RLogCallback style function to be called
64 */
r_log_del_callback(RLogCallback cbfunc)65 R_API void r_log_del_callback(RLogCallback cbfunc) {
66 if (log_cbs) {
67 r_list_delete_data (log_cbs, cbfunc);
68 }
69 }
70
r_vlog(const char * funcname,const char * filename,ut32 lineno,RLogLevel level,const char * tag,const char * fmtstr,va_list args)71 R_API void r_vlog(const char *funcname, const char *filename,
72 ut32 lineno, RLogLevel level, const char *tag, const char *fmtstr, va_list args) {
73 va_list args_copy;
74 va_copy (args_copy, args);
75
76 if (level < cfg_loglvl && level < cfg_logtraplvl) {
77 // Don't print if output level is lower than current level
78 // Don't ignore fatal/trap errors
79 va_end (args_copy);
80 return;
81 }
82
83 // TODO: Colors
84
85 // Build output string with src info, and formatted output
86 char output_buf[LOG_OUTPUTBUF_SIZE] = ""; // Big buffer for building the output string
87 if (!tag) {
88 tag = R_BETWEEN (0, level, R_ARRAY_SIZE (level_tags) - 1)? level_tags[level]: "";
89 }
90 int offset = snprintf (output_buf, LOG_OUTPUTBUF_SIZE, "%s: ", tag);
91 if (cfg_logsrcinfo) {
92 offset += snprintf (output_buf + offset, LOG_OUTPUTBUF_SIZE - offset, "%s in %s:%i: ", funcname, filename, lineno);
93 }
94 vsnprintf (output_buf + offset, LOG_OUTPUTBUF_SIZE - offset, fmtstr, args);
95
96 // Actually print out the string with our callbacks
97 if (log_cbs && r_list_length (log_cbs) > 0) {
98 RListIter *it;
99 RLogCallback cb;
100
101 r_list_foreach (log_cbs, it, cb) {
102 cb (output_buf, funcname, filename, lineno, level, NULL, fmtstr, args_copy);
103 }
104 } else {
105 fprintf (stderr, "%s", output_buf);
106 }
107 va_end (args_copy);
108
109 // Log to file if enabled
110 if (cfg_logfile[0] != 0x00) {
111 FILE *file = r_sandbox_fopen (cfg_logfile, "a+"); // TODO: Optimize (static? Needs to remake on cfg change though)
112 if (!file) {
113 file = r_sandbox_fopen (cfg_logfile, "w+");
114 }
115 if (file) {
116 fprintf (file, "%s", output_buf);
117 fclose (file);
118 } else {
119 eprintf ("%s failed to write to file: %s\n", MACRO_LOG_FUNC, cfg_logfile);
120 }
121 }
122
123 if (level >= cfg_logtraplvl && level != R_LOGLVL_NONE) {
124 fflush (stdout); // We're about to exit HARD, flush buffers before dying
125 fflush (stderr);
126 // TODO: call r_cons_flush if libr_cons is being used
127 r_sys_breakpoint (); // *oof*
128 }
129 }
130
131 /**
132 * \brief Internal logging function used by preprocessor macros
133 * \param funcname Contains the function name of the calling function
134 * \param filename Contains the filename that funcname is defined in
135 * \param lineno The line number that this log call is being made from in filename
136 * \param lvl Logging level for output
137 * \param fmtstr A printf like string
138
139 This function is used by the R_LOG_* preprocessor macros for logging
140 */
r_log(const char * funcname,const char * filename,ut32 lineno,RLogLevel level,const char * tag,const char * fmtstr,...)141 R_API void r_log(const char *funcname, const char *filename,
142 ut32 lineno, RLogLevel level, const char *tag, const char *fmtstr, ...) {
143 va_list args;
144
145 va_start (args, fmtstr);
146 r_vlog (funcname, filename, lineno, level, tag, fmtstr, args);
147 va_end (args);
148 }
149