66113fcf59caa04d0133c39d31af5e37ddf707e4
[nobug] / src / nobug_env.c
1 /*
2  This file is part of the NoBug debugging library.
3
4  Copyright (C)
5    2007, 2008, 2010,                    Christian Thaeter <ct@pipapo.org>
6
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  GNU General Public License for more details.
16
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, contact Christian Thaeter <ct@pipapo.org>.
19 */
20 #define NOBUG_LIBNOBUG_C
21 #include "nobug.h"
22
23 #include <unistd.h>
24
25 /*
26   Env variable parsing
27 */
28
29 /*
30   Syntax:
31
32   logdecl_list --> logdecl, any( ',' logdecl_list)
33   logdecl --> flag, opt(limitdecl, any(targetdecl))
34   flag --> "identifier of a flag"
35   limitdecl --> ':', "LIMITNAME"
36   targetdecl --> '@', "targetname", opt(targetopts)
37   targetopts --> '(', "options for target", ')', opt(targetopts)
38
39   examples:
40   NOBUG_LOG='flag,other'                        # set the limit of the 'default' target a default limit
41   NOBUG_LOG='flag:DEBUG'                        # set the limit of the 'default' target to DEBUG
42   NOBUG_LOG='flag:DEBUG@console@syslog'         # set console and syslog limits for flag to DEBUG
43   NOBUG_LOG='flag:DEBUG,other:TRACE@ringbuffer'
44
45   TODO:
46   (options) on targets are not yet implemented
47
48 */
49
50 const struct
51 {
52   const char * name;
53   int value;
54 } nobug_limits[] =
55   {
56     { ":EMERG", LOG_EMERG },
57     { ":ALERT", LOG_ALERT },
58     { ":CRIT", LOG_CRIT },
59     { ":ERR", LOG_ERR },
60     { ":ERROR", LOG_ERR },
61     { ":WARNING", LOG_WARNING },
62     { ":WARN", LOG_WARNING },
63     { ":NOTICE", LOG_NOTICE },
64     { ":INFO", LOG_INFO },
65     { ":DEBUG", LOG_DEBUG },
66     { ":TRACE", LOG_DEBUG },
67   };
68
69 const char* nobug_targets[] =
70   {"@ringbuffer", "@console", "@file", "@syslog", "@application"};
71
72
73 static int
74 nobug_env_parse_string_option (const char** env, const char* key, char* value)
75 {
76   char* end = strchr (*env, ')');
77   if (!end)
78     return -1;
79
80   if (!strncasecmp (key, *env+1, strlen (key)))
81     {
82       char* delim = strchr (*env, '=');
83       if (delim)
84         {
85           if (delim > end || end-delim > 256)
86             return -1;
87           strncat (value, delim+1, end-delim-1);
88         }
89       *env = end + 1;
90       return 1;
91     }
92   return 0;
93 }
94
95 static int
96 nobug_env_parse_size_option (const char** env, const char* key, size_t* value)
97 {
98   char* end = strchr (*env, ')');
99   if (!end)
100     return -1;
101
102   if (!strncasecmp (key, *env+1, strlen (key)))
103     {
104       char* delim = strchr (*env, '=');
105       if (!delim || sscanf (delim+1, "%zi", value) != 1)
106         return -1;
107       *env = end + 1;
108       return 1;
109     }
110   return 0;
111 }
112
113 static int
114 nobug_env_parse_flag_option (const char** env, const char* key, int* flag, int mask)
115 {
116
117   char* end = strchr (*env, ')');
118   if (!end)
119     return -1;
120
121   if (!strncasecmp (key, *env+1, strlen (key)))
122     {
123       //char* delim = strchr (*env, '='); TODO =yes =no =true =false =1 =0
124       /*|| !delim || delim > end*/
125       *flag ^= mask;
126       *env = end + 1;
127       return 1;
128     }
129   return 0;
130 }
131
132 static int
133 nobug_env_parse_int_option (const char** env, const char* key, int* value)
134 {
135   char* end = strchr (*env, ')');
136   char* delim = strchr (*env, '=');
137   if (!end || !delim || delim > end)
138     return -1;
139
140   if (!strncasecmp (key, *env+1, strlen (key)))
141     {
142       if (sscanf (delim+1, "%i", value) != 1)
143         return -1;
144       *env = end + 1;
145       return 1;
146     }
147   return 0;
148 }
149
150 static int
151 nobug_env_parse_drop_option (const char** env)
152 {
153   char* end = strchr (*env, ')');
154   if (!end)
155     return -1;
156   *env = end + 1;
157   return 0;
158 }
159
160 int
161 nobug_env_parse_flag (const char* env, struct nobug_flag* flag, int default_target, int default_limit, const struct nobug_context context)
162 {
163   int ret = -1;
164   if (!env || !flag->name)
165     return ret;
166   size_t flaglen = strlen (flag->name);
167
168   if (flag != &nobug_flag_nobug)
169     nobug_log (&nobug_flag_nobug, LOG_NOTICE, "NOBUG", context, " INIT_FLAG: %s", flag->name);
170
171   do
172     {
173       if (!strncmp(env, flag->name, flaglen))
174         {
175           /* flagname matches */
176           int limit;
177           env += flaglen;
178           if (*env == ':')
179             for (limit = 0; limit<11; ++limit)
180               {
181                 size_t limitlen = strlen (nobug_limits[limit].name);
182                 if (!strncmp(env, nobug_limits[limit].name, limitlen))
183                   {
184                     /* found :LIMIT */
185                     int target;
186                     env += limitlen;
187                     if (*env == '@')
188                       while (*env == '@')
189                         {
190                           for (target = 0; target<5; ++target)
191                             {
192                               size_t targetlen = strlen (nobug_targets[target]);
193                               if (!strncmp(env, nobug_targets[target], targetlen))
194                                 {
195                                   /* @target found */
196                                   env += targetlen;
197                                   int given = 0;
198                                   int s = 0;
199
200                                   int fd = -1;
201                                   char name[256];
202                                   *name = '\0';
203                                   size_t size = 4096;
204                                   size_t guard = 4096;
205                                   int flags = 0;
206
207                                   switch (target)
208                                     {
209                                     case NOBUG_TARGET_RINGBUFFER:
210                                       while (*env == '(' && given >= 0)
211                                         {
212                                           /* (file=name) */
213                                           if ((s = nobug_env_parse_string_option (&env, "file", name)))
214                                             {
215                                               given |= s;
216                                               continue;
217                                             }
218
219                                           /* (size=N) */
220                                           if ((s = nobug_env_parse_size_option (&env, "size", &size)))
221                                             {
222                                               given |= s;
223                                               continue;
224                                             }
225
226                                           /* (guard=N) */
227                                           if ((s = nobug_env_parse_size_option (&env, "guard", &guard)))
228                                             {
229                                               given |= s;
230                                               continue;
231                                             }
232
233                                           /* (append) (temp) (keep) */
234                                           if ((s = nobug_env_parse_flag_option (&env, "append",
235                                                                                 &flags, NOBUG_RINGBUFFER_APPEND)))
236                                             {
237                                               given |= s;
238                                               continue;
239                                             }
240                                           if ((s = nobug_env_parse_flag_option (&env, "temp",
241                                                                                 &flags, NOBUG_RINGBUFFER_TEMP)))
242                                             {
243                                               given |= s;
244                                               continue;
245                                             }
246                                           if ((s = nobug_env_parse_flag_option (&env, "keep",
247                                                                                 &flags, NOBUG_RINGBUFFER_KEEP)))
248                                             {
249                                               given |= s;
250                                               continue;
251                                             }
252                                           if (nobug_env_parse_drop_option (&env))
253                                             return -1;
254                                         }
255                                       if (given < 0)
256                                         return -1;
257                                       if (given)
258                                         {
259                                           /* create new ringbuffer for flag */
260                                           if (flag->ringbuffer_target != &nobug_default_ringbuffer)
261                                             nobug_ringbuffer_delete (flag->ringbuffer_target);
262                                           flag->ringbuffer_target = nobug_ringbuffer_new (size, guard, name, flags);
263                                         }
264                                       break;
265                                     case NOBUG_TARGET_CONSOLE:
266                                       while (*env == '(' && given >= 0)
267                                         {
268                                           /* (fd=N) */
269                                           if ((s = nobug_env_parse_int_option (&env, "fd", &fd)))
270                                             {
271                                               given |= s;
272                                               continue;
273                                             }
274                                           if (nobug_env_parse_drop_option (&env))
275                                             return -1;
276                                         }
277                                       if (given < 0)
278                                         return -1;
279                                       if (given)
280                                         {
281                                           // open fd for console
282                                           if (!write (fd, NULL, 0) && flag->console_target == stderr)
283                                             flag->console_target = fdopen (fd, "w");
284                                         }
285                                       break;
286                                     case NOBUG_TARGET_FILE:
287                                       while (*env == '(' && given >= 0)
288                                         {
289                                           /* (name=name) */
290                                           if ((s = nobug_env_parse_string_option (&env, "name", name)))
291                                             {
292                                               given |= s;
293                                               continue;
294                                             }
295
296                                           /* (append) */
297                                           if ((s = nobug_env_parse_flag_option (&env, "append",
298                                                                                 &flags, NOBUG_RINGBUFFER_APPEND)))
299                                             {
300                                               given |= s;
301                                               continue;
302                                             }
303                                           if (nobug_env_parse_drop_option (&env))
304                                             return -1;
305                                         }
306                                       if (given < 0)
307                                         return -1;
308                                       if (given)
309                                         {
310                                           FILE* new = fopen (name, flags?"a":"w");
311                                           if (new)
312                                             {
313                                               if (flag->file_target)
314                                                 fclose (flag->file_target);
315                                               flag->file_target = new;
316                                             }
317                                         }
318                                       break;
319                                     case NOBUG_TARGET_SYSLOG:
320                                       while (*env == '(' && given >= 0)
321                                         {
322                                           /* (facility=name) unimplemented */
323                                           //given |= nobug_env_parse_string_option (&env, "facility", .. );
324
325                                           /* (ident=string) */
326                                           if ((s = nobug_env_parse_string_option (&env, "ident", name)))
327                                             {
328                                               given |= s;
329                                               continue;
330                                             }
331
332                                           /* (cons) (pid) */
333                                           if ((s = nobug_env_parse_flag_option (&env, "cons",
334                                                                                 &flags, LOG_CONS)))
335                                             {
336                                               given |= s;
337                                               continue;
338                                             }
339                                           if ((s = nobug_env_parse_flag_option (&env, "pid",
340                                                                                 &flags, LOG_PID)))
341                                             {
342                                               given |= s;
343                                               continue;
344                                             }
345 #if HAVE_LOG_PERROR
346                                           if ((s = nobug_env_parse_flag_option (&env, "perror",
347                                                                                 &flags, LOG_PERROR)))
348                                             {
349                                               given |= s;
350                                               continue;
351                                             }
352 #endif
353                                           if (nobug_env_parse_drop_option (&env))
354                                             return -1;
355                                         }
356                                       if (given < 0)
357                                         return -1;
358                                       if (given)
359                                         {
360                                           closelog ();
361                                           openlog (*name?strdup (name): NULL, flags, LOG_USER);
362                                         }
363                                       break;
364                                     case NOBUG_TARGET_APPLICATION:
365                                       // TODO
366                                       break;
367                                     }
368                                   ret = flag->limits[target] = nobug_limits[limit].value;
369                                 }
370                             }
371                         }
372                     else
373                       {
374                         /* flag:LIMIT with no @target */
375                         ret = flag->limits[default_target] = nobug_limits[limit].value;
376                       }
377                   }
378               }
379           else
380             {
381               /* flag with no :LIMIT */
382               ret = flag->limits[default_target] = default_limit;
383             }
384         }
385       env = strchr(env, ',');
386       if (env)
387         ++env;
388     }
389   while (env);
390   return ret;
391 }
392
393
394 int
395 nobug_env_init_flag (struct nobug_flag* flag, int default_target, int default_limit, const struct nobug_context context)
396 {
397   int i;
398   nobug_init(context);
399
400   if (flag->initialized)
401     return 1;
402   flag->initialized = 1;
403
404   /* set some defaults */
405   flag->ringbuffer_target = &nobug_default_ringbuffer;
406   flag->console_target = stderr;
407   flag->file_target = nobug_default_file;
408
409   if (flag->parent)
410     {
411       nobug_env_init_flag (flag->parent, default_target, default_limit, context);
412
413       for (i = NOBUG_TARGET_CONSOLE; i <= NOBUG_TARGET_APPLICATION; ++i)
414         {
415           flag->limits[i] = flag->parent->limits[i];
416         }
417       flag->ringbuffer_target = flag->parent->ringbuffer_target;
418       flag->console_target = flag->parent->console_target;
419       flag->file_target = flag->parent->file_target;
420     }
421
422   /* parse $NOBUG_LOG */
423   int ret = nobug_env_parse_flag (getenv("NOBUG_LOG"), flag, default_target, default_limit, context);
424
425   /* ensure that the ringbuffer is the most verbose */
426   int max;
427
428   for (max = i = NOBUG_TARGET_CONSOLE; i <= NOBUG_TARGET_APPLICATION; ++i)
429     {
430       max = max > flag->limits[i] ? max : flag->limits[i];
431     }
432
433   flag->limits[NOBUG_TARGET_RINGBUFFER] = max > flag->limits[NOBUG_TARGET_RINGBUFFER]
434     ? max : flag->limits[NOBUG_TARGET_RINGBUFFER];
435
436   return ret;
437 }