dc2c771f5d8f34b6495816602e5285d4afc4a722
[platform/core/system/dlog.git] / src / logger / log_buffer.c
1 #include "log_buffer.h"
2 #include "logger_internal.h"
3 #include <ptrs_list.h>
4
5 /**
6  * @brief Append to buffer
7  * @details Appends an entry to the buffer
8  * @param[in] s The logger server
9  * @param[in] entry The entry to append
10  * @param[in] b The buffer whither to append
11  */
12 int buffer_append(const dlogutil_entry_s *entry, struct log_buffer *b)
13 {
14         if (!log_storage_add_new_entry(b->log_storage_ptr, entry))
15                 return -ENOMEM;
16         return 0;
17 }
18
19 /**
20  * @brief Service pipe log data
21  * @details Handle log messages incoming through a pipe
22  * @param[in] server The logger server
23  * @param[in] wr The writer who sent the logs
24  * @param[in] event The event associated with the data
25  * @return 0 on success, else -errno
26  */
27 static int service_writer_pipe(struct logger *server, struct writer *wr, struct epoll_event *event)
28 {
29         if (event->events & EPOLLIN) {
30                 int r = read(wr->fd_entity.fd, wr->buffer + wr->readed, sizeof wr->buffer - wr->readed);
31
32                 if (r == -1 && errno == EAGAIN)
33                         return 0;
34                 else if ((r == 0 || r == -1) && event->events & EPOLLHUP)
35                         return -EINVAL;
36                 else if (r == 0)
37                         return -EBADF;
38
39                 wr->readed += r;
40
41                 struct pipe_logger_entry *const ple = (struct pipe_logger_entry *const)wr->buffer;
42                 while ((wr->readed >= sizeof(ple->len)) && (ple->len <= wr->readed)) {
43                         const int payload_size = ple->len - sizeof *ple;
44                         if (payload_size < 0 || payload_size > LOG_MAX_PAYLOAD_SIZE)
45                                 return -EINVAL;
46
47                         struct dlogutil_entry_with_msg lem;
48                         parse_pipe_message(ple, &lem.header, ple->len);
49                         add_recv_timestamp(&lem.header, server->time);
50                         fixup_pipe_msg(&lem, payload_size);
51                         if (qos_is_enabled(&server->qos))
52                                 qos_add_log(&server->qos, &lem.header);
53                         r = buffer_append(&lem.header, wr->buf_ptr);
54                         wr->readed -= ple->len;
55                         memmove(wr->buffer, wr->buffer + ple->len, sizeof wr->buffer - ple->len);
56
57                         if (r)
58                                 return r;
59                 }
60         } else if (event->events & EPOLLHUP)
61                 return -EBADF;
62
63         return 0;
64 }
65
66 /**
67  * @brief Service util request
68  * @details Handle a request from util
69  * @param[in] server The logger server
70  * @param[in] wr The writer who sent the request
71  * @param[in] msg The message containing the request
72  * @return 0 on success, else -errno
73  */
74 static int service_writer_handle_req_util(struct logger* server, struct writer* wr, struct dlog_control_msg* msg)
75 {
76         assert(server);
77         assert(wr);
78         assert(msg);
79
80         // check request type, that should be always DLOG_REQ_HANDLE_LOGUTIL
81         // as dispatched by service_writer_handle_req_ctrl handler
82         // don't assert for compatibility with service_writer_handle_req_pipe
83         // and possible mistakes in the future that would be hard to track
84         if (msg->request != DLOG_REQ_HANDLE_LOGUTIL)
85                 return -EINVAL;
86
87         if (msg->length <= sizeof(struct dlog_control_msg) ||
88             msg->length > sizeof(struct dlog_control_msg) + MAX_LOGGER_REQUEST_LEN)
89                 return -EINVAL;
90
91         if (msg->data[msg->length - sizeof(struct dlog_control_msg)] != 0)
92                 return -EINVAL;
93
94         __attribute__((cleanup(reader_pipe_cleanup))) struct reader_pipe *reader = NULL;
95
96         int retval;
97         __attribute__((cleanup(free_dlogutil_line_params))) struct dlogutil_line_params params;
98         if (!initialize_dlogutil_line_params(&params)) {
99                 retval = -ENOMEM;
100                 goto cleanup;
101         }
102
103         retval = get_dlogutil_line_params(msg->data, &params);
104         if (retval < 0) {
105                 retval = -ENOMEM;
106                 goto cleanup;
107         }
108
109         if (params.file_path) {
110                 /* Do not trust writer-based readers (only config-based).
111                  * The control socket's privilege checks are fairly lenient
112                  * so this prevents people from asking us to overwrite
113                  * some potentially important files at logger privilege.
114                  *
115                  * At some point it would be good to be able to skip the
116                  * middleman and become able to write to a file directly
117                  * though. The daemon should become able to receive an
118                  * opened file descriptor from a writer. */
119                 retval = -EPERM;
120                 goto cleanup;
121         }
122
123         retval = reader_pipe_init_with_writer(&reader, wr, server, params.filter, &params.file, params.monitor, params.is_dumping);
124         if (retval != 0)
125                 goto cleanup;
126
127         int write_fd = -1, read_fd = -1;
128         retval = create_fifo_fds(server, wr->fd_entity.fd, &write_fd, &read_fd, reader->is_dumping);
129         if (retval < 0)
130                 goto cleanup;
131
132         set_write_fd_entity(&reader->common.fd_entity_sink, write_fd);
133         retval = send_pipe(wr->fd_entity.fd, read_fd);
134         if (read_fd > 0)
135                 close(read_fd);
136         if (retval)
137                 goto cleanup;
138
139         retval = add_reader_pipe(server, reader);
140         if (retval < 0)
141                 goto cleanup;
142
143         reader = NULL;
144         return 0;
145
146 cleanup:
147         retval = send_dlog_reply(wr->fd_entity.fd, DLOG_REQ_HANDLE_LOGUTIL, DLOG_REQ_RESULT_ERR, NULL, 0);
148         if (retval < 0)
149                 printf("ERROR: both create_reader_from_dlogutil_line() and send_dlog_reply() failed\n");
150
151         return retval;
152 }
153
154 static int service_writer_handle_req_get_usage(struct logger *server, struct writer *wr, struct dlog_control_msg *msg)
155 {
156         assert(server);
157         assert(wr);
158         assert(msg);
159         assert(msg->request == DLOG_REQ_GET_USAGE);
160
161         uint32_t buf_size = log_storage_get_usage(wr->buf_ptr->log_storage_ptr);
162
163         return send_dlog_reply(wr->fd_entity.fd, DLOG_REQ_GET_USAGE, DLOG_REQ_RESULT_OK, &buf_size, sizeof buf_size);
164 }
165
166 static int service_writer_handle_req_get_capacity(struct logger *server, struct writer *wr, struct dlog_control_msg *msg)
167 {
168         assert(server);
169         assert(wr);
170         assert(msg);
171         assert(msg->request == DLOG_REQ_GET_CAPACITY);
172
173         uint32_t buf_size = log_storage_get_capacity(wr->buf_ptr->log_storage_ptr);
174
175         return send_dlog_reply(wr->fd_entity.fd, DLOG_REQ_GET_CAPACITY, DLOG_REQ_RESULT_OK, &buf_size, sizeof buf_size);
176 }
177
178 /**
179  * @brief Service clear request
180  * @details Handle a clear-buffer request
181  * @param[in] server The logger server
182  * @param[in] wr The writer who sent the request
183  * @param[in] msg The message containing the request
184  * @return 0 on success, else -errno
185  */
186 static int service_writer_handle_req_clear(struct logger* server, struct writer* wr, struct dlog_control_msg* msg)
187 {
188         (void) server;
189         assert(msg);
190
191         // check request type, that should be always DLOG_REQ_CLEAR
192         // as dispatched by service_writer_handle_req_ctrl handler
193         // don't assert for compatibility with service_writer_handle_req_pipe
194         // and possible mistakes in the future that would be hard to track
195         if (msg->request != DLOG_REQ_CLEAR)
196                 return -EINVAL;
197
198         if (msg->length != (sizeof(struct dlog_control_msg)))
199                 return -EINVAL;
200
201         if (!wr || !wr->buf_ptr)
202                 return -EINVAL;
203
204         log_storage_clear(wr->buf_ptr->log_storage_ptr);
205
206         return 0;
207 }
208
209 /**
210  * @brief Service control request
211  * @details Handle a clear-buffer or util request in respect to msg request type
212  * @param[in] server The logger server
213  * @param[in] wr The writer who sent the request
214  * @param[in] msg The message containing the request
215  * @return 0 on success, else -errno
216  */
217 static int service_writer_handle_req_ctrl(struct logger *server, struct writer *wr, struct dlog_control_msg *msg)
218 {
219         assert(server);
220         assert(wr);
221         assert(msg);
222
223         int ret;
224         switch (msg->request) {
225                 case DLOG_REQ_CLEAR:          ret = service_writer_handle_req_clear       (server, wr, msg); break;
226                 case DLOG_REQ_HANDLE_LOGUTIL: ret = service_writer_handle_req_util        (server, wr, msg); break;
227                 case DLOG_REQ_GET_CAPACITY:   ret = service_writer_handle_req_get_capacity(server, wr, msg); break;
228                 case DLOG_REQ_GET_USAGE:      ret = service_writer_handle_req_get_usage   (server, wr, msg); break;
229
230                 default: ret = -EINVAL;
231         }
232
233         if (wr->readed > msg->length) {
234                 wr->readed -= msg->length;
235                 memmove(wr->buffer, wr->buffer + msg->length, wr->readed);
236         } else
237                 wr->readed = 0;
238
239         return ret;
240 }
241
242 /**
243  * @brief Service a pipe acquisition request
244  * @details Handle a pipe request
245  * @param[in] server The logger server
246  * @param[in] wr The writer who sent the request
247  * @param[in] msg The message containing the request
248  * @return 0 on success, else -errno
249  */
250 static int service_writer_handle_req_pipe(struct logger* server, struct writer* wr, struct dlog_control_msg* msg)
251 {
252         int r;
253
254         assert(msg);
255
256         // check request type given by user
257         // don't assert that as the message is not parsed before
258         if (msg->request != DLOG_REQ_PIPE)
259                 return -EINVAL;
260
261         if (msg->length != sizeof(struct dlog_control_msg))
262                 return -EINVAL;
263
264         int pipe_fd[2];
265         if (pipe2(pipe_fd, O_CLOEXEC | O_NONBLOCK) < 0) { // O_NONBLOCK just for pipe_fd[0]; writer removes it for pipe_fd[1] on its own
266                 check_if_fd_limit_reached(server, errno);
267                 return -errno;
268         }
269
270         if (fcntl(pipe_fd[1], F_SETPIPE_SZ, PIPE_REQUESTED_SIZE) < 0) {
271                 /* Ignore failures. This call is just a performance optimisation
272                  * and doesn't affect functionality; we can't do anything about
273                  * an error anyway. */
274         }
275
276         assert(server);
277         assert(wr);
278
279         struct fd_entity pipe_entity = wr->fd_entity;
280         set_read_fd_entity(&pipe_entity, pipe_fd[0]);
281         r = add_fd_entity(&server->epoll_common, &pipe_entity);
282         if (r < 0)
283                 goto err_close;
284
285         r = send_pipe(wr->fd_entity.fd, pipe_fd[1]);
286         if (r < 0)
287                 goto err_remove;
288         close(pipe_fd[1]);
289
290         writer_close_fd(server, wr);
291         wr->service_writer = service_writer_pipe;
292         wr->fd_entity = pipe_entity;
293         wr->readed = 0;
294         return 0;
295
296 err_remove:
297         remove_fd_entity(&server->epoll_common, &pipe_entity);
298
299 err_close:
300         close(pipe_fd[0]);
301         close(pipe_fd[1]);
302
303         return r;
304 }
305
306 /**
307  * @brief Create buffer
308  * @details Allocate a buffer structure
309  * @param[out] lb The newly-allocated buffer
310  * @param[in] buf_id The buffer ID
311  * @param[in] data Buffer config data
312  * @return 0 on success, -errno on failure
313  */
314 int buffer_create(struct log_buffer **log_buffer, log_id_t buf_id, struct buffer_config_data *data)
315 {
316         assert(data);
317         struct log_buffer *lb = (struct log_buffer *) calloc(1, sizeof(*lb));
318
319         if (!lb)
320                 return -ENOMEM;
321
322         lb->id = buf_id;
323         lb->log_storage_ptr = log_storage_create(data->size, data->sort_by);
324         if (!lb->log_storage_ptr) {
325                 free(lb);
326                 return -ENOMEM;
327         }
328
329         int r;
330         r = socket_initialize(&lb->sock_ctl, lb, service_writer_handle_req_ctrl, &data->ctl_socket);
331         if (r < 0) {
332                 log_storage_free(lb->log_storage_ptr);
333                 free(lb);
334                 return r;
335         }
336
337         r = socket_initialize(&lb->sock_wr, lb, service_writer_handle_req_pipe, &data->write_socket);
338         if (r < 0) {
339                 socket_close(&lb->sock_ctl);
340                 log_storage_free(lb->log_storage_ptr);
341                 free(lb);
342                 return r;
343         }
344
345         *log_buffer = lb;
346         return 0;
347 }
348
349 static bool cond_reader_pipe_free(void *ptr, void *user_data)
350 {
351         struct reader_pipe *reader = (struct reader_pipe *)ptr;
352         struct logger *logger = (struct logger *)user_data;
353         assert(reader);
354         // TODO: This is absurd, why isn't this in the reader_pipe_free function?
355         if (reader->common.fd_entity_sink.fd >= 0)
356                 remove_fd_entity(&logger->epoll_common, &reader->common.fd_entity_sink);
357         if (reader->common.fd_entity_source.fd >= 0)
358                 remove_fd_entity(&logger->epoll_common, &reader->common.fd_entity_source);
359         reader_pipe_free(reader);
360         return true;
361 }
362
363 /**
364  * @brief Free buffer
365  * @details Deallocate a buffer
366  * @param[in] buffer The buffer to deallocate
367  * @param[in] logger The server to remove buffer readers from fd loop
368  */
369 void buffer_free(struct log_buffer *buffer, struct logger *logger)
370 {
371         assert(buffer);
372
373         list_remove_if(&buffer->readers_pipe, logger, cond_reader_pipe_free);
374
375         socket_close(&buffer->sock_ctl);
376         socket_close(&buffer->sock_wr);
377
378         log_storage_free(buffer->log_storage_ptr);
379
380         free(buffer);
381 }