LCOV - code coverage report
Current view: top level - daemon - dlt_daemon_connection.c (source / functions) Hit Total Coverage
Test: dlt_final_coverage.info Lines: 102 115 88.7 %
Date: 2024-10-22 04:07:23 Functions: 8 8 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_connection.c
      26             :  */
      27             : 
      28             : #include <errno.h>
      29             : #include <stdio.h>
      30             : #include <stdlib.h>
      31             : #include <string.h>
      32             : #include <unistd.h>
      33             : 
      34             : #include <sys/socket.h>
      35             : #include <syslog.h>
      36             : #include <sys/stat.h>
      37             : #include <sys/types.h>
      38             : 
      39             : #include "dlt_daemon_connection_types.h"
      40             : #include "dlt_daemon_connection.h"
      41             : #include "dlt_daemon_event_handler_types.h"
      42             : #include "dlt_daemon_event_handler.h"
      43             : #include "dlt-daemon.h"
      44             : #include "dlt-daemon_cfg.h"
      45             : #include "dlt_daemon_common.h"
      46             : #include "dlt_common.h"
      47             : #include "dlt_log.h"
      48             : #include "dlt_gateway.h"
      49             : #include "dlt_daemon_socket.h"
      50             : 
      51             : static DltConnectionId connectionId;
      52             : extern char *app_recv_buffer;
      53             : 
      54             : /** @brief Generic sending function.
      55             :  *
      56             :  * We manage different type of connection which have similar send/write
      57             :  * functions. We can then abstract the data transfer using this function,
      58             :  * moreover as we often transfer data to different kind of connection
      59             :  * within the same loop.
      60             :  *
      61             :  * @param conn The connection structure.
      62             :  * @param msg The message buffer to be sent
      63             :  * @param msg_size The length of the message to be sent
      64             :  *
      65             :  * @return DLT_DAEMON_ERROR_OK on success, DLT_DAEMON_ERROR_SEND_FAILED
      66             :  *         on send failure, DLT_DAEMON_ERROR_UNKNOWN otherwise.
      67             :  *         errno is appropriately set.
      68             :  */
      69         436 : DLT_STATIC int dlt_connection_send(DltConnection *conn,
      70             :                                    void *msg,
      71             :                                    size_t msg_size)
      72             : {
      73             :     DltConnectionType type = DLT_CONNECTION_TYPE_MAX;
      74             :     int ret = 0;
      75             : 
      76         436 :     if ((conn != NULL) && (conn->receiver != NULL))
      77         436 :         type = conn->type;
      78             : 
      79         436 :     switch (type) {
      80           1 :     case DLT_CONNECTION_CLIENT_MSG_SERIAL:
      81             : 
      82           1 :         if (write(conn->receiver->fd, msg, msg_size) > 0)
      83           1 :             return DLT_DAEMON_ERROR_OK;
      84             : 
      85             :         return DLT_DAEMON_ERROR_UNKNOWN;
      86             : 
      87         434 :     case DLT_CONNECTION_CLIENT_MSG_TCP:
      88         434 :         ret = dlt_daemon_socket_sendreliable(conn->receiver->fd,
      89             :                                              msg,
      90             :                                              msg_size);
      91         434 :         return ret;
      92             :     default:
      93             :         return DLT_DAEMON_ERROR_UNKNOWN;
      94             :     }
      95             : }
      96             : 
      97             : /** @brief Send up to two messages through a connection.
      98             :  *
      99             :  * We often need to send 2 messages through a specific connection, plus
     100             :  * the serial header. This function groups these different calls.
     101             :  *
     102             :  * @param con The connection to send the messages through.
     103             :  * @param data1 The first message to be sent.
     104             :  * @param size1 The size of the first message.
     105             :  * @param data2 The second message to be send.
     106             :  * @param size2 The second message size.
     107             :  * @param sendserialheader Whether we need or not to send the serial header.
     108             :  *
     109             :  * @return DLT_DAEMON_ERROR_OK on success, -1 otherwise. errno is properly set.
     110             :  */
     111         219 : int dlt_connection_send_multiple(DltConnection *con,
     112             :                                  void *data1,
     113             :                                  int size1,
     114             :                                  void *data2,
     115             :                                  int size2,
     116             :                                  int sendserialheader)
     117             : {
     118             :     int ret = 0;
     119             : 
     120         219 :     if (con == NULL)
     121             :         return DLT_DAEMON_ERROR_UNKNOWN;
     122             : 
     123         218 :     if (sendserialheader)
     124           1 :         ret = dlt_connection_send(con,
     125             :                                   (void *)dltSerialHeader,
     126             :                                   sizeof(dltSerialHeader));
     127             : 
     128         218 :     if ((data1 != NULL) && (ret == DLT_RETURN_OK))
     129         218 :         ret = dlt_connection_send(con, data1, size1);
     130             : 
     131         218 :     if ((data2 != NULL) && (ret == DLT_RETURN_OK))
     132         214 :         ret = dlt_connection_send(con, data2, size2);
     133             : 
     134             :     return ret;
     135             : }
     136             : 
     137             : /** @brief Get the next connection filtered with a type mask.
     138             :  *
     139             :  * In some cases we need the next connection available of a specific type or
     140             :  * specific different types. This function returns the next available connection
     141             :  * that is of one of the types included in the mask. The current connection can
     142             :  * be returned.
     143             :  *
     144             :  * @param current The current connection pointer.
     145             :  * @param type_mask A bit mask representing the connection types to be filtered.
     146             :  *
     147             :  * @return The next available connection of the considered types or NULL.
     148             :  */
     149           2 : DltConnection *dlt_connection_get_next(DltConnection *current, int type_mask)
     150             : {
     151           3 :     while (current && !((1 << current->type) & type_mask))
     152           1 :         current = current->next;
     153             : 
     154           2 :     return current;
     155             : }
     156             : 
     157          49 : DLT_STATIC void dlt_connection_destroy_receiver(DltConnection *con)
     158             : {
     159          49 :     if (!con)
     160             :         return;
     161             : 
     162          49 :     switch (con->type) {
     163             :     case DLT_CONNECTION_GATEWAY:
     164             :         /* We rely on the gateway for clean-up */
     165             :         break;
     166           9 :     case DLT_CONNECTION_APP_MSG:
     167           9 :         dlt_receiver_free_global_buffer(con->receiver);
     168           9 :         free(con->receiver);
     169           9 :         con->receiver = NULL;
     170           9 :         break;
     171          36 :     default:
     172          36 :         (void)dlt_receiver_free(con->receiver);
     173          36 :         free(con->receiver);
     174          36 :         con->receiver = NULL;
     175          36 :         break;
     176             :     }
     177             : }
     178             : 
     179             : /** @brief Get the receiver structure associated to a connection.
     180             :  *
     181             :  * The receiver structure is sometimes needed while handling the event.
     182             :  * This behavior is mainly due to the fact that it's not intended to modify
     183             :  * the whole design of the daemon while implementing the new event handling.
     184             :  * Based on the connection type provided, this function returns the pointer
     185             :  * to the DltReceiver structure corresponding.
     186             :  *
     187             :  * @param daemon_local Structure where to take the DltReceiver pointer from.
     188             :  * @param type Type of the connection.
     189             :  * @param fd File descriptor
     190             :  *
     191             :  * @return DltReceiver structure or NULL if none corresponds to the type.
     192             :  */
     193          48 : DLT_STATIC DltReceiver *dlt_connection_get_receiver(DltDaemonLocal *daemon_local,
     194             :                                                     DltConnectionType type,
     195             :                                                     int fd)
     196             : {
     197             :     DltReceiver *ret = NULL;
     198             :     DltReceiverType receiver_type = DLT_RECEIVE_FD;
     199             :     struct stat statbuf;
     200             : 
     201          48 :     switch (type) {
     202          25 :     case DLT_CONNECTION_CONTROL_CONNECT:
     203             :     /* FALL THROUGH */
     204             :     case DLT_CONNECTION_CONTROL_MSG:
     205             :     /* FALL THROUGH */
     206             :     case DLT_CONNECTION_CLIENT_CONNECT:
     207             :     /* FALL THROUGH */
     208             :     case DLT_CONNECTION_CLIENT_MSG_TCP:
     209          25 :         ret = calloc(1, sizeof(DltReceiver));
     210             : 
     211          25 :         if (ret)
     212          25 :             dlt_receiver_init(ret, fd, DLT_RECEIVE_SOCKET, DLT_DAEMON_RCVBUFSIZESOCK);
     213             : 
     214             :         break;
     215           1 :     case DLT_CONNECTION_CLIENT_MSG_SERIAL:
     216           1 :         ret = calloc(1, sizeof(DltReceiver));
     217             : 
     218           1 :         if (ret)
     219           1 :             dlt_receiver_init(ret, fd, DLT_RECEIVE_FD, DLT_DAEMON_RCVBUFSIZESERIAL);
     220             : 
     221             :         break;
     222           9 :     case DLT_CONNECTION_APP_MSG:
     223           9 :         ret = calloc(1, sizeof(DltReceiver));
     224             : 
     225             :         receiver_type = DLT_RECEIVE_FD;
     226             : 
     227           9 :         if (fstat(fd, &statbuf) == 0) {
     228           9 :             if (S_ISSOCK(statbuf.st_mode))
     229             :                 receiver_type = DLT_RECEIVE_SOCKET;
     230             :         } else {
     231           0 :             dlt_vlog(LOG_WARNING,
     232             :                      "Failed to determine receive type for DLT_CONNECTION_APP_MSG, using \"FD\"\n");
     233             :         }
     234             : 
     235           9 :         if (ret)
     236           9 :             dlt_receiver_init_global_buffer(ret, fd, receiver_type, &app_recv_buffer);
     237             : 
     238             :         break;
     239             : #if defined DLT_DAEMON_USE_UNIX_SOCKET_IPC || defined DLT_DAEMON_VSOCK_IPC_ENABLE
     240             :     case DLT_CONNECTION_APP_CONNECT:
     241             :     /* FALL THROUGH */
     242             : #endif
     243          10 :     case DLT_CONNECTION_ONE_S_TIMER:
     244             :     /* FALL THROUGH */
     245             :     case DLT_CONNECTION_SIXTY_S_TIMER:
     246             : #ifdef DLT_SYSTEMD_WATCHDOG_ENABLE
     247             :     /* FALL THROUGH */
     248             :     case DLT_CONNECTION_SYSTEMD_TIMER:
     249             : #endif
     250             :     /* FALL THROUGH */
     251             :     case DLT_CONNECTION_GATEWAY_TIMER:
     252          10 :         ret = calloc(1, sizeof(DltReceiver));
     253             : 
     254          10 :         if (ret)
     255          10 :             dlt_receiver_init(ret, fd, DLT_RECEIVE_FD, DLT_DAEMON_RCVBUFSIZE);
     256             : 
     257             :         break;
     258           3 :     case DLT_CONNECTION_GATEWAY:
     259             :         /* We rely on the gateway for init */
     260           3 :         ret = dlt_gateway_get_connection_receiver(&daemon_local->pGateway, fd);
     261           3 :         break;
     262             :     default:
     263             :         ret = NULL;
     264             :     }
     265             : 
     266          48 :     return ret;
     267             : }
     268             : 
     269             : /** @brief Get the callback from a specific connection.
     270             :  *
     271             :  * The callback retrieved that way is used to handle event for this connection.
     272             :  * It as been chosen to proceed that way instead of having the callback directly
     273             :  * in the structure in order to have some way to check that the structure is
     274             :  * still valid, or at least gracefully handle errors instead of crashing.
     275             :  *
     276             :  * @param con The connection to retrieve the callback from.
     277             :  *
     278             :  * @return Function pointer or NULL.
     279             :  */
     280        1338 : void *dlt_connection_get_callback(DltConnection *con)
     281             : {
     282             :     void *ret = NULL;
     283             :     DltConnectionType type = DLT_CONNECTION_TYPE_MAX;
     284             : 
     285        1338 :     if (con)
     286        1338 :         type = con->type;
     287             : 
     288        1338 :     switch (type) {
     289             :     case DLT_CONNECTION_CLIENT_CONNECT:
     290             :         ret = dlt_daemon_process_client_connect;
     291             :         break;
     292           7 :     case DLT_CONNECTION_CLIENT_MSG_TCP:
     293             :         ret = dlt_daemon_process_client_messages;
     294           7 :         break;
     295           0 :     case DLT_CONNECTION_CLIENT_MSG_SERIAL:
     296             :         ret = dlt_daemon_process_client_messages_serial;
     297           0 :         break;
     298             : #if defined DLT_DAEMON_USE_UNIX_SOCKET_IPC || defined DLT_DAEMON_VSOCK_IPC_ENABLE
     299             :     case DLT_CONNECTION_APP_CONNECT:
     300             :         ret = dlt_daemon_process_app_connect;
     301             :         break;
     302             : #endif
     303        1306 :     case DLT_CONNECTION_APP_MSG:
     304             :         ret = dlt_daemon_process_user_messages;
     305        1306 :         break;
     306          11 :     case DLT_CONNECTION_ONE_S_TIMER:
     307             :         ret = dlt_daemon_process_one_s_timer;
     308          11 :         break;
     309           0 :     case DLT_CONNECTION_SIXTY_S_TIMER:
     310             :         ret = dlt_daemon_process_sixty_s_timer;
     311           0 :         break;
     312             : #ifdef DLT_SYSTEMD_WATCHDOG_ENABLE
     313             :     case DLT_CONNECTION_SYSTEMD_TIMER:
     314             :         ret = dlt_daemon_process_systemd_timer;
     315             :         break;
     316             : #endif
     317           2 :     case DLT_CONNECTION_CONTROL_CONNECT:
     318             :         ret = dlt_daemon_process_control_connect;
     319           2 :         break;
     320           4 :     case DLT_CONNECTION_CONTROL_MSG:
     321             :         ret = dlt_daemon_process_control_messages;
     322           4 :         break;
     323           4 :     case DLT_CONNECTION_GATEWAY:
     324             :         ret = dlt_gateway_process_passive_node_messages;
     325           4 :         break;
     326           0 :     case DLT_CONNECTION_GATEWAY_TIMER:
     327             :         ret = dlt_gateway_process_gateway_timer;
     328           0 :         break;
     329           0 :     default:
     330             :         ret = NULL;
     331             :     }
     332             : 
     333        1338 :     return ret;
     334             : }
     335             : 
     336             : /** @brief Destroys a connection.
     337             :  *
     338             :  * This function closes and frees the corresponding connection. This is expected
     339             :  * to be called by the connection owner: the DltEventHandler.
     340             :  * Ownership of the connection is given during the registration to
     341             :  * the DltEventHandler.
     342             :  *
     343             :  * @param to_destroy Connection to be destroyed.
     344             :  */
     345          48 : void dlt_connection_destroy(DltConnection *to_destroy)
     346             : {
     347          48 :     to_destroy->id = 0;
     348          48 :     close(to_destroy->receiver->fd);
     349          48 :     dlt_connection_destroy_receiver(to_destroy);
     350          48 :     free(to_destroy);
     351          48 : }
     352             : 
     353             : /** @brief Creates a connection and registers it to the DltEventHandler.
     354             :  *
     355             :  * The function will allocate memory for the connection, and give the pointer
     356             :  * to the DltEventHandler in order to register it for incoming events.
     357             :  * The connection is then destroyed later on, once it's not needed anymore or
     358             :  * it the event handler is destroyed.
     359             :  *
     360             :  * @param daemon_local Structure were some needed information is.
     361             :  * @param evh DltEventHandler to register the connection to.
     362             :  * @param fd File descriptor of the connection.
     363             :  * @param mask Event list bit mask.
     364             :  * @param type Connection type.
     365             :  *
     366             :  * @return 0 On success, -1 otherwise.
     367             :  */
     368          47 : int dlt_connection_create(DltDaemonLocal *daemon_local,
     369             :                           DltEventHandler *evh,
     370             :                           int fd,
     371             :                           int mask,
     372             :                           DltConnectionType type)
     373             : {
     374             :     DltConnection *temp = NULL;
     375             : 
     376          47 :     if (fd < 0)
     377             :         /* Nothing to do */
     378             :         return 0;
     379             : 
     380          47 :     if (dlt_event_handler_find_connection(evh, fd) != NULL)
     381             :         /* No need for the same client to be registered twice
     382             :          * for the same event.
     383             :          * TODO: If another mask can be expected,
     384             :          * we need it to update the poll event here.
     385             :          */
     386             :         return 0;
     387             : 
     388          47 :     temp = (DltConnection *)malloc(sizeof(DltConnection));
     389             : 
     390          47 :     if (temp == NULL) {
     391           0 :         dlt_log(LOG_CRIT, "Allocation of client handle failed\n");
     392           0 :         return -1;
     393             :     }
     394             : 
     395             :     memset(temp, 0, sizeof(DltConnection));
     396             : 
     397          47 :     temp->receiver = dlt_connection_get_receiver(daemon_local, type, fd);
     398             : 
     399          47 :     if (!temp->receiver) {
     400           0 :         dlt_vlog(LOG_CRIT, "Unable to get receiver from %u connection.\n",
     401             :                  type);
     402           0 :         free(temp);
     403           0 :         return -1;
     404             :     }
     405             : 
     406             :     struct timeval timeout;
     407          47 :     timeout.tv_sec = 5;
     408          47 :     timeout.tv_usec = 0;
     409             : #ifdef DLT_SYSTEMD_WATCHDOG_ENABLE
     410             :     char *watchdogUSec = getenv("WATCHDOG_USEC");
     411             :     if (watchdogUSec) {
     412             :         timeout.tv_sec = atoi(watchdogUSec) / 1000000;
     413             :         timeout.tv_usec = atoi(watchdogUSec) % 1000000;
     414             :     }
     415             : #endif
     416             : 
     417          47 :     if (setsockopt (temp->receiver->fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof timeout) < 0)  {
     418          20 :         dlt_vlog(LOG_WARNING, "Unable to set send timeout %s.\n", strerror(errno));
     419             :         // as this function is used for non socket connection as well
     420             :         // we only can return an error here if it is a socket
     421          20 :         if (errno != ENOTSOCK) {
     422           1 :             free(temp);
     423           1 :             return -1;
     424             :         }
     425             :     }
     426             : 
     427             :     /* We are single threaded no need for protection. */
     428          46 :     temp->id = connectionId++;
     429             : 
     430          46 :     if (!temp->id)
     431             :         /* Skipping 0 */
     432          10 :         temp->id = connectionId++;
     433             : 
     434          46 :     temp->type = type;
     435          46 :     temp->status = ACTIVE;
     436             : 
     437             :     /* Now give the ownership of the newly created connection
     438             :      * to the event handler, by registering for events.
     439             :      */
     440          46 :     return dlt_event_handler_register_connection(evh, daemon_local, temp, mask);
     441             : }

Generated by: LCOV version 1.14