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