]> mj.ucw.cz Git - libucw.git/blob - ucw/log-conf.c
bea6ad0c1d4ccb0f2ef7095f1f3ac6050a1b5ff5
[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   u32 levels;
24   clist substreams;                     // simple_list of names
25   int microseconds;                     // Enable logging of precise timestamps
26   int syslog_pids;
27   int errors_fatal;
28   struct log_stream *ls;
29   int mark;                             // Used temporarily in log_config_commit()
30 };
31
32 static char *
33 stream_init(void *ptr)
34 {
35   struct stream_config *c = ptr;
36
37   c->levels = ~0U;
38   return NULL;
39 }
40
41 static char *
42 stream_commit(void *ptr)
43 {
44   struct stream_config *c = ptr;
45
46   if (c->file_name && c->syslog_facility)
47     return "Both FileName and SyslogFacility selected";
48   if (c->syslog_facility && !log_syslog_facility_exists(c->syslog_facility))
49     return cf_printf("SyslogFacility `%s' is not recognized", c->syslog_facility);
50   if (c->syslog_facility && c->microseconds)
51     return "Syslog streams do not support microsecond precision";
52   return NULL;
53 }
54
55 static const char * const level_names[] = {
56 #define P(x) #x,
57   LOG_LEVEL_NAMES
58 #undef P
59   NULL
60 };
61
62 static struct cf_section stream_config = {
63   CF_TYPE(struct stream_config),
64   CF_INIT(stream_init),
65   CF_COMMIT(stream_commit),
66   CF_ITEMS {
67 #define P(x) PTR_TO(struct stream_config, x)
68     CF_STRING("Name", P(name)),
69     CF_STRING("FileName", P(file_name)),
70     CF_STRING("SyslogFacility", P(syslog_facility)),
71     CF_BITMAP_LOOKUP("Levels", P(levels), level_names),
72     CF_LIST("Substream", P(substreams), &cf_string_list_config),
73     CF_INT("Microseconds", P(microseconds)),
74     CF_INT("SyslogPID", P(syslog_pids)),
75     CF_INT("ErrorsFatal", P(errors_fatal)),
76 #undef P
77     CF_END
78   }
79 };
80
81 static clist log_stream_confs;
82
83 static struct stream_config *
84 stream_find(const char *name)
85 {
86   CLIST_FOR_EACH(struct stream_config *, c, log_stream_confs)
87     if (!strcmp(c->name, name))
88       return c;
89   return NULL;
90 }
91
92 static char *
93 stream_resolve(struct stream_config *c)
94 {
95   if (c->mark == 2)
96     return NULL;
97   if (c->mark == 1)
98     return cf_printf("Log stream `%s' has substreams which refer to itself", c->name);
99
100   c->mark = 1;
101   char *err;
102   CLIST_FOR_EACH(simp_node *, s, c->substreams)
103     {
104       struct stream_config *d = stream_find(s->s);
105       if (!d)
106         return cf_printf("Log stream `%s' refers to unknown substream `%s'", c->name, s->s);
107       if (err = stream_resolve(d))
108         return err;
109     }
110   c->mark = 2;
111   return NULL;
112 }
113
114 static char *
115 log_config_commit(void *ptr UNUSED)
116 {
117   // Verify uniqueness of names
118   CLIST_FOR_EACH(struct stream_config *, c, log_stream_confs)
119     if (stream_find(c->name) != c)
120       return cf_printf("Log stream `%s' defined twice", c->name);
121
122   // Check that all substreams resolve and that there are no cycles
123   char *err;
124   CLIST_FOR_EACH(struct stream_config *, c, log_stream_confs)
125     if (err = stream_resolve(c))
126       return err;
127
128   return NULL;
129 }
130
131 static struct cf_section log_config = {
132   CF_COMMIT(log_config_commit),
133   CF_ITEMS {
134     CF_LIST("Stream", &log_stream_confs, &stream_config),
135     CF_END
136   }
137 };
138
139 static void CONSTRUCTOR
140 log_config_init(void)
141 {
142   cf_declare_section("Logging", &log_config, 0);
143 }
144
145 char *
146 log_check_configured(const char *name)
147 {
148   if (stream_find(name))
149     return NULL;
150   else
151     return cf_printf("Log stream `%s' not found", name);
152 }
153
154 static struct log_stream *
155 do_new_configured(struct stream_config *c)
156 {
157   struct log_stream *ls;
158   ASSERT(c);
159
160   if (c->ls)
161     return c->ls;
162
163   if (c->file_name)
164     ls = log_new_file(c->file_name);
165   else if (c->syslog_facility)
166     ls = log_new_syslog(c->syslog_facility, (c->syslog_pids ? LOG_PID : 0));
167   else
168     ls = log_new_stream(sizeof(*ls));
169
170   CLIST_FOR_EACH(simp_node *, s, c->substreams)
171     log_add_substream(ls, do_new_configured(stream_find(s->s)));
172
173   ls->levels = c->levels;
174   if (c->microseconds)
175     ls->msgfmt |= LSFMT_USEC;
176   if (c->errors_fatal)
177     ls->stream_flags |= LSFLAG_ERR_IS_FATAL;
178
179   c->ls = ls;
180   return ls;
181 }
182
183 struct log_stream *
184 log_new_configured(const char *name)
185 {
186   struct stream_config *c = stream_find(name);
187   if (!c)
188     die("Unable to find log stream %s", name);
189   if (c->ls)
190     return log_ref_stream(c->ls);
191   return do_new_configured(c);
192 }
193
194 void
195 log_configured(const char *name)
196 {
197   struct log_stream *ls = log_new_configured(name);
198   struct log_stream *def = log_stream_by_flags(0);
199   log_rm_substream(def, NULL);
200   log_add_substream(def, ls);
201   log_close_stream(ls);
202 }
203
204 #ifdef TEST
205
206 #include "ucw/getopt.h"
207
208 int main(int argc, char **argv)
209 {
210   log_init(argv[0]);
211   int c;
212   while ((c = cf_getopt(argc, argv, CF_SHORT_OPTS, CF_NO_LONG_OPTS, NULL)) >= 0)
213     die("No options here.");
214
215   struct log_stream *ls = log_new_configured("combined");
216   msg(L_INFO | ls->regnum, "Hello, universe!");
217
218   log_close_all();
219   return 0;
220 }
221
222 #endif