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