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 2 : int i = ev->nfds;
115 2 : int max = 2 * ev->max_nfds;
116 2 : struct pollfd *tmp = realloc(ev->pfd, 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 = 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 = 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 1601 : 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 1601 : if ((pEvent == NULL) || (daemon == NULL) || (daemon_local == NULL))
193 : return DLT_RETURN_ERROR;
194 :
195 1600 : ret = poll(pEvent->pfd, pEvent->nfds, DLT_EV_TIMEOUT_MSEC);
196 :
197 1600 : 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 : #ifdef DLT_SYSTEMD_WATCHDOG_ENABLE
211 : unsigned int start_time = dlt_uptime();
212 : #endif
213 :
214 8187 : for (i = 0; i < pEvent->nfds; i++) {
215 : int fd = 0;
216 : DltConnection *con = NULL;
217 : DltConnectionType type = DLT_CONNECTION_TYPE_MAX;
218 :
219 6597 : if (pEvent->pfd[i].revents == 0)
220 5005 : continue;
221 :
222 1592 : con = dlt_event_handler_find_connection(pEvent, pEvent->pfd[i].fd);
223 :
224 1592 : if (con && con->receiver) {
225 1592 : type = con->type;
226 1592 : fd = con->receiver->fd;
227 : }
228 : else { /* connection might have been destroyed in the meanwhile */
229 0 : dlt_event_handler_disable_fd(pEvent, pEvent->pfd[i].fd);
230 0 : continue;
231 : }
232 :
233 : /* First of all handle error events */
234 1592 : if (pEvent->pfd[i].revents & DLT_EV_MASK_REJECTED) {
235 : /* An error occurred, we need to clean-up the concerned event
236 : */
237 2 : if (type == DLT_CONNECTION_CLIENT_MSG_TCP)
238 : /* To transition to BUFFER state if this is final TCP client connection,
239 : * call dedicated function. this function also calls
240 : * dlt_event_handler_unregister_connection() inside the function.
241 : */
242 2 : dlt_daemon_close_socket(fd, daemon, daemon_local, 0);
243 : else
244 0 : dlt_event_handler_unregister_connection(pEvent,
245 : daemon_local,
246 : fd);
247 :
248 2 : continue;
249 : }
250 :
251 : /* Get the function to be used to handle the event */
252 1590 : callback = dlt_connection_get_callback(con);
253 :
254 1590 : if (!callback) {
255 0 : dlt_vlog(LOG_CRIT, "Unable to find function for %u handle type.\n",
256 : type);
257 : /* keep handling remaining events */
258 0 : continue;
259 : }
260 :
261 : /* From now on, callback is correct */
262 1590 : if (callback(daemon,
263 : daemon_local,
264 : con->receiver,
265 : daemon_local->flags.vflag) == -1) {
266 0 : dlt_vlog(LOG_CRIT, "Processing from %u handle type failed!\n",
267 : type);
268 0 : return -1;
269 : }
270 : #ifdef DLT_SYSTEMD_WATCHDOG_ENABLE
271 : dlt_daemon_trigger_systemd_watchdog_if_necessary(&start_time, daemon->watchdog_trigger_interval);
272 : #endif
273 : }
274 :
275 : return 0;
276 : }
277 :
278 : /** @brief Find connection with a specific \a fd in the connection list.
279 : *
280 : * There can be only one event per \a fd. We can then find a specific connection
281 : * based on this \a fd. That allows to check if a specific \a fd has already been
282 : * registered.
283 : *
284 : * @param ev The event handler structure where the list of connection is.
285 : * @param fd The file descriptor of the connection to be found.
286 : *
287 : * @return The found connection pointer, NULL otherwise.
288 : */
289 1122 : DltConnection *dlt_event_handler_find_connection(DltEventHandler *ev, int fd)
290 : {
291 2719 : DltConnection *temp = ev->connections;
292 :
293 5089 : while (temp != NULL) {
294 5041 : if ((temp->receiver != NULL) && (temp->receiver->fd == fd))
295 1074 : return temp;
296 2370 : temp = temp->next;
297 : }
298 :
299 : return temp;
300 : }
301 :
302 : /** @brief Remove a connection from the list and destroy it.
303 : *
304 : * This function will first look for the connection in the event handler list,
305 : * remove it from the list and then destroy it.
306 : *
307 : * @param ev The event handler structure where the list of connection is.
308 : * @param to_remove The connection to remove from the list.
309 : *
310 : * @return 0 on success, -1 if the connection is not found.
311 : */
312 47 : DLT_STATIC int dlt_daemon_remove_connection(DltEventHandler *ev,
313 : DltConnection *to_remove)
314 : {
315 47 : if ((ev == NULL) || (to_remove == NULL))
316 : return DLT_RETURN_ERROR;
317 :
318 47 : DltConnection *curr = ev->connections;
319 : DltConnection *prev = curr;
320 :
321 : /* Find the address where to_remove value is registered */
322 70 : while (curr && (curr != to_remove)) {
323 : prev = curr;
324 23 : curr = curr->next;
325 : }
326 :
327 47 : if (!curr) {
328 : /* Must not be possible as we check for existence before */
329 0 : dlt_log(LOG_CRIT, "Connection not found for removal.\n");
330 0 : return -1;
331 : }
332 47 : else if (curr == ev->connections)
333 : {
334 41 : ev->connections = curr->next;
335 : }
336 : else {
337 6 : prev->next = curr->next;
338 : }
339 :
340 : /* Now we can destroy our pointer */
341 47 : dlt_connection_destroy(to_remove);
342 :
343 47 : return 0;
344 : }
345 :
346 : /** @brief Destroy the connection list.
347 : *
348 : * This function runs through the connection list and destroy them one by one.
349 : *
350 : * @param ev Pointer to the event handler structure.
351 : */
352 10 : void dlt_event_handler_cleanup_connections(DltEventHandler *ev)
353 : {
354 : unsigned int i = 0;
355 :
356 10 : if (ev == NULL)
357 : /* Nothing to do. */
358 : return;
359 :
360 50 : while (ev->connections != NULL)
361 : /* We don really care on failure */
362 40 : (void)dlt_daemon_remove_connection(ev, ev->connections);
363 :
364 49 : for (i = 0; i < ev->nfds; i++)
365 39 : init_poll_fd(&ev->pfd[i]);
366 :
367 10 : free(ev->pfd);
368 : }
369 :
370 : /** @brief Add a new connection to the list.
371 : *
372 : * The connection is added at the tail of the list.
373 : *
374 : * @param ev The event handler structure where the connection list is.
375 : * @param connection The connection to be added.
376 : */
377 6 : DLT_STATIC void dlt_daemon_add_connection(DltEventHandler *ev,
378 : DltConnection *connection)
379 : {
380 :
381 50 : DltConnection **temp = &ev->connections;
382 :
383 143 : while (*temp != NULL)
384 93 : temp = &(*temp)->next;
385 :
386 50 : *temp = connection;
387 6 : }
388 :
389 : /** @brief Check for connection activation
390 : *
391 : * If the connection is active and it's not allowed anymore or it the user
392 : * ask for deactivation, the connection will be deactivated.
393 : * If the connection is inactive, the user asks for activation and it's
394 : * allowed for it to be activated, the connection will be activated.
395 : *
396 : * @param evhdl The event handler structure.
397 : * @param con The connection to act on
398 : * @param activation_type The type of activation requested ((DE)ACTIVATE)
399 : *
400 : * @return 0 on success, -1 otherwise
401 : */
402 57 : int dlt_connection_check_activate(DltEventHandler *evhdl,
403 : DltConnection *con,
404 : int activation_type)
405 : {
406 57 : if (!evhdl || !con || !con->receiver) {
407 1 : dlt_vlog(LOG_ERR, "%s: wrong parameters.\n", __func__);
408 1 : return -1;
409 : }
410 :
411 56 : switch (con->status) {
412 7 : case ACTIVE:
413 :
414 7 : if (activation_type == DEACTIVATE) {
415 7 : dlt_vlog(LOG_INFO, "Deactivate connection type: %u\n", con->type);
416 :
417 7 : dlt_event_handler_disable_fd(evhdl, con->receiver->fd);
418 :
419 7 : if (con->type == DLT_CONNECTION_CLIENT_CONNECT)
420 0 : con->receiver->fd = -1;
421 :
422 7 : con->status = INACTIVE;
423 : }
424 :
425 : break;
426 49 : case INACTIVE:
427 :
428 49 : if (activation_type == ACTIVATE) {
429 49 : dlt_vlog(LOG_INFO, "Activate connection type: %u\n", con->type);
430 :
431 49 : dlt_event_handler_enable_fd(evhdl,
432 49 : con->receiver->fd,
433 : con->ev_mask);
434 :
435 49 : con->status = ACTIVE;
436 : }
437 :
438 : break;
439 0 : default:
440 0 : dlt_vlog(LOG_ERR, "Unknown connection status: %u\n", con->status);
441 0 : return -1;
442 : }
443 :
444 : return 0;
445 : }
446 :
447 : /** @brief Registers a connection for event handling and takes its ownership.
448 : *
449 : * As we add the connection to the list of connection, we take its ownership.
450 : * That's the only place where the connection pointer is stored.
451 : * The connection is then used to create a new event trigger.
452 : * If the connection is of type DLT_CONNECTION_CLIENT_MSG_TCP, we increase
453 : * the daemon_local->client_connections counter. TODO: Move this counter inside
454 : * the event handler structure.
455 : *
456 : * @param evhdl The event handler structure where the connection list is.
457 : * @param daemon_local Structure containing needed information.
458 : * @param connection The connection to be registered.
459 : * @param mask The bit mask of event to be registered.
460 : *
461 : * @return 0 on success, -1 otherwise.
462 : */
463 49 : int dlt_event_handler_register_connection(DltEventHandler *evhdl,
464 : DltDaemonLocal *daemon_local,
465 : DltConnection *connection,
466 : int mask)
467 : {
468 49 : if (!evhdl || !connection || !connection->receiver) {
469 1 : dlt_log(LOG_ERR, "Wrong parameters when registering connection.\n");
470 1 : return -1;
471 : }
472 :
473 4 : dlt_daemon_add_connection(evhdl, connection);
474 :
475 48 : if ((connection->type == DLT_CONNECTION_CLIENT_MSG_TCP) ||
476 : (connection->type == DLT_CONNECTION_CLIENT_MSG_SERIAL))
477 4 : daemon_local->client_connections++;
478 :
479 : /* On creation the connection is not active by default */
480 48 : connection->status = INACTIVE;
481 :
482 48 : connection->next = NULL;
483 48 : connection->ev_mask = mask;
484 :
485 48 : return dlt_connection_check_activate(evhdl,
486 : connection,
487 : ACTIVATE);
488 : }
489 :
490 : /** @brief Unregisters a connection from the event handler and destroys it.
491 : *
492 : * We first look for the connection to be unregistered, delete the event
493 : * corresponding and then destroy the connection.
494 : * If the connection is of type DLT_CONNECTION_CLIENT_MSG_TCP, we decrease
495 : * the daemon_local->client_connections counter. TODO: Move this counter inside
496 : * the event handler structure.
497 : *
498 : * @param evhdl The event handler structure where the connection list is.
499 : * @param daemon_local Structure containing needed information.
500 : * @param fd The file descriptor of the connection to be unregistered.
501 : *
502 : * @return 0 on success, -1 otherwise.
503 : */
504 7 : int dlt_event_handler_unregister_connection(DltEventHandler *evhdl,
505 : DltDaemonLocal *daemon_local,
506 : int fd)
507 : {
508 7 : if ((evhdl == NULL) || (daemon_local == NULL))
509 : return DLT_RETURN_ERROR;
510 :
511 : /* Look for the pointer in the client list.
512 : * There shall be only one event handler with the same fd.
513 : */
514 2 : DltConnection *temp = dlt_event_handler_find_connection(evhdl, fd);
515 :
516 7 : if (!temp) {
517 1 : dlt_log(LOG_ERR, "Connection not found for unregistration.\n");
518 1 : return -1;
519 : }
520 :
521 6 : if ((temp->type == DLT_CONNECTION_CLIENT_MSG_TCP) ||
522 : (temp->type == DLT_CONNECTION_CLIENT_MSG_SERIAL)) {
523 3 : daemon_local->client_connections--;
524 :
525 3 : if (daemon_local->client_connections < 0) {
526 0 : daemon_local->client_connections = 0;
527 0 : dlt_log(LOG_CRIT, "Unregistering more client than registered!\n");
528 : }
529 : }
530 :
531 6 : if (dlt_connection_check_activate(evhdl,
532 : temp,
533 : DEACTIVATE) < 0)
534 0 : dlt_log(LOG_ERR, "Unable to unregister event.\n");
535 :
536 : /* Cannot fail as far as dlt_daemon_find_connection succeed */
537 6 : return dlt_daemon_remove_connection(evhdl, temp);
538 : }
|