LCOV - code coverage report
Current view: top level - daemon - dlt_daemon_event_handler.c (source / functions) Coverage Total Hit
Test: dlt_final_coverage.info Lines: 80.4 % 148 119
Test Date: 2026-08-27 10:11:10 Functions: 100.0 % 11 11

            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              : }
        

Generated by: LCOV version 2.0-1