LCOV - code coverage report
Current view: top level - daemon - dlt_daemon_event_handler.c (source / functions) Hit Total Coverage
Test: dlt_final_coverage.info Lines: 125 150 83.3 %
Date: 2024-11-20 10:17:27 Functions: 11 11 100.0 %

          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        1489 : 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        1489 :     if ((pEvent == NULL) || (daemon == NULL) || (daemon_local == NULL))
     193             :         return DLT_RETURN_ERROR;
     194             : 
     195        1488 :     ret = poll(pEvent->pfd, pEvent->nfds, DLT_EV_TIMEOUT_MSEC);
     196             : 
     197        1488 :     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        7631 :     for (i = 0; i < pEvent->nfds; i++) {
     215             :         int fd = 0;
     216             :         DltConnection *con = NULL;
     217             :         DltConnectionType type = DLT_CONNECTION_TYPE_MAX;
     218             : 
     219        6153 :         if (pEvent->pfd[i].revents == 0)
     220        4672 :             continue;
     221             : 
     222        1481 :         con = dlt_event_handler_find_connection(pEvent, pEvent->pfd[i].fd);
     223             : 
     224        1481 :         if (con && con->receiver) {
     225        1481 :             type = con->type;
     226        1481 :             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        1481 :         if (pEvent->pfd[i].revents & DLT_EV_MASK_REJECTED) {
     235             :             /* An error occurred, we need to clean-up the concerned event
     236             :              */
     237           1 :             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           1 :                 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           1 :             continue;
     249             :         }
     250             : 
     251             :         /* Get the function to be used to handle the event */
     252        1480 :         callback = dlt_connection_get_callback(con);
     253             : 
     254        1480 :         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        1480 :         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        2608 :     DltConnection *temp = ev->connections;
     292             : 
     293        4987 :     while (temp != NULL) {
     294        4939 :         if ((temp->receiver != NULL) && (temp->receiver->fd == fd))
     295        1074 :             return temp;
     296        2379 :         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             : }

Generated by: LCOV version 1.14