Began removing $Id$ tags. This will be an ongoing process.
[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         pche++;
161         *FunctionLine = atoi(pche);
162
163         return 1;
164 }
165
166 static int ParseBacktrace(char *Line, 
167                           StrBuf *Function, 
168                           StrBuf *FileName, 
169                           unsigned int *FunctionLine)
170 {
171         char *pch, *pche;
172
173         pch = Line;
174         pche = strchr(pch, '(');
175         if (pche == NULL) return 0;
176         StrBufAppendBufPlain(FileName, pch, pche - pch, 0);
177         pch = pche + 1;
178         pche = strchr(pch, '+');
179         if (pche == NULL) return 0;
180         StrBufAppendBufPlain(Function, pch, pche - pch, 0);
181         pch = pche + 1;
182         pche = strchr(pch, ')');
183         if (pche == NULL) return 0;
184         *pche = '\0';
185         sscanf(pch, "%x", FunctionLine);
186         StrBufAppendBufPlain(Function, pche + 1, -1, 0);
187         return 1;
188 }
189 #endif
190 long BaseFrames = 0;
191 StrBuf *FullBinaryName = NULL;
192
193 void WildFireShutdown(void)
194 {
195         close(addr2line_write_pipe[0]);
196         close(addr2line_read_pipe[0]);
197
198         FreeStrBuf(&FullBinaryName);
199 }
200
201 void WildFireInitBacktrace(const char *argvNull, int AddBaseFrameSkip)
202 {
203
204 #ifdef HAVE_BACKTRACE
205         void *stack_frames[100];
206         size_t size;
207         long i;
208         char **strings;
209         StrBuf *FileName;
210         StrBuf *Function;
211         StrBuf *Pointer;
212         StrBuf *Buf;
213         unsigned int FunctionLine;
214         struct stat filestats;
215
216         FileName = NewStrBuf();
217         Function = NewStrBuf();
218         Pointer = NewStrBuf();
219         Buf = NewStrBuf();
220
221         BaseFrames = size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
222         BaseFrames --;
223         BaseFrames += AddBaseFrameSkip;
224         strings = backtrace_symbols(stack_frames, size);
225         for (i = 1; i < size; i++) {
226                 if (strings != NULL){
227                         ParseBacktrace(strings[i], Function, 
228                                        FileName, 
229                                        &FunctionLine);
230                         FullBinaryName = NewStrBufDup(FileName);
231                         size = i;
232                 }
233                 else {
234                         char path[256];
235                         getcwd(path, sizeof(path));
236                         FullBinaryName = NewStrBufPlain(path, -1);
237                         StrBufAppendBufPlain(FullBinaryName, HKEY("/"), 0);
238                         StrBufAppendBufPlain(FullBinaryName, argvNull, -1, 0);
239                         i = size;
240                  }
241         }
242         if ((stat(ChrPtr(FullBinaryName), &filestats)==-1) ||
243             (filestats.st_size==0)){
244                 FlushStrBuf(FullBinaryName);
245                 StrBufAppendBufPlain(FullBinaryName, argvNull, -1, 0);
246                 if ((stat(ChrPtr(FullBinaryName), &filestats)==-1) ||
247                     (filestats.st_size==0)){
248                         FlushStrBuf(FullBinaryName);
249                         fprintf(stderr, "unable to open my binary for addr2line checking, verbose backtraces won't work.\n");
250                 }
251         }
252         free(strings);
253         FreeStrBuf(&FileName);
254         FreeStrBuf(&Function);
255         FreeStrBuf(&Pointer);
256         FreeStrBuf(&Buf);
257         if (StrLength(FullBinaryName) > 0)
258                 start_addr2line_daemon(ChrPtr(FullBinaryName));
259 #endif
260
261
262 }
263
264
265 JsonValue *WildFireException(const char *Filename, long FileLen,
266                              long LineNo,
267                              StrBuf *Message,
268                              int StackOffset)
269 {
270         JsonValue *ExcClass;
271         JsonValue *Val;
272         Val = NewJsonArray(NULL, 0);
273
274         JsonArrayAppend(Val, WFInfo(Filename, FileLen,
275                                     LineNo, eEXCEPTION));
276
277         ExcClass = NewJsonObject(WF_MsgStrs[eTRACE].Key, 
278                                  WF_MsgStrs[eTRACE].len);
279         
280         JsonArrayAppend(Val, ExcClass);
281         JsonObjectAppend(ExcClass, 
282                          NewJsonPlainString(HKEY("Class"), 
283                                             HKEY("Exception")));
284         JsonObjectAppend(ExcClass, 
285                          NewJsonString(HKEY("Message"), Message));
286         JsonObjectAppend(ExcClass, 
287                          NewJsonPlainString(HKEY("File"), 
288                                             Filename, FileLen));
289 /*
290         JsonObjectAppend(ExcClass, 
291                          NewJsonPlainString(HKEY("Type"), 
292                                             HKEY("throw")));
293 */
294         JsonObjectAppend(ExcClass, 
295                          NewJsonNumber(HKEY("Line"), LineNo));
296
297 #ifdef HAVE_BACKTRACE
298         {
299                 void *stack_frames[100];
300                 size_t size;
301                 long i;
302                 char **strings;
303                 JsonValue *Trace;
304                 JsonValue *Frame;
305                 StrBuf *FileName;
306                 StrBuf *Function;
307                 StrBuf *Pointer;
308                 StrBuf *Buf;
309                 unsigned int FunctionLine;
310
311                 Trace = NewJsonArray(HKEY("Trace"));
312                 JsonObjectAppend(ExcClass, Trace);
313                 FileName = NewStrBuf();
314                 Function = NewStrBuf();
315                 Pointer = NewStrBuf();
316                 Buf = NewStrBuf();
317
318                 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
319                 strings = backtrace_symbols(stack_frames, size);
320                 for (i = StackOffset + 1; i < size; i++) {
321                         if (strings != NULL){
322                                 ParseBacktrace(strings[i], Function, 
323                                                FileName,
324                                                &FunctionLine);
325                                 
326                         }
327                         StrBufPrintf(Pointer, "%p\n", stack_frames[i]);
328                         
329                         addr2lineBacktrace(Function, 
330                                            FileName, 
331                                            Pointer, 
332                                            Buf, 
333                                            &FunctionLine);
334
335                         Frame = NewJsonObject(NULL, 0);
336                         JsonArrayAppend(Trace, Frame);
337                         JsonObjectAppend(Frame, 
338                                          NewJsonString(HKEY("function"), Function));
339                         JsonObjectAppend(Frame, 
340                                          NewJsonString(HKEY("file"), FileName));
341                         JsonObjectAppend(Frame, 
342                                          NewJsonNumber(HKEY("line"), FunctionLine));
343                         JsonObjectAppend(Frame, 
344                                          NewJsonArray(HKEY("args")));/* not supportet... */
345
346                         FunctionLine = 0;
347                         FlushStrBuf(FileName);
348                         FlushStrBuf(Function);
349                         FlushStrBuf(Pointer);
350                 }
351                 free(strings);
352                 FreeStrBuf(&FileName);
353                 FreeStrBuf(&Function);
354                 FreeStrBuf(&Pointer);
355                 FreeStrBuf(&Buf);
356         }
357 #endif
358         return Val;
359 }
360
361 void WildFireSerializePayload(StrBuf *JsonBuffer, StrBuf *OutBuf, int *MsgCount, AddHeaderFunc AddHdr)
362 {
363         int n = *MsgCount;
364         StrBuf *Buf;
365         StrBuf *HeaderName;
366         StrBuf *N; 
367         const char Concatenate[] = "\\";
368         const char empty[] = "";
369         const char *Cat;
370         StrBuf *Header;
371
372         if (*MsgCount == 0) {
373                 if (OutBuf != NULL) {
374                         StrBufAppendBufPlain(OutBuf, 
375                                              HKEY( 
376                                                      "X-Wf-Protocol-1" 
377                                                      ": "
378                                                      "http://meta.wildfirehq.org/Protocol/JsonStream/0.2\r\n"), 0);
379                         StrBufAppendBufPlain(OutBuf, 
380                                              HKEY( 
381                                                      "X-Wf-1-Plugin-1" 
382                                                      ": " 
383                                                      "http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.2.0\r\n"), 0);
384                         StrBufAppendBufPlain(OutBuf, 
385                                              HKEY(
386                                                      "X-Wf-1-Structure-1"
387                                                      ": "
388                                                      "http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1\r\n"), 0);
389                 }
390                 else {
391                         Header = NewStrBuf();
392                         AddHdr("X-Wf-Protocol-1", 
393                                "http://meta.wildfirehq.org/Protocol/JsonStream/0.2");
394                         AddHdr("X-Wf-1-Plugin-1",
395                                "http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.2.0");
396                         AddHdr("X-Wf-1-Structure-1",
397                                "http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1");
398                 }
399         }
400
401         N = NewStrBuf();
402         StrBufPrintf(N, "%d", StrLength(JsonBuffer));
403         Buf = NewStrBufPlain(NULL, 1024);
404         HeaderName = NewStrBuf();
405
406         while (StrLength(JsonBuffer) > 0) {
407                 FlushStrBuf(Buf);
408                 StrBufPrintf(HeaderName, "X-Wf-"WF_MAJOR"-"WF_STRUCTINDEX"-"WF_SUB"-%d", n);
409                 if (StrLength(JsonBuffer) > 800) {
410                         StrBufAppendBufPlain(Buf, ChrPtr(JsonBuffer), 800, 0);
411                         StrBufCutLeft(JsonBuffer, 800);
412                         Cat = Concatenate;
413                 }
414                 else {
415                         StrBufAppendBuf(Buf, JsonBuffer, 0);
416                         FlushStrBuf(JsonBuffer);
417                         Cat = empty;
418                 }
419                 if (OutBuf != NULL) {
420                         StrBufAppendPrintf(OutBuf, 
421                                            "%s: %s|%s|%s\r\n", 
422                                            ChrPtr(HeaderName), 
423                                            ChrPtr(N),
424                                            ChrPtr(Buf), 
425                                            Cat);
426                 }
427                 else {
428                         StrBufAppendPrintf(Header, 
429                                            "%s|%s|%s", 
430                                            ChrPtr(N),
431                                            ChrPtr(Buf), 
432                                            Cat);
433                         AddHdr(ChrPtr(HeaderName), ChrPtr(Header));
434                         
435                 }
436
437                 FlushStrBuf(N);
438                 n++;
439         }
440         *MsgCount = n;
441         if (OutBuf == NULL) {
442                 FreeStrBuf(&Header);
443         }
444         FreeStrBuf(&N);
445         FreeStrBuf(&Buf);
446         FreeStrBuf(&HeaderName);
447 }
448
449
450
451
452
453
454 /* this is how we do it...
455 void CreateWildfireSampleMessage(void)
456 {
457         JsonValue *Error;
458                 
459         StrBuf *Buf;
460         StrBuf *Header;
461         StrBuf *Json;
462         int n = 1;
463
464         Header = NewStrBuf();
465         Json = NewStrBuf();
466
467         Error = WildFireMessagePlain(HKEY(__FILE__), __LINE__, HKEY("Info message"), eINFO);
468         SerializeJson(Json, Error);
469         WildFireSerializePayload(Json, Header, &n, NULL);
470         StrBufAppendBuf(WC->HBuf, Header, 0);
471         DeleteJSONValue(Error);
472         FlushStrBuf(Json);
473         FlushStrBuf(Header);
474
475         Error = WildFireMessagePlain(HKEY(__FILE__), __LINE__,  HKEY("Warn message"), eWARN);
476         SerializeJson(Json, Error);
477         WildFireSerializePayload(Json, Header, &n, NULL);
478         StrBufAppendBuf(WC->HBuf, Header, 0);
479         DeleteJSONValue(Error);
480         FlushStrBuf(Json);
481         FlushStrBuf(Header);
482
483         Error = WildFireMessagePlain(HKEY(__FILE__), __LINE__, HKEY("Error message"), eERROR);
484         SerializeJson(Json, Error);
485         WildFireSerializePayload(Json, Header, &n, NULL);
486         StrBufAppendBuf(WC->HBuf, Header, 0);
487         DeleteJSONValue(Error);
488         FlushStrBuf(Json);
489         FlushStrBuf(Header);
490
491         Error = WildFireMessagePlain(HKEY(__FILE__), __LINE__, HKEY("Info message"), eINFO);
492         SerializeJson(Json, Error);
493         WildFireSerializePayload(Json, Header, &n, NULL);
494         StrBufAppendBuf(WC->HBuf, Header, 0);
495         DeleteJSONValue(Error);
496         FlushStrBuf(Json);
497         FlushStrBuf(Header);
498
499         Error = WildFireMessagePlain(HKEY(__FILE__), __LINE__, HKEY("Info message"), eINFO);
500         SerializeJson(Json, Error);
501         WildFireSerializePayload(Json, Header, &n, NULL);
502         StrBufAppendBuf(WC->HBuf, Header, 0);
503         DeleteJSONValue(Error);
504         FlushStrBuf(Json);
505         FlushStrBuf(Header);
506
507
508         Buf = NewStrBufPlain(HKEY("test error message"));
509         Error = WildFireException(Buf, HKEY(__FILE__), __LINE__, 1);
510         SerializeJson(Json, Error);
511         WildFireSerializePayload(Json, Header, &n, NULL);
512         StrBufAppendBuf(WC->HBuf, Header, 0);
513         DeleteJSONValue(Error);
514
515         FlushStrBuf(Json);
516         FlushStrBuf(Header);
517
518 }
519
520 */