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 293 : pfd->fd = -1;
67 293 : pfd->events = 0;
68 293 : pfd->revents = 0;
69 7 : }
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 16 : int dlt_daemon_prepare_event_handling(DltEventHandler *ev)
80 : {
81 : int i = 0;
82 :
83 16 : if (ev == NULL)
84 : return DLT_RETURN_ERROR;
85 :
86 15 : ev->pfd = calloc(DLT_EV_BASE_FD, sizeof(struct pollfd));
87 :
88 15 : if (ev->pfd == NULL) {
89 0 : dlt_log(LOG_CRIT, "Creation of poll instance failed!\n");
90 0 : return -1;
91 : }
92 :
93 255 : for (i = 0; i < DLT_EV_BASE_FD; i++)
94 240 : init_poll_fd(&ev->pfd[i]);
95 :
96 15 : ev->nfds = 0;
97 15 : ev->max_nfds = DLT_EV_BASE_FD;
98 :
99 15 : 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 49 : static void dlt_event_handler_enable_fd(DltEventHandler *ev, int fd, int mask)
112 : {
113 49 : 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,
120 : "Unable to register new fd for the event handler.\n");
121 0 : return;
122 : }
123 :
124 2 : ev->pfd = tmp;
125 2 : ev->max_nfds = max;
126 :
127 2 : for (; i < max; i++)
128 0 : init_poll_fd(&ev->pfd[i]);
129 : }
130 :
131 49 : ev->pfd[ev->nfds].fd = fd;
132 49 : ev->pfd[ev->nfds].events = (short)mask;
133 49 : ev->nfds++;
134 : }
135 :
136 : /** @brief Disable a file descriptor for watching
137 : *
138 : * The file descriptor is removed from the descriptor list, the list is
139 : * compressed during the process.
140 : *
141 : * @param ev The event handler structure containing the list
142 : * @param fd The file descriptor to be removed
143 : */
144 7 : static void dlt_event_handler_disable_fd(DltEventHandler *ev, int fd)
145 : {
146 : unsigned int i = 0;
147 : unsigned int j = 0;
148 7 : unsigned int nfds = (unsigned int)ev->nfds;
149 :
150 36 : for (; i < nfds; i++, j++) {
151 29 : if (ev->pfd[i].fd == fd) {
152 : init_poll_fd(&ev->pfd[i]);
153 7 : j++;
154 7 : ev->nfds--;
155 : }
156 :
157 29 : if (i == j)
158 22 : continue;
159 :
160 : /* Compressing the table */
161 7 : if (i < ev->nfds) {
162 0 : ev->pfd[i].fd = ev->pfd[j].fd;
163 0 : ev->pfd[i].events = ev->pfd[j].events;
164 0 : ev->pfd[i].revents = ev->pfd[j].revents;
165 : }
166 : else {
167 : init_poll_fd(&ev->pfd[i]);
168 : }
169 : }
170 7 : }
171 :
172 : /** @brief Catch and process incoming events.
173 : *
174 : * This function waits for events on all connections. Once an event raise,
175 : * the callback for the specific connection is called, or the connection is
176 : * destroyed if a hangup occurs.
177 : *
178 : * @param daemon Structure to be passed to the callback.
179 : * @param daemon_local Structure containing needed information.
180 : * @param pEvent Event handler structure.
181 : *
182 : * @return 0 on success, -1 otherwise. May be interrupted.
183 : */
184 1296 : int dlt_daemon_handle_event(DltEventHandler *pEvent,
185 : DltDaemon *daemon,
186 : DltDaemonLocal *daemon_local)
187 : {
188 : int ret = 0;
189 : unsigned int i = 0;
190 : int (*callback)(DltDaemon *, DltDaemonLocal *, DltReceiver *, int) = NULL;
191 :
192 1296 : if ((pEvent == NULL) || (daemon == NULL) || (daemon_local == NULL))
193 : return DLT_RETURN_ERROR;
194 :
195 1295 : ret = poll(pEvent->pfd, pEvent->nfds, DLT_EV_TIMEOUT_MSEC);
196 :
197 1295 : if (ret <= 0) {
198 : /* We are not interested in EINTR has it comes
199 : * either from timeout or signal.
200 : */
201 10 : if (errno == EINTR)
202 : ret = 0;
203 :
204 1 : if (ret < 0)
205 0 : dlt_vlog(LOG_CRIT, "poll() failed: %s\n", strerror(errno));
206 :
207 10 : return ret;
208 : }
209 :
210 6682 : for (i = 0; i < pEvent->nfds; i++) {
211 : int fd = 0;
212 : DltConnection *con = NULL;
213 : DltConnectionType type = DLT_CONNECTION_TYPE_MAX;
214 :
215 5397 : if (pEvent->pfd[i].revents == 0)
216 4110 : continue;
217 :
218 1287 : con = dlt_event_handler_find_connection(pEvent, pEvent->pfd[i].fd);
219 :
220 1287 : if (con && con->receiver) {
221 1287 : type = con->type;
222 1287 : fd = con->receiver->fd;
223 : }
224 : else { /* connection might have been destroyed in the meanwhile */
225 0 : dlt_event_handler_disable_fd(pEvent, pEvent->pfd[i].fd);
226 0 : continue;
227 : }
228 :
229 : /* First of all handle error events */
230 1287 : if (pEvent->pfd[i].revents & DLT_EV_MASK_REJECTED) {
231 : /* An error occurred, we need to clean-up the concerned event
232 : */
233 2 : if (type == DLT_CONNECTION_CLIENT_MSG_TCP)
234 : /* To transition to BUFFER state if this is final TCP client connection,
235 : * call dedicated function. this function also calls
236 : * dlt_event_handler_unregister_connection() inside the function.
237 : */
238 2 : dlt_daemon_close_socket(fd, daemon, daemon_local, 0);
239 : else
240 0 : dlt_event_handler_unregister_connection(pEvent,
241 : daemon_local,
242 : fd);
243 :
244 2 : continue;
245 : }
246 :
247 : /* Get the function to be used to handle the event */
248 : union {
249 : void *ptr;
250 : int (*callback_func)(DltDaemon *, DltDaemonLocal *, DltReceiver *, int);
251 : } callback_converter;
252 :
253 1285 : callback_converter.ptr = dlt_connection_get_callback(con);
254 1285 : callback = callback_converter.callback_func;
255 :
256 1285 : if (!callback) {
257 0 : dlt_vlog(LOG_CRIT, "Unable to find function for %u handle type.\n",
258 : type);
259 : /* keep handling remaining events */
260 0 : continue;
261 : }
262 :
263 : /* From now on, callback is correct */
264 1285 : if (callback(daemon,
265 : daemon_local,
266 : con->receiver,
267 : daemon_local->flags.vflag) == -1) {
268 0 : dlt_vlog(LOG_CRIT, "Processing from %u handle type failed!\n",
269 : type);
270 : return -1;
271 : }
272 : #ifdef DLT_SYSTEMD_WATCHDOG_ENABLE
273 : // no need to yield here, it will be called in a loop anyways.
274 : // therefore we also do not log.
275 : dlt_daemon_trigger_systemd_watchdog_if_necessary(daemon);
276 : #endif
277 : }
278 :
279 : return 0;
280 : }
281 :
282 : /** @brief Find connection with a specific \a fd in the connection list.
283 : *
284 : * There can be only one event per \a fd. We can then find a specific connection
285 : * based on this \a fd. That allows one to check if a specific \a fd has already been
286 : * registered.
287 : *
288 : * @param ev The event handler structure where the list of connection is.
289 : * @param fd The file descriptor of the connection to be found.
290 : *
291 : * @return The found connection pointer, NULL otherwise.
292 : */
293 1122 : DltConnection *dlt_event_handler_find_connection(DltEventHandler *ev, int fd)
294 : {
295 2414 : DltConnection *temp = ev->connections;
296 :
297 4822 : while (temp != NULL) {
298 4774 : if ((temp->receiver != NULL) && (temp->receiver->fd == fd))
299 1074 : return temp;
300 2408 : temp = temp->next;
301 : }
302 :
303 : return temp;
304 : }
305 :
306 : /** @brief Remove a connection from the list and destroy it.
307 : *
308 : * This function will first look for the connection in the event handler list,
309 : * remove it from the list and then destroy it.
310 : *
311 : * @param ev The event handler structure where the list of connection is.
312 : * @param to_remove The connection to remove from the list.
313 : *
314 : * @return 0 on success, -1 if the connection is not found.
315 : */
316 47 : DLT_STATIC int dlt_daemon_remove_connection(DltEventHandler *ev,
317 : DltConnection *to_remove)
318 : {
319 47 : if ((ev == NULL) || (to_remove == NULL))
320 : return DLT_RETURN_ERROR;
321 :
322 47 : DltConnection *curr = ev->connections;
323 : DltConnection *prev = curr;
324 :
325 : /* Find the address where to_remove value is registered */
326 70 : while (curr && (curr != to_remove)) {
327 : prev = curr;
328 23 : curr = curr->next;
329 : }
330 :
331 47 : if (!curr) {
332 : /* Must not be possible as we check for existence before */
333 0 : dlt_log(LOG_CRIT, "Connection not found for removal.\n");
334 0 : return -1;
335 : }
336 47 : else if (curr == ev->connections)
337 : {
338 41 : ev->connections = curr->next;
339 : }
340 : else {
341 6 : prev->next = curr->next;
342 : }
343 :
344 : /* Now we can destroy our pointer */
345 47 : dlt_connection_destroy(to_remove);
346 :
347 47 : return 0;
348 : }
349 :
350 : /** @brief Destroy the connection list.
351 : *
352 : * This function runs through the connection list and destroy them one by one.
353 : *
354 : * @param ev Pointer to the event handler structure.
355 : */
356 10 : void dlt_event_handler_cleanup_connections(DltEventHandler *ev)
357 : {
358 : unsigned int i = 0;
359 :
360 10 : if (ev == NULL)
361 : /* Nothing to do. */
362 : return;
363 :
364 50 : while (ev->connections != NULL)
365 : /* We don really care on failure */
366 40 : (void)dlt_daemon_remove_connection(ev, ev->connections);
367 :
368 49 : for (i = 0; i < ev->nfds; i++)
369 39 : init_poll_fd(&ev->pfd[i]);
370 :
371 10 : free(ev->pfd);
372 : }
373 :
374 : /** @brief Add a new connection to the list.
375 : *
376 : * The connection is added at the tail of the list.
377 : *
378 : * @param ev The event handler structure where the connection list is.
379 : * @param connection The connection to be added.
380 : */
381 6 : DLT_STATIC void dlt_daemon_add_connection(DltEventHandler *ev,
382 : DltConnection *connection)
383 : {
384 :
385 50 : DltConnection **temp = &ev->connections;
386 :
387 143 : while (*temp != NULL)
388 93 : temp = &(*temp)->next;
389 :
390 50 : *temp = connection;
391 6 : }
392 :
393 : /** @brief Check for connection activation
394 : *
395 : * If the connection is active and it's not allowed anymore or it the user
396 : * ask for deactivation, the connection will be deactivated.
397 : * If the connection is inactive, the user asks for activation and it's
398 : * allowed for it to be activated, the connection will be activated.
399 : *
400 : * @param evhdl The event handler structure.
401 : * @param con The connection to act on
402 : * @param activation_type The type of activation requested ((DE)ACTIVATE)
403 : *
404 : * @return 0 on success, -1 otherwise
405 : */
406 57 : int dlt_connection_check_activate(DltEventHandler *evhdl,
407 : DltConnection *con,
408 : int activation_type)
409 : {
410 57 : if (!evhdl || !con || !con->receiver) {
411 1 : dlt_vlog(LOG_ERR, "%s: wrong parameters.\n", __func__);
412 1 : return -1;
413 : }
414 :
415 56 : switch (con->status) {
416 7 : case ACTIVE:
417 :
418 7 : if (activation_type == DEACTIVATE) {
419 7 : dlt_vlog(LOG_INFO, "Deactivate connection type: %u\n", con->type);
420 :
421 7 : dlt_event_handler_disable_fd(evhdl, con->receiver->fd);
422 :
423 7 : if (con->type == DLT_CONNECTION_CLIENT_CONNECT)
424 0 : con->receiver->fd = -1;
425 :
426 7 : con->status = INACTIVE;
427 : }
428 :
429 : break;
430 49 : case INACTIVE:
431 :
432 49 : if (activation_type == ACTIVATE) {
433 49 : dlt_vlog(LOG_INFO, "Activate connection type: %u\n", con->type);
434 :
435 49 : dlt_event_handler_enable_fd(evhdl,
436 49 : con->receiver->fd,
437 : con->ev_mask);
438 :
439 49 : con->status = ACTIVE;
440 : }
441 :
442 : break;
443 0 : default:
444 0 : dlt_vlog(LOG_ERR, "Unknown connection status: %u\n", con->status);
445 0 : return -1;
446 : }
447 :
448 : return 0;
449 : }
450 :
451 : /** @brief Registers a connection for event handling and takes its ownership.
452 : *
453 : * As we add the connection to the list of connection, we take its ownership.
454 : * That's the only place where the connection pointer is stored.
455 : * The connection is then used to create a new event trigger.
456 : * If the connection is of type DLT_CONNECTION_CLIENT_MSG_TCP, we increase
457 : * the daemon_local->client_connections counter. TODO: Move this counter inside
458 : * the event handler structure.
459 : *
460 : * @param evhdl The event handler structure where the connection list is.
461 : * @param daemon_local Structure containing needed information.
462 : * @param connection The connection to be registered.
463 : * @param mask The bit mask of event to be registered.
464 : *
465 : * @return 0 on success, -1 otherwise.
466 : */
467 49 : int dlt_event_handler_register_connection(DltEventHandler *evhdl,
468 : DltDaemonLocal *daemon_local,
469 : DltConnection *connection,
470 : int mask)
471 : {
472 49 : if (!evhdl || !connection || !connection->receiver) {
473 1 : dlt_log(LOG_ERR, "Wrong parameters when registering connection.\n");
474 1 : return -1;
475 : }
476 :
477 4 : dlt_daemon_add_connection(evhdl, connection);
478 :
479 48 : if ((connection->type == DLT_CONNECTION_CLIENT_MSG_TCP) ||
480 : (connection->type == DLT_CONNECTION_CLIENT_MSG_SERIAL))
481 4 : daemon_local->client_connections++;
482 :
483 : /* On creation the connection is not active by default */
484 48 : connection->status = INACTIVE;
485 :
486 48 : connection->next = NULL;
487 48 : connection->ev_mask = mask;
488 :
489 48 : return dlt_connection_check_activate(evhdl,
490 : connection,
491 : ACTIVATE);
492 : }
493 :
494 : /** @brief Unregisters a connection from the event handler and destroys it.
495 : *
496 : * We first look for the connection to be unregistered, delete the event
497 : * corresponding and then destroy the connection.
498 : * If the connection is of type DLT_CONNECTION_CLIENT_MSG_TCP, we decrease
499 : * the daemon_local->client_connections counter. TODO: Move this counter inside
500 : * the event handler structure.
501 : *
502 : * @param evhdl The event handler structure where the connection list is.
503 : * @param daemon_local Structure containing needed information.
504 : * @param fd The file descriptor of the connection to be unregistered.
505 : *
506 : * @return 0 on success, -1 otherwise.
507 : */
508 7 : int dlt_event_handler_unregister_connection(DltEventHandler *evhdl,
509 : DltDaemonLocal *daemon_local,
510 : int fd)
511 : {
512 7 : if ((evhdl == NULL) || (daemon_local == NULL))
513 : return DLT_RETURN_ERROR;
514 :
515 : /* Look for the pointer in the client list.
516 : * There shall be only one event handler with the same fd.
517 : */
518 2 : DltConnection *temp = dlt_event_handler_find_connection(evhdl, fd);
519 :
520 7 : if (!temp) {
521 1 : dlt_log(LOG_ERR, "Connection not found for unregistration.\n");
522 1 : return -1;
523 : }
524 :
525 6 : if ((temp->type == DLT_CONNECTION_CLIENT_MSG_TCP) ||
526 : (temp->type == DLT_CONNECTION_CLIENT_MSG_SERIAL)) {
527 3 : daemon_local->client_connections--;
528 :
529 3 : if (daemon_local->client_connections < 0) {
530 0 : daemon_local->client_connections = 0;
531 0 : dlt_log(LOG_CRIT, "Unregistering more client than registered!\n");
532 : }
533 : }
534 :
535 6 : if (dlt_connection_check_activate(evhdl,
536 : temp,
537 : DEACTIVATE) < 0)
538 0 : dlt_log(LOG_ERR, "Unable to unregister event.\n");
539 :
540 : /* Cannot fail as far as dlt_daemon_find_connection succeed */
541 6 : return dlt_daemon_remove_connection(evhdl, temp);
542 : }
|