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: 83.3 % 150 125
Test Date: 2025-03-25 20:53:42 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          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              : }
        

Generated by: LCOV version 2.0-1