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