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