]> mj.ucw.cz Git - libucw.git/blob - ucw/log-conf.c
Config parser: Let `:append' and `:prepend' on bitmaps be equivalent to `:set'.
[libucw.git] / ucw / log-conf.c
1 /*
2  *      UCW Library -- Logging: Configuration of Log Streams
3  *
4  *      (c) 2009 Martin Mares <mj@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU Lesser General Public License.
8  */
9
10 #include "ucw/lib.h"
11 #include "ucw/log.h"
12 #include "ucw/conf.h"
13 #include "ucw/simple-lists.h"
14
15 #include <string.h>
16 #include <syslog.h>
17
18 struct stream_config {
19   cnode n;
20   char *name;
21   char *file_name;
22   char *syslog_facility;
23   clist substreams;                     // simple_list of names
24   int microseconds;                     // Enable logging of precise timestamps
25   int syslog_pids;
26   struct log_stream *ls;
27   int mark;                             // Used temporarily in log_config_commit()
28 };
29
30 static char *
31 stream_commit(void *ptr)
32 {
33   struct stream_config *c = ptr;
34
35   if (c->file_name && c->syslog_facility)
36     return "Both FileName and SyslogFacility selected";
37   if (c->syslog_facility && !log_syslog_facility_exists(c->syslog_facility))
38     return cf_printf("SyslogFacility `%s' is not recognized", c->syslog_facility);
39   if (c->syslog_facility && c->microseconds)
40     return "Syslog streams do not support microsecond precision";
41   return NULL;
42 }
43
44 static struct cf_section stream_config = {
45   CF_TYPE(struct stream_config),
46   CF_COMMIT(stream_commit),
47   CF_ITEMS {
48 #define P(x) PTR_TO(struct stream_config, x)
49     CF_STRING("Name", P(name)),
50     CF_STRING("FileName", P(file_name)),
51     CF_STRING("SyslogFacility", P(syslog_facility)),
52     CF_LIST("Substream", P(substreams), &cf_string_list_config),
53     CF_INT("Microseconds", P(microseconds)),
54     CF_INT("SyslogPID", P(syslog_pids)),
55 #undef P
56     CF_END
57   }
58 };
59
60 static clist log_stream_confs;
61
62 static struct stream_config *
63 stream_find(const char *name)
64 {
65   CLIST_FOR_EACH(struct stream_config *, c, log_stream_confs)
66     if (!strcmp(c->name, name))
67       return c;
68   return NULL;
69 }
70
71 static char *
72 stream_resolve(struct stream_config *c)
73 {
74   if (c->mark == 2)
75     return NULL;
76   if (c->mark == 1)
77     return cf_printf("Log stream `%s' has substreams which refer to itself", c->name);
78
79   c->mark = 1;
80   char *err;
81   CLIST_FOR_EACH(simp_node *, s, c->substreams)
82     {
83       struct stream_config *d = stream_find(s->s);
84       if (!d)
85         return cf_printf("Log stream `%s' refers to unknown substream `%s'", c->name, s->s);
86       if (err = stream_resolve(d))
87         return err;
88     }
89   c->mark = 2;
90   return NULL;
91 }
92
93 static char *
94 log_config_commit(void *ptr UNUSED)
95 {
96   // Verify uniqueness of names
97   CLIST_FOR_EACH(struct stream_config *, c, log_stream_confs)
98     if (stream_find(c->name) != c)
99       return cf_printf("Log stream `%s' defined twice", c->name);
100
101   // Check that all substreams resolve and that there are no cycles
102   char *err;
103   CLIST_FOR_EACH(struct stream_config *, c, log_stream_confs)
104     if (err = stream_resolve(c))
105       return err;
106
107   return NULL;
108 }
109
110 static struct cf_section log_config = {
111   CF_COMMIT(log_config_commit),
112   CF_ITEMS {
113     CF_LIST("Stream", &log_stream_confs, &stream_config),
114     CF_END
115   }
116 };
117
118 static void CONSTRUCTOR
119 log_config_init(void)
120 {
121   cf_declare_section("Logging", &log_config, 0);
122 }
123
124 char *
125 log_check_configured(const char *name)
126 {
127   if (stream_find(name))
128     return NULL;
129   else
130     return cf_printf("Log stream `%s' not found", name);
131 }
132
133 static struct log_stream *
134 do_new_configured(struct stream_config *c)
135 {
136   struct log_stream *ls;
137   ASSERT(c);
138
139   if (c->ls)
140     return c->ls;
141
142   if (c->file_name)
143     ls = log_new_file(c->file_name);
144   else if (c->syslog_facility)
145     ls = log_new_syslog(c->syslog_facility, (c->syslog_pids ? LOG_PID : 0));
146   else
147     ls = log_new_stream(sizeof(*ls));
148
149   CLIST_FOR_EACH(simp_node *, s, c->substreams)
150     log_add_substream(ls, do_new_configured(stream_find(s->s)));
151
152   if (c->microseconds)
153     ls->msgfmt |= LSFMT_USEC;
154
155   c->ls = ls;
156   return ls;
157 }
158
159 struct log_stream *
160 log_new_configured(const char *name)
161 {
162   struct stream_config *c = stream_find(name);
163   if (!c)
164     die("Unable to find log stream %s", name);
165   if (c->ls)
166     return log_ref_stream(c->ls);
167   return do_new_configured(c);
168 }
169
170 void
171 log_configured(const char *name)
172 {
173   struct log_stream *ls = log_new_configured(name);
174   struct log_stream *def = log_stream_by_flags(0);
175   log_rm_substream(def, NULL);
176   log_add_substream(def, ls);
177   log_close_stream(ls);
178 }
179
180 #ifdef TEST
181
182 #include "ucw/getopt.h"
183
184 int main(int argc, char **argv)
185 {
186   log_init(argv[0]);
187   int c;
188   while ((c = cf_getopt(argc, argv, CF_SHORT_OPTS, CF_NO_LONG_OPTS, NULL)) >= 0)
189     die("No options here.");
190
191   struct log_stream *ls = log_new_configured("combined");
192   msg(L_INFO | ls->regnum, "Hello, universe!");
193
194   log_close_all();
195   return 0;
196 }
197
198 #endif