Prepare client socket class for connecting cynara service
authorLukasz Wojciechowski <l.wojciechow@partner.samsung.com>
Mon, 30 Jun 2014 07:58:18 +0000 (09:58 +0200)
committerRafal Krypa <r.krypa@samsung.com>
Thu, 3 Jul 2014 12:19:10 +0000 (14:19 +0200)
Class is based on implementation imported from security-server
in previous commit.

Change-Id: I77643de6ec7321d3f6af0b395e1b1e4bf06b3774

src/client/CMakeLists.txt
src/client/sockets/Socket.cpp [new file with mode: 0644]
src/client/sockets/Socket.h [new file with mode: 0644]
src/client/sockets/client-common.cpp [deleted file]
src/common/exceptions/ServerConnectionErrorException.h [new file with mode: 0644]

index 89fe001..9b5e695 100644 (file)
@@ -24,6 +24,7 @@ SET(CYNARA_LIB_CYNARA_PATH ${CYNARA_PATH}/client)
 SET(LIB_CYNARA_SOURCES
     ${CYNARA_LIB_CYNARA_PATH}/api/client-api.cpp
     ${CYNARA_LIB_CYNARA_PATH}/logic/Logic.cpp
+    ${CYNARA_LIB_CYNARA_PATH}/sockets/Socket.cpp
     )
 
 INCLUDE_DIRECTORIES(
diff --git a/src/client/sockets/Socket.cpp b/src/client/sockets/Socket.cpp
new file mode 100644 (file)
index 0000000..cddd05f
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ *  Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  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
+ */
+/*
+ * @file        Socket.cpp
+ * @author      Bartlomiej Grzelewski <b.grzelewski@samsung.com>
+ * @author      Lukasz Wojciechowski <l.wojciechow@partner.samsung.com>
+ * @version     1.0
+ * @brief       This file contains implementation of UNIX client socket class
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <exceptions/InitException.h>
+#include <exceptions/ServerConnectionErrorException.h>
+#include <exceptions/UnexpectedErrorException.h>
+#include <log/log.h>
+
+#include "Socket.h"
+
+namespace Cynara {
+
+Socket::Socket(const std::string &socketPath, int timeoutMiliseconds) : m_sock(-1),
+    m_socketPath(socketPath), m_pollTimeout(timeoutMiliseconds) {
+}
+
+Socket::~Socket() {
+    close();
+}
+
+void Socket::close(void) noexcept {
+    if (m_sock > -1)
+        ::close(m_sock);
+    m_sock = -1;
+}
+
+bool Socket::waitForSocket(int event) {
+    int ret;
+    pollfd desc[1];
+    desc[0].fd = m_sock;
+    desc[0].events = event;
+
+    ret = TEMP_FAILURE_RETRY(poll(desc, 1, m_pollTimeout));
+
+    if (ret == -1) {
+        int err = errno;
+        LOGE("'poll' function error [%d] : <%s>", err, strerror(err));
+        close();
+        throw UnexpectedErrorException(err, strerror(err));
+    } else if (ret == 0) {
+        LOGD("Poll timeout");
+    }
+
+    return (ret == 1);
+}
+
+int Socket::getSocketError(void) {
+    int err = 0;
+    socklen_t len = sizeof(err);
+    int ret = getsockopt(m_sock, SOL_SOCKET, SO_ERROR, &err, &len);
+    if (ret < 0) {
+        int err = errno;
+        close();
+        LOGE("'getsockopt' function error [%d] : <%s>", err, strerror(err));
+        throw UnexpectedErrorException(err, strerror(err));
+    }
+    return err;
+}
+
+bool Socket::isConnected(void) {
+    if (m_sock < 0)
+        return false;
+
+    if (getSocketError() != 0) {
+        close();
+        return false;
+    }
+
+    return true;
+}
+
+bool Socket::connect(void) {
+    sockaddr_un clientAddr;
+    int flags;
+
+    if (isConnected())
+        return true;
+
+    close();
+
+    m_sock = socket(AF_UNIX, SOCK_STREAM, 0);
+    if (m_sock < 0) {
+        int err = errno;
+        LOGE("'socket' function error [%d] : <%s>", err, strerror(err));
+        throw UnexpectedErrorException(err, strerror(err));
+    }
+
+    if ((flags = fcntl(m_sock, F_GETFL, 0)) < 0 ||
+        fcntl(m_sock, F_SETFL, flags | O_NONBLOCK) < 0)
+    {
+        int err = errno;
+        close();
+        LOGE("'fcntl' function error [%d] : <%s>", err, strerror(err));
+        throw UnexpectedErrorException(err, strerror(err));
+    }
+
+    memset(&clientAddr, 0, sizeof(clientAddr));
+
+    clientAddr.sun_family = AF_UNIX;
+
+    if (m_socketPath.length() >= sizeof(clientAddr.sun_path)) {
+        close();
+        LOGE("Error: socket path <%s> is too long [%zu]. Max len is [%u]", m_socketPath.c_str(),
+             m_socketPath.length(), sizeof(clientAddr.sun_path));
+        throw InitException();
+    }
+
+    strcpy(clientAddr.sun_path, m_socketPath.c_str());
+
+    LOGD("ClientAddr.sun_path <%s>", clientAddr.sun_path);
+
+    int retval = TEMP_FAILURE_RETRY(::connect(m_sock, (struct sockaddr*)&clientAddr,
+                                            SUN_LEN(&clientAddr)));
+    int err = 0;
+    if (retval == -1) {
+        err = errno;
+        if (err == EINPROGRESS) {
+            if (!waitForSocket(POLLOUT)) {
+                return false;
+            }
+            err = getSocketError();
+        }
+        if (err == ECONNREFUSED) {
+            //no one is listening
+            return false;
+        }
+        close();
+        LOGE("'connect' function error [%d] : <%s>", err, strerror(err));
+        throw UnexpectedErrorException(err, strerror(err));
+    }
+
+    return isConnected();
+}
+
+bool Socket::sendToServer(BinaryQueue &queue) {
+    ssize_t done = 0;
+    bool retry = false;
+
+    RawBuffer buffer(queue.size());
+    queue.flattenConsume(buffer.data(), queue.size());
+
+    do {
+        if (!connect()) {
+            LOGE("Error connecting to socket");
+            throw ServerConnectionErrorException();
+        }
+
+        retry = false;
+        done = 0;
+        while ((buffer.size() - done) > 0) {
+            if (! waitForSocket(POLLOUT)) {
+                LOGE("Error in poll(POLLOUT)");
+                throw ServerConnectionErrorException();
+            }
+            ssize_t t = TEMP_FAILURE_RETRY(write(m_sock, buffer.data() + done,
+                                           buffer.size() - done));
+            if (t == -1) {
+                int err = errno;
+                if (err == EPIPE) {
+                    close();
+                    LOGN("Connection closed by server. Retrying to connect.");
+                    retry = true;
+                    break;
+                }
+                close();
+                LOGE("'write' function error [%d] : <%s>", err, strerror(err));
+                throw UnexpectedErrorException(err, strerror(err));
+            }
+            done += t;
+        }
+    } while (retry);
+
+    return true;
+}
+
+bool Socket::receiveFromServer(BinaryQueue &queue)
+{
+    if (!waitForSocket(POLLIN)) {
+        LOGE("Error in poll(POLLIN)");
+        throw ServerConnectionErrorException();
+    }
+
+    RawBuffer readBuffer(BUFSIZ);
+    ssize_t size = TEMP_FAILURE_RETRY(read(m_sock, readBuffer.data(), BUFSIZ));
+
+    if (size == -1) {
+        int err = errno;
+        LOGE("'read' function error [%d] : <%s>", err, strerror(err));
+        throw UnexpectedErrorException(err, strerror(err));
+    }
+
+    if (size == 0) {
+        LOGW("read return 0 / Connection closed by server.");
+        return false;
+    }
+    queue.appendCopy(readBuffer.data(), size);
+
+    return true;
+}
+
+} // namespace Cynara
diff --git a/src/client/sockets/Socket.h b/src/client/sockets/Socket.h
new file mode 100644 (file)
index 0000000..7c6a286
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ *  Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  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
+ */
+/*
+ * @file        Socket.h
+ * @author      Bartlomiej Grzelewski <b.grzelewski@samsung.com>
+ * @author      Lukasz Wojciechowski <l.wojciechow@partner.samsung.com>
+ * @version     1.0
+ * @brief       This file contains definition of UNIX client socket class
+ */
+
+#ifndef SRC_CLIENT_SOCKETS_SOCKET_H_
+#define SRC_CLIENT_SOCKETS_SOCKET_H_
+
+#include <string>
+
+#include <common.h>
+
+namespace Cynara {
+
+class Socket {
+private:
+    int m_sock;
+
+    std::string m_socketPath;
+    int m_pollTimeout;
+
+    void close(void) noexcept;
+
+    //returns true      if socket is ready
+    //returns false     in case of timeout
+    //throws            in critical situations
+    bool waitForSocket(int event);
+
+    //returns int       errorcode read from socket
+    //throws            in critical situations
+    int getSocketError(void);
+
+public:
+    Socket(const std::string &socketPath, int timeoutMiliseconds = -1);
+    ~Socket();
+
+    //returns bool      connection state (true - connected, false - not connected))
+    //throws            in critical situations
+    bool isConnected(void);
+
+    //returns true      if connection succeeded
+    //returns false     if connection was timeout or no one is listening
+    //throws            in critical situations
+    bool connect(void);
+
+    //returns true                              if data was successfully send to server
+    //returns false                             if connection was lost
+    //throws ServerConnectionErrorException     if cannot connect server (or timeout)
+    //throws other exceptions                   in critical situations
+    bool sendToServer(BinaryQueue &queue);
+
+    //returns true                              if data was successfully read from server
+    //returns false                             if connection was lost
+    //throws ServerConnectionErrorException     if cannot connect server (or timeout)
+    //throws other exceptions                   in critical situations
+    bool receiveFromServer(BinaryQueue &queue);
+};
+
+} // namespace Cynara
+
+#endif /* SRC_CLIENT_SOCKETS_SOCKET_H_ */
diff --git a/src/client/sockets/client-common.cpp b/src/client/sockets/client-common.cpp
deleted file mode 100644 (file)
index 00f099f..0000000
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- *  Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  Contact: Bumjin Im <bj.im@samsung.com>
- *
- *  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
- */
-/*
- * @file        client-common.cpp
- * @author      Bartlomiej Grzelewski (b.grzelewski@samsung.com)
- * @version     1.0
- * @brief       This file is implementation of client-common functions.
- */
-
-#include <fcntl.h>
-#include <poll.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-#include <dpl/log/log.h>
-#include <dpl/serialization.h>
-#include <dpl/singleton.h>
-#include <dpl/singleton_safe_impl.h>
-
-#include <message-buffer.h>
-
-#include <security-server.h>
-
-IMPLEMENT_SAFE_SINGLETON(SecurityServer::Log::LogSystem);
-
-namespace {
-
-const int POLL_TIMEOUT = 2000;
-
-void securityClientEnableLogSystem(void) {
-    SecurityServer::Singleton<SecurityServer::Log::LogSystem>::Instance().SetTag("SECURITY_SERVER_CLIENT");
-}
-
-int waitForSocket(int sock, int event, int timeout) {
-    int retval;
-    pollfd desc[1];
-    desc[0].fd = sock;
-    desc[0].events = event;
-
-    while((-1 == (retval = poll(desc, 1, timeout))) && (errno == EINTR)) {
-        timeout >>= 1;
-        errno = 0;
-    }
-
-    if (0 == retval) {
-        LogDebug("Poll timeout");
-    } else if (-1 == retval) {
-        int err = errno;
-        LogError("Error in poll: " << strerror(err));
-    }
-    return retval;
-}
-
-class SockRAII {
-public:
-    SockRAII()
-      : m_sock(-1)
-    {}
-
-    virtual ~SockRAII() {
-        if (m_sock > -1)
-            close(m_sock);
-    }
-
-    int Connect(char const * const interface) {
-        sockaddr_un clientAddr;
-        int flags;
-
-        if (m_sock != -1) // guard
-            close(m_sock);
-
-        m_sock = socket(AF_UNIX, SOCK_STREAM, 0);
-        if (m_sock < 0) {
-            int err = errno;
-            LogError("Error creating socket: " << strerror(err));
-            return SECURITY_SERVER_API_ERROR_SOCKET;
-        }
-
-        if ((flags = fcntl(m_sock, F_GETFL, 0)) < 0 ||
-            fcntl(m_sock, F_SETFL, flags | O_NONBLOCK) < 0)
-        {
-            int err = errno;
-            LogError("Error in fcntl: " << strerror(err));
-            return SECURITY_SERVER_API_ERROR_SOCKET;
-        }
-
-        memset(&clientAddr, 0, sizeof(clientAddr));
-
-        clientAddr.sun_family = AF_UNIX;
-
-        if (strlen(interface) >= sizeof(clientAddr.sun_path)) {
-            LogError("Error: interface name " << interface << "is too long. Max len is:" << sizeof(clientAddr.sun_path));
-            return SECURITY_SERVER_API_ERROR_NO_SUCH_SERVICE;
-        }
-
-        strcpy(clientAddr.sun_path, interface);
-
-        LogDebug("ClientAddr.sun_path = " << interface);
-
-        int retval = TEMP_FAILURE_RETRY(connect(m_sock, (struct sockaddr*)&clientAddr, SUN_LEN(&clientAddr)));
-        if ((retval == -1) && (errno == EINPROGRESS)) {
-            if (0 >= waitForSocket(m_sock, POLLIN, POLL_TIMEOUT)) {
-                LogError("Error in waitForSocket.");
-                return SECURITY_SERVER_API_ERROR_SOCKET;
-            }
-            int error = 0;
-            socklen_t len = sizeof(error);
-            retval = getsockopt(m_sock, SOL_SOCKET, SO_ERROR, &error, &len);
-
-            if (-1 == retval) {
-                int err = errno;
-                LogError("Error in getsockopt: " << strerror(err));
-                return SECURITY_SERVER_API_ERROR_SOCKET;
-            }
-
-            if (error == EACCES) {
-                LogError("Access denied");
-                return SECURITY_SERVER_API_ERROR_ACCESS_DENIED;
-            }
-
-            if (error != 0) {
-                LogError("Error in connect: " << strerror(error));
-                return SECURITY_SERVER_API_ERROR_SOCKET;
-            }
-
-            return SECURITY_SERVER_API_SUCCESS;
-        }
-
-        if (-1 == retval) {
-            int err = errno;
-            LogError("Error connecting socket: " << strerror(err));
-            if (err == EACCES)
-                return SECURITY_SERVER_API_ERROR_ACCESS_DENIED;
-            if (err == ENOTSOCK)
-                return SECURITY_SERVER_API_ERROR_NO_SUCH_SERVICE;
-            return SECURITY_SERVER_API_ERROR_SOCKET;
-        }
-
-        return SECURITY_SERVER_API_SUCCESS;
-    }
-
-    int Get() {
-        return m_sock;
-    }
-
-private:
-    int m_sock;
-};
-
-} // namespace anonymous
-
-namespace SecurityServer {
-
-
-int sendToServer(char const * const interface, const RawBuffer &send, MessageBuffer &recv) {
-    int ret;
-    SockRAII sock;
-    ssize_t done = 0;
-    char buffer[2048];
-
-    if (SECURITY_SERVER_API_SUCCESS != (ret = sock.Connect(interface))) {
-        LogError("Error in SockRAII");
-        return ret;
-    }
-
-    while ((send.size() - done) > 0) {
-        if (0 >= waitForSocket(sock.Get(), POLLOUT, POLL_TIMEOUT)) {
-            LogError("Error in poll(POLLOUT)");
-            return SECURITY_SERVER_API_ERROR_SOCKET;
-        }
-        ssize_t temp = TEMP_FAILURE_RETRY(write(sock.Get(), &send[done], send.size() - done));
-        if (-1 == temp) {
-            int err = errno;
-            LogError("Error in write: " << strerror(err));
-            return SECURITY_SERVER_API_ERROR_SOCKET;
-        }
-        done += temp;
-    }
-
-    do {
-        if (0 >= waitForSocket(sock.Get(), POLLIN, POLL_TIMEOUT)) {
-            LogError("Error in poll(POLLIN)");
-            return SECURITY_SERVER_API_ERROR_SOCKET;
-        }
-        ssize_t temp = TEMP_FAILURE_RETRY(read(sock.Get(), buffer, 2048));
-        if (-1 == temp) {
-            int err = errno;
-            LogError("Error in read: " << strerror(err));
-            return SECURITY_SERVER_API_ERROR_SOCKET;
-        }
-
-        if (0 == temp) {
-            LogError("Read return 0/Connection closed by server(?)");
-            return SECURITY_SERVER_API_ERROR_SOCKET;
-        }
-
-        RawBuffer raw(buffer, buffer+temp);
-        recv.Push(raw);
-    } while(!recv.Ready());
-    return SECURITY_SERVER_API_SUCCESS;
-}
-
-int sendToServerAncData(char const * const interface, const RawBuffer &send, struct msghdr &hdr) {
-    int ret;
-    SockRAII sock;
-    ssize_t done = 0;
-
-    if (SECURITY_SERVER_API_SUCCESS != (ret = sock.Connect(interface))) {
-        LogError("Error in SockRAII");
-        return ret;
-    }
-
-    while ((send.size() - done) > 0) {
-        if (0 >= waitForSocket(sock.Get(), POLLOUT, POLL_TIMEOUT)) {
-            LogError("Error in poll(POLLOUT)");
-            return SECURITY_SERVER_API_ERROR_SOCKET;
-        }
-        ssize_t temp = TEMP_FAILURE_RETRY(write(sock.Get(), &send[done], send.size() - done));
-        if (-1 == temp) {
-            int err = errno;
-            LogError("Error in write: " << strerror(err));
-            return SECURITY_SERVER_API_ERROR_SOCKET;
-        }
-        done += temp;
-    }
-
-    if (0 >= waitForSocket(sock.Get(), POLLIN, POLL_TIMEOUT)) {
-        LogError("Error in poll(POLLIN)");
-        return SECURITY_SERVER_API_ERROR_SOCKET;
-    }
-
-    ssize_t temp = TEMP_FAILURE_RETRY(recvmsg(sock.Get(), &hdr, MSG_CMSG_CLOEXEC));
-
-    if (temp < 0) {
-        int err = errno;
-        LogError("Error in recvmsg(): " << strerror(err) << " errno: " << err);
-        return SECURITY_SERVER_API_ERROR_SOCKET;
-    }
-
-    if (0 == temp) {
-        LogError("Read return 0/Connection closed by server(?)");
-        return SECURITY_SERVER_API_ERROR_SOCKET;
-    }
-
-    return SECURITY_SERVER_API_SUCCESS;
-}
-
-int try_catch(const std::function<int()>& func)
-{
-    try {
-        return func();
-    } catch (MessageBuffer::Exception::Base &e) {
-        LogError("SecurityServer::MessageBuffer::Exception " << e.DumpToString());
-    } catch (std::exception &e) {
-        LogError("STD exception " << e.what());
-    } catch (...) {
-        LogError("Unknown exception occured");
-    }
-    return SECURITY_SERVER_API_ERROR_UNKNOWN;
-}
-
-} // namespace SecurityServer
-
-static void init_lib(void) __attribute__ ((constructor));
-static void init_lib(void)
-{
-    securityClientEnableLogSystem();
-}
-
-static void fini_lib(void) __attribute__ ((destructor));
-static void fini_lib(void)
-{
-
-}
-
diff --git a/src/common/exceptions/ServerConnectionErrorException.h b/src/common/exceptions/ServerConnectionErrorException.h
new file mode 100644 (file)
index 0000000..ebdf574
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    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.
+ */
+/*
+ * @file        ServerConnectionErrorException.h
+ * @author      Lukasz Wojciechowski <l.wojciechow@partner.samsung.com>
+ * @version     1.0
+ * @brief       Implementation of ServerConnectionErrorException
+ */
+
+#ifndef SRC_COMMON_EXCEPTIONS_SERVERCONNECTIONERROREXCEPTION_H_
+#define SRC_COMMON_EXCEPTIONS_SERVERCONNECTIONERROREXCEPTION_H_
+
+#include "Exception.h"
+
+#include <exception>
+
+namespace Cynara {
+
+class ServerConnectionErrorException : public Exception {
+public:
+    ServerConnectionErrorException() = default;
+    virtual ~ServerConnectionErrorException() = default;
+    virtual const char* what() const noexcept {
+        return "ServerConnectionError";
+    }
+};
+
+} /* namespace Cynara */
+
+#endif /* SRC_COMMON_EXCEPTIONS_SERVERCONNECTIONERROREXCEPTION_H_ */