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