r23792: convert Samba4 to GPLv3
[jelmer/samba4-debian.git] / source / lib / util / debug.c
1 /*
2    Unix SMB/CIFS implementation.
3    Samba debug functions
4    Copyright (C) Andrew Tridgell 2003
5    Copyright (C) James J Myers   2003
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "system/filesys.h"
23 #include "system/time.h"
24 #include "dynconfig.h"
25
26 /**
27  * @file
28  * @brief Debug logging
29  **/
30
31 /** 
32  * this global variable determines what messages are printed 
33  */
34 _PUBLIC_ int DEBUGLEVEL;
35
36 /* the registered mutex handlers */
37 static struct {
38         const char *name;
39         struct debug_ops ops;
40 } debug_handlers;
41
42 /* state variables for the debug system */
43 static struct {
44         int fd;
45         enum debug_logtype logtype;
46         const char *prog_name;
47 } state;
48
49 static BOOL reopen_logs_scheduled;
50 static BOOL check_reopen_logs(void)
51 {
52         if (state.fd == 0 || reopen_logs_scheduled) {
53                 reopen_logs_scheduled = False;
54                 reopen_logs();
55         }
56
57         if (state.fd <= 0) return False;
58
59         return True;
60 }
61
62 _PUBLIC_ void debug_schedule_reopen_logs(void)
63 {
64         reopen_logs_scheduled = True;
65 }
66
67 static void log_timestring(int level, const char *location, const char *func)
68 {
69         char *t = NULL;
70         char *s = NULL;
71
72         if (!check_reopen_logs()) return;
73
74         if (state.logtype != DEBUG_FILE) return;
75
76         t = timestring(NULL, time(NULL));
77         if (!t) return;
78
79         asprintf(&s, "[%s, %d %s:%s()]\n", t, level, location, func);
80         talloc_free(t);
81         if (!s) return;
82
83         write(state.fd, s, strlen(s));
84         free(s);
85 }
86
87 /**
88   the backend for debug messages. Note that the DEBUG() macro has already
89   ensured that the log level has been met before this is called
90 */
91 _PUBLIC_ void do_debug_header(int level, const char *location, const char *func)
92 {
93         log_timestring(level, location, func);
94         log_task_id();
95 }
96
97 /**
98   the backend for debug messages. Note that the DEBUG() macro has already
99   ensured that the log level has been met before this is called
100
101   @note You should never have to call this function directly. Call the DEBUG()
102   macro instead.
103 */
104 _PUBLIC_ void do_debug(const char *format, ...) _PRINTF_ATTRIBUTE(1,2)
105 {
106         va_list ap;
107         char *s = NULL;
108
109         if (!check_reopen_logs()) return;
110
111         va_start(ap, format);
112         vasprintf(&s, format, ap);
113         va_end(ap);
114
115         write(state.fd, s, strlen(s));
116         free(s);
117 }
118
119 _PUBLIC_ const char *logfile = NULL;
120
121 /**
122   reopen the log file (usually called because the log file name might have changed)
123 */
124 _PUBLIC_ void reopen_logs(void)
125 {
126         char *fname = NULL;
127         int old_fd = state.fd;
128
129         switch (state.logtype) {
130         case DEBUG_STDOUT:
131                 state.fd = 1;
132                 break;
133
134         case DEBUG_STDERR:
135                 state.fd = 2;
136                 break;
137
138         case DEBUG_FILE:
139                 if ((*logfile) == '/') {
140                         fname = strdup(logfile);
141                 } else {
142                         asprintf(&fname, "%s/%s.log", dyn_LOGFILEBASE, state.prog_name);
143                 }
144                 if (fname) {
145                         int newfd = open(fname, O_CREAT|O_APPEND|O_WRONLY, 0600);
146                         if (newfd == -1) {
147                                 DEBUG(1, ("Failed to open new logfile: %s\n", fname));
148                                 old_fd = -1;
149                         } else {
150                                 state.fd = newfd;
151                         }
152                         free(fname);
153                 } else {
154                         DEBUG(1, ("Failed to find name for file-based logfile!\n"));
155                 }
156
157                 break;
158         }
159
160         if (old_fd > 2) {
161                 close(old_fd);
162         }
163 }
164
165 /**
166   control the name of the logfile and whether logging will be to stdout, stderr
167   or a file
168 */
169 _PUBLIC_ void setup_logging(const char *prog_name, enum debug_logtype new_logtype)
170 {
171         if (state.logtype < new_logtype) {
172                 state.logtype = new_logtype;
173         }
174         if (prog_name) {
175                 state.prog_name = prog_name;
176         }
177         reopen_logs();
178 }
179
180 /**
181   return a string constant containing n tabs
182   no more than 10 tabs are returned
183 */
184 _PUBLIC_ const char *do_debug_tab(int n)
185 {
186         const char *tabs[] = {"", "\t", "\t\t", "\t\t\t", "\t\t\t\t", "\t\t\t\t\t", 
187                               "\t\t\t\t\t\t", "\t\t\t\t\t\t\t", "\t\t\t\t\t\t\t\t", 
188                               "\t\t\t\t\t\t\t\t\t", "\t\t\t\t\t\t\t\t\t\t"};
189         return tabs[MIN(n, 10)];
190 }
191
192
193 /**
194   log suspicious usage - print comments and backtrace
195 */      
196 _PUBLIC_ void log_suspicious_usage(const char *from, const char *info)
197 {
198         if (!debug_handlers.ops.log_suspicious_usage) return;
199
200         debug_handlers.ops.log_suspicious_usage(from, info);
201 }
202
203
204 /**
205   print suspicious usage - print comments and backtrace
206 */      
207 _PUBLIC_ void print_suspicious_usage(const char* from, const char* info)
208 {
209         if (!debug_handlers.ops.print_suspicious_usage) return;
210
211         debug_handlers.ops.print_suspicious_usage(from, info);
212 }
213
214 _PUBLIC_ uint32_t get_task_id(void)
215 {
216         if (debug_handlers.ops.get_task_id) {
217                 return debug_handlers.ops.get_task_id();
218         }
219         return getpid();
220 }
221
222 _PUBLIC_ void log_task_id(void)
223 {
224         if (!debug_handlers.ops.log_task_id) return;
225
226         if (!check_reopen_logs()) return;
227
228         debug_handlers.ops.log_task_id(state.fd);
229 }
230
231 /**
232   register a set of debug handlers. 
233 */
234 _PUBLIC_ void register_debug_handlers(const char *name, struct debug_ops *ops)
235 {
236         debug_handlers.name = name;
237         debug_handlers.ops = *ops;
238 }