Line data Source code
1 : /*
2 : * SPDX license identifier: MPL-2.0
3 : *
4 : * Copyright (C) 2015 Advanced Driver Information Technology.
5 : * This code is developed by Advanced Driver Information Technology.
6 : * Copyright of Advanced Driver Information Technology, Bosch and DENSO.
7 : *
8 : * This file is part of COVESA Project DLT - Diagnostic Log and Trace.
9 : *
10 : * This Source Code Form is subject to the terms of the
11 : * Mozilla Public License (MPL), v. 2.0.
12 : * If a copy of the MPL was not distributed with this file,
13 : * You can obtain one at http://mozilla.org/MPL/2.0/.
14 : *
15 : * For further information see http://www.covesa.org/.
16 : */
17 :
18 : /*!
19 : * \author
20 : * Frederic Berat <fberat@de.adit-jv.com>
21 : *
22 : * \copyright Copyright © 2015 Advanced Driver Information Technology. \n
23 : * License MPL-2.0: Mozilla Public License version 2.0 http://mozilla.org/MPL/2.0/.
24 : *
25 : * \file dlt_daemon_event_handler.c
26 : */
27 :
28 : #include <stdio.h>
29 : #include <stdlib.h>
30 : #include <string.h>
31 : #include <errno.h>
32 :
33 : #include <poll.h>
34 : #include <syslog.h>
35 :
36 : #include "dlt_common.h"
37 : #include "dlt_log.h"
38 :
39 : #include "dlt-daemon.h"
40 : #include "dlt-daemon_cfg.h"
41 : #include "dlt_daemon_common.h"
42 : #include "dlt_daemon_connection.h"
43 : #include "dlt_daemon_connection_types.h"
44 : #include "dlt_daemon_event_handler.h"
45 : #include "dlt_daemon_event_handler_types.h"
46 : #include "dlt_daemon_common.h"
47 :
48 : /**
49 : * \def DLT_EV_TIMEOUT_MSEC
50 : * The maximum amount of time to wait for a poll event.
51 : * Set to 1 second to avoid unnecessary wake ups.
52 : */
53 : #define DLT_EV_TIMEOUT_MSEC 1000
54 : #define DLT_EV_BASE_FD 16
55 :
56 : #define DLT_EV_MASK_REJECTED (POLLERR | POLLNVAL)
57 :
58 : /** @brief Initialize a pollfd structure
59 : *
60 : * That ensures that no event will be mis-watched.
61 : *
62 : * @param pfd The element to initialize
63 : */
64 : static void init_poll_fd(struct pollfd* pfd)
65 : {
66 122 : pfd->fd = -1;
67 122 : pfd->events = 0;
68 122 : pfd->revents = 0;
69 3 : }
70 :
71 : /** @brief Prepare the event handler
72 : *
73 : * This will create the base poll file descriptor list.
74 : *
75 : * @param ev The event handler to prepare.
76 : *
77 : * @return 0 on success, -1 otherwise.
78 : */
79 8 : int dlt_daemon_prepare_event_handling(DltEventHandler* ev)
80 : {
81 : int i = 0;
82 :
83 8 : if (ev == NULL)
84 : return DLT_RETURN_ERROR;
85 :
86 7 : ev->pfd = calloc(DLT_EV_BASE_FD, sizeof(struct pollfd));
87 :
88 7 : if (ev->pfd == NULL) {
89 0 : dlt_log(LOG_CRIT, "Creation of poll instance failed!\n");
90 0 : return -1;
91 : }
92 :
93 119 : for (i = 0; i < DLT_EV_BASE_FD; i++)
94 112 : init_poll_fd(&ev->pfd[i]);
95 :
96 7 : ev->nfds = 0;
97 7 : ev->max_nfds = DLT_EV_BASE_FD;
98 :
99 7 : return 0;
100 : }
101 :
102 : /** @brief Enable a file descriptor to be watched
103 : *
104 : * Adds a file descriptor to the descriptor list. If the list is to small,
105 : * increase its size.
106 : *
107 : * @param ev The event handler structure, containing the list
108 : * @param fd The file descriptor to add
109 : * @param mask The mask of event to be watched
110 : */
111 10 : static void dlt_event_handler_enable_fd(DltEventHandler* ev, int fd, int mask)
112 : {
113 10 : if (ev->max_nfds <= ev->nfds) {
114 : nfds_t i = ev->nfds;
115 2 : nfds_t max = 2 * ev->max_nfds;
116 2 : struct pollfd* tmp = realloc(ev->pfd, (size_t)max * sizeof(*ev->pfd));
117 :
118 2 : if (!tmp) {
119 0 : dlt_log(LOG_CRIT, "Unable to register new fd for the event handler.\n");
120 0 : return;
121 : }
122 :
123 2 : ev->pfd = tmp;
124 2 : ev->max_nfds = max;
125 :
126 2 : for (; i < max; i++)
127 0 : init_poll_fd(&ev->pfd[i]);
128 : }
129 :
130 10 : ev->pfd[ev->nfds].fd = fd;
131 10 : ev->pfd[ev->nfds].events = (short)mask;
132 10 : ev->nfds++;
133 : }
134 :
135 : /** @brief Disable a file descriptor for watching
136 : *
137 : * The file descriptor is removed from the descriptor list, the list is
138 : * compressed during the process.
139 : *
140 : * @param ev The event handler structure containing the list
141 : * @param fd The file descriptor to be removed
142 : */
143 3 : static void dlt_event_handler_disable_fd(DltEventHandler* ev, int fd)
144 : {
145 : unsigned int i = 0;
146 : unsigned int j = 0;
147 3 : unsigned int nfds = (unsigned int)ev->nfds;
148 :
149 10 : for (; i < nfds; i++, j++) {
150 7 : if (ev->pfd[i].fd == fd) {
151 : init_poll_fd(&ev->pfd[i]);
152 3 : j++;
153 3 : ev->nfds--;
154 : }
155 :
156 7 : if (i == j)
157 4 : continue;
158 :
159 : /* Compressing the table */
160 3 : if (i < ev->nfds) {
161 0 : ev->pfd[i].fd = ev->pfd[j].fd;
162 0 : ev->pfd[i].events = ev->pfd[j].events;
163 0 : ev->pfd[i].revents = ev->pfd[j].revents;
164 : } else {
165 : init_poll_fd(&ev->pfd[i]);
166 : }
167 : }
168 3 : }
169 :
170 : /** @brief Catch and process incoming events.
171 : *
172 : * This function waits for events on all connections. Once an event raise,
173 : * the callback for the specific connection is called, or the connection is
174 : * destroyed if a hangup occurs.
175 : *
176 : * @param daemon Structure to be passed to the callback.
177 : * @param daemon_local Structure containing needed information.
178 : * @param pEvent Event handler structure.
179 : *
180 : * @return 0 on success, -1 otherwise. May be interrupted.
181 : */
182 273 : int dlt_daemon_handle_event(DltEventHandler* pEvent, DltDaemon* daemon, DltDaemonLocal* daemon_local)
183 : {
184 : int ret = 0;
185 : unsigned int i = 0;
186 : int (*callback)(DltDaemon*, DltDaemonLocal*, DltReceiver*, int) = NULL;
187 :
188 273 : if ((pEvent == NULL) || (daemon == NULL) || (daemon_local == NULL))
189 : return DLT_RETURN_ERROR;
190 :
191 272 : ret = poll(pEvent->pfd, pEvent->nfds, DLT_EV_TIMEOUT_MSEC);
192 :
193 272 : if (ret <= 0) {
194 : /* We are not interested in EINTR has it comes
195 : * either from timeout or signal.
196 : */
197 1 : if (errno == EINTR)
198 : ret = 0;
199 :
200 1 : if (ret < 0)
201 0 : dlt_vlog(LOG_CRIT, "poll() failed: %s\n", strerror(errno));
202 :
203 1 : return ret;
204 : }
205 :
206 1357 : for (i = 0; i < pEvent->nfds; i++) {
207 : int fd = 0;
208 : DltConnection* con = NULL;
209 : DltConnectionType type = DLT_CONNECTION_TYPE_MAX;
210 :
211 1087 : if (pEvent->pfd[i].revents == 0)
212 815 : continue;
213 :
214 272 : con = dlt_event_handler_find_connection(pEvent, pEvent->pfd[i].fd);
215 :
216 272 : if (con && con->receiver) {
217 272 : type = con->type;
218 272 : fd = con->receiver->fd;
219 : } else { /* connection might have been destroyed in the meanwhile */
220 0 : dlt_event_handler_disable_fd(pEvent, pEvent->pfd[i].fd);
221 0 : continue;
222 : }
223 :
224 : /* First of all handle error events */
225 272 : if (pEvent->pfd[i].revents & DLT_EV_MASK_REJECTED) {
226 : /* An error occurred, we need to clean-up the concerned event
227 : */
228 0 : if (type == DLT_CONNECTION_CLIENT_MSG_TCP)
229 : /* To transition to BUFFER state if this is final TCP client connection,
230 : * call dedicated function. this function also calls
231 : * dlt_event_handler_unregister_connection() inside the function.
232 : */
233 0 : dlt_daemon_close_socket(fd, daemon, daemon_local, 0);
234 : else
235 0 : dlt_event_handler_unregister_connection(pEvent, daemon_local, fd);
236 :
237 0 : continue;
238 : }
239 :
240 : /* Get the function to be used to handle the event */
241 : union {
242 : void* ptr;
243 : int (*callback_func)(DltDaemon*, DltDaemonLocal*, DltReceiver*, int);
244 : } callback_converter;
245 :
246 272 : callback_converter.ptr = dlt_connection_get_callback(con);
247 272 : callback = callback_converter.callback_func;
248 :
249 272 : if (!callback) {
250 0 : dlt_vlog(LOG_CRIT, "Unable to find function for %u handle type.\n", type);
251 : /* keep handling remaining events */
252 0 : continue;
253 : }
254 :
255 : /* From now on, callback is correct */
256 272 : if (callback(daemon, daemon_local, con->receiver, daemon_local->flags.vflag) == -1) {
257 1 : dlt_vlog(LOG_CRIT, "Processing from %u handle type failed!\n", type);
258 : return -1;
259 : }
260 : #ifdef DLT_SYSTEMD_WATCHDOG_ENABLE
261 : // no need to yield here, it will be called in a loop anyways.
262 : // therefore we also do not log.
263 : dlt_daemon_trigger_systemd_watchdog_if_necessary(daemon);
264 : #endif
265 : }
266 :
267 : return 0;
268 : }
269 :
270 : /** @brief Find connection with a specific \a fd in the connection list.
271 : *
272 : * There can be only one event per \a fd. We can then find a specific connection
273 : * based on this \a fd. That allows one to check if a specific \a fd has already been
274 : * registered.
275 : *
276 : * @param ev The event handler structure where the list of connection is.
277 : * @param fd The file descriptor of the connection to be found.
278 : *
279 : * @return The found connection pointer, NULL otherwise.
280 : */
281 11 : DltConnection* dlt_event_handler_find_connection(DltEventHandler* ev, int fd)
282 : {
283 284 : DltConnection* temp = ev->connections;
284 :
285 310 : while (temp != NULL) {
286 301 : if ((temp->receiver != NULL) && (temp->receiver->fd == fd))
287 2 : return temp;
288 26 : temp = temp->next;
289 : }
290 :
291 : return temp;
292 : }
293 :
294 : /** @brief Remove a connection from the list and destroy it.
295 : *
296 : * This function will first look for the connection in the event handler list,
297 : * remove it from the list and then destroy it.
298 : *
299 : * @param ev The event handler structure where the list of connection is.
300 : * @param to_remove The connection to remove from the list.
301 : *
302 : * @return 0 on success, -1 if the connection is not found.
303 : */
304 8 : DLT_STATIC int dlt_daemon_remove_connection(DltEventHandler* ev, DltConnection* to_remove)
305 : {
306 8 : if ((ev == NULL) || (to_remove == NULL))
307 : return DLT_RETURN_ERROR;
308 :
309 8 : DltConnection* curr = ev->connections;
310 : DltConnection* prev = curr;
311 :
312 : /* Find the address where to_remove value is registered */
313 13 : while (curr && (curr != to_remove)) {
314 : prev = curr;
315 5 : curr = curr->next;
316 : }
317 :
318 8 : if (!curr) {
319 : /* Must not be possible as we check for existence before */
320 0 : dlt_log(LOG_CRIT, "Connection not found for removal.\n");
321 0 : return -1;
322 8 : } else if (curr == ev->connections) {
323 6 : ev->connections = curr->next;
324 : } else {
325 2 : prev->next = curr->next;
326 : }
327 :
328 : /* Now we can destroy our pointer */
329 8 : dlt_connection_destroy(to_remove);
330 :
331 8 : return 0;
332 : }
333 :
334 : /** @brief Destroy the connection list.
335 : *
336 : * This function runs through the connection list and destroy them one by one.
337 : *
338 : * @param ev Pointer to the event handler structure.
339 : */
340 2 : void dlt_event_handler_cleanup_connections(DltEventHandler* ev)
341 : {
342 : unsigned int i = 0;
343 :
344 2 : if (ev == NULL)
345 : /* Nothing to do. */
346 : return;
347 :
348 7 : while (ev->connections != NULL)
349 : /* We don really care on failure */
350 5 : (void)dlt_daemon_remove_connection(ev, ev->connections);
351 :
352 6 : for (i = 0; i < ev->nfds; i++)
353 4 : init_poll_fd(&ev->pfd[i]);
354 :
355 2 : free(ev->pfd);
356 : }
357 :
358 : /** @brief Add a new connection to the list.
359 : *
360 : * The connection is added at the tail of the list.
361 : *
362 : * @param ev The event handler structure where the connection list is.
363 : * @param connection The connection to be added.
364 : */
365 6 : DLT_STATIC void dlt_daemon_add_connection(DltEventHandler* ev, DltConnection* connection)
366 : {
367 11 : DltConnection** temp = &ev->connections;
368 :
369 25 : while (*temp != NULL)
370 14 : temp = &(*temp)->next;
371 :
372 11 : *temp = connection;
373 6 : }
374 :
375 : /** @brief Check for connection activation
376 : *
377 : * If the connection is active and it's not allowed anymore or it the user
378 : * ask for deactivation, the connection will be deactivated.
379 : * If the connection is inactive, the user asks for activation and it's
380 : * allowed for it to be activated, the connection will be activated.
381 : *
382 : * @param evhdl The event handler structure.
383 : * @param con The connection to act on
384 : * @param activation_type The type of activation requested ((DE)ACTIVATE)
385 : *
386 : * @return 0 on success, -1 otherwise
387 : */
388 14 : int dlt_connection_check_activate(DltEventHandler* evhdl, DltConnection* con, int activation_type)
389 : {
390 14 : if (!evhdl || !con || !con->receiver) {
391 1 : dlt_vlog(LOG_ERR, "%s: wrong parameters.\n", __func__);
392 1 : return -1;
393 : }
394 :
395 13 : switch (con->status) {
396 3 : case ACTIVE:
397 :
398 3 : if (activation_type == DEACTIVATE) {
399 3 : dlt_vlog(LOG_INFO, "Deactivate connection type: %u\n", con->type);
400 :
401 3 : dlt_event_handler_disable_fd(evhdl, con->receiver->fd);
402 :
403 3 : if (con->type == DLT_CONNECTION_CLIENT_CONNECT)
404 0 : con->receiver->fd = -1;
405 :
406 3 : con->status = INACTIVE;
407 : }
408 :
409 : break;
410 10 : case INACTIVE:
411 :
412 10 : if (activation_type == ACTIVATE) {
413 10 : dlt_vlog(LOG_INFO, "Activate connection type: %u\n", con->type);
414 :
415 10 : dlt_event_handler_enable_fd(evhdl, con->receiver->fd, con->ev_mask);
416 :
417 10 : con->status = ACTIVE;
418 : }
419 :
420 : break;
421 0 : default:
422 0 : dlt_vlog(LOG_ERR, "Unknown connection status: %u\n", con->status);
423 0 : return -1;
424 : }
425 :
426 : return 0;
427 : }
428 :
429 : /** @brief Registers a connection for event handling and takes its ownership.
430 : *
431 : * As we add the connection to the list of connection, we take its ownership.
432 : * That's the only place where the connection pointer is stored.
433 : * The connection is then used to create a new event trigger.
434 : * If the connection is of type DLT_CONNECTION_CLIENT_MSG_TCP, we increase
435 : * the daemon_local->client_connections counter. TODO: Move this counter inside
436 : * the event handler structure.
437 : *
438 : * @param evhdl The event handler structure where the connection list is.
439 : * @param daemon_local Structure containing needed information.
440 : * @param connection The connection to be registered.
441 : * @param mask The bit mask of event to be registered.
442 : *
443 : * @return 0 on success, -1 otherwise.
444 : */
445 10 : int dlt_event_handler_register_connection(
446 : DltEventHandler* evhdl, DltDaemonLocal* daemon_local, DltConnection* connection, int mask)
447 : {
448 10 : if (!evhdl || !connection || !connection->receiver) {
449 1 : dlt_log(LOG_ERR, "Wrong parameters when registering connection.\n");
450 1 : return -1;
451 : }
452 :
453 4 : dlt_daemon_add_connection(evhdl, connection);
454 :
455 9 : if ((connection->type == DLT_CONNECTION_CLIENT_MSG_TCP) || (connection->type == DLT_CONNECTION_CLIENT_MSG_SERIAL))
456 0 : daemon_local->client_connections++;
457 :
458 : /* On creation the connection is not active by default */
459 9 : connection->status = INACTIVE;
460 :
461 9 : connection->next = NULL;
462 9 : connection->ev_mask = mask;
463 :
464 9 : return dlt_connection_check_activate(evhdl, connection, ACTIVATE);
465 : }
466 :
467 : /** @brief Unregisters a connection from the event handler and destroys it.
468 : *
469 : * We first look for the connection to be unregistered, delete the event
470 : * corresponding and then destroy the connection.
471 : * If the connection is of type DLT_CONNECTION_CLIENT_MSG_TCP, we decrease
472 : * the daemon_local->client_connections counter. TODO: Move this counter inside
473 : * the event handler structure.
474 : *
475 : * @param evhdl The event handler structure where the connection list is.
476 : * @param daemon_local Structure containing needed information.
477 : * @param fd The file descriptor of the connection to be unregistered.
478 : *
479 : * @return 0 on success, -1 otherwise.
480 : */
481 3 : int dlt_event_handler_unregister_connection(DltEventHandler* evhdl, DltDaemonLocal* daemon_local, int fd)
482 : {
483 3 : if ((evhdl == NULL) || (daemon_local == NULL))
484 : return DLT_RETURN_ERROR;
485 :
486 : /* Look for the pointer in the client list.
487 : * There shall be only one event handler with the same fd.
488 : */
489 2 : DltConnection* temp = dlt_event_handler_find_connection(evhdl, fd);
490 :
491 3 : if (!temp) {
492 1 : dlt_log(LOG_ERR, "Connection not found for unregistration.\n");
493 1 : return -1;
494 : }
495 :
496 2 : if ((temp->type == DLT_CONNECTION_CLIENT_MSG_TCP) || (temp->type == DLT_CONNECTION_CLIENT_MSG_SERIAL)) {
497 0 : daemon_local->client_connections--;
498 :
499 0 : if (daemon_local->client_connections < 0) {
500 0 : daemon_local->client_connections = 0;
501 0 : dlt_log(LOG_CRIT, "Unregistering more client than registered!\n");
502 : }
503 : }
504 :
505 2 : if (dlt_connection_check_activate(evhdl, temp, DEACTIVATE) < 0)
506 0 : dlt_log(LOG_ERR, "Unable to unregister event.\n");
507 :
508 : /* Cannot fail as far as dlt_daemon_find_connection succeed */
509 2 : return dlt_daemon_remove_connection(evhdl, temp);
510 : }
|