TcpIpcModule: added ipc module for tcp socket communication
authorTimo Lotterbach <timo.lotterbach@bmw-carit.de>
Fri, 15 Jun 2012 13:15:14 +0000 (06:15 -0700)
committerTimo Lotterbach <timo.lotterbach@bmw-carit.de>
Thu, 21 Jun 2012 07:15:22 +0000 (00:15 -0700)
- implemented in C
- depends on socket libraries
- implements low-level tcp socket communication
- unit tests prepared, but not implemented
- set environment variable LM_TCP_HOST to override IP address of LayerManagerService instance
- set environment variable LM_TCP_PORT to set TCP port for communication

LayerManagerPlugins/IpcModules/CMakeLists.txt
LayerManagerPlugins/IpcModules/TcpIpcModule/CMakeLists.txt [new file with mode: 0644]
LayerManagerPlugins/IpcModules/TcpIpcModule/include/socketConfiguration.h [new file with mode: 0644]
LayerManagerPlugins/IpcModules/TcpIpcModule/include/socketShared.h [new file with mode: 0644]
LayerManagerPlugins/IpcModules/TcpIpcModule/src/append.c [new file with mode: 0644]
LayerManagerPlugins/IpcModules/TcpIpcModule/src/get.c [new file with mode: 0644]
LayerManagerPlugins/IpcModules/TcpIpcModule/src/initialization.c [new file with mode: 0644]
LayerManagerPlugins/IpcModules/TcpIpcModule/src/message.c [new file with mode: 0644]
LayerManagerPlugins/IpcModules/TcpIpcModule/tests/LoopbackTest.cpp [new file with mode: 0644]

index c6ca5f6..289d0d3 100644 (file)
 cmake_minimum_required (VERSION 2.6)
 
 add_subdirectory(IpcModuleLoader)
-add_subdirectory(DbusIpcModule)
+
+#==============================================================================
+# TCP Socket IPC MODULE
+#==============================================================================
+option (WITH_IPC_MODULE_TCP "Build with TCP Ipc Module" OFF)
+
+if (WITH_IPC_MODULE_TCP)
+    add_subdirectory(TcpIpcModule)
+endif (WITH_IPC_MODULE_TCP)
+
+#==============================================================================
+# DBUS IPC MODULE
+#==============================================================================
+option (WITH_IPC_MODULE_DBUS "Build with D-Bus Ipc Module" ON)
+
+if (WITH_IPC_MODULE_DBUS)
+    add_subdirectory(DbusIpcModule)
+endif (WITH_IPC_MODULE_DBUS)
diff --git a/LayerManagerPlugins/IpcModules/TcpIpcModule/CMakeLists.txt b/LayerManagerPlugins/IpcModules/TcpIpcModule/CMakeLists.txt
new file mode 100644 (file)
index 0000000..edbfdd4
--- /dev/null
@@ -0,0 +1,52 @@
+############################################################################
+# 
+# Copyright 2012 BMW Car IT GmbH
+# 
+# 
+# Licensed under the Apache License, Version 2.0 (the "License"); 
+# you may not use this file except in compliance with the License. 
+# You may obtain a copy of the License at 
+#
+#              http://www.apache.org/licenses/LICENSE-2.0 
+#
+# Unless required by applicable law or agreed to in writing, software 
+# distributed under the License is distributed on an "AS IS" BASIS, 
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+# See the License for the specific language governing permissions and 
+# limitations under the License.
+#
+############################################################################
+
+cmake_minimum_required (VERSION 2.6)
+
+project (TcpIpcModule)
+
+include_directories(
+    include
+    ${CMAKE_SOURCE_DIR}/LayerManagerClient/ilmClient/include
+    ${CMAKE_SOURCE_DIR}/LayerManagerPlugins/IpcModules/IpcModuleLoader/include
+)
+
+add_library(${PROJECT_NAME} SHARED
+    src/append.c
+    src/get.c
+    src/initialization.c
+    src/message.c
+)
+
+set(LIBS
+    ${LIBS}
+)
+
+target_link_libraries(${PROJECT_NAME} ${LIBS})
+
+install (TARGETS             ${PROJECT_NAME}
+         LIBRARY DESTINATION lib/layermanager/ipcmodules
+)
+
+if (WITH_TESTS)
+    add_executable(${PROJECT_NAME}_Test tests/LoopbackTest.cpp)
+    target_link_libraries(${PROJECT_NAME}_Test ${LIBS} IpcModuleLoader gtest gmock pthread)
+    enable_testing()
+    add_test(${PROJECT_NAME} ${PROJECT_NAME}_Test)
+endif(WITH_TESTS) 
\ No newline at end of file
diff --git a/LayerManagerPlugins/IpcModules/TcpIpcModule/include/socketConfiguration.h b/LayerManagerPlugins/IpcModules/TcpIpcModule/include/socketConfiguration.h
new file mode 100644 (file)
index 0000000..e13a5be
--- /dev/null
@@ -0,0 +1,44 @@
+/***************************************************************************
+ *
+ * Copyright 2012 BMW Car IT GmbH
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ****************************************************************************/
+
+#ifndef _TCPSOCKETCONFIGURATION_H_
+#define _TCPSOCKETCONFIGURATION_H_
+
+//=============================================================================
+// tcp socket configuration
+//=============================================================================
+#define SOCKET_TCP_HOST                 "localhost"
+#define SOCKET_TCP_PORT                 22232
+#define SOCKET_MAX_MESSAGE_SIZE         1024
+#define SOCKET_MAX_PENDING_CONNECTIONS  10
+
+#define SOCKET_MESSAGE_TYPE_NORMAL      'n'
+#define SOCKET_MESSAGE_TYPE_ERROR       'e'
+
+#define SOCKET_MESSAGE_TYPE_INT         'i'
+#define SOCKET_MESSAGE_TYPE_UINT        'u'
+#define SOCKET_MESSAGE_TYPE_BOOL        'b'
+#define SOCKET_MESSAGE_TYPE_DOUBLE      'd'
+#define SOCKET_MESSAGE_TYPE_STRING      's'
+#define SOCKET_MESSAGE_TYPE_ARRAY       'a'
+
+#define ENV_TCP_HOST                    "LM_TCP_HOST"
+#define ENV_TCP_PORT                    "LM_TCP_PORT"
+
+#endif // _TCPSOCKETCONFIGURATION_H_
diff --git a/LayerManagerPlugins/IpcModules/TcpIpcModule/include/socketShared.h b/LayerManagerPlugins/IpcModules/TcpIpcModule/include/socketShared.h
new file mode 100644 (file)
index 0000000..096bc34
--- /dev/null
@@ -0,0 +1,77 @@
+/***************************************************************************
+ *
+ * Copyright 2012 BMW Car IT GmbH
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __SOCKETSHARED_H__
+#define __SOCKETSHARED_H__
+
+#include "socketConfiguration.h"
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>  // struct hostent
+#include <string.h>
+#include <stdio.h>
+
+//=============================================================================
+// type definitions
+//=============================================================================
+struct SocketState
+{
+    t_ilm_bool isClient;
+    int server;
+    int client;
+    struct sockaddr_in serverAddrIn;
+    struct sockaddr_in clientAddrIn;
+    fd_set monitoredSockets;
+    int monitoredSocketMax;
+};
+
+struct SocketMessage
+{
+    int size;
+    int type;
+    char data[SOCKET_MAX_MESSAGE_SIZE];
+};
+
+struct State
+{
+    struct SocketState socket;
+    struct SocketMessage message;
+    unsigned int writeIndex;
+    unsigned int readIndex;
+    int senderSocket;
+    char messageName[128];
+};
+
+
+//=============================================================================
+// global variables
+//=============================================================================
+struct State gState;
+
+
+//=============================================================================
+// logging / debugging
+//=============================================================================
+//#define LOG_ENTER_FUNCTION printf("      --> TcpSocketIpcModule::%s()\n", __FUNCTION__)
+#define LOG_ENTER_FUNCTION
+
+void printMessage();
+
+
+#endif // __SOCKETSHARED_H__
diff --git a/LayerManagerPlugins/IpcModules/TcpIpcModule/src/append.c b/LayerManagerPlugins/IpcModules/TcpIpcModule/src/append.c
new file mode 100644 (file)
index 0000000..d98e8b0
--- /dev/null
@@ -0,0 +1,121 @@
+/***************************************************************************
+ *
+ * Copyright 2012 BMW Car IT GmbH
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ****************************************************************************/
+#include "IpcModule.h"
+#include "socketShared.h"
+
+//-----------------------------------------------------------------------------
+// append simple data types
+//-----------------------------------------------------------------------------
+
+t_ilm_bool appendGenericValue(const char protocolType, const char size, const void* value)
+{
+    LOG_ENTER_FUNCTION;
+
+    // TODO: size check: is message size reached?
+
+    // append protocol type
+    gState.message.data[gState.writeIndex] = protocolType;
+    gState.writeIndex += sizeof(protocolType);
+
+    // append size of data
+    gState.message.data[gState.writeIndex] = size;
+    gState.writeIndex += sizeof(size);
+
+    // append data
+    memcpy(&gState.message.data[gState.writeIndex], value, size);
+    gState.writeIndex += size;
+
+    return ILM_TRUE;
+}
+
+t_ilm_bool appendUint(const t_ilm_uint value)
+{
+    return appendGenericValue(SOCKET_MESSAGE_TYPE_UINT, sizeof(t_ilm_uint), &value);
+}
+
+t_ilm_bool appendInt(const t_ilm_int value)
+{
+    return appendGenericValue(SOCKET_MESSAGE_TYPE_INT, sizeof(t_ilm_int), &value);
+}
+
+t_ilm_bool appendBool(const t_ilm_bool value)
+{
+    return appendGenericValue(SOCKET_MESSAGE_TYPE_BOOL, sizeof(t_ilm_bool), &value);
+}
+
+t_ilm_bool appendDouble(const t_ilm_float value)
+{
+    return appendGenericValue(SOCKET_MESSAGE_TYPE_DOUBLE, sizeof(t_ilm_float), &value);
+}
+
+t_ilm_bool appendString(t_ilm_const_string value)
+{
+    return appendGenericValue(SOCKET_MESSAGE_TYPE_STRING, strlen(value), value);
+}
+
+//-----------------------------------------------------------------------------
+// append array data types
+//-----------------------------------------------------------------------------
+
+t_ilm_bool appendGenericArray(const char arraySize, const char protocolType, const char size, const void* value)
+{
+    LOG_ENTER_FUNCTION;
+    t_ilm_bool result = ILM_TRUE;
+
+    // TODO: size check: is message size reached?
+
+    // append array type
+    gState.message.data[gState.writeIndex] = SOCKET_MESSAGE_TYPE_ARRAY;
+    gState.writeIndex += sizeof(protocolType);
+
+    // append size of array
+    gState.message.data[gState.writeIndex] = arraySize;
+    gState.writeIndex += sizeof(arraySize);
+
+    // append data for each array entry
+    char i = 0;
+    for (i = 0; i < arraySize; ++i)
+    {
+        result &= appendGenericValue(protocolType, size, value + i * size);
+    }
+
+    return result;
+}
+
+t_ilm_bool appendUintArray(const t_ilm_uint* valueArray, t_ilm_int arraySize)
+{
+    return appendGenericArray(arraySize, SOCKET_MESSAGE_TYPE_UINT, sizeof(t_ilm_uint), valueArray);
+}
+
+t_ilm_bool appendIntArray(const t_ilm_int* valueArray, t_ilm_int arraySize)
+{
+    return appendGenericArray(arraySize, SOCKET_MESSAGE_TYPE_INT, sizeof(t_ilm_int), valueArray);
+}
+
+t_ilm_bool appendBoolArray(const t_ilm_bool* valueArray, t_ilm_int arraySize)
+{
+    return appendGenericArray(arraySize, SOCKET_MESSAGE_TYPE_BOOL, sizeof(t_ilm_bool), valueArray);
+}
+
+t_ilm_bool appendDoubleArray(const t_ilm_float* valueArray, t_ilm_int arraySize)
+{
+    return appendGenericArray(arraySize, SOCKET_MESSAGE_TYPE_DOUBLE, sizeof(t_ilm_float), valueArray);
+}
+
+// TODO appendStringArray()
diff --git a/LayerManagerPlugins/IpcModules/TcpIpcModule/src/get.c b/LayerManagerPlugins/IpcModules/TcpIpcModule/src/get.c
new file mode 100644 (file)
index 0000000..f511718
--- /dev/null
@@ -0,0 +1,157 @@
+/***************************************************************************
+ *
+ * Copyright 2012 BMW Car IT GmbH
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ****************************************************************************/
+#include "IpcModule.h"
+#include "socketShared.h"
+#include <stdio.h>
+#include <stdlib.h> // malloc
+
+//-----------------------------------------------------------------------------
+// get simple data types
+//-----------------------------------------------------------------------------
+
+t_ilm_bool getGenericValue(void* value, const char protocolType, const char expectedSize)
+{
+    LOG_ENTER_FUNCTION;
+    t_ilm_bool result = ILM_FALSE;
+
+    // get protocol value from message
+    char readType = gState.message.data[gState.readIndex];
+    gState.readIndex += sizeof(readType);
+
+    // if type mismatch, return to previous state, return with error
+    if (readType != protocolType)
+    {
+        gState.readIndex -= sizeof(readType);
+        printf("command value type mismatch: expected '%s', got '%s'.\n",
+               &protocolType, &readType);
+        return ILM_FALSE;
+    }
+
+    // get size of value
+    char size = gState.message.data[gState.readIndex];
+    gState.readIndex += sizeof(size);
+
+    // if size mismatch, return to previous state, return with error
+    // exception: strings have varying length
+    if (protocolType != SOCKET_MESSAGE_TYPE_STRING
+        && size != expectedSize)
+    {
+        gState.readIndex -= sizeof(readType);
+        gState.readIndex -= sizeof(size);
+        printf("command value size mismatch for type '%s': "
+               "expected %d bytes, got %d bytes.\n",
+               &protocolType, expectedSize, size);
+        return ILM_FALSE;
+    }
+
+    // copy data to caller
+    memcpy(value, &gState.message.data[gState.readIndex], size);
+    gState.readIndex += size;
+
+    // if value is string, add end of string
+    if (protocolType == SOCKET_MESSAGE_TYPE_STRING)
+    {
+        char* str = value;
+        str[size] = '\0';
+    }
+
+    return ILM_TRUE;
+}
+
+t_ilm_bool getUint(t_ilm_uint* value)
+{
+    LOG_ENTER_FUNCTION;
+    return getGenericValue(value, SOCKET_MESSAGE_TYPE_UINT, sizeof(t_ilm_uint));
+}
+
+t_ilm_bool getInt(t_ilm_int* value)
+{
+    LOG_ENTER_FUNCTION;
+    return getGenericValue(value, SOCKET_MESSAGE_TYPE_INT, sizeof(t_ilm_int));
+}
+
+t_ilm_bool getBool(t_ilm_bool* value)
+{
+    LOG_ENTER_FUNCTION;
+    return getGenericValue(value, SOCKET_MESSAGE_TYPE_BOOL, sizeof(t_ilm_bool));
+}
+
+t_ilm_bool getDouble(t_ilm_float* value)
+{
+    LOG_ENTER_FUNCTION;
+    return getGenericValue(value, SOCKET_MESSAGE_TYPE_DOUBLE, sizeof(t_ilm_float));
+}
+
+t_ilm_bool getString(char* value)
+{
+    LOG_ENTER_FUNCTION;
+    return getGenericValue(value, SOCKET_MESSAGE_TYPE_STRING, sizeof(t_ilm_const_string));
+}
+
+//-----------------------------------------------------------------------------
+// get array data types
+//-----------------------------------------------------------------------------
+
+t_ilm_bool getGenericArray(t_ilm_int* arraySize, void** value, const char protocolType, const char expectedSize)
+{
+    LOG_ENTER_FUNCTION;
+    t_ilm_bool result = ILM_TRUE;
+
+    // get protocol value from message
+    char readType = gState.message.data[gState.readIndex];
+    gState.readIndex += sizeof(readType);
+
+    // if type mismatch, return to previous state, return with error
+    if (readType != SOCKET_MESSAGE_TYPE_ARRAY)
+    {
+        gState.readIndex -= sizeof(readType);
+        printf("command value type mismatch: expected '%c', got '%s'.\n",
+               SOCKET_MESSAGE_TYPE_ARRAY, &readType);
+        return ILM_FALSE;
+    }
+
+    // get size of array
+    *arraySize = gState.message.data[gState.readIndex];
+    gState.readIndex += sizeof(gState.message.data[gState.readIndex]);
+
+    // create array for result and set callers pointer
+    *value = malloc(*arraySize * expectedSize);
+
+    // get all values from array
+    char i = 0;
+    for (i = 0; i < *arraySize; ++i)
+    {
+        result &= getGenericValue(((*value) + expectedSize * i), protocolType, expectedSize);
+    }
+
+    return result;
+}
+
+t_ilm_bool getIntArray(t_ilm_int** valueArray, t_ilm_int* arraySize)
+{
+    LOG_ENTER_FUNCTION;
+    return getGenericArray(arraySize, (void**)valueArray, SOCKET_MESSAGE_TYPE_INT, sizeof(t_ilm_int));
+}
+
+t_ilm_bool getUintArray(t_ilm_uint** valueArray, t_ilm_int* arraySize)
+{
+    LOG_ENTER_FUNCTION;
+    return getGenericArray(arraySize, (void**)valueArray, SOCKET_MESSAGE_TYPE_UINT, sizeof(t_ilm_uint));
+}
+
diff --git a/LayerManagerPlugins/IpcModules/TcpIpcModule/src/initialization.c b/LayerManagerPlugins/IpcModules/TcpIpcModule/src/initialization.c
new file mode 100644 (file)
index 0000000..8261828
--- /dev/null
@@ -0,0 +1,132 @@
+/***************************************************************************
+ *
+ * Copyright 2012 BMW Car IT GmbH
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ****************************************************************************/
+#include "IpcModule.h"
+#include "socketShared.h"
+#include <stdio.h>
+#include <stdlib.h>  // getenv
+#include <string.h>  // memset
+
+
+t_ilm_bool init(t_ilm_bool isClient)
+{
+    LOG_ENTER_FUNCTION;
+    t_ilm_bool result = ILM_TRUE;
+
+    struct SocketState* sock = &gState.socket;
+
+    sock->isClient = isClient;
+
+    sock->server = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+    if (sock->server < 0)
+    {
+        printf("TcpIpcModule: socket()...failed\n");
+        result = ILM_FALSE;
+    }
+
+    const char* portString = getenv(ENV_TCP_PORT);
+    int port = SOCKET_TCP_PORT;
+    if (portString)
+    {
+        port = atoi(portString);
+    }
+
+    sock->serverAddrIn.sin_family = AF_INET;
+    sock->serverAddrIn.sin_port = htons(port);
+    memset(&(sock->serverAddrIn.sin_zero), '\0', 8);
+
+    if (sock->isClient)  // Client
+    {
+        const char* hostname = getenv(ENV_TCP_HOST);
+        if (!hostname)
+        {
+            hostname = SOCKET_TCP_HOST;
+        }
+
+        struct hostent* server;
+        server = gethostbyname(hostname);
+        if (!server)
+        {
+            printf("TcpIpcModule: error: could not resolve host '%s'.\n", hostname);
+            result = ILM_FALSE;
+        }
+        else
+        {
+            memcpy(&sock->serverAddrIn.sin_addr.s_addr, server->h_addr, server->h_length);
+        }
+
+        if (0 != connect(sock->server,
+                         (struct sockaddr *) &sock->serverAddrIn,
+                         sizeof(sock->serverAddrIn)))
+        {
+            result = ILM_FALSE;
+        }
+
+        printf("TcpIpcModule: connection to %s:%d %s.\n",
+                hostname, port,
+                (ILM_TRUE == result) ? "established" : "failed");
+
+        FD_SET(sock->server, &sock->monitoredSockets);
+        sock->monitoredSocketMax = sock->server;
+    }
+    else  // LayerManagerService
+    {
+        int on = 1;
+        setsockopt(sock->server, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+        sock->serverAddrIn.sin_addr.s_addr = htonl(INADDR_ANY);
+
+        if (0 > bind(sock->server,
+                    (struct sockaddr *) &sock->serverAddrIn,
+                     sizeof(sock->serverAddrIn)))
+        {
+            printf("TcpIpcModule: bind()...failed\n");
+            result = ILM_FALSE;
+        }
+
+        if (listen(sock->server, SOCKET_MAX_PENDING_CONNECTIONS) < 0)
+        {
+            printf("TcpIpcModule: listen()...failed\n");
+            result = ILM_FALSE;
+        }
+
+        FD_SET(sock->server, &sock->monitoredSockets);
+        sock->monitoredSocketMax = sock->server;
+
+        printf("TcpIpcModule: listening to TCP port: %d\n", port);
+    }
+
+    return result;
+}
+
+t_ilm_bool destroy()
+{
+    LOG_ENTER_FUNCTION;
+    int socketNumber;
+    for (socketNumber = 0; socketNumber <= gState.socket.monitoredSocketMax; ++socketNumber)
+    {
+        if (FD_ISSET(socketNumber, &gState.socket.monitoredSockets))
+        {
+            printf("TcpIpcModule: Closing socket %d\n", socketNumber);
+            close(socketNumber);
+        }
+    }
+    return ILM_TRUE;
+}
+
diff --git a/LayerManagerPlugins/IpcModules/TcpIpcModule/src/message.c b/LayerManagerPlugins/IpcModules/TcpIpcModule/src/message.c
new file mode 100644 (file)
index 0000000..3fc8650
--- /dev/null
@@ -0,0 +1,309 @@
+/***************************************************************************
+ *
+ * Copyright 2012 BMW Car IT GmbH
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ****************************************************************************/
+#include "IpcModule.h"
+#include "socketShared.h"
+#include <errno.h>
+#include <stdio.h>
+#include <sys/select.h>
+
+
+//=============================================================================
+// prototypes
+//=============================================================================
+t_ilm_bool acceptClientConnection();
+int receiveFromSocket();
+
+
+//=============================================================================
+// message handling
+//=============================================================================
+t_ilm_bool createMessage(t_ilm_const_string name)
+{
+    LOG_ENTER_FUNCTION;
+
+    memset(&gState.message, 0, sizeof(gState.message));
+
+    gState.message.type = SOCKET_MESSAGE_TYPE_NORMAL;
+    gState.readIndex = 0;
+    gState.writeIndex = 0;
+
+    return appendString(name);
+}
+
+t_ilm_bool sendMessage()
+{
+    LOG_ENTER_FUNCTION;
+    int activesocket = 0;
+    int sentBytes = 0;
+    int retVal = 0;
+
+    int headerSize = sizeof(gState.message) - sizeof(gState.message.data);
+    gState.message.size = gState.writeIndex + headerSize;
+
+    if (gState.socket.isClient)
+    {
+        activesocket = gState.socket.server;
+    }
+    else
+    {
+        activesocket = gState.senderSocket;
+    }
+
+    int sendSize = gState.message.size;
+
+    do
+    {
+        retVal += send(activesocket,
+                       &gState.message + sentBytes,
+                       sendSize - sentBytes,
+                       0);
+        sentBytes += retVal;
+    } while (retVal > 0 && sentBytes < sendSize);
+
+    //printf("      --> TcpIpcModule: %d bytes sent to socket %d\n", sentBytes, activesocket);
+
+    return (sentBytes == sendSize) ? ILM_TRUE : ILM_FALSE;
+}
+
+t_ilm_bool sendError(t_ilm_const_string desc)
+{
+    LOG_ENTER_FUNCTION;
+
+    gState.message.type = SOCKET_MESSAGE_TYPE_ERROR;
+
+    // reset content to error description
+    gState.writeIndex = 0;
+    appendString(desc);
+
+    return sendMessage();
+}
+
+enum IpcMessageType receiveMessage(t_ilm_int timeoutInMs)
+{
+    LOG_ENTER_FUNCTION;
+    enum IpcMessageType result = IpcMessageTypeNone;
+
+    gState.readIndex = 0;
+    gState.writeIndex = 0;
+
+    fd_set readFds = gState.socket.monitoredSockets;
+
+    struct timeval timeoutValue;
+    timeoutValue.tv_sec = timeoutInMs / 1000;
+    timeoutValue.tv_usec = (timeoutInMs % 1000) * 1000;
+
+    int numberOfFdsReady = select(gState.socket.monitoredSocketMax + 1, &readFds, 0, 0, &timeoutValue);
+
+    if (-1  == numberOfFdsReady)
+    {
+        printf("TcpIpcModule: select() failed\n");
+    }
+    else if (0 < numberOfFdsReady)
+    {
+        int socketNumber;
+        for (socketNumber = 0; socketNumber <= gState.socket.monitoredSocketMax; ++socketNumber)
+        {
+            if (FD_ISSET(socketNumber, &readFds))
+            {
+                if (!gState.socket.isClient)
+                {
+                    if (gState.socket.server == socketNumber)
+                    {
+                        // New client connected
+                        acceptClientConnection();
+                        result = IpcMessageTypeNone;  // no data received
+                        continue;
+                    }
+
+                    // receive data from socket
+                    gState.senderSocket = socketNumber;
+                    receiveFromSocket();
+
+                    if(gState.message.size > 0)
+                    {
+                        // new message from client
+                        getString(gState.messageName);
+                        result = IpcMessageTypeCommand;
+                        continue;
+                    }
+
+                    if(gState.message.size == 0)
+                    {
+                        // client disconnected
+                        close(socketNumber);
+                        FD_CLR(socketNumber, &gState.socket.monitoredSockets);
+                        result = IpcMessageTypeDisconnect;
+                        continue;
+                    }
+
+                    // error
+                    const char* errorMsg = (char*)strerror(errno);
+                    printf("      --> TcpIpcModule: Error receiving data from socket %d (%s)\n",
+                           gState.senderSocket, errorMsg);
+                    result = IpcMessageTypeError;
+                }
+                else
+                {
+                    // receive LayerManager response
+                    gState.senderSocket = socketNumber;
+                    receiveFromSocket(gState.senderSocket);
+                    getString(gState.messageName);
+                    result = IpcMessageTypeCommand;
+                }
+            }
+        }
+    }
+
+    return result;
+}
+
+t_ilm_const_string getMessageName()
+{
+    LOG_ENTER_FUNCTION;
+    return gState.messageName;
+}
+
+t_ilm_bool isErrorMessage()
+{
+    LOG_ENTER_FUNCTION;
+    return (SOCKET_MESSAGE_TYPE_ERROR == gState.message.type);
+}
+
+t_ilm_const_string getSenderName()
+{
+    LOG_ENTER_FUNCTION;
+    char buffer[16];
+    sprintf(buffer, "socket %d", gState.senderSocket);
+    return strdup(buffer);
+}
+
+//=============================================================================
+//private
+//=============================================================================
+t_ilm_bool acceptClientConnection()
+{
+    LOG_ENTER_FUNCTION;
+
+    t_ilm_bool result = ILM_TRUE;
+    unsigned int clientlen = sizeof(gState.socket.clientAddrIn);
+
+    gState.socket.client = accept(gState.socket.server, (struct sockaddr *) &gState.socket.clientAddrIn, &clientlen);
+
+    if (gState.socket.client < 0)
+    {
+        printf("TcpIpcModule: accept() failed.\n");
+        result = ILM_FALSE;
+    }
+
+    FD_SET(gState.socket.client, &gState.socket.monitoredSockets);
+    gState.socket.monitoredSocketMax = (gState.socket.monitoredSocketMax > gState.socket.client) ? gState.socket.monitoredSocketMax : gState.socket.client;
+
+    return result;
+}
+
+int receiveFromSocket()
+{
+    LOG_ENTER_FUNCTION;
+    int receivedBytes = 0;
+    int retVal = 0;
+
+    // receive header in first run (contains message size)
+    gState.message.size = sizeof(gState.message) - sizeof(gState.message.data);
+
+    char* messageBuffer = (char*)&gState.message;
+
+    do
+    {
+        retVal = recv(gState.senderSocket,
+                      &messageBuffer[receivedBytes],
+                      gState.message.size - receivedBytes,
+                      0);
+        receivedBytes += retVal;
+    } while ((retVal > 0) && (receivedBytes < gState.message.size));
+
+    if (0 == retVal)
+    {
+        // client disconnect
+        gState.message.size = 0;
+    }
+}
+
+
+//=============================================================================
+// logging / debugging
+//=============================================================================
+void printMessage()
+{
+    LOG_ENTER_FUNCTION;
+
+    printf("          --> Message type: %s\n", (gState.message.type == SOCKET_MESSAGE_TYPE_NORMAL) ? "normal" : "error");
+    printf("          --> Message size: %d\n", gState.message.size);
+
+    int i = 0;
+
+    char headersize = sizeof(gState.message) - sizeof(gState.message.data);
+
+    // iterate over message data
+    while (i < gState.message.size - headersize)
+    {
+        // read type information
+        char type  = gState.message.data[i++];
+        char datasize = gState.message.data[i++];
+
+        // set pointer to value data
+        char* data = &gState.message.data[i];
+
+        // print data
+        switch (type)
+        {
+        case SOCKET_MESSAGE_TYPE_ARRAY:
+            printf("          --> Array, %d entries:\n", datasize);
+            datasize = 0;
+            break;
+
+        case SOCKET_MESSAGE_TYPE_STRING:
+            printf("          --> String: '%s' (%d bytes)\n", data, datasize);
+            break;
+
+        case SOCKET_MESSAGE_TYPE_UINT:
+            printf("          --> UInt: %u (%d bytes)\n", *(t_ilm_uint*)data, datasize);
+            break;
+
+        case SOCKET_MESSAGE_TYPE_INT:
+            printf("          --> Int: %d (%d bytes)\n", *(t_ilm_int*)data, datasize);
+            break;
+
+        case SOCKET_MESSAGE_TYPE_DOUBLE:
+            printf("          --> Double: %lf (%d bytes)\n", *(t_ilm_float*)data, datasize);
+            break;
+
+        case SOCKET_MESSAGE_TYPE_BOOL:
+            printf("          --> Bool: %d (%d bytes)\n", *data, datasize);
+            break;
+
+        default:
+            printf("          --> Unknown type (%s), %d bytes\n", (char*)&type, datasize);
+            break;
+        }
+
+        // skip already processed data
+        i += datasize;
+    }
+}
+
diff --git a/LayerManagerPlugins/IpcModules/TcpIpcModule/tests/LoopbackTest.cpp b/LayerManagerPlugins/IpcModules/TcpIpcModule/tests/LoopbackTest.cpp
new file mode 100644 (file)
index 0000000..5fe4194
--- /dev/null
@@ -0,0 +1,64 @@
+/***************************************************************************
+ *
+ * Copyright 2012 BMW Car IT GmbH
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ****************************************************************************/
+#include "IpcModuleLoader.h"
+#include <gtest/gtest.h>
+
+#define PLATFORM_PTR_SIZE sizeof(unsigned int*)
+
+class Loopback : public ::testing::Test
+{
+public:
+    void SetUp()
+    {
+        loadAndCheckIpcModule(&mService);
+        //loadAndCheckIpcModule(&mClient);
+    }
+
+    void loadAndCheckIpcModule(IpcModule* ipcModule)
+    {
+        memset(ipcModule, 0, sizeof(*ipcModule));
+        ASSERT_EQ(ILM_TRUE, loadIpcModule(ipcModule));
+
+        int apiEntryCount = sizeof(*ipcModule) / PLATFORM_PTR_SIZE;
+        unsigned int* base = (unsigned int*)ipcModule;
+        for (int i = 0; i < apiEntryCount; ++i)
+        {
+            ASSERT_NE(0, base[i]);
+        }
+    }
+
+    void TearDown()
+    {
+        //memset(&mClient, 0, sizeof(mClient));
+        memset(&mService, 0, sizeof(mService));
+    }
+
+protected:
+    IpcModule mService;
+    //IpcModule mClient;
+};
+
+TEST_F(Loopback, DISABLE_lifecycle)
+{
+    ASSERT_TRUE(mService.init(ILM_FALSE));
+    //ASSERT_TRUE(mClient.init(ILM_TRUE));
+
+    //ASSERT_TRUE(mClient.destroy());
+    ASSERT_TRUE(mService.destroy());
+}