From 5574d46a4083d783a915688e0e05593b9558497b Mon Sep 17 00:00:00 2001 From: Frederic Berat Date: Wed, 7 Oct 2015 19:35:25 +0900 Subject: Control application: Control appliction to support offline log storage trigger Features : 1. One shot trigger with path 2. Automounter based trigger 3. Udev based trigger Signed-off-by: Frederic Berat Signed-off-by: Christoph Lipka Signed-off-by: anitha.ba Signed-off-by: S. Hameed --- src/console/dlt-control-common.c | 588 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 588 insertions(+) create mode 100644 src/console/dlt-control-common.c (limited to 'src/console/dlt-control-common.c') diff --git a/src/console/dlt-control-common.c b/src/console/dlt-control-common.c new file mode 100644 index 0000000..c3d82cf --- /dev/null +++ b/src/console/dlt-control-common.c @@ -0,0 +1,588 @@ +/** + * @licence app begin@ + * Copyright (C) 2015 Advanced Driver Information Technology. + * This code is developed by Advanced Driver Information Technology. + * Copyright of Advanced Driver Information Technology, Bosch and DENSO. + * + * This file is part of GENIVI Project Dlt - Diagnostic Log and Trace console apps. + * + * + * \copyright + * This Source Code Form is subject to the terms of the + * Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with + * this file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * + * \author Christoph Lipka ADIT 2015 + * \author Frederic Berat ADIT 2015 + * + * \file dlt-control-common.c + * For further information see http://www.genivi.org/. + * @licence end@ + */ + +/******************************************************************************* +** ** +** SRC-MODULE: dlt-control-common.c ** +** ** +** TARGET : linux ** +** ** +** PROJECT : DLT ** +** ** +** AUTHOR : Christoph Lipka clipka@jp.adit-jv.com ** +** PURPOSE : ** +** ** +** REMARKS : ** +** ** +** PLATFORM DEPENDANT [yes/no]: yes ** +** ** +** TO BE CHANGED BY USER [yes/no]: no ** +** ** +*******************************************************************************/ + +/******************************************************************************* +** Author Identity ** +******************************************************************************** +** ** +** Initials Name Company ** +** -------- ------------------------- ---------------------------------- ** +** cl Christoph Lipka ADIT ** +** fb Frederic Berat ADIT ** +*******************************************************************************/ +#define pr_fmt(fmt) "Common control: "fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dlt_common.h" +#include "dlt_protocol.h" +#include "dlt_client.h" + +#include "dlt-control-common.h" + +#define DLT_CTRL_APID "DLTC" +#define DLT_CTRL_CTID "DLTC" +#define DLT_CTRL_SOCK "/tmp/dlt-ctrl.sock" + +#define DLT_RECEIVE_TEXTBUFSIZE 1024 + +/** @brief Analyze the daemon answer + * + * This function as to be provided by the user of the connection. + * + * @param answer The textual answer of the daemon + * @param payload The daemons answer payload + * @param length The daemons answer payload length + * + * @return User defined. + */ +static int (*response_analyzer_cb)(char *, void *, int); + +static pthread_t daemon_connect_thread; +static DltClient client; +static int callback_return = -1; +static pthread_mutex_t answer_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t answer_cond = PTHREAD_COND_INITIALIZER; + +static int local_verbose; +static char local_ecuid[DLT_CTRL_ECUID_LEN]; /* Name of ECU */ +static long local_timeout; + +int get_verbosity(void) +{ + return local_verbose; +} + +void set_verbosity(int v) +{ + local_verbose = !!v; +} + +char *get_ecuid(void) +{ + return local_ecuid; +} + +void set_ecuid(char *ecuid) +{ + if (local_ecuid != ecuid) + { + memset(local_ecuid, 0, DLT_CTRL_ECUID_LEN); + strncpy(local_ecuid, ecuid, DLT_CTRL_ECUID_LEN); + local_ecuid[DLT_CTRL_ECUID_LEN - 1] = '\0'; + } +} + +long get_timeout(void) +{ + return local_timeout; +} + +void set_timeout(long t) +{ + local_timeout = DLT_CTRL_TIMEOUT; + + if (t > 1) + { + local_timeout = t; + } + else + { + pr_error("Timeout to small. Set to default: %d", + DLT_CTRL_TIMEOUT); + } +} + +/** @brief Send a message to the daemon through the socket. + * + * The socket as to be opened and active before sending. + * + * @param sock The socket to send the message through + * @param msg The message to be send in DLT format. + * + * @return 0 on success, -1 otherwise. + */ +static int dlt_control_send_message_to_socket(int sock, DltMessage *msg) +{ + if (send(sock, + (const char *)(msg->headerbuffer + sizeof(DltStorageHeader)), + msg->headersize - sizeof(DltStorageHeader), 0) == -1) + { + pr_error("Sending message header failed: %s\n", strerror(errno)); + return -1; + } + + if (send(sock, (const char *) msg->databuffer, msg->datasize, 0) == -1) + { + pr_error("Sending message failed: %s\n", strerror(errno)); + return -1; + } + + return 0; +} + +/** @brief Prepare the extra headers of a DLT message + * + * Modifies the extra headers of the message so that it can be sent. + * + * @param msg The message to be prepared + * @param header The base header to be used. + * + * @return 0 on success, -1 otherwise. + */ +static int prepare_extra_headers(DltMessage *msg, uint8_t *header) +{ + int shift = 0; + + pr_verbose("Preparing extra headers.\n"); + + if (!msg || !header) + { + return -1; + } + + shift = sizeof(DltStorageHeader) + + sizeof(DltStandardHeader) + + DLT_STANDARD_HEADER_EXTRA_SIZE(msg->standardheader->htyp); + + /* Set header extra parameters */ + dlt_set_id(msg->headerextra.ecu, get_ecuid()); + + msg->headerextra.tmsp = dlt_uptime(); + + /* Copy header extra parameters to header buffer */ + if (dlt_message_set_extraparameters(msg, get_verbosity()) == -1) + { + pr_error("Cannot copy header extra parameter\n"); + return -1; + } + + /* prepare extended header */ + msg->extendedheader = (DltExtendedHeader *)(header + shift); + + msg->extendedheader->msin = DLT_MSIN_CONTROL_REQUEST; + + msg->extendedheader->noar = 1; /* one payload packet */ + + /* Dummy values have to be set */ + dlt_set_id(msg->extendedheader->apid, DLT_CTRL_APID); + dlt_set_id(msg->extendedheader->ctid, DLT_CTRL_CTID); + + return 0; +} + +/** @brief Prepare the headers of a DLT message + * + * Modifies the headers of the message so that it can be sent. + * + * @param msg The message to be prepared + * @param header The base header to be used. + * + * @return 0 on success, -1 otherwise. + */ +static int prepare_headers(DltMessage *msg, uint8_t *header) +{ + uint32_t len = 0; + + pr_verbose("Preparing headers.\n"); + + if (!msg || !header) + { + return -1; + } + + msg->storageheader = (DltStorageHeader *) header; + + if (dlt_set_storageheader(msg->storageheader, "") == -1) + { + pr_error("Storage header initialization failed.\n"); + return -1; + } + + /* prepare standard header */ + msg->standardheader = + (DltStandardHeader *) (header + sizeof(DltStorageHeader)); + + msg->standardheader->htyp = DLT_HTYP_WEID | + DLT_HTYP_WTMS | DLT_HTYP_UEH | DLT_HTYP_PROTOCOL_VERSION1; + +#if (BYTE_ORDER == BIG_ENDIAN) + msg->standardheader->htyp = (msg->standardheader->htyp | DLT_HTYP_MSBF); +#endif + + msg->standardheader->mcnt = 0; + + /* prepare length information */ + msg->headersize = sizeof(DltStorageHeader) + + sizeof(DltStandardHeader) + + sizeof(DltExtendedHeader) + + DLT_STANDARD_HEADER_EXTRA_SIZE(msg->standardheader->htyp); + + len = msg->headersize - sizeof(DltStorageHeader) + msg->datasize; + + if (len > UINT16_MAX) + { + pr_error("Message header is too long.\n"); + return -1; + } + + msg->standardheader->len = DLT_HTOBE_16(len); + + return 0; +} + +/** @brief Prepare a DLT message. + * + * The DLT message is built using the data given by the user. + * The data is basically composed of a buffer and a size. + * + * @param data The message body to be used to build the DLT message. + * + * @return 0 on success, -1 otherwise. + */ +static DltMessage *dlt_control_prepare_message(DltControlMsgBody *data) +{ + DltMessage *msg = NULL; + + pr_verbose("Preparing message.\n"); + + if (data == NULL) + { + pr_error("Data for message body is NULL\n"); + return NULL; + } + + msg = calloc(1, sizeof(DltMessage)); + + if (msg == NULL) + { + pr_error("Cannot allocate memory for Dlt Message\n"); + return NULL; + } + + if (dlt_message_init(msg, get_verbosity()) == -1) + { + pr_error("Cannot initialize Dlt Message\n"); + free(msg); + return NULL; + } + + /* prepare payload of data */ + msg->databuffersize = msg->datasize = data->size; + + /* Allocate memory for Dlt Message's buffer */ + msg->databuffer = (uint8_t *)calloc(1, data->size); + + if (msg->databuffer == NULL) + { + pr_error("Cannot allocate memory for data buffer\n"); + free(msg); + return NULL; + } + + /* copy data into message */ + memcpy(msg->databuffer, data->data, data->size); + + /* prepare storage header */ + if (prepare_headers(msg, msg->headerbuffer)) + { + dlt_message_free(msg, get_verbosity()); + free(msg); + return NULL; + } + + /* prepare extra headers */ + if (prepare_extra_headers(msg, msg->headerbuffer)) + { + dlt_message_free(msg, get_verbosity()); + free(msg); + return NULL; + } + + return msg; +} + +/** @brief Initialize the connection with the daemon + * + * The connection is initialized using an internal callback. The user's + * response analyzer will be finally executed by this callback. + * The client pointer is used to established the connection. + * + * @param client A pointer to a valid client structure + * @param cb The internal callback to be executed while receiving a new message + * + * @return 0 on success, -1 otherwise. + */ +static int dlt_control_init_connection(DltClient *client, void *cb) +{ + int (*callback)(DltMessage *message, void *data) = cb; + + if (!cb || !client) + { + pr_error("%s Invalid parameters (%p, %p)\n", __func__, client, cb); + return -1; + } + + pr_verbose("Initializing the connection.\n"); + if (dlt_client_init(client, get_verbosity()) != 0) + { + pr_error("Failed to register callback (NULL)\n"); + return -1; + } + + dlt_client_register_message_callback(callback); + + client->socketPath = DLT_CTRL_SOCK; + client->mode = DLT_CLIENT_MODE_UNIX; + + return dlt_client_connect(client, get_verbosity()); +} + +/** @brief Daemon listener function + * + * This function will continuously read on the DLT socket, until an error occurs + * or the thread executing this function is canceled. + * + * @param data Thread parameter + * + * @return The thread parameter given as argument. + */ +static void *dlt_control_listen_to_daemon(void *data) +{ + pr_verbose("Ready to receive DLT answers.\n"); + dlt_client_main_loop(&client, NULL, get_verbosity()); + return data; +} + +/** @brief Internal callback for DLT response + * + * This function is called by the dlt_client_main_loop once a response is read + * from the DLT socket. + * After some basic checks, the user's response analyzer is called. The return + * value of the analyzer is then provided back to the dlt_control_send_message + * function to be given back as a return value. + * As this function is called in a dedicated thread, the return value is + * provided using a global variable. + * Access to this variable is controlled through a dedicated mutex. + * New values are signaled using a dedicated condition variable. + * + * @param message The DLT answer + * @data Unused + * + * @return The analyzer return value or -1 on early errors. + */ +static int dlt_control_callback(DltMessage *message, void *data) +{ + char text[DLT_RECEIVE_TEXTBUFSIZE] = { 0 }; + (void) data; + + if (message == NULL) + { + pr_error("Received message is null\n"); + return -1; + } + + /* prepare storage header */ + if (DLT_IS_HTYP_WEID(message->standardheader->htyp)) + { + dlt_set_storageheader(message->storageheader, message->headerextra.ecu); + } + else + { + dlt_set_storageheader(message->storageheader, "LCTL"); + } + + dlt_message_header(message, text, DLT_RECEIVE_TEXTBUFSIZE, get_verbosity()); + + /* Extracting payload */ + dlt_message_payload(message, text, + DLT_RECEIVE_TEXTBUFSIZE, + DLT_OUTPUT_ASCII, + get_verbosity()); + + /* + * Checking payload with the provided callback and return the result + */ + pthread_mutex_lock(&answer_lock); + callback_return = response_analyzer_cb(text, + message->databuffer, + message->datasize); + pthread_cond_signal(&answer_cond); + pthread_mutex_unlock(&answer_lock); + + return callback_return; +} + +/** @brief Send a message to the daemon and wait for the asynchronous answer. + * + * The answer is received and analyzed by a dedicated thread. Thus we need + * to wait for the signal from this thread and then read the return value + * to be provided to the user. + * In case of timeout, this function fails. + * The message provided by the user is formated in DLT format before sending. + * + * @param body The message provided by the user + * @param timeout The time to wait before considering that no answer will come + * + * @return The user response analyzer return value, -1 in case of early error. + */ +int dlt_control_send_message(DltControlMsgBody *body, int timeout) +{ + struct timespec t; + DltMessage *msg = NULL; + + if (!body) + { + pr_error("Invalid input (%p).\n", body); + return -1; + } + + if (clock_gettime(CLOCK_REALTIME, &t) == -1) + { + pr_error("Cannot read system time.\n"); + return -1; + } + + t.tv_sec += timeout; + + /* send command to daemon here */ + msg = dlt_control_prepare_message(body); + + if (msg == NULL) + { + pr_error("Control message preparation failed\n"); + return -1; + } + + pthread_mutex_lock(&answer_lock); + + /* Re-init the return value */ + callback_return = -1; + + if (dlt_control_send_message_to_socket(client.sock, msg) != 0) + { + pr_error("Sending message to daemon failed\n"); + free(msg); + return -1; + } + + /* If we timeout the lock is not taken back */ + if (!pthread_cond_timedwait(&answer_cond, &answer_lock, &t)) + { + pthread_mutex_unlock(&answer_lock); + } + + /* Destroying the message */ + dlt_message_free(msg, get_verbosity()); + free(msg); + + /* At this point either the value is already correct, either it's still -1. + * Then, we don't care to lock the access. + */ + return callback_return; +} + +/** @brief Control communication initialization + * + * This will prepare the DLT connection and the thread dedicated to the + * response listening. + * + * @param response_analyzer User defined function used to analyze the response + * @param ecuid The ECUID to provide to the daemon + * @param verbosity The verbosity level + * + * @return 0 on success, -1 otherwise. + */ +int dlt_control_init(int (*response_analyzer)(char *, void *, int), + char *ecuid, + int verbosity) +{ + if (!response_analyzer || !ecuid) + { + pr_error("Invalid input (%p %p).\n", response_analyzer, ecuid); + return -1; + } + + response_analyzer_cb = response_analyzer; + set_ecuid(ecuid); + set_verbosity(verbosity); + + if (dlt_control_init_connection(&client, dlt_control_callback) != 0) + { + pr_error("Connection initialization failed\n"); + dlt_client_cleanup(&client, get_verbosity()); + return -1; + } + + /* Contact DLT daemon */ + if (pthread_create(&daemon_connect_thread, + NULL, + dlt_control_listen_to_daemon, + NULL) != 0) + { + pr_error("Cannot create thread to communicate with DLT daemon.\n"); + return -1; + } + + return 0; +} + +/** @brief Control communication clean-up + * + * Cancels the listener thread and clean=up the dlt client structure. + * + * @return 0 on success, -1 otherwise. + */ +int dlt_control_deinit(void) +{ + /* Stopping the listener thread */ + pthread_cancel(daemon_connect_thread); + /* Closing the socket */ + return dlt_client_cleanup(&client, get_verbosity()); +} -- cgit v1.2.1