Wildfire: fix memory leak.
[citadel.git] / libcitadel / lib / wildfire.c
1 /*@{*/
2
3 #include "sysdep.h"
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <unistd.h>
7 #include <dirent.h>
8 #include <errno.h>
9 #include <stdio.h>
10 #include <stdarg.h>
11 #include <string.h>
12
13 #if HAVE_BACKTRACE
14 #include <execinfo.h>
15 #endif
16
17 #include "libcitadel.h"
18 #include "libcitadellocal.h"
19
20
21 ConstStr WF_MsgStrs[] = {
22         {HKEY("LOG")},
23         {HKEY("INFO")},
24         {HKEY("WARN")},
25         {HKEY("ERROR")},
26         {HKEY("TRACE")},
27         {HKEY("EXCEPTION")}
28 };
29
30 static JsonValue *WFInfo(const char *Filename, long fnlen,
31                          long LineNo, 
32                          WF_MessageType Type)
33 {
34         JsonValue *Val;
35
36         Val = NewJsonObject(NULL, 0);
37         JsonObjectAppend(Val, 
38                          NewJsonPlainString(HKEY("Type"),
39                                             WF_MsgStrs[Type].Key, 
40                                             WF_MsgStrs[Type].len));
41         JsonObjectAppend(Val, 
42                          NewJsonPlainString(HKEY("File"), 
43                                             Filename, fnlen));
44         JsonObjectAppend(Val, 
45                          NewJsonNumber(HKEY("Line"), LineNo));
46         return Val;
47 }
48                             
49
50 JsonValue *WildFireMessage(const char *Filename, long fnlen,
51                            long LineNo,
52                            StrBuf *Msg, 
53                            WF_MessageType Type)
54 {
55         JsonValue *Ret;
56
57         Ret = NewJsonArray(NULL, 0);
58         JsonArrayAppend(Ret, WFInfo(Filename, fnlen,
59                                     LineNo, Type));
60
61         JsonArrayAppend(Ret, 
62                         NewJsonString(NULL, 0, Msg));
63         return Ret;
64 }
65
66 JsonValue *WildFireMessagePlain(const char *Filename, long fnlen,
67                                 long LineNo,
68                                 const char *Message, long len, 
69                                 WF_MessageType Type)
70 {
71         JsonValue *Val;
72         Val = NewJsonArray(NULL, 0);
73
74         JsonArrayAppend(Val, WFInfo(Filename, fnlen,
75                                     LineNo, Type));
76         JsonArrayAppend(Val, 
77                         NewJsonPlainString(NULL, 0, Message, len));
78         return Val;
79 }
80
81 void WildFireAddArray(JsonValue *ReportBase, JsonValue *Array, WF_MessageType Type)
82 {
83         JsonValue *Val;
84         Val = NewJsonArray(NULL, 0);
85         JsonArrayAppend(Val, 
86                         NewJsonPlainString(NULL, 0, 
87                                            WF_MsgStrs[Type].Key, 
88                                            WF_MsgStrs[Type].len));
89
90         JsonArrayAppend(Val, Array);
91 }
92
93 int addr2line_write_pipe[2];
94 int addr2line_read_pipe[2];
95 pid_t addr2line_pid;
96
97 #ifdef HAVE_BACKTRACE
98 /* 
99  * Start up the addr2line daemon so we can decode function pointers
100  */
101 static void start_addr2line_daemon(const char *binary) 
102 {
103         struct stat filestats;
104         int i;
105         const char *addr2line = "/usr/bin/addr2line";
106         const char minuse[] = "-e";
107
108         printf("Starting addr2line daemon for decoding of backtraces\n");
109
110         if ((stat(addr2line, &filestats)==-1) ||
111             (filestats.st_size==0)){
112                 printf("didn't find addr2line daemon in %s: %s\n", addr2line, strerror(errno));
113                 abort();
114         }
115         if (pipe(addr2line_write_pipe) != 0) {
116                 printf("Unable to create pipe for addr2line daemon: %s\n", strerror(errno));
117                 abort();
118         }
119         if (pipe(addr2line_read_pipe) != 0) {
120                 printf("Unable to create pipe for addr2line daemon: %s\n", strerror(errno));
121                 abort();
122         }
123
124         addr2line_pid = fork();
125         if (addr2line_pid < 0) {
126                 printf("Unable to fork addr2line daemon: %s\n", strerror(errno));
127                 abort();
128         }
129         if (addr2line_pid == 0) {
130                 dup2(addr2line_write_pipe[0], 0);
131                 dup2(addr2line_read_pipe[1], 1);
132                 for (i=2; i<256; ++i) close(i);
133                 execl(addr2line, addr2line, minuse, binary, NULL);
134                 printf("Unable to exec addr2line daemon: %s\n", strerror(errno));
135                 abort();
136                 exit(errno);
137         }
138 }
139
140 static int addr2lineBacktrace(StrBuf *Function, 
141                               StrBuf *FileName, 
142                               StrBuf *Pointer, 
143                               StrBuf *Buf,
144                               unsigned int *FunctionLine)
145
146 {
147         const char *err;
148         const char *pch, *pche;
149
150         write(addr2line_write_pipe[1], SKEY(Pointer));
151         if (StrBufTCP_read_line(Buf, &addr2line_read_pipe[0], 0, &err) <= 0)
152         {
153                 StrBufAppendBufPlain(Buf, err, -1, 0);
154                 return 0;
155         }
156         pch = ChrPtr(Buf);
157         pche = strchr(pch, ':');
158         FlushStrBuf(FileName);
159         StrBufAppendBufPlain(FileName, pch, pche - pch, 0);
160         if (pche != NULL)
161         {
162                 pche++;
163                 *FunctionLine = atoi(pche);
164         }
165         else 
166                 *FunctionLine = 0;
167         return 1;
168 }
169
170 static int ParseBacktrace(char *Line, 
171                           StrBuf *Function, 
172                           StrBuf *FileName, 
173                           unsigned int *FunctionLine)
174 {
175         char *pch, *pche;
176
177         pch = Line;
178         pche = strchr(pch, '(');
179         if (pche == NULL) return 0;
180         StrBufAppendBufPlain(FileName, pch, pche - pch, 0);
181         pch = pche + 1;
182         pche = strchr(pch, '+');
183         if (pche == NULL) return 0;
184         StrBufAppendBufPlain(Function, pch, pche - pch, 0);
185         pch = pche + 1;
186         pche = strchr(pch, ')');
187         if (pche == NULL) return 0;
188         *pche = '\0';
189         sscanf(pch, "%x", FunctionLine);
190         StrBufAppendBufPlain(Function, pche + 1, -1, 0);
191         return 1;
192 }
193 #endif
194 long BaseFrames = 0;
195 StrBuf *FullBinaryName = NULL;
196
197 void WildFireShutdown(void)
198 {
199         close(addr2line_write_pipe[0]);
200         close(addr2line_read_pipe[0]);
201
202         FreeStrBuf(&FullBinaryName);
203 }
204
205 void WildFireInitBacktrace(const char *argvNull, int AddBaseFrameSkip)
206 {
207
208 #ifdef HAVE_BACKTRACE
209         void *stack_frames[100];
210         size_t size;
211         long i;
212         char **strings;
213         StrBuf *FileName;
214         StrBuf *Function;
215         StrBuf *Pointer;
216         StrBuf *Buf;
217         unsigned int FunctionLine;
218         struct stat filestats;
219
220         FileName = NewStrBuf();
221         Function = NewStrBuf();
222         Pointer = NewStrBuf();
223         Buf = NewStrBuf();
224
225         BaseFrames = size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
226         BaseFrames --;
227         BaseFrames += AddBaseFrameSkip;
228         strings = backtrace_symbols(stack_frames, size);
229         for (i = 1; i < size; i++) {
230                 if (strings != NULL){
231                         ParseBacktrace(strings[i], Function, 
232                                        FileName, 
233                                        &FunctionLine);
234                         FullBinaryName = NewStrBufDup(FileName);
235                         size = i;
236                 }
237                 else {
238                         char path[256];
239                         getcwd(path, sizeof(path));
240                         FullBinaryName = NewStrBufPlain(path, -1);
241                         StrBufAppendBufPlain(FullBinaryName, HKEY("/"), 0);
242                         StrBufAppendBufPlain(FullBinaryName, argvNull, -1, 0);
243                         i = size;
244                  }
245         }
246         if ((stat(ChrPtr(FullBinaryName), &filestats)==-1) ||
247             (filestats.st_size==0)){
248                 FlushStrBuf(FullBinaryName);
249                 StrBufAppendBufPlain(FullBinaryName, argvNull, -1, 0);
250                 if ((stat(ChrPtr(FullBinaryName), &filestats)==-1) ||
251                     (filestats.st_size==0)){
252                         FlushStrBuf(FullBinaryName);
253                         fprintf(stderr, "unable to open my binary for addr2line checking, verbose backtraces won't work.\n");
254                 }
255         }
256         free(strings);
257         FreeStrBuf(&FileName);
258         FreeStrBuf(&Function);
259         FreeStrBuf(&Pointer);
260         FreeStrBuf(&Buf);
261         if (StrLength(FullBinaryName) > 0)
262                 start_addr2line_daemon(ChrPtr(FullBinaryName));
263 #endif
264
265
266 }
267
268
269 JsonValue *WildFireException(const char *Filename, long FileLen,
270                              long LineNo,
271                              StrBuf *Message,
272                              int StackOffset)
273 {
274         JsonValue *ExcClass;
275         JsonValue *Val;
276         Val = NewJsonArray(NULL, 0);
277
278         JsonArrayAppend(Val, WFInfo(Filename, FileLen,
279                                     LineNo, eEXCEPTION));
280
281         ExcClass = NewJsonObject(WF_MsgStrs[eTRACE].Key, 
282                                  WF_MsgStrs[eTRACE].len);
283         
284         JsonArrayAppend(Val, ExcClass);
285         JsonObjectAppend(ExcClass, 
286                          NewJsonPlainString(HKEY("Class"), 
287                                             HKEY("Exception")));
288         JsonObjectAppend(ExcClass, 
289                          NewJsonString(HKEY("Message"), Message));
290         JsonObjectAppend(ExcClass, 
291                          NewJsonPlainString(HKEY("File"), 
292                                             Filename, FileLen));
293 /*
294         JsonObjectAppend(ExcClass, 
295                          NewJsonPlainString(HKEY("Type"), 
296                                             HKEY("throw")));
297 */
298         JsonObjectAppend(ExcClass, 
299                          NewJsonNumber(HKEY("Line"), LineNo));
300
301 #ifdef HAVE_BACKTRACE
302         {
303                 void *stack_frames[100];
304                 size_t size;
305                 long i;
306                 char **strings;
307                 JsonValue *Trace;
308                 JsonValue *Frame;
309                 StrBuf *FileName;
310                 StrBuf *Function;
311                 StrBuf *Pointer;
312                 StrBuf *Buf;
313                 unsigned int FunctionLine;
314
315                 Trace = NewJsonArray(HKEY("Trace"));
316                 JsonObjectAppend(ExcClass, Trace);
317                 FileName = NewStrBuf();
318                 Function = NewStrBuf();
319                 Pointer = NewStrBuf();
320                 Buf = NewStrBuf();
321
322                 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
323                 strings = backtrace_symbols(stack_frames, size);
324                 for (i = StackOffset + 1; i < size; i++) {
325                         if (strings != NULL){
326                                 ParseBacktrace(strings[i], Function, 
327                                                FileName,
328                                                &FunctionLine);
329                                 
330                         }
331                         StrBufPrintf(Pointer, "%p\n", stack_frames[i]);
332                         
333                         addr2lineBacktrace(Function, 
334                                            FileName, 
335                                            Pointer, 
336                                            Buf, 
337                                            &FunctionLine);
338
339                         Frame = NewJsonObject(NULL, 0);
340                         JsonArrayAppend(Trace, Frame);
341                         JsonObjectAppend(Frame, 
342                                          NewJsonString(HKEY("function"), Function));
343                         JsonObjectAppend(Frame, 
344                                          NewJsonString(HKEY("file"), FileName));
345                         JsonObjectAppend(Frame, 
346                                          NewJsonNumber(HKEY("line"), FunctionLine));
347                         JsonObjectAppend(Frame, 
348                                          NewJsonArray(HKEY("args")));/* not supportet... */
349
350                         FunctionLine = 0;
351                         FlushStrBuf(FileName);
352                         FlushStrBuf(Function);
353                         FlushStrBuf(Pointer);
354                 }
355                 free(strings);
356                 FreeStrBuf(&FileName);
357                 FreeStrBuf(&Function);
358                 FreeStrBuf(&Pointer);
359                 FreeStrBuf(&Buf);
360         }
361 #endif
362         return Val;
363 }
364
365 void WildFireSerializePayload(StrBuf *JsonBuffer, StrBuf *OutBuf, int *MsgCount, AddHeaderFunc AddHdr)
366 {
367         int n = *MsgCount;
368         StrBuf *Buf;
369         StrBuf *HeaderName;
370         StrBuf *N; 
371         const char Concatenate[] = "\\";
372         const char empty[] = "";
373         const char *Cat;
374         StrBuf *Header;
375
376         if (OutBuf == NULL)
377                 Header = NewStrBuf();
378         if (*MsgCount == 0) {
379                 if (OutBuf != NULL) {
380                         StrBufAppendBufPlain(OutBuf, 
381                                              HKEY( 
382                                                      "X-Wf-Protocol-1" 
383                                                      ": "
384                                                      "http://meta.wildfirehq.org/Protocol/JsonStream/0.2\r\n"), 0);
385                         StrBufAppendBufPlain(OutBuf, 
386                                              HKEY( 
387                                                      "X-Wf-1-Plugin-1" 
388                                                      ": " 
389                                                      "http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.2.0\r\n"), 0);
390                         StrBufAppendBufPlain(OutBuf, 
391                                              HKEY(
392                                                      "X-Wf-1-Structure-1"
393                                                      ": "
394                                                      "http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1\r\n"), 0);
395                 }
396                 else {
397                         AddHdr("X-Wf-Protocol-1", 
398                                "http://meta.wildfirehq.org/Protocol/JsonStream/0.2");
399                         AddHdr("X-Wf-1-Plugin-1",
400                                "http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.2.0");
401                         AddHdr("X-Wf-1-Structure-1",
402                                "http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1");
403                 }
404         }
405
406         N = NewStrBuf();
407         StrBufPrintf(N, "%d", StrLength(JsonBuffer));
408         Buf = NewStrBufPlain(NULL, 1024);
409         HeaderName = NewStrBuf();
410
411         while (StrLength(JsonBuffer) > 0) {
412                 FlushStrBuf(Buf);
413                 StrBufPrintf(HeaderName, "X-Wf-"WF_MAJOR"-"WF_STRUCTINDEX"-"WF_SUB"-%d", n);
414                 if (StrLength(JsonBuffer) > 800) {
415                         StrBufAppendBufPlain(Buf, ChrPtr(JsonBuffer), 800, 0);
416                         StrBufCutLeft(JsonBuffer, 800);
417                         Cat = Concatenate;
418                 }
419                 else {
420                         StrBufAppendBuf(Buf, JsonBuffer, 0);
421                         FlushStrBuf(JsonBuffer);
422                         Cat = empty;
423                 }
424                 if (OutBuf != NULL) {
425                         StrBufAppendPrintf(OutBuf, 
426                                            "%s: %s|%s|%s\r\n", 
427                                            ChrPtr(HeaderName), 
428                                            ChrPtr(N),
429                                            ChrPtr(Buf), 
430                                            Cat);
431                 }
432                 else {
433                         StrBufAppendPrintf(Header, 
434                                            "%s|%s|%s", 
435                                            ChrPtr(N),
436                                            ChrPtr(Buf), 
437                                            Cat);
438                         AddHdr(ChrPtr(HeaderName), ChrPtr(Header));
439                         
440                 }
441
442                 FlushStrBuf(N);
443                 n++;
444         }
445         *MsgCount = n;
446         if (OutBuf == NULL) {
447                 FreeStrBuf(&Header);
448         }
449         FreeStrBuf(&N);
450         FreeStrBuf(&Buf);
451         FreeStrBuf(&HeaderName);
452 }
453
454
455
456
457
458
459 /* this is how we do it...
460 void CreateWildfireSampleMessage(void)
461 {
462         JsonValue *Error;
463                 
464         StrBuf *Buf;
465         StrBuf *Header;
466         StrBuf *Json;
467         int n = 1;
468
469         Header = NewStrBuf();
470         Json = NewStrBuf();
471
472         Error = WildFireMessagePlain(HKEY(__FILE__), __LINE__, HKEY("Info message"), eINFO);
473         SerializeJson(Json, Error);
474         WildFireSerializePayload(Json, Header, &n, NULL);
475         StrBufAppendBuf(WC->HBuf, Header, 0);
476         DeleteJSONValue(Error);
477         FlushStrBuf(Json);
478         FlushStrBuf(Header);
479
480         Error = WildFireMessagePlain(HKEY(__FILE__), __LINE__,  HKEY("Warn message"), eWARN);
481         SerializeJson(Json, Error);
482         WildFireSerializePayload(Json, Header, &n, NULL);
483         StrBufAppendBuf(WC->HBuf, Header, 0);
484         DeleteJSONValue(Error);
485         FlushStrBuf(Json);
486         FlushStrBuf(Header);
487
488         Error = WildFireMessagePlain(HKEY(__FILE__), __LINE__, HKEY("Error message"), eERROR);
489         SerializeJson(Json, Error);
490         WildFireSerializePayload(Json, Header, &n, NULL);
491         StrBufAppendBuf(WC->HBuf, Header, 0);
492         DeleteJSONValue(Error);
493         FlushStrBuf(Json);
494         FlushStrBuf(Header);
495
496         Error = WildFireMessagePlain(HKEY(__FILE__), __LINE__, HKEY("Info message"), eINFO);
497         SerializeJson(Json, Error);
498         WildFireSerializePayload(Json, Header, &n, NULL);
499         StrBufAppendBuf(WC->HBuf, Header, 0);
500         DeleteJSONValue(Error);
501         FlushStrBuf(Json);
502         FlushStrBuf(Header);
503
504         Error = WildFireMessagePlain(HKEY(__FILE__), __LINE__, HKEY("Info message"), eINFO);
505         SerializeJson(Json, Error);
506         WildFireSerializePayload(Json, Header, &n, NULL);
507         StrBufAppendBuf(WC->HBuf, Header, 0);
508         DeleteJSONValue(Error);
509         FlushStrBuf(Json);
510         FlushStrBuf(Header);
511
512
513         Buf = NewStrBufPlain(HKEY("test error message"));
514         Error = WildFireException(Buf, HKEY(__FILE__), __LINE__, 1);
515         SerializeJson(Json, Error);
516         WildFireSerializePayload(Json, Header, &n, NULL);
517         StrBufAppendBuf(WC->HBuf, Header, 0);
518         DeleteJSONValue(Error);
519
520         FlushStrBuf(Json);
521         FlushStrBuf(Header);
522
523 }
524
525 */