chiark / gitweb /
main: remove testing assert
[elogind.git] / log.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 2 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <sys/socket.h>
28 #include <sys/un.h>
29
30 #include "log.h"
31 #include "util.h"
32 #include "macro.h"
33
34 #define SYSLOG_TIMEOUT_USEC (5*USEC_PER_SEC)
35 #define LOG_BUFFER_MAX 1024
36
37 static LogTarget log_target = LOG_TARGET_CONSOLE;
38 static int log_max_level = LOG_DEBUG;
39
40 static int syslog_fd = -1;
41 static int kmsg_fd = -1;
42
43 void log_close_kmsg(void) {
44
45         if (kmsg_fd >= 0) {
46                 close_nointr(kmsg_fd);
47                 kmsg_fd = -1;
48         }
49 }
50
51 int log_open_kmsg(void) {
52
53         if (log_target != LOG_TARGET_KMSG) {
54                 log_close_kmsg();
55                 return 0;
56         }
57
58         if (kmsg_fd >= 0)
59                 return 0;
60
61         if ((kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC)) < 0) {
62                 log_info("Failed to open syslog for logging: %s", strerror(errno));
63                 return -errno;
64         }
65
66         log_info("Succesfully opened /dev/kmsg for logging.");
67
68         return 0;
69 }
70
71 void log_close_syslog(void) {
72
73         if (syslog_fd >= 0) {
74                 close_nointr(syslog_fd);
75                 syslog_fd = -1;
76         }
77 }
78
79 int log_open_syslog(void) {
80         union {
81                 struct sockaddr sa;
82                 struct sockaddr_un un;
83         } sa;
84         struct timeval tv;
85         int r;
86
87         if (log_target != LOG_TARGET_SYSLOG) {
88                 log_close_syslog();
89                 return 0;
90         }
91
92         if (syslog_fd >= 0)
93                 return 0;
94
95         if ((syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0)
96                 return -errno;
97
98         /* Make sure we don't block for more than 5s when talking to
99          * syslog */
100         timeval_store(&tv, SYSLOG_TIMEOUT_USEC);
101         if (setsockopt(syslog_fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) {
102                 r = -errno;
103                 log_close_syslog();
104                 return r;
105         }
106
107         zero(sa);
108         sa.un.sun_family = AF_UNIX;
109         strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path));
110
111         if (connect(syslog_fd, &sa.sa, sizeof(sa)) < 0) {
112                 r = -errno;
113                 log_close_syslog();
114
115                 log_info("Failed to open syslog for logging: %s", strerror(-r));
116                 return r;
117         }
118
119         log_info("Succesfully opened syslog for logging.");
120
121         return 0;
122 }
123
124 void log_set_target(LogTarget target) {
125         assert(target >= 0);
126         assert(target < _LOG_TARGET_MAX);
127
128         log_target = target;
129 }
130
131 void log_set_max_level(int level) {
132         assert((level & LOG_PRIMASK) == level);
133
134         log_max_level = level;
135 }
136
137 static void write_to_console(
138         int level,
139         const char*file,
140         int line,
141         const char *func,
142         const char *format,
143         va_list ap) {
144
145         const char *prefix, *suffix;
146
147         if (LOG_PRI(level) <= LOG_ERR) {
148                 prefix = "\x1B[1;31m";
149                 suffix = "\x1B[0m";
150         } else {
151                 prefix = "";
152                 suffix = "";
153         }
154
155         fprintf(stderr, "(%s:%u) %s", file, line, prefix);
156         vfprintf(stderr, format, ap);
157         fprintf(stderr, "%s\n", suffix);
158 }
159
160 static int write_to_syslog(
161         int level,
162         const char*file,
163         int line,
164         const char *func,
165         const char *format,
166         va_list ap) {
167
168         char header_priority[16], header_time[64], header_pid[16];
169         char buffer[LOG_BUFFER_MAX];
170         struct iovec iovec[5];
171         struct msghdr msghdr;
172         time_t t;
173         struct tm *tm;
174
175         if (syslog_fd < 0)
176                 return -EIO;
177
178         snprintf(header_priority, sizeof(header_priority), "<%i>", LOG_MAKEPRI(LOG_DAEMON, LOG_PRI(level)));
179         char_array_0(header_priority);
180
181         t = (time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC);
182         if (!(tm = localtime(&t)))
183                 return -EINVAL;
184
185         if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0)
186                 return -EINVAL;
187
188         snprintf(header_pid, sizeof(header_pid), "[%llu]: ", (unsigned long long) getpid());
189         char_array_0(header_pid);
190
191         vsnprintf(buffer, sizeof(buffer), format, ap);
192         char_array_0(buffer);
193
194         zero(iovec);
195         IOVEC_SET_STRING(iovec[0], header_priority);
196         IOVEC_SET_STRING(iovec[1], header_time);
197         IOVEC_SET_STRING(iovec[2], __progname);
198         IOVEC_SET_STRING(iovec[3], header_pid);
199         IOVEC_SET_STRING(iovec[4], buffer);
200
201         zero(msghdr);
202         msghdr.msg_iov = iovec;
203         msghdr.msg_iovlen = ELEMENTSOF(iovec);
204
205         if (sendmsg(syslog_fd, &msghdr, 0) < 0)
206                 return -errno;
207
208         return 0;
209 }
210
211 static int write_to_kmsg(
212         int level,
213         const char*file,
214         int line,
215         const char *func,
216         const char *format,
217         va_list ap) {
218
219         char header_priority[16], header_pid[16];
220         char buffer[LOG_BUFFER_MAX];
221         struct iovec iovec[5];
222
223         if (kmsg_fd < 0)
224                 return -EIO;
225
226         snprintf(header_priority, sizeof(header_priority), "<%i>", LOG_PRI(level));
227         char_array_0(header_priority);
228
229         snprintf(header_pid, sizeof(header_pid), "[%llu]: ", (unsigned long long) getpid());
230         char_array_0(header_pid);
231
232         vsnprintf(buffer, sizeof(buffer), format, ap);
233         char_array_0(buffer);
234
235         zero(iovec);
236         IOVEC_SET_STRING(iovec[0], header_priority);
237         IOVEC_SET_STRING(iovec[1], __progname);
238         IOVEC_SET_STRING(iovec[2], header_pid);
239         IOVEC_SET_STRING(iovec[3], buffer);
240         IOVEC_SET_STRING(iovec[4], (char*) "\n");
241
242         if (writev(kmsg_fd, iovec, ELEMENTSOF(iovec)) < 0)
243                 return -errno;
244
245         return 0;
246 }
247
248 void log_meta(
249         int level,
250         const char*file,
251         int line,
252         const char *func,
253         const char *format, ...) {
254
255         va_list ap;
256         bool written;
257         int saved_errno;
258
259         if (LOG_PRI(level) > log_max_level)
260                 return;
261
262         saved_errno = errno;
263         written = false;
264
265         if (log_target == LOG_TARGET_KMSG) {
266                 va_start(ap, format);
267                 written = write_to_kmsg(level, file, line, func, format, ap) >= 0;
268                 va_end(ap);
269         } else if (log_target == LOG_TARGET_SYSLOG) {
270                 va_start(ap, format);
271                 written = write_to_syslog(level, file, line, func, format, ap) >= 0;
272                 va_end(ap);
273         }
274
275         if (!written) {
276                 va_start(ap, format);
277                 write_to_console(level, file, line, func, format, ap);
278                 va_end(ap);
279         }
280
281         errno = saved_errno;
282 }
283
284 int log_set_target_from_string(const char *e) {
285         LogTarget t;
286
287         if ((t = log_target_from_string(e)) < 0)
288                 return -EINVAL;
289
290         log_set_target(t);
291         return 0;
292 }
293
294 int log_set_max_level_from_string(const char *e) {
295         int t;
296
297         if ((t = log_level_from_string(e)) < 0)
298                 return -EINVAL;
299
300         log_set_max_level(t);
301         return 0;
302 }
303
304 void log_parse_environment(void) {
305         const char *e;
306
307         if ((e = getenv("SYSTEMD_LOG_TARGET")))
308                 if (log_set_target_from_string(e) < 0)
309                         log_warning("Failed to parse log target %s. Ignoring.", e);
310
311         if ((e = getenv("SYSTEMD_LOG_LEVEL")))
312                 if (log_set_max_level_from_string(e) < 0)
313                         log_warning("Failed to parse log level %s. Ignoring.", e);
314 }
315
316 LogTarget log_get_target(void) {
317         return log_target;
318 }
319
320 int log_get_max_level(void) {
321         return log_max_level;
322 }
323
324 static const char *const log_target_table[] = {
325         [LOG_TARGET_CONSOLE] = "console",
326         [LOG_TARGET_SYSLOG] = "syslog",
327         [LOG_TARGET_KMSG] = "kmsg",
328 };
329
330 DEFINE_STRING_TABLE_LOOKUP(log_target, LogTarget);