e7577cdd3c98383a5ec2602699adf9bcee1d339d
[protos/libecoli.git] / libecoli / ecoli_log.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2016, Olivier MATZ <zer0@droids-corp.org>
3  */
4
5 #define _GNU_SOURCE /* for vasprintf */
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <errno.h>
10 #include <syslog.h>
11
12 #include <ecoli_malloc.h>
13 #include <ecoli_string.h>
14 #include <ecoli_log.h>
15 #include <ecoli_test.h>
16
17 EC_LOG_TYPE_REGISTER(log);
18
19 static ec_log_t ec_log_fct = ec_log_default_cb;
20 static void *ec_log_opaque;
21
22 struct ec_log_type {
23         char *name;
24         enum ec_log_level level;
25 };
26
27 static struct ec_log_type *log_types;
28 static size_t log_types_len;
29 static enum ec_log_level global_level = EC_LOG_WARNING;
30
31 int ec_log_level_set(enum ec_log_level level)
32 {
33         if (level > EC_LOG_DEBUG)
34                 return -1;
35         global_level = level;
36
37         return 0;
38 }
39
40 enum ec_log_level ec_log_level_get(void)
41 {
42         return global_level;
43 }
44
45 int ec_log_default_cb(int type, enum ec_log_level level, void *opaque,
46                 const char *str)
47 {
48         (void)opaque;
49
50         if (level > ec_log_level_get())
51                 return 0;
52
53         if (fprintf(stderr, "[%d] %-12s %s", level, ec_log_name(type), str) < 0)
54                 return -1;
55
56         return 0;
57 }
58
59 int ec_log_fct_register(ec_log_t usr_log, void *opaque)
60 {
61         if (usr_log == NULL) {
62                 ec_log_fct = ec_log_default_cb;
63                 ec_log_opaque = NULL;
64         } else {
65                 ec_log_fct = usr_log;
66                 ec_log_opaque = opaque;
67         }
68
69         return 0;
70 }
71
72 static int
73 ec_log_lookup(const char *name)
74 {
75         size_t i;
76
77         for (i = 0; i < log_types_len; i++) {
78                 if (log_types[i].name == NULL)
79                         continue;
80                 if (strcmp(name, log_types[i].name) == 0)
81                         return i;
82         }
83
84         return -1;
85 }
86
87 int
88 ec_log_type_register(const char *name)
89 {
90         struct ec_log_type *new_types;
91         char *copy;
92         int id;
93
94         id = ec_log_lookup(name);
95         if (id >= 0)
96                 return id;
97
98         new_types = ec_realloc(log_types,
99                 sizeof(*new_types) * (log_types_len + 1));
100         if (new_types == NULL)
101                 return -1; /* errno is set */
102         log_types = new_types;
103
104         copy = ec_strdup(name);
105         if (copy == NULL)
106                 return -1; /* errno is set */
107
108         id = log_types_len++;
109         log_types[id].name = copy;
110         log_types[id].level = EC_LOG_DEBUG;
111
112         return id;
113 }
114
115 const char *
116 ec_log_name(int type)
117 {
118         if (type < 0 || (unsigned int)type >= log_types_len)
119                 return "unknown";
120         return log_types[type].name;
121 }
122
123 int ec_vlog(int type, enum ec_log_level level, const char *format, va_list ap)
124 {
125         char *s;
126         int ret;
127
128         /* don't use ec_vasprintf here, because it will call
129          * ec_malloc(), then ec_log(), ec_vasprintf()...
130          * -> stack overflow */
131         ret = vasprintf(&s, format, ap);
132         if (ret < 0)
133                 return ret;
134
135         ret = ec_log_fct(type, level, ec_log_opaque, s);
136         free(s);
137
138         return ret;
139 }
140
141 int ec_log(int type, enum ec_log_level level, const char *format, ...)
142 {
143         va_list ap;
144         int ret;
145
146         va_start(ap, format);
147         ret = ec_vlog(type, level, format, ap);
148         va_end(ap);
149
150         return ret;
151 }
152
153 /* LCOV_EXCL_START */
154 static int
155 log_cb(int type, enum ec_log_level level, void *opaque, const char *str)
156 {
157         (void)type;
158         (void)level;
159         (void)str;
160         *(int *)opaque = 1;
161
162         return 0;
163 }
164
165 static int ec_log_testcase(void)
166 {
167         ec_log_t prev_log_cb;
168         void *prev_opaque;
169         const char *logname;
170         int testres = 0;
171         int check_cb = 0;
172         int logtype;
173         int level;
174         int ret;
175
176         prev_log_cb = ec_log_fct;
177         prev_opaque = ec_log_opaque;
178
179         ret = ec_log_fct_register(log_cb, &check_cb);
180         testres |= EC_TEST_CHECK(ret == 0,
181                                 "cannot register log function\n");
182         EC_LOG(LOG_ERR, "test\n");
183         testres |= EC_TEST_CHECK(check_cb == 1,
184                                 "log callback was not invoked\n");
185         logtype = ec_log_lookup("dsdedesdes");
186         testres |= EC_TEST_CHECK(logtype == -1,
187                                 "lookup invalid name should return -1");
188         logtype = ec_log_lookup("log");
189         logname = ec_log_name(logtype);
190         testres |= EC_TEST_CHECK(logname != NULL &&
191                                 !strcmp(logname, "log"),
192                                 "cannot get log name\n");
193         logname = ec_log_name(-1);
194         testres |= EC_TEST_CHECK(logname != NULL &&
195                                 !strcmp(logname, "unknown"),
196                                 "cannot get invalid log name\n");
197         logname = ec_log_name(34324);
198         testres |= EC_TEST_CHECK(logname != NULL &&
199                                 !strcmp(logname, "unknown"),
200                                 "cannot get invalid log name\n");
201         level = ec_log_level_get();
202         ret = ec_log_level_set(2);
203         testres |= EC_TEST_CHECK(ret == 0 && ec_log_level_get() == 2,
204                                 "cannot set log level\n");
205         ret = ec_log_level_set(10);
206         testres |= EC_TEST_CHECK(ret != 0,
207                                 "should not be able to set log level\n");
208
209         ret = ec_log_fct_register(NULL, NULL);
210         ec_log_level_set(LOG_DEBUG);
211         EC_LOG(LOG_DEBUG, "test log\n");
212         ec_log_level_set(LOG_INFO);
213         EC_LOG(LOG_DEBUG, "test log (not displayed)\n");
214         ec_log_level_set(level);
215
216         ec_log_fct = prev_log_cb;
217         ec_log_opaque = prev_opaque;
218
219         return testres;
220 }
221 /* LCOV_EXCL_STOP */
222
223 static struct ec_test ec_log_test = {
224         .name = "log",
225         .test = ec_log_testcase,
226 };
227
228 EC_TEST_REGISTER(ec_log_test);