summaryrefslogtreecommitdiff
path: root/src/daemon/dlt_daemon_event_handler.c
diff options
context:
space:
mode:
authorFrederic Berat <fberat@de.adit-jv.com>2015-07-14 16:36:14 +0200
committerLutz Helwing <lutz_helwing@mentor.com>2015-11-11 15:24:22 +0100
commitd29b6be9496db80e37a452bd42dc7813f369c33e (patch)
tree163ae1bf2e9488e2cff585cfa8cbb953630bfaf0 /src/daemon/dlt_daemon_event_handler.c
parent9e101ff434230a95fb8f4fd33dc48f4970496d1c (diff)
downloadDLT-daemon-d29b6be9496db80e37a452bd42dc7813f369c33e.tar.gz
dlt-daemon: Implement epoll based event handling
The event handling has been reworked in order to use epoll and restructure the code. There are 2 new structures. The DltConnection which contains all basic connection information, like the type, the file descriptor, and the receiver structure corresponding. The DltEventHandler that manages the DltConnections and the associated events. The concept is basically the following. The daemon will create different connections, serial connections, socket connections, fifos etc ... Each of them will then register itself to the event handler, and give it the ownership of this connection. From this point in time, the daemon can act on the connections. Once an event is triggered, the event handler will call the connection specific callback, creates new connections when clients arrives, and potentially destroy the connection in case of hangup. On exit, the daemon cleanup the event handler, which leads to the destruction of the connections. The work there is a first step for a global restructuring. Several modification will follow, in order to rationalize the different daemon structures, and avoid variable and code duplication. Signed-off-by: Frederic Berat <fberat@de.adit-jv.com>
Diffstat (limited to 'src/daemon/dlt_daemon_event_handler.c')
-rw-r--r--src/daemon/dlt_daemon_event_handler.c373
1 files changed, 373 insertions, 0 deletions
diff --git a/src/daemon/dlt_daemon_event_handler.c b/src/daemon/dlt_daemon_event_handler.c
new file mode 100644
index 0000000..3762ca2
--- /dev/null
+++ b/src/daemon/dlt_daemon_event_handler.c
@@ -0,0 +1,373 @@
+/*
+ * @licence app begin@
+ * SPDX license identifier: MPL-2.0
+ *
+ * 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.
+ *
+ * This Source Code Form is subject to the terms of the
+ * Mozilla Public License (MPL), 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/.
+ *
+ * For further information see http://www.genivi.org/.
+ * @licence end@
+ */
+
+/*!
+ * \author
+ * Frederic Berat <fberat@de.adit-jv.com>
+ *
+ * \copyright Copyright © 2015 Advanced Driver Information Technology. \n
+ * License MPL-2.0: Mozilla Public License version 2.0 http://mozilla.org/MPL/2.0/.
+ *
+ * \file dlt_daemon_event_handler.c
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/epoll.h>
+#include <sys/syslog.h>
+
+#include "dlt_common.h"
+
+#include "dlt-daemon.h"
+#include "dlt-daemon_cfg.h"
+#include "dlt_daemon_common.h"
+#include "dlt_daemon_connection.h"
+#include "dlt_daemon_connection_types.h"
+#include "dlt_daemon_event_handler.h"
+#include "dlt_daemon_event_handler_types.h"
+
+/**
+ * \def DLT_EPOLL_TIMEOUT_MSEC
+ * The maximum amount of time to wait for an epoll event.
+ * Set to 1 second to avoid unnecessary wake ups.
+ */
+#define DLT_EPOLL_TIMEOUT_MSEC 1000
+
+/** @brief Prepare the event handler
+ *
+ * This will create the epoll file descriptor.
+ *
+ * @param ev The event handler to prepare.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+int dlt_daemon_prepare_event_handling(DltEventHandler *ev)
+{
+ ev->epfd = epoll_create(DLT_EPOLL_MAX_EVENTS);
+
+ if (ev->epfd < 0)
+ {
+ dlt_log(LOG_CRIT, "Creation of epoll instance failed!\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/** @brief Catch and process incoming events.
+ *
+ * This function waits for events on all connections. Once an event raise,
+ * the callback for the specific connection is called, or the connection is
+ * destroyed if a hangup occurs.
+ *
+ * @param daemon Structure to be passed to the callback.
+ * @param daemon_local Structure containing needed information.
+ * @param pEvent Event handler structure.
+ *
+ * @return 0 on success, -1 otherwise. May be interrupted.
+ */
+int dlt_daemon_handle_event(DltEventHandler *pEvent,
+ DltDaemon *daemon,
+ DltDaemonLocal *daemon_local)
+{
+ int nfds = 0;
+ int i = 0;
+ char str[DLT_DAEMON_TEXTBUFSIZE];
+ int (*callback)(DltDaemon *, DltDaemonLocal *, int) = NULL;
+
+ /*CM Change begin*/
+ nfds = epoll_wait(pEvent->epfd,
+ pEvent->events,
+ DLT_EPOLL_MAX_EVENTS,
+ DLT_EPOLL_TIMEOUT_MSEC);
+
+ if (nfds < 0)
+ {
+ /* We are not interested in EINTR has it comes
+ * either from timeout or signal.
+ */
+ if (errno != EINTR)
+ {
+ snprintf(str,
+ DLT_DAEMON_TEXTBUFSIZE,
+ "epoll_wait() failed: %s\n",
+ strerror(errno));
+ dlt_log(LOG_CRIT, str);
+ return -1;
+ }
+
+ return 0;
+ }
+
+ for (i = 0 ; i < nfds ; i++)
+ {
+ struct epoll_event *ev = &pEvent->events[i];
+ int fd = 0;
+ DltConnectionType type = DLT_CONNECTION_TYPE_MAX;
+
+ if ((DltConnection *)ev->data.ptr)
+ {
+ type = ((DltConnection *)ev->data.ptr)->type;
+ fd = ((DltConnection *)ev->data.ptr)->fd;
+ }
+
+ /* First of all handle epoll error events
+ * We only expect EPOLLIN or EPOLLOUT
+ */
+ if ((ev->events != EPOLLIN) && (ev->events != EPOLLOUT))
+ {
+ /* epoll reports an error, we need to clean-up the concerned event
+ */
+ dlt_event_handler_unregister_connection(pEvent,
+ daemon_local,
+ fd,
+ type);
+ continue;
+ }
+
+ /* Get the function to be used to handle the event */
+ callback = dlt_connection_get_callback((DltConnection *)ev->data.ptr);
+
+ if (!callback)
+ {
+ snprintf(str,
+ DLT_DAEMON_TEXTBUFSIZE,
+ "Unable to find function for %d handle type.\n",
+ type);
+ dlt_log(LOG_CRIT, str);
+ return -1;
+ }
+
+ /* TODO: Review the design to clean-up this line.
+ * fd are currently wrongly duplicated which may lead to errors. */
+ ((DltConnection *)ev->data.ptr)->receiver->fd = fd;
+
+ /* From now on, callback is correct */
+ if (callback(daemon, daemon_local, daemon_local->flags.vflag) == -1)
+ {
+ snprintf(str,
+ DLT_DAEMON_TEXTBUFSIZE,
+ "Processing from %d handle type failed!\n",
+ type );
+ dlt_log(LOG_CRIT, str);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/** @brief Find connection with a specific \a fd in the connection list.
+ *
+ * There can be only one event per \a fd. We can then find a specific connection
+ * based on this \a fd. That allows to check if a specific \a fd has already been
+ * registered.
+ *
+ * @param ev The event handler structure where the list of connection is.
+ * @param fd The file descriptor of the connection to be found.
+ *
+ * @return The found connection pointer, NULL otherwise.
+ */
+DltConnection *dlt_event_handler_find_connection(DltEventHandler *ev,
+ int fd)
+{
+ DltConnection *temp = ev->connections;
+
+ while ((temp != NULL) && (temp->fd != fd))
+ {
+ temp = temp->next;
+ }
+
+ return temp;
+}
+
+/** @brief Remove a connection from the list and destroy it.
+ *
+ * This function will first look for the connection in the event handler list,
+ * remove it from the list and then destroy it.
+ *
+ * @param ev The event handler structure where the list of connection is.
+ * @param to_remove The connection to remove from the list.
+ *
+ * @return 0 on success, -1 if the connection is not found.
+ */
+static int dlt_daemon_remove_connection(DltEventHandler *ev,
+ DltConnection *to_remove)
+{
+ DltConnection **curr = &ev->connections;
+
+ /* Find the address where to_remove value is registered */
+ while (*curr && (*curr != to_remove))
+ {
+ curr = &(*curr)->next;
+ }
+
+ if (!*curr)
+ {
+ /* Must not be possible as we check for existence before */
+ dlt_log(LOG_CRIT, "Connection not found for removal.\n");
+ return -1;
+ }
+
+ /* Replace the content of the address by the next value */
+ *curr = (*curr)->next;
+
+ /* Now we can destroy our pointer */
+ dlt_connection_destroy(to_remove);
+
+ return 0;
+}
+
+/** @brief Destroy the connection list.
+ *
+ * This function runs through the connection list and destroy them one by one.
+ *
+ * @param ev Pointer to the event handler structure.
+ */
+void dlt_event_handler_cleanup_connections(DltEventHandler *ev)
+{
+ if (ev == NULL)
+ {
+ /* Nothing to do. */
+ return;
+ }
+
+ while (ev->connections != NULL)
+ {
+ /* We don really care on failure */
+ (void)dlt_daemon_remove_connection(ev, ev->connections);
+ }
+}
+
+/** @brief Add a new connection to the list.
+ *
+ * The connection is added at the tail of the list.
+ *
+ * @param ev The event handler structure where the connection list is.
+ * @param connection The connection to be added.
+ */
+static void dlt_daemon_add_connection(DltEventHandler *ev,
+ DltConnection *connection)
+{
+ DltConnection **temp = &ev->connections;
+
+ while (*temp != NULL)
+ {
+ temp = &(*temp)->next;
+ }
+
+ *temp = connection;
+}
+
+/** @brief Registers a connection for event handling and takes its ownership.
+ *
+ * As we add the connection to the list of connection, we take its ownership.
+ * That's the only place where the connection pointer is stored.
+ * The connection is then used to create a new event trigger.
+ * If the connection is of type DLT_CONNECTION_CLIENT_MSG_TCP, we increase
+ * the daemon_local->client_connections counter. TODO: Move this counter inside
+ * the event handler structure.
+ *
+ * @param evhdl The event handler structure where the connection list is.
+ * @param daemon_local Structure containing needed information.
+ * @param connection The connection to be registered.
+ * @param mask The bit mask of event to be registered.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+int dlt_event_handler_register_connection(DltEventHandler *evhdl,
+ DltDaemonLocal *daemon_local,
+ DltConnection *connection,
+ int mask)
+{
+ struct epoll_event ev; /* Content will be copied by the kernel */
+
+ dlt_daemon_add_connection(evhdl, connection);
+
+ ev.events = mask;
+ ev.data.ptr = (void *)connection;
+
+ if (epoll_ctl(evhdl->epfd, EPOLL_CTL_ADD, connection->fd, &ev) == -1)
+ {
+ dlt_log(LOG_ERR, "epoll_ctl() failed!\n");
+ dlt_daemon_remove_connection(evhdl, connection);
+ return -1;
+ }
+
+ if (connection->type == DLT_CONNECTION_CLIENT_MSG_TCP)
+ {
+ daemon_local->client_connections++;
+ }
+
+ return 0;
+}
+
+/** @brief Unregisters a connection from the event handler and destroys it.
+ *
+ * We first look for the connection to be unregistered, delete the event
+ * corresponding and then destroy the connection.
+ * If the connection is of type DLT_CONNECTION_CLIENT_MSG_TCP, we decrease
+ * the daemon_local->client_connections counter. TODO: Move this counter inside
+ * the event handler structure.
+ *
+ * @param evhdl The event handler structure where the connection list is.
+ * @param daemon_local Structure containing needed information.
+ * @param fd The file descriptor of the connection to be unregistered.
+ * @param type the connection type.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+int dlt_event_handler_unregister_connection(DltEventHandler *evhdl,
+ DltDaemonLocal *daemon_local,
+ int fd,
+ DltConnectionType type)
+{
+ /* Look for the pointer in the client list.
+ * There shall be only one event handler with the same fd.
+ */
+ DltConnection *temp = dlt_event_handler_find_connection(evhdl, fd);
+
+ if (!temp)
+ {
+ dlt_log(LOG_ERR, "Connection not found for unregistration.\n");
+ return -1;
+ }
+
+ if (epoll_ctl(evhdl->epfd, EPOLL_CTL_DEL, fd, NULL) < 0)
+ {
+ dlt_log(LOG_ERR, "Unable to unregister event.\n");
+ return -1;
+ }
+
+ if (type == DLT_CONNECTION_CLIENT_MSG_TCP)
+ {
+ daemon_local->client_connections--;
+
+ if (daemon_local->client_connections < 0)
+ {
+ daemon_local->client_connections = 0;
+ dlt_log(LOG_CRIT, "Unregistering more client than registered!\n");
+ }
+ }
+
+ /* Cannot fail as far as dlt_daemon_find_connection succeed */
+ return dlt_daemon_remove_connection(evhdl, temp);
+}