Use buffer traits over specific IDs
[platform/core/system/dlog.git] / src / logger / logger.c
1 /*
2  * Copyright (c) 2016, Samsung Electronics Co., Ltd. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *              http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #ifndef _GNU_SOURCE
18 #define _GNU_SOURCE
19 #endif
20
21 #include "logger_internal.h"
22 #include "logger_privileges.h"
23 #include "subreader_file.h"
24 #include "subreader_metrics.h"
25 #include "subreader_memory.h"
26
27 #include <buffer_traits.h>
28 #include <metrics.h>
29 #include <getopt.h>
30 #include <dynamic_config.h>
31 #include <qos_defaults.h>
32
33 /**
34  * @addtogroup DLOG_IMPLEMENTATION
35  * @{
36  * @defgroup DLOG_LOGGER Logger
37  * @brief Logger daemon
38  * @details The logger is the core component of the logging framework. It is responsible for collecting, processing and exposing logs.
39  * @{
40  */
41
42 /** global state when logger is not interrupted by any handled signals */
43 static volatile sig_atomic_t g_logger_run = 1;
44
45 static const int DEFAULT_EPOLL_TIME_MS = 1000;
46
47 static const int DEFAULT_LAZY_POLLING_TOTAL_MS = 0;
48 static const int DEFAULT_LAZY_POLLING_SLEEP_MS = 1000;
49
50 static const int S_TO_MS = 1000;
51 static const int MS_TO_NS = 1000000;
52
53 struct backend_data g_backend = {};
54
55 static void reader_logger_free(void *ptr, void *user_data)
56 {
57         struct reader_logger *reader = (struct reader_logger *)ptr;
58         assert(reader);
59         reader_free(&reader->common);
60 }
61
62 // TODO: Consider inlining struct now_t everywhere
63 int get_now(struct now_t *now)
64 {
65         if (clock_gettime(CLOCK_MONOTONIC, &now->mono))
66                 return -errno;
67         if (clock_gettime(CLOCK_REALTIME, &now->real))
68                 return -errno;
69         return 0;
70 }
71
72 /**
73  * @brief FD limit handler
74  * @details Checks whether the FD limit was reached and leaves logs about it if so
75  * @param[in] server The logger server
76  * @param[in] err errno from the call that failed to create an FD
77  */
78 void check_if_fd_limit_reached(struct logger *server, int err)
79 {
80         assert(server);
81
82         if (err != ENFILE && err != EMFILE)
83                 return;
84
85         printf("ERROR: dlog_logger fd limit reached!\n");
86
87         /* pick one representative buffer to send the log to;
88          * no point spamming all the buffers, especially since
89          * default dlogutil invocations display 3 buffers so
90          * it would appear multiple times */
91         struct log_buffer *const buf = server->buffers[LOG_ID_MAIN];
92         if (!buf)
93                 return;
94
95         struct dlogutil_entry_with_msg entry;
96         create_pipe_message(&entry,
97                 DLOG_FATAL, /* not really FATAL, but since we're at the FD limit
98                              * there are thousands of logging programs so this one
99                              * would likely get lost in the flood since developers
100                              * tend to overuse ERROR (and FATAL is the one above) */
101                 "DLOG",
102                 "\x1b[31m DLOG DAEMON FD LIMIT REACHED \x1b[0m" // make it stand out
103                 , getpid(), gettid()
104         );
105         struct now_t now;
106         int r = get_now(&now);
107         if (r < 0)
108                 return;
109         set_pipe_message_sent_timestamp((struct pipe_logger_entry *) &entry, &now.mono, &now.real);
110
111         if (buffer_append(&entry.header, buf))
112                 printf("ERROR: not enough memory either, please check platform settings as the daemon is seriously resource-starved!\n");
113 }
114
115 void flush_logfile_timely(struct log_file *file, struct timespec ts, int flush_time)
116 {
117         if (file->buffer.position > 0) {
118                 if (ts.tv_sec - file->buffer.oldest_log.tv_sec +
119                                         (ts.tv_nsec > file->buffer.oldest_log.tv_nsec ? 1 : 0) >
120                                 flush_time)
121                         logfile_flush(file);
122         }
123 }
124
125 void reader_pipe_notify_losing_log(const dlogutil_entry_s *le, void *reader_)
126 {
127         struct reader_pipe *reader = (struct reader_pipe *) reader_;
128         assert(le);
129         assert(reader);
130         reader_pipe_print_out_single_log(reader, (dlogutil_entry_s *)le);
131 }
132
133 /**
134  * @brief Add reader to log buffer
135  * @details Adds a reader to the log buffers reader list
136  * @param[in] log_buffer The buffer whither to add the reader
137  * @param[in] reader The reader to add to the buffer
138  * @return 0 on success, -ENOMEM on memory allocation error
139  */
140 int add_buffer_reader(struct log_buffer *buffer, struct reader_pipe *reader)
141 {
142         assert(reader);
143         assert(buffer);
144
145         reader->log_storage_reader_ptr = log_storage_new_reader(buffer->log_storage_ptr,
146                         reader->is_dumping, reader->monitor, reader_pipe_notify_losing_log, reader);
147
148         if (!reader->log_storage_reader_ptr)
149                 return -ENOMEM;
150
151         return list_add(&buffer->readers_pipe, reader) ? 0 : -ENOMEM;
152 }
153
154 static int add_reader_common(struct logger *server, struct reader_common *reader)
155 {
156         assert(reader);
157         assert(server);
158
159         if (reader->fd_entity_sink.fd >= 0) {
160                 /* Readers who write to file have no sink FD entity (or, strictly
161                  * speaking, they do have one but it is not filled) since a file
162                  * is not eligible to be added to epoll. However, the entity primarily
163                  * serves to handle pipes getting clogged mid-write (which cannot
164                  * happen for files) and is not involved in starting a write,
165                  * which is handled by the source FD entity, not sink (and which is
166                  * also done periodically for all readers anyway regardless
167                  * of whether they've been added to epoll or not). The other use
168                  * case for epoll - the one where connection FDs leak for unused
169                  * buffers - is not a problem here because the daemon is not
170                  * supposed to ever stop writing to files. */
171
172                 int r = add_fd_entity(&server->epoll_common, &reader->fd_entity_sink);
173                 if (r < 0)
174                         return r;
175         }
176
177         return 0;
178 }
179
180 int add_reader_pipe(struct logger *server, struct reader_pipe *reader)
181 {
182         assert(reader);
183         assert(server);
184
185         int ret = add_reader_common(server, &reader->common);
186         if (ret < 0)
187                 return ret;
188
189         ret = add_buffer_reader(reader->buf_ptr, reader);
190         if (ret < 0 && reader->common.fd_entity_sink.fd >= 0)
191                 remove_fd_entity(&server->epoll_common, &reader->common.fd_entity_sink);
192
193         return ret;
194 }
195
196 int add_reader_memory(struct logger *server, struct reader_memory *reader)
197 {
198         assert(reader);
199         assert(server);
200
201         int ret = add_reader_common(server, &reader->common);
202         if (ret < 0)
203                 return ret;
204
205         // `readers_logger` actually accepts any readers
206         if (!list_add(&server->readers_logger, reader)) {
207                 if (reader->common.fd_entity_sink.fd >= 0)
208                         remove_fd_entity(&server->epoll_common, &reader->common.fd_entity_sink);
209                 return -ENOMEM;
210         }
211
212         return 0;
213 }
214
215 #ifndef UNIT_TEST
216 /* This really belongs to the UNIT_TEST block at the bottom since it is only used
217  * by a function living there, but has not been moved there to keep patches small
218  * and because this function is going to be removed soon in an upcoming patch. */
219
220 static int add_reader_logger(struct logger *server, struct reader_logger *reader)
221 {
222         assert(reader);
223         assert(server);
224
225         int ret = add_reader_common(server, &reader->common);
226         if (ret < 0)
227                 return ret;
228
229         assert(reader->common.fd_entity_source.fd >= 0);
230         ret = add_fd_entity(&server->epoll_common, &reader->common.fd_entity_source);
231         if (ret < 0)
232                 goto failure;
233
234         if (!list_add(&server->readers_logger, reader)) {
235                 remove_fd_entity(&server->epoll_common, &reader->common.fd_entity_source);
236                 ret = -ENOMEM;
237                 goto failure;
238         }
239
240         return 0;
241
242 failure:
243         if (reader->common.fd_entity_sink.fd >= 0)
244                 remove_fd_entity(&server->epoll_common, &reader->common.fd_entity_sink);
245
246         return ret;
247 }
248 #endif
249
250 int create_fifo_fds(struct logger *server, int pipe_fd[2], int flags, bool dump)
251 {
252         int ret = get_nonblocking_fifo(pipe_fd, flags);
253         if (ret < 0)
254                 return ret;
255
256         /* Speed up dumping dlogutils by increasing their pipe size,
257          * as otherwise this pipe's size becomes a major bottleneck.
258          *
259          * Continuous connections don't really care if the initial
260          * burst of "historic" logs is slow and the following flow
261          * of logs is just fine with a small pipe. Meanwhile they
262          * live for a long time during which they would take away
263          * from the valuable total pipe size. */
264         if (dump)
265                 if (fcntl(pipe_fd[1], F_SETPIPE_SZ, DUMPING_READER_PIPE_SIZE) < 0) {
266                         /* Ignore; this is just a performance optimisation
267                          * and doesn't affect functionality so while errors
268                          * are worrisome they not a big deal or something
269                          * we can do anything about, at any rate. */
270                 }
271
272         return ret;
273 }
274
275 static int create_memory_subreader_for_common(struct dlogutil_line_params *params, struct reader_common *reader, struct logger *server)
276 {
277         struct log_compressed_storage *storage = log_compressed_storage_create(params->file.rotate_size_kbytes * 1024, params->compression, params->mem_algo);
278         if (!storage)
279                 return -ENOMEM;
280
281         list_add(&server->compressed_memories, storage);
282
283         return reader_add_subreader_memory(reader, params->filter, storage);
284 }
285
286 static int create_memory_subreader_from_dlogutil_line(struct dlogutil_line_params *params, struct logger *server)
287 {
288         assert(params);
289         assert(params->compression);
290
291         if (params->file_path) // We're writing to memory, not to file
292                 return -EINVAL;
293
294         if (params->buf_id == LOG_ID_INVALID)
295                 return -EINVAL;
296
297         struct reader_logger *const reader = g_backend.logger_readers[params->buf_id];
298         if (!reader)
299                 return -ENOENT;
300
301         return create_memory_subreader_for_common(params, &reader->common, server);
302 }
303
304 static int create_logger_subreader_from_dlogutil_line(struct dlogutil_line_params *params)
305 {
306         assert(params);
307         assert(!params->compression);
308
309         if (params->file_path) {
310                 int retval = logfile_set_path(&params->file, params->file_path);
311                 if (retval < 0)
312                         return retval;
313
314                 retval = logfile_open(&params->file);
315                 if (retval < 0)
316                         return retval;
317         }
318
319         if (params->buf_id == LOG_ID_INVALID)
320                 return -EINVAL;
321
322         if (params->file.path == NULL)
323                 return -EINVAL;
324
325         struct reader_logger *const reader = g_backend.logger_readers[params->buf_id];
326         if (!reader)
327                 return -ENOENT;
328
329         return reader_add_subreader_file(&reader->common, params->filter, &params->file, DLOGUTIL_SORT_SENT_REAL);
330 }
331
332 static int create_reader_pipe_from_dlogutil_line(struct dlogutil_line_params *params, struct logger *server, struct reader_pipe **rd)
333 {
334         assert(server);
335         assert(rd);
336
337         __attribute__((cleanup(reader_free_ptr))) struct reader_pipe *reader = NULL;
338         int retval;
339
340         if (params->file_path) {
341                 retval = logfile_set_path(&params->file, params->file_path);
342                 if (retval < 0)
343                         return retval;
344
345                 retval = logfile_open(&params->file);
346                 if (retval < 0)
347                         return retval;
348         }
349
350         if (params->buf_id == LOG_ID_INVALID)
351                 return -EINVAL;
352
353         if ((params->file.path == NULL && !params->compression)
354         ||  (params->file.path != NULL &&  params->compression))
355                 return -EINVAL;
356
357         retval = reader_pipe_init(&reader, params->buf_id, server, params->monitor, params->is_dumping);
358         if (retval != 0)
359                 return retval;
360
361         if (params->file.path == NULL) {
362                 assert(params->compression);
363                 retval = create_memory_subreader_for_common(params, &reader->common, server);
364                 if (retval != 0)
365                         return retval;
366         } else {
367                 /* FIXME: in theory these could be added independently (1 reader 2 subs),
368                  * but so far that has not been needed and it sounds too fragile to do in haste.
369                  * Keep in mind the reader only has one sink FD entity though, so unless
370                  * that is rewritten, there can be only one sub with poll-dependent output. */
371                 assert(!params->compression);
372                 retval = reader_add_subreader_file(&reader->common, params->filter, &params->file, server->buffers[params->buf_id]->sort_by);
373                 if (retval != 0)
374                         return retval;
375         }
376
377         *rd = reader;
378         reader = NULL;
379
380         return 0;
381 }
382
383 /**
384  * @brief Service a socket request
385  * @details Handle a socket request
386  * @param[in] server The logger server
387  * @param[in] wr The writer who sent the request
388  * @param[in] event The event containing the request
389  * @return 0 on success, else -errno
390  */
391 int service_writer_socket(struct logger *server, struct writer *wr, struct epoll_event *event)
392 {
393         int r = 0;
394
395         assert(wr);
396         struct dlog_control_msg *const msg = (struct dlog_control_msg *) wr->buffer;
397
398         assert(event);
399         if (event->events & EPOLLIN)
400                 r = read(wr->fd_entity.fd, wr->buffer + wr->readed, sizeof wr->buffer - wr->readed);
401
402         if ((r == 0 || r == -1) && event->events & EPOLLHUP)
403                 return -EINVAL;
404
405         do {
406                 if (r > 0)
407                         wr->readed += r;
408
409                 /* The socket is SOCK_STREAM (see `listen_fd_create`), so one message
410                  * could be split into chunks returned across multiple read() calls. */
411                 if (wr->readed < sizeof(msg->length)
412                 ||  wr->readed < msg->length)
413                         goto dont_process_yet_and_read_more_data;
414
415                 assert(wr->service_socket);
416                 r = wr->service_socket(server, wr, msg);
417                 if (r <= 0)
418                         return r;
419
420 dont_process_yet_and_read_more_data:
421                 r = read(wr->fd_entity.fd, wr->buffer + wr->readed, sizeof wr->buffer - wr->readed);
422         } while (r > 0 || ((wr->readed >= sizeof(msg->length) && wr->readed >= msg->length)));
423
424         return (r >= 0 || errno == EAGAIN)  ? 0 : r;
425 }
426
427 /**
428  * @brief Service /dev/kmsg
429  * @details Read from the /dev/kmsg device
430  * @param[in] server The logger server
431  * @param[in] wr The writer who sent the request
432  * @param[in] event The relevant event
433  * @return 0 on success, else -errno
434  */
435 int service_writer_kmsg(struct logger *server, struct writer *wr, struct epoll_event *event)
436 {
437         (void) server;
438         if (event->events & EPOLLHUP)
439                 return -EBADF;
440         if (!(event->events & EPOLLIN))
441                 return 0;
442
443         /* The KMSG device returns just 1 log per read() so it is done in a loop.
444          * In theory this could starve everything else out if logs appeared faster
445          * than the daemon could process them, which would then necessitate some
446          * sort of throttling. In practice, KMSG doesn't really get flooded with
447          * logs the same way Android Logger devices are so the throttling is mostly
448          * there because we get it for free anyway and consistency doesn't hurt. */
449
450         int max_loop_iterations = g_backend.logger_device_throttling[LOG_ID_KMSG];
451         while (max_loop_iterations--) {
452                 int r = read(wr->fd_entity.fd, wr->buffer, sizeof wr->buffer - 1);
453
454                 if (r == -1 && (errno == EAGAIN || errno == EPIPE))
455                         return 0;
456                 else if ((r == 0 || r == -1) && event->events & EPOLLHUP)
457                         return -EINVAL;
458                 else if (r == 0)
459                         return -EBADF;
460                 else if (r == -1)
461                         return -errno;
462
463                 wr->buffer[r] = '\0';
464                 struct dlogutil_entry_with_msg lem;
465                 if (parse_kmsg_message(wr->buffer, &lem, r)) {
466                         // don't signal an error: KMSG writer is too important to remove; besides, it would not get fixed that way.
467                         return 0;
468                 }
469
470                 struct now_t now;
471                 r = get_now(&now);
472                 if (r < 0)
473                         return r;
474                 add_recv_timestamp(&lem.header, now);
475
476                 r = buffer_append(&lem.header, wr->buf_ptr);
477                 if (r < 0)
478                         return r;
479         }
480
481         return 0;
482 }
483
484 static void service_reader_common(void *ptr, void *user_data)
485 {
486         struct reader_common *reader = (struct reader_common *)ptr;
487         struct logger *logger = (struct logger *)user_data;
488
489         assert(reader);
490         assert(logger);
491
492         struct now_t now;
493         int r = get_now(&now);
494         if (r < 0) {
495                 reader_free(reader);
496                 return;
497         }
498
499         r = reader->service_reader(reader, now);
500         if (r > 0) {
501                 reader_free(reader);
502                 return;
503         }
504
505         /* `service_reader()` returns -1 if everything was flushed, or 0 if
506          * a mild error happened that can be recovered from simply by waiting,
507          * the prime example being the pipe getting clogged. As soon as the
508          * reader is available again, we'd like to know about it to ensure
509          * logs are flushed as quickly as possible, which is why the EPOLLOUT.
510          *
511          * On the other hand, we don't want to remove readers from epoll even
512          * if they successfully flushed and have no logs to wait for. Consider
513          * the case where a buffer is unused (for example through libdlog's
514          * buffer disabling feature). If we relied on receiving an error on
515          * calling `write()` to learn that the connection had been closed,
516          * we would never learn about it because there would be no incoming
517          * logs to trigger the flush and so any FDs representing connections
518          * to such buffer would leak until a log finally arrived (which could
519          * be never). This is why waiting is also done on EPOLLHUP. */
520         if (modify_fd_entity(&logger->epoll_common, &reader->fd_entity_sink, (r == 0) ? EPOLLOUT : EPOLLHUP) < 0) {
521                 /* ignore, can't really happen and it's not
522                  * like we can do anything about it either */
523         }
524
525         // Ditto, can't do much about it
526         (void) reader_flush(reader, now.mono, logger->buf_params.time);
527 }
528
529 /**
530  * @brief Service syslog
531  * @details Read from the syslog socket
532  * @param[in] server The logger server
533  * @param[in] wr The writer who sent the request
534  * @param[in] event The relevant event
535  * @return 0 on success, else -errno
536  */
537 int service_writer_syslog(struct logger *server, struct writer *wr, struct epoll_event *event)
538 {
539         if (event->events & EPOLLHUP)
540                 return -EBADF;
541         if (!(event->events & EPOLLIN))
542                 return 0;
543
544         int r = read(wr->fd_entity.fd, wr->buffer, sizeof wr->buffer - 1);
545
546         if (r == -1 && (errno == EAGAIN || errno == EPIPE))
547                 return 0;
548         else if ((r == 0 || r == -1) && event->events & EPOLLHUP)
549                 return -EINVAL;
550         else if (r == 0)
551                 return -EBADF;
552         else if (r == -1)
553                 return -errno;
554
555         wr->buffer[r] = '\0';
556
557         struct dlogutil_entry_with_msg lem;
558         if (parse_syslog_datagram(wr->buffer, r + 1, &lem.header)) {
559                 /* don't return parse error as writers are removed then */
560                 return 0;
561         }
562
563         struct now_t now;
564         r = get_now(&now);
565         if (r < 0)
566                 return r;
567         add_recv_timestamp(&lem.header, now);
568
569         return buffer_append(&lem.header, wr->buf_ptr);
570 }
571
572 /**
573  * @brief Service all readers
574  * @details Update all readers with latest data
575  * @param[in] server The logger server
576  * @param[in] force_push Whether to force logs to be pushed to the readers
577  */
578 static void service_all_readers(struct logger *server)
579 {
580         for (int i = 0; i < LOG_ID_MAX; i++) {
581                 struct log_buffer *const buffer = server->buffers[i];
582                 if (!buffer)
583                         continue;
584
585                 list_foreach(buffer->readers_pipe, server, service_reader_common);
586         }
587         list_foreach(server->readers_logger, server, service_reader_common);
588 }
589
590 /**
591  * @brief Add writer to server
592  * @details Adds a writer to the server's witers list and registers its event to epoll loop
593  * @param[in] l The server to add the writer to
594  * @param[in] writer The writer to add to the server and register its working_fd to epoll loop
595  */
596 void logger_add_writer(struct logger *l, struct writer *wr)
597 {
598         assert(l);
599         assert(wr);
600
601         list_add(&l->writers, wr);
602         add_fd_entity(&l->epoll_common, &wr->fd_entity);
603 }
604
605 /**
606  * @brief Create the logger server
607  * @details Allocate the logger server's auxiliary structures (buffers etc.)
608  * @param[in] data Initialisation data
609  * @param[out] l The logger server
610  * @return 0 on success, -errno on failure
611  */
612 #ifndef UNIT_TEST
613 static
614 #endif
615 int logger_create(struct logger_config_data *data, struct logger *l)
616 {
617         memset(l, 0, sizeof *l);
618         l->epoll_socket.fd = -1;
619
620         int r = epoll_metadata_initialize(&l->epoll_common);
621         if (r < 0)
622                 return r;
623         r = epoll_metadata_initialize(&l->epoll_socket);
624         if (r < 0)
625                 return r;
626
627         l->epolltime = data->epoll_time;
628
629         l->buf_params = data->buf_params;
630
631         if (data->qos) {
632                 l->qos = data->qos;
633                 data->qos = NULL;
634
635                 l->qos->log_metrics = metrics_create();
636                 if (!l->qos->log_metrics)
637                         return -ENOMEM;
638         }
639
640         // Check if the daemon is being launched for the first time since reboot
641         bool first_time;
642         if (data->first_time_file_path) {
643                 int fd = open(data->first_time_file_path, O_CREAT | O_EXCL | O_RDONLY, 0644);
644                 if (fd == -1) {
645                         if (errno == EEXIST)
646                                 first_time = false;
647                         else
648                                 return -errno;
649                 } else {
650                         first_time = true;
651                         close(fd);
652                 }
653         } else
654                 // If no path, assume first time
655                 first_time = true;
656
657         if (g_backend.use_logger_by_default)
658                 for (log_id_t id = 0; id < LOG_ID_MAX; ++id) {
659                         if (!is_core_buffer(id))
660                                 continue;
661
662                         int r = reader_logger_init(g_backend.logger_readers + id, id, l, !first_time);
663                         if (r < 0)
664                                 return r;
665                         if (!g_backend.logger_readers[id])
666                                 continue;
667
668                         if (l->qos) {
669                                 r = reader_add_subreader_metrics(&g_backend.logger_readers[id]->common, l->qos);
670                                 if (r < 0) {
671                                         reader_free(&g_backend.logger_readers[id]->common);
672                                         g_backend.logger_readers[id] = NULL;
673                                         return r;
674                                 }
675                         }
676
677                         /* The reader is not yet complete; later in `finalize_init`
678                          * it will receive further readers and be added to epoll
679                          * or alternatively get culled if it gets no sub-readers. */
680                 }
681
682         for (log_id_t id = 0; id < LOG_ID_MAX; id++)
683                 if (data->is_buffer_enabled[id]) {
684                         int r = buffer_create(&l->buffers[id], id, data->buffers + id);
685                         if (r < 0)
686                                 return r;
687                 }
688
689         for (log_id_t id = 0; id < LOG_ID_MAX; id++)
690                 if (l->buffers[id]) {
691                         add_fd_entity(&l->epoll_common, &l->buffers[id]->sock_ctl.fd_entity);
692                         add_fd_entity(&l->epoll_common, &l->buffers[id]->sock_conn.fd_entity);
693
694                         add_fd_entity(&l->epoll_socket, &l->buffers[id]->sock_ctl.fd_entity);
695                         add_fd_entity(&l->epoll_socket, &l->buffers[id]->sock_conn.fd_entity);
696                 }
697
698         /* TODO: make writers creation optional/configurable */
699         int (*writers_factories[LOG_ID_MAX])(struct writer **writer, struct log_buffer *log_buf) = {
700                 [LOG_ID_KMSG] = create_kmsg_writer,
701                 [LOG_ID_SYSLOG] = create_syslog_writer,
702         };
703         for (unsigned u = 0; u < NELEMS(writers_factories); ++u)
704                 if (writers_factories[u] && data->is_buffer_enabled[u]) {
705                         struct writer *wr;
706                         int r = writers_factories[u](&wr, l->buffers[u]);
707                         if (r < 0)
708                                 return r;
709
710                         logger_add_writer(l, wr);
711                 }
712
713         return 0;
714 }
715
716 /**
717  * @brief Free logger
718  * @details Deallocate the logger and its auxiliary structures
719  * @param[in] l The logger server
720  */
721 #ifndef UNIT_TEST
722 static
723 #endif
724 void logger_free(struct logger *l)
725 {
726         assert(l);
727
728         list_foreach(l->writers, l, foreach_writer_free);
729         list_foreach(l->readers_logger, l, reader_logger_free);
730
731         int j;
732         for (j = 0; j < LOG_ID_MAX; j++)
733                 if (l->buffers[j])
734                         buffer_free(l->buffers[j]);
735
736         epoll_metadata_destroy(&l->epoll_common);
737         epoll_metadata_destroy(&l->epoll_socket);
738
739         qos_free(l->qos);
740 }
741
742 /**
743  * @brief Handle interrupting/terminating signals
744  * @details Clears global flag to stop main loop
745  * @param[in] signo signal number
746  */
747 static void handle_signals(int signo)
748 {
749         (void) signo;
750         g_logger_run = 0;
751 }
752
753 static void ensure_epoll_size(struct epoll_event **events, unsigned *size, unsigned wanted_size)
754 {
755         assert(events);
756         assert(size);
757
758         if (wanted_size <= *size)
759                 return;
760
761         typeof(*events) temp = realloc(*events, wanted_size * sizeof **events);
762         if (!temp)
763                 return;
764
765         *events = temp;
766         *size = wanted_size;
767 }
768
769 void dispatch_epoll_event(struct logger *server, struct epoll_event *event)
770 {
771         struct fd_entity *const entity = (struct fd_entity *) event->data.ptr;
772         assert(entity->dispatch_event);
773         entity->dispatch_event(server, event, entity->dispatch_data);
774 }
775
776 int handle_epoll_events(struct logger *server, struct epoll_metadata *metadata, int timeout)
777 {
778         ensure_epoll_size(&metadata->events, &metadata->events_size, metadata->cnt);
779
780         int nfds = epoll_wait(metadata->fd, metadata->events, metadata->events_size, timeout);
781         if (nfds < 0)
782                 return errno == EINTR ? 0 : -errno;
783
784         for (int i = 0; i < nfds; i++)
785                 dispatch_epoll_event(server, metadata->events + i);
786
787         return 0;
788 }
789
790 int sleep_while_handling_socket(struct logger *server, struct epoll_metadata *metadata, int timeout)
791 {
792         struct timespec ts_start;
793         if (clock_gettime(CLOCK_MONOTONIC, &ts_start))
794                 return -errno;
795         struct timespec ts_current;
796
797         do {
798                 int r = handle_epoll_events(server, metadata, timeout);
799                 if (r < 0)
800                         return r;
801                 if (clock_gettime(CLOCK_MONOTONIC, &ts_current))
802                         return -errno;
803         } while (
804                 timeout > (ts_current.tv_sec  - ts_start.tv_sec ) *  S_TO_MS
805                         + (ts_current.tv_nsec - ts_start.tv_nsec) / MS_TO_NS
806         );
807
808         return 0;
809 }
810
811 void setup_signals(struct logger *server)
812 {
813         struct sigaction action = {
814                 .sa_handler = handle_signals,
815                 .sa_flags   = 0
816         };
817         sigemptyset(&action.sa_mask);
818
819         static const int handled_signals[] = { SIGINT, SIGTERM };
820         for (unsigned u = 0; u < NELEMS(handled_signals); ++u)
821                 sigaction(handled_signals[u], &action, NULL);
822 }
823
824 static bool do_logger_one_iteration(struct logger *server, bool *use_lazy_polling)
825 {
826         if (*use_lazy_polling)
827                 sleep_while_handling_socket(server, &server->epoll_socket, g_backend.lazy_polling_sleep);
828
829         if (g_backend.lazy_polling_total > 0) {
830                 g_backend.lazy_polling_total -= g_backend.lazy_polling_sleep;
831                 *use_lazy_polling = (g_backend.lazy_polling_total > 0);
832         }
833
834         int r = handle_epoll_events(server, &server->epoll_common, server->epolltime * !*use_lazy_polling);
835         if (r < 0)
836                 return false;
837
838         service_all_readers(server);
839
840         if (server->qos)
841                 qos_periodic_check(server->qos);
842
843         return true;
844 }
845
846 /**
847  * @brief Do logging
848  * @details The main logging loop
849  * @param[in] server The logger server
850  * @return 0 on success, else -errno
851  */
852 #ifndef UNIT_TEST
853 static
854 #endif
855 int do_logger(struct logger *server)
856 {
857         setup_signals(server);
858
859         /* Note, negative values are applied here, but not in the check in
860          * the inner function. This leaves lazy polling enabled forever
861          * and is a feature. */
862         bool use_lazy_polling = (g_backend.lazy_polling_total != 0);
863         while (g_logger_run)
864                 if (!do_logger_one_iteration(server, &use_lazy_polling))
865                         break;
866
867         /* ensure all logs are written no matter when the program was interrupted */
868         server->exiting = 1;
869         service_all_readers(server);
870
871         return 0;
872 }
873
874 /**
875  * @brief Prepare socket data
876  * @details Extracts initialisation data specific to each socket
877  * @param[in] conf Config database
878  * @param[out] data Socket init config data
879  * @param[in] buf_name The name of the buffer the socket belongs to
880  * @param[in] type The name of the buffer type
881  * @return 0 on success, -errno on failure
882  */
883 int prepare_socket_data(const struct log_config *conf, struct socket_config_data *data, char *buf_name, const char *type)
884 {
885         char conf_key[MAX_CONF_KEY_LEN];
886         int r;
887
888         r = snprintf(conf_key, MAX_CONF_KEY_LEN, "%s_%s_sock", buf_name, type);
889         if (r < 0)
890                 return -errno;
891         const char * const path = log_config_get(conf, conf_key);
892
893         r = snprintf(conf_key, MAX_CONF_KEY_LEN, "%s_%s_sock_rights", buf_name, type);
894         if (r < 0)
895                 return -errno;
896         const char * const permissions_str = log_config_get(conf, conf_key);
897
898         if (!permissions_str || !path)
899                 return -ENOENT;
900
901         data->permissions = parse_permissions(permissions_str);
902         if (data->permissions <= 0)
903                 return -EINVAL;
904
905         strncpy(data->path, path, MAX_CONF_VAL_LEN - 1);
906
907         return 0;
908 }
909
910 /**
911  * @brief Prepare buffer data
912  * @details Extracts data specific for each buffer
913  * @param[in] conf Config database
914  * @param[out] data Buffer init config data
915  * @param[in] buf_id Index of the buffer to work with
916  * @return 0 on success, -errno on failure
917  */
918 int prepare_buffer_data(const struct log_config *conf, struct buffer_config_data *data, log_id_t buf_id)
919 {
920         char * const buf_name = log_name_by_id(buf_id);
921         char * validity_check_ptr;
922         char conf_key[MAX_CONF_KEY_LEN];
923         int r;
924
925         r = prepare_socket_data(conf, &data->conn_socket, buf_name, "conn");
926         if (r < 0)
927                 return r;
928
929         r = prepare_socket_data(conf, &data->ctl_socket, buf_name, "ctl");
930         if (r < 0)
931                 return r;
932
933         r = snprintf(conf_key, MAX_CONF_KEY_LEN, "%s_size", buf_name);
934         if (r < 0)
935                 return -errno;
936
937         const char * const size_str = log_config_get(conf, conf_key);
938         if (!size_str)
939                 return -ENOENT;
940
941         data->size = strtol(size_str, &validity_check_ptr, 10);
942         if (*validity_check_ptr)
943                 return -EINVAL;
944         if (data->size <= 0)
945                 return -EINVAL;
946
947         return 0;
948 }
949
950 /**
951  * @brief Save logfile line
952  * @detail Saves logfile config line in user_data list
953  * @param[in] key Config entry key
954  * @param[in] value Config entry value
955  * @param[in] userdata Userdata
956  */
957 void save_logfile_config(char const *key, char const *value, void *userdata)
958 {
959         assert(userdata);
960         if (strncmp(key, CONF_PREFIX, sizeof CONF_PREFIX - 1))
961                 return;
962
963         struct logger_config_data *data = (struct logger_config_data *)userdata;
964         char *cmd = strdup(value);
965         if (cmd)
966                 list_add(&data->logfile_configs, cmd);
967         //ignore errors
968 }
969
970 /**
971  * @brief Initialize config data
972  * @param[out] The data structure to fill with initial values
973  */
974 void initialize_config_data(struct logger_config_data *data)
975 {
976         memset(data, 0, sizeof *data);
977 }
978
979 /**
980  * @brief Prepare config data
981  * @details Extracts relevant config data for logger initialisation
982  * @param[out] data Parsed configuration
983  * @return 0 on success, -errno on failure
984  */
985 int prepare_config_data(struct logger_config_data *data)
986 {
987         assert(data);
988
989         struct log_config conf;
990         int ret = log_config_read(&conf);
991         if (ret < 0)
992                 return ret;
993
994         const dlogutil_sorting_order_e sort_by = get_order_from_config(&conf);
995
996         int throttling_default = log_config_get_int(&conf, "logger_dev_throttling", LOGGER_DEVICE_THROTTLING_DEFAULT);
997         const char *const dynamic_config_dir = log_config_get(&conf, DYNAMIC_CONFIG_CONF_KEY);
998
999         const char * const backend = log_config_claim_backend(&conf);
1000         if (!backend) {
1001                 ret = -ENOENT;
1002                 goto end;
1003         }
1004
1005         if (!strcmp(backend, "null")) {
1006                 ret = -ENODATA;
1007                 goto end;
1008         }
1009
1010         for (int i = 0; i < LOG_ID_MAX; i++) {
1011                 char key[MAX_CONF_KEY_LEN];
1012                 const int r = snprintf(key, sizeof key, "logger_dev_throttling_%s", log_name_by_id((log_id_t)i));
1013                 if (r < 0)
1014                         continue;
1015
1016                 g_backend.logger_device_throttling[i] = max_int(1, log_config_get_int(&conf, key, throttling_default));
1017         }
1018
1019         if (dynamic_config_dir && dynamic_config_dir[0] == '/') {
1020                 data->dynamic_config_dir = strdup(dynamic_config_dir);
1021                 if (!data->dynamic_config_dir) {
1022                         ret = -ENOMEM;
1023                         goto end;
1024                 }
1025         } else {
1026                 data->dynamic_config_dir = NULL; // Technically unnecessary but no reason not to put it
1027         }
1028
1029         data->epoll_time = log_config_get_int(&conf, "epoll_time_ms", DEFAULT_EPOLL_TIME_MS);
1030         g_backend.lazy_polling_total = 0; // Android Logger backend only, read below
1031
1032         struct qos_module qos = {0, };
1033
1034         qos.file_path = (char *) log_config_get(&conf, "qos_file_path");
1035         qos.limit_duration = log_config_get_int(&conf, "qos_refresh_rate_s", DEFAULT_QOS_LIMIT_DURATION_S);
1036         qos.max_throughput = log_config_get_int(&conf, "qos_max_throughput_logs", DEFAULT_QOS_THROUGHPUT_LOGS);
1037         qos.threshold = log_config_get_int(&conf, "qos_threshold_logs", DEFAULT_QOS_THRESHOLD_LOGS);
1038         qos.threshold_reapply = log_config_get_int(&conf, "qos_threshold_reapply_logs", DEFAULT_QOS_THRESHOLD_REAPPLY_LOGS);
1039         qos.distribution_func = qos_get_distribution_func_by_name(log_config_get(&conf, "qos_method"));
1040
1041         if (qos.threshold > 0
1042         &&  qos.max_throughput > 0
1043         &&  qos.file_path && strlen(qos.file_path) > 0) {
1044                 data->qos = malloc(sizeof *data->qos);
1045                 if (!data->qos) {
1046                         ret = -ENOMEM;
1047                         goto end;
1048                 }
1049                 *data->qos = qos;
1050                 data->qos->file_path = strdup(qos.file_path);
1051                 if (!data->qos->file_path) {
1052                         ret = -ENOMEM;
1053                         goto end;
1054                 }
1055         }
1056
1057         const char *const first_time_file_path = log_config_get(&conf, "first_time_file_path");
1058         data->first_time_file_path = first_time_file_path ? strdup(first_time_file_path) : NULL;
1059
1060         memset(data->is_buffer_enabled, 0, sizeof(data->is_buffer_enabled));
1061         if (!strcmp(backend, "pipe")) {
1062                 for (int i = 0; i < LOG_ID_MAX; ++i)
1063                         if (is_core_buffer(i))
1064                                 data->is_buffer_enabled[i] = true;
1065                 g_backend.use_logger_by_default = false;
1066         } else if (!strcmp(backend, "logger")) {
1067                 g_backend.use_logger_by_default = true;
1068                 for (log_id_t buf_id = 0; buf_id < LOG_ID_MAX; ++buf_id) {
1069                         const char *const logger_device = log_config_get(&conf, log_name_by_id(buf_id));
1070                         if (logger_device)
1071                                 strncpy(g_backend.logger_devices[buf_id], logger_device,
1072                                         NELEMS(g_backend.logger_devices[buf_id]) - 1);
1073                 }
1074                 g_backend.lazy_polling_total = log_config_get_int(&conf, "lazy_polling_total_ms", DEFAULT_LAZY_POLLING_TOTAL_MS);
1075                 g_backend.lazy_polling_sleep = log_config_get_int(&conf, "lazy_polling_sleep_ms", DEFAULT_LAZY_POLLING_SLEEP_MS);
1076
1077                 /* The total can be 0 (no lazy polling) or negative (infinite),
1078                  * but the sleep length has to be positive. */
1079                 if (g_backend.lazy_polling_sleep < 1)
1080                         g_backend.lazy_polling_sleep = 1;
1081
1082         } else if (!strcmp(backend, "zero-copy")) {
1083                 /* HACK: This essentially skips reading the most important configuration fields
1084                  * (buffer reading and persistent logs) while ensuring the variables are initialized.
1085                  * This will result in daemon quitting. Hopefully we will either reenable some of these
1086                  * in the future or at least make it exit more gracefully (TODO), but for now this is ok. */
1087                 g_backend.use_logger_by_default = false;
1088                 goto end;
1089         } else {
1090                 ret = -ENOENT;
1091                 goto end;
1092         }
1093         data->is_buffer_enabled[LOG_ID_KMSG] = log_config_get_boolean(&conf, "handle_kmsg", true);
1094         data->is_buffer_enabled[LOG_ID_SYSLOG] =
1095                 dev_log_sock_get() >= 0 || log_config_get_boolean(&conf, "syslog_force", false);
1096
1097         for (log_id_t buf_id = 0; buf_id < LOG_ID_MAX; ++buf_id) {
1098                 if (data->is_buffer_enabled[buf_id]) {
1099                         ret = prepare_buffer_data(&conf, data->buffers + buf_id, buf_id);
1100                         if (ret < 0)
1101                                 goto end;
1102                         data->buffers[buf_id].sort_by = sort_by;
1103                 }
1104         }
1105         log_config_foreach(&conf, save_logfile_config, data);
1106
1107         data->default_format = get_default_format_from_config(&conf);
1108
1109 end:
1110         log_config_free(&conf);
1111         return ret;
1112 }
1113
1114 #ifndef UNIT_TEST
1115 static
1116 #endif
1117 void free_config_data(struct logger_config_data *data)
1118 {
1119         list_clear_free_contents(&data->logfile_configs);
1120         free(data->dynamic_config_dir);
1121         qos_free(data->qos);
1122         free(data->first_time_file_path);
1123 }
1124
1125 struct parse_logfile_config_data {
1126         struct logger *server;
1127         struct logger_config_data *data;
1128 };
1129
1130 /**
1131  * @brief Parse logfile line
1132  * @detail Parses a logfile config line
1133  * @param[in] key Config entry key
1134  * @param[in] value Config entry value
1135  * @param[in] userdata Userdata
1136  */
1137 void parse_logfile_config(void *value, void *userdata)
1138 {
1139         assert(value);
1140         assert(userdata);
1141
1142         struct logger *server = ((struct parse_logfile_config_data *) userdata)->server;
1143         struct logger_config_data *data = ((struct parse_logfile_config_data *) userdata)->data;
1144         char *configline = (char *) value;
1145
1146         __attribute__((cleanup(free_dlogutil_line_params))) struct dlogutil_line_params params;
1147         if (!initialize_dlogutil_line_params(&params, server->buf_params))
1148                 return;
1149
1150         get_dlogutil_line_params(configline, data->default_format, &params);
1151
1152         int r;
1153         if (g_backend.use_logger_by_default && is_core_buffer(params.buf_id)) {
1154                 r = params.compression
1155                         ? create_memory_subreader_from_dlogutil_line(&params, server)
1156                         : create_logger_subreader_from_dlogutil_line(&params)
1157                 ;
1158         } else {
1159                 struct reader_pipe *reader = NULL;
1160                 r = create_reader_pipe_from_dlogutil_line(&params, server, &reader);
1161                 if (r == 0)
1162                         add_reader_pipe(server, reader);
1163         }
1164
1165         if (r != 0) {
1166                 errno = -r;
1167                 printf("Warning: unable to add logutil reader for provided configuration. Ignoring.\n"
1168                        "  Config line: %s\n"
1169                        "  Reason given: %m\n",
1170                        configline);
1171         }
1172 }
1173
1174 #ifndef UNIT_TEST
1175 /**
1176  * @brief Print help
1177  * @details Prints basic usage tips
1178  */
1179 static void help(void)
1180 {
1181         printf("Usage: %s [options]\n"
1182                "\t-h    Show this help\n"
1183                "\t-b N  Set the size of the log buffer (in bytes)\n"
1184                "\t-t N  Set time between writes to file (in seconds)\n",
1185                program_invocation_short_name);
1186 }
1187
1188 /**
1189  * @brief Parse args
1190  * @details Parses execution parameters of the program
1191  * @param[in] argc Argument count
1192  * @param[in] argv Argument values
1193  * @param[out] b Buffering parameters
1194  * @return 0 or 1 on success, else -errno. Nonzero if the program is to close.
1195  */
1196 static int parse_args(int argc, char **argv, struct buf_params *b)
1197 {
1198         b->time = BUF_PARAM_TIME_DEFAULT;
1199         b->bytes = BUF_PARAM_BYTES_DEFAULT;
1200
1201         int option;
1202         while ((option = getopt(argc, argv, "hb:t:")) != -1) {
1203                 switch (option) {
1204                 case 't':
1205                         if (!isdigit(optarg[0]))
1206                                 return -EINVAL;
1207                         b->time = clamp_int(atoi(optarg), BUF_PARAM_TIME_MIN, BUF_PARAM_TIME_MAX);
1208                         break;
1209                 case 'b':
1210                         if (!isdigit(optarg[0]))
1211                                 return -EINVAL;
1212                         b->bytes = clamp_int(atoi(optarg), BUF_PARAM_BYTES_MIN, BUF_PARAM_BYTES_MAX);
1213                         break;
1214                 case 'h':
1215                         return 1;
1216                 default:
1217                         return -EINVAL;
1218                 }
1219         }
1220
1221         return 0;
1222 }
1223
1224 /**
1225  * @brief Finalize initialisation
1226  * @details Do misc stuff needed at the end of the initialisation
1227  * @param[in] data configuration dat to read logfiles config lines from
1228  * @param[in] server logger instance to configure
1229  * @return 0 on success, -errno on failure
1230  */
1231 static int finalize_init(struct logger_config_data *data, struct logger *server)
1232 {
1233         int r = sd_notify(0, "READY=1");
1234         if (r < 0)
1235                 return r;
1236
1237         //create files after resetting self privileges
1238         list_foreach(data->logfile_configs, &(struct parse_logfile_config_data) {
1239                 .server = server,
1240                 .data = data,
1241         }, parse_logfile_config);
1242
1243         /* The above created subs for logger readers.
1244          * There is no way for them to gain more at
1245          * runtime, so this is the time to do some
1246          * processing that relies on subs being ready. */
1247         for (log_id_t id = 0; id < LOG_ID_MAX; ++id) {
1248                 struct reader_logger *const reader = g_backend.logger_readers[id];
1249                 if (!reader)
1250                         continue;
1251
1252                 if (reader->common.subs == NULL) {
1253                         reader_free(&reader->common);
1254                         g_backend.logger_readers[id] = NULL;
1255                         continue;
1256                 }
1257
1258                 r = add_reader_logger(server, reader);
1259                 if (r < 0) {
1260                         reader_free(&reader->common);
1261                         g_backend.logger_readers[id] = NULL;
1262                         return r;
1263                 }
1264         }
1265
1266         return 0;
1267 }
1268
1269 static void precreate_logctl_file(struct logger_config_data *data)
1270 {
1271         __attribute__((cleanup(free_ptr))) char *logctl_file_path = NULL;
1272         if (asprintf(&logctl_file_path, "%s/%s", data->dynamic_config_dir, DYNAMIC_CONFIG_FILENAME) < 0)
1273                 // This is worrying but unimportant
1274                 return;
1275
1276         int fd = open(logctl_file_path, O_RDONLY | O_CREAT, 0644);
1277         if (fd < 0)
1278                 // Again, no big deal
1279                 return;
1280
1281         close(fd);
1282 }
1283
1284 bool early_termination(const struct logger_config_data *data, const struct logger *logger) {
1285         /* In order to check if the deamon will be doing nothing
1286          * and can quit instantaneously, we do three checks: */
1287
1288         /* 1. We make sure that there are no Android Logger readers.
1289          * Note that an earlier function gets rid of any readers that
1290          * don't do anything (i.e. have no subs and no way to get them). */
1291         for (log_id_t id = 0; id < LOG_ID_MAX; ++id) {
1292                 const struct reader_logger *const reader = g_backend.logger_readers[id];
1293                 if (!reader)
1294                         continue;
1295
1296                 assert(reader->common.subs != NULL);
1297                 return false;
1298         }
1299
1300         /* 2. We make sure that no pipe-like buffer is enabled.
1301          * When the pipe backend is being used or pipe-like buffers
1302          * (i.e. KMSG, SYSLOG) are handled, the logger allocates space for them.
1303          * In this case, we cannot terminate even if the buffer is enabled
1304          * but unused at the moment, since it might be connected to
1305          * (for instance by dlogutil). */
1306         for (log_id_t id = 0; id < LOG_ID_MAX; id++)
1307                 if (data->is_buffer_enabled[id])
1308                         return false;
1309
1310         /* 3. We make sure that there are no writers.
1311          * In theory, previous checks are sufficient.
1312          * However, we do another check just to be sure
1313          * (since if there are writers, we shouldn't terminate).
1314          * Additionally, this could change in the future,
1315          * so let's hedge against someone forgetting that this function exists. */
1316         return logger->writers == NULL;
1317 }
1318
1319 /**
1320  * @brief The logger
1321  * @return 0 on success, nonzero on failure
1322  * @retval 1 Configuration error
1323  * @retval 2 Runtime error
1324  */
1325 int main(int argc, char **argv)
1326 {
1327         int r, ret;
1328
1329         signal(SIGPIPE, SIG_IGN);
1330
1331         r = reset_self_privileges();
1332         if (r < 0) {
1333                 errno = -r;
1334                 printf("Unable to drop privileges to build-time defaults (%m). Exiting.\n");
1335                 return DLOG_EXIT_ERR_RUNTIME;
1336         }
1337
1338         struct logger_config_data data;
1339         initialize_config_data(&data);
1340
1341         if ((r = parse_args(argc, argv, &data.buf_params)) != 0) {
1342                 help();
1343
1344                 if (r > 0)
1345                         return DLOG_EXIT_SUCCESS; // --help option
1346
1347                 errno = -r;
1348                 printf("Unable to parse command line args (%m). Exiting.\n");
1349                 return DLOG_EXIT_ERR_CONFIG;
1350         }
1351
1352         if ((r = prepare_config_data(&data)) != 0) {
1353                 errno = -r;
1354                 printf("Unable to prepare config (%m). Exiting.\n");
1355                 return DLOG_EXIT_ERR_CONFIG;
1356         }
1357
1358         precreate_logctl_file(&data);
1359
1360         struct logger server;
1361         if ((r = logger_create(&data, &server)) < 0) {
1362                 errno = -r;
1363                 printf("Unable to initialize logger with provided configuration (%m). Exiting.\n");
1364                 ret = DLOG_EXIT_ERR_CONFIG;
1365                 goto cleanup;
1366         }
1367
1368         if ((r = finalize_init(&data, &server)) < 0) {
1369                 errno = -r;
1370                 printf("Unable to finalize initialisation (%m). Exiting.\n");
1371                 ret = DLOG_EXIT_ERR_CONFIG;
1372                 goto cleanup;
1373         }
1374
1375         if (early_termination(&data, &server)) {
1376                 printf("No work to do according to provided configuration. Exiting.\n");
1377                 ret = DLOG_EXIT_SUCCESS;
1378                 goto cleanup;
1379         }
1380
1381         if ((r = do_logger(&server)) < 0) {
1382                 errno = -r;
1383                 printf("Runtime failure (%m). Exiting.\n");
1384                 ret = DLOG_EXIT_ERR_RUNTIME;
1385                 goto cleanup;
1386         }
1387
1388         ret = DLOG_EXIT_SUCCESS;
1389
1390 cleanup:
1391         free_config_data(&data);
1392         logger_free(&server);
1393         return ret;
1394 }
1395 #endif
1396
1397 /**
1398  * @}
1399  * @}
1400  */