Ack.

Thanks,
Ramesh.

On 2/10/2017 7:59 PM, Anders Widell wrote:
>   src/base/Makefile.am               |   3 +
>   src/base/file_descriptor.cc        |  27 +++++++++++++++++
>   src/base/file_descriptor.h         |  27 +++++++++++++++++
>   src/base/tests/unix_socket_test.cc |  60 
> +++++++++++++++++++++++++++++++++----
>   src/base/unix_client_socket.cc     |  10 +-----
>   src/base/unix_server_socket.cc     |  12 ++++++-
>   src/base/unix_socket.cc            |  32 +++++++++++++------
>   src/base/unix_socket.h             |  49 ++++++++++++++++++++----------
>   8 files changed, 177 insertions(+), 43 deletions(-)
>
>
> Multiple issues have been fixed in the UnixSocket class:
>
> * The UnixServerSocket failed if the named already exists in the file system.
> * The UnixSocket used a mutex to protect Open() and Close(), but was is not
>    enough to make it thread safe. The mutex has been removed and users are now
>    responsible for protecting the instance if needed.
> * The fd() method was not so useful since it required a message to be sent or
>    received before it returned a valid file descriptor. The fd() method now 
> opens
>    the socket if it is not already open.
> * The UnixClientSocket implemented a wait-loop in case connect() returns
>    EINPROGRESS. This has been changed to use a blocking call to connect()
>    instead.
>
> diff --git a/src/base/Makefile.am b/src/base/Makefile.am
> --- a/src/base/Makefile.am
> +++ b/src/base/Makefile.am
> @@ -32,6 +32,7 @@ lib_libopensaf_core_la_LDFLAGS += \
>   lib_libopensaf_core_la_SOURCES += \
>       src/base/condition_variable.cc \
>       src/base/daemon.c \
> +     src/base/file_descriptor.cc \
>       src/base/file_notify.cc \
>       src/base/getenv.cc \
>       src/base/hj_dec.c \
> @@ -76,6 +77,7 @@ noinst_HEADERS += \
>       src/base/buffer.h \
>       src/base/condition_variable.h \
>       src/base/daemon.h \
> +     src/base/file_descriptor.h \
>       src/base/file_notify.h \
>       src/base/getenv.h \
>       src/base/log_message.h \
> @@ -180,6 +182,7 @@ bin_libbase_test_LDFLAGS = \
>       src/base/lib_libopensaf_core_la-getenv.lo \
>       src/base/lib_libopensaf_core_la-log_message.lo \
>       src/base/lib_libopensaf_core_la-process.lo \
> +     src/base/lib_libopensaf_core_la-file_descriptor.lo \
>       src/base/lib_libopensaf_core_la-file_notify.lo
>   
>   bin_libbase_test_SOURCES = \
> diff --git a/src/base/file_descriptor.cc b/src/base/file_descriptor.cc
> new file mode 100644
> --- /dev/null
> +++ b/src/base/file_descriptor.cc
> @@ -0,0 +1,27 @@
> +/*      -*- OpenSAF  -*-
> + *
> + * Copyright Ericsson AB 2017 - All Rights Reserved.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
> + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed
> + * under the GNU Lesser General Public License Version 2.1, February 1999.
> + * The complete license can be accessed from the following location:
> + * http://opensource.org/licenses/lgpl-license.php
> + * See the Copying file included with the OpenSAF distribution for full
> + * licensing terms.
> + *
> + */
> +
> +#include "base/file_descriptor.h"
> +#include <fcntl.h>
> +#include <unistd.h>
> +
> +namespace base {
> +
> +bool MakeFdNonblocking(int fd) {
> +  int flags = fcntl(fd, F_GETFL);
> +  return flags != -1 && fcntl(fd, F_SETFL, flags | O_NONBLOCK) != -1;
> +}
> +
> +}  // namespace base
> diff --git a/src/base/file_descriptor.h b/src/base/file_descriptor.h
> new file mode 100644
> --- /dev/null
> +++ b/src/base/file_descriptor.h
> @@ -0,0 +1,27 @@
> +/*      -*- OpenSAF  -*-
> + *
> + * Copyright Ericsson AB 2017 - All Rights Reserved.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
> + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed
> + * under the GNU Lesser General Public License Version 2.1, February 1999.
> + * The complete license can be accessed from the following location:
> + * http://opensource.org/licenses/lgpl-license.php
> + * See the Copying file included with the OpenSAF distribution for full
> + * licensing terms.
> + *
> + */
> +
> +#ifndef BASE_FILE_DESCRIPTOR_H_
> +#define BASE_FILE_DESCRIPTOR_H_
> +
> +namespace base {
> +
> +// Set the a file descriptor's non-blocking flag. Returns true if successful,
> +// and false otherwise.
> +bool MakeFdNonblocking(int fd);
> +
> +}  // namespace base
> +
> +#endif  // BASE_FILE_DESCRIPTOR_H_
> diff --git a/src/base/tests/unix_socket_test.cc 
> b/src/base/tests/unix_socket_test.cc
> --- a/src/base/tests/unix_socket_test.cc
> +++ b/src/base/tests/unix_socket_test.cc
> @@ -1,6 +1,7 @@
>   /*      -*- OpenSAF  -*-
>    *
>    * (C) Copyright 2016 The OpenSAF Foundation
> + * Copyright Ericsson AB 2017 - All Rights Reserved.
>    *
>    * This program is distributed in the hope that it will be useful, but
>    * WITHOUT ANY WARRANTY; without even the implied warranty of 
> MERCHANTABILITY
> @@ -15,18 +16,18 @@
>    *
>    */
>   
> +#include <sys/socket.h>
>   #include <sys/stat.h>
>   #include <unistd.h>
> +#include <cerrno>
>   #include <cstdlib>
> +#include <cstring>
>   #include <string>
>   #include "base/unix_server_socket.h"
>   #include "gtest/gtest.h"
>   
>   class UnixSocketTest : public ::testing::Test {
> - public:
> -
>    protected:
> -
>     UnixSocketTest() {
>     }
>   
> @@ -43,7 +44,6 @@ class UnixSocketTest : public ::testing:
>       return result == 0 && S_ISSOCK(buf.st_mode);
>     }
>   
> -  // cppcheck-suppress unusedFunction
>     virtual void SetUp() {
>       char dir_tmpl[] = "/tmp/unix_socket_testXXXXXX";
>       char* dir_name = mkdtemp(dir_tmpl);
> @@ -51,7 +51,6 @@ class UnixSocketTest : public ::testing:
>       tmpdir_ = std::string{dir_name};
>     }
>   
> -  // cppcheck-suppress unusedFunction
>     virtual void TearDown() {
>       if (!tmpdir_.empty()) {
>         unlink(SocketName().c_str());
> @@ -62,8 +61,8 @@ class UnixSocketTest : public ::testing:
>     std::string tmpdir_;
>   };
>   
> -TEST_F(UnixSocketTest, CheckThatServerUnlinksSocket) {
> -  EXPECT_FALSE(SocketExists());
> +TEST_F(UnixSocketTest, DestructorShallUnlinkSocket) {
> +  ASSERT_FALSE(SocketExists());
>     base::UnixServerSocket* sock = new base::UnixServerSocket(SocketName());
>     EXPECT_FALSE(SocketExists());
>     char buf[32];
> @@ -72,3 +71,50 @@ TEST_F(UnixSocketTest, CheckThatServerUn
>     delete sock;
>     EXPECT_FALSE(SocketExists());
>   }
> +
> +TEST_F(UnixSocketTest, OpenShallUnlinkExistingSocket) {
> +  ASSERT_FALSE(SocketExists());
> +  int sock = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
> +  ASSERT_GE(sock, 0);
> +  struct sockaddr_un addr{AF_UNIX, {}};
> +  memcpy(addr.sun_path, SocketName().c_str(), SocketName().size() + 1);
> +  ASSERT_EQ(bind(sock,
> +                 reinterpret_cast<const struct sockaddr*>(&addr),
> +                 sizeof(addr)), 0);
> +  ASSERT_EQ(close(sock), 0);
> +  EXPECT_TRUE(SocketExists());
> +  base::UnixServerSocket* server = new base::UnixServerSocket(SocketName());
> +  char buf[32];
> +  ssize_t result = server->Recv(buf, sizeof(buf));
> +  int saved_errno = errno;
> +  EXPECT_EQ(result, -1);
> +  EXPECT_EQ(saved_errno, EAGAIN);
> +  EXPECT_GE(server->fd(), 0);
> +  delete server;
> +}
> +
> +TEST_F(UnixSocketTest, DontUnlinkExistingSocketIfServerStillAlive) {
> +  ASSERT_FALSE(SocketExists());
> +  base::UnixServerSocket* sock = new base::UnixServerSocket(SocketName());
> +  char buf[32];
> +  ssize_t result = sock->Recv(buf, sizeof(buf));
> +  EXPECT_EQ(result, -1);
> +  EXPECT_EQ(errno, EAGAIN);
> +  base::UnixServerSocket* sock2 = new base::UnixServerSocket(SocketName());
> +  result = sock2->Recv(buf, sizeof(buf));
> +  EXPECT_EQ(result, -1);
> +  EXPECT_EQ(errno, EADDRINUSE);
> +  delete sock2;
> +  EXPECT_TRUE(SocketExists());
> +  delete sock;
> +  EXPECT_FALSE(SocketExists());
> +}
> +
> +TEST_F(UnixSocketTest, FdMethodShallOpenTheSocket) {
> +  ASSERT_FALSE(SocketExists());
> +  base::UnixServerSocket* sock = new base::UnixServerSocket(SocketName());
> +  int fd = sock->fd();
> +  EXPECT_GE(fd, 0);
> +  EXPECT_TRUE(SocketExists());
> +  delete sock;
> +}
> diff --git a/src/base/unix_client_socket.cc b/src/base/unix_client_socket.cc
> --- a/src/base/unix_client_socket.cc
> +++ b/src/base/unix_client_socket.cc
> @@ -1,6 +1,7 @@
>   /*      -*- OpenSAF  -*-
>    *
>    * (C) Copyright 2016 The OpenSAF Foundation
> + * Copyright Ericsson AB 2017 - All Rights Reserved.
>    *
>    * This program is distributed in the hope that it will be useful, but
>    * WITHOUT ANY WARRANTY; without even the implied warranty of 
> MERCHANTABILITY
> @@ -17,7 +18,6 @@
>   
>   #include "base/unix_client_socket.h"
>   #include <sys/socket.h>
> -#include <time.h>
>   #include <cerrno>
>   
>   namespace base {
> @@ -31,15 +31,9 @@ UnixClientSocket::~UnixClientSocket() {
>   
>   bool UnixClientSocket::OpenHook(int sock) {
>     int result;
> -  int e;
>     do {
>       result = connect(sock, addr(), addrlen());
> -    e = errno;
> -    if (result != 0 && (e == EALREADY || e == EINPROGRESS)) {
> -      struct timespec delay{0, 10000000};
> -      clock_nanosleep(CLOCK_MONOTONIC, 0, &delay, nullptr);
> -    }
> -  } while (result != 0 && (e == EINTR || e == EALREADY || e == EINPROGRESS));
> +  } while (result != 0 && errno == EINTR);
>     return result == 0;
>   }
>   
> diff --git a/src/base/unix_server_socket.cc b/src/base/unix_server_socket.cc
> --- a/src/base/unix_server_socket.cc
> +++ b/src/base/unix_server_socket.cc
> @@ -1,6 +1,7 @@
>   /*      -*- OpenSAF  -*-
>    *
>    * (C) Copyright 2016 The OpenSAF Foundation
> + * Copyright Ericsson AB 2017 - All Rights Reserved.
>    *
>    * This program is distributed in the hope that it will be useful, but
>    * WITHOUT ANY WARRANTY; without even the implied warranty of 
> MERCHANTABILITY
> @@ -27,10 +28,19 @@ UnixServerSocket::UnixServerSocket(const
>   }
>   
>   UnixServerSocket::~UnixServerSocket() {
> -  if (fd() >= 0) UnixServerSocket::CloseHook();
> +  if (get_fd() >= 0) UnixServerSocket::CloseHook();
>   }
>   
>   bool UnixServerSocket::OpenHook(int sock) {
> +  int tmp_sock = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
> +  int connect_result;
> +  int connect_errno;
> +  do {
> +    connect_result = connect(tmp_sock, addr(), addrlen());
> +    connect_errno = errno;
> +  } while (connect_result != 0 && connect_errno == EINTR);
> +  close(tmp_sock);
> +  if (connect_result != 0 && connect_errno == ECONNREFUSED) unlink(path());
>     return bind(sock, addr(), addrlen()) == 0;
>   }
>   
> diff --git a/src/base/unix_socket.cc b/src/base/unix_socket.cc
> --- a/src/base/unix_socket.cc
> +++ b/src/base/unix_socket.cc
> @@ -1,6 +1,7 @@
>   /*      -*- OpenSAF  -*-
>    *
>    * (C) Copyright 2016 The OpenSAF Foundation
> + * Copyright Ericsson AB 2017 - All Rights Reserved.
>    *
>    * This program is distributed in the hope that it will be useful, but
>    * WITHOUT ANY WARRANTY; without even the implied warranty of 
> MERCHANTABILITY
> @@ -19,14 +20,17 @@
>   #include <sys/socket.h>
>   #include <unistd.h>
>   #include <cstring>
> +#include "base/file_descriptor.h"
>   #include "base/osaf_utility.h"
> +#include "base/time.h"
>   
>   namespace base {
>   
>   UnixSocket::UnixSocket(const std::string& path) :
>       fd_{-1},
>       addr_{AF_UNIX, {}},
> -    mutex_{} {
> +    last_failed_open_{},
> +    saved_errno_{} {
>     if (path.size() < sizeof(addr_.sun_path)) {
>       memcpy(addr_.sun_path, path.c_str(), path.size() + 1);
>     } else {
> @@ -35,18 +39,27 @@ UnixSocket::UnixSocket(const std::string
>   }
>   
>   int UnixSocket::Open() {
> -  Lock lock(mutex_);
>     int sock = fd_;
>     if (sock < 0) {
>       if (addr_.sun_path[0] != '\0') {
> -      sock = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
> -      if (sock >= 0 && !OpenHook(sock)) {
> -        int e = errno;
> -        close(sock);
> -        sock = -1;
> -        errno = e;
> +      struct timespec current_time = ReadMonotonicClock();
> +      if (TimespecToMillis(current_time - last_failed_open_) != 0) {
> +        sock = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
> +        if (sock >= 0 && !OpenHook(sock)) {
> +          int e = errno;
> +          close(sock);
> +          sock = -1;
> +          errno = e;
> +        }
> +        fd_ = sock;
> +        if (sock >=0 && MakeFdNonblocking(sock) == false) Close();
> +        if (fd_ < 0) {
> +          last_failed_open_ = current_time;
> +          saved_errno_ = errno;
> +        }
> +      } else {
> +        errno = saved_errno_;
>         }
> -      fd_ = sock;
>       } else {
>         errno = ENAMETOOLONG;
>       }
> @@ -61,7 +74,6 @@ UnixSocket::~UnixSocket() {
>   }
>   
>   void UnixSocket::Close() {
> -  Lock lock(mutex_);
>     int sock = fd_;
>     if (sock >= 0) {
>       int e = errno;
> diff --git a/src/base/unix_socket.h b/src/base/unix_socket.h
> --- a/src/base/unix_socket.h
> +++ b/src/base/unix_socket.h
> @@ -1,6 +1,7 @@
>   /*      -*- OpenSAF  -*-
>    *
>    * (C) Copyright 2016 The OpenSAF Foundation
> + * Copyright Ericsson AB 2017 - All Rights Reserved.
>    *
>    * This program is distributed in the hope that it will be useful, but
>    * WITHOUT ANY WARRANTY; without even the implied warranty of 
> MERCHANTABILITY
> @@ -21,25 +22,40 @@
>   #include <pthread.h>
>   #include <sys/socket.h>
>   #include <sys/un.h>
> +#include <time.h>
>   #include <cerrno>
>   #include <string>
>   #include "base/macros.h"
> -#include "base/mutex.h"
>   
>   namespace base {
>   
> -// A class implementing non-blocking operations on a UNIX domain socket.
> +// A class implementing non-blocking operations on a UNIX domain socket. This
> +// class is not thread-safe and must be protected by a mutex if shared 
> between
> +// multiple threads.
>   class UnixSocket {
>    public:
>     // Close the socket.
>     virtual ~UnixSocket();
> +  // Returns the current file descriptor for this UNIX socket, or -1 if the
> +  // socket could not be opened. Note that the Send() and Recv() methods may
> +  // open and/or close the socket, and the file descriptor can therefore
> +  // potentially be change as a result of calling any of these two methods. 
> If a
> +  // UnixSocket instance is shared between multiple threads (and therefore
> +  // protected with a mutex), this means that the result from the fd() 
> method is
> +  // only valid for as long as the mutex protecting the instance is held.
> +  int fd() {
> +    int sock = fd_;
> +    if (sock < 0) sock = Open();
> +    return sock;
> +  }
>     // Send a message in non-blocking mode. This call will open the socket if 
> it
>     // was not already open. The EINTR error code from the send() libc 
> function is
> -  // handled by retrying the send() call in a loop. In case of other errors, 
> the
> -  // socket will be closed.
> +  // handled by retrying the send() call in a loop. In case of other errors, 
> -1
> +  // is returned and errno contains one of the codes listed in the recv() 
> libc
> +  // function. The socket will be closed in case the error was not EINTR, 
> EAGAIN
> +  // or EWOULDBLOCK.
>     ssize_t Send(const void* buffer, size_t length) {
> -    int sock = fd_;
> -    if (sock < 0) sock = Open();
> +    int sock = fd();
>       ssize_t result = -1;
>       if (sock >= 0) {
>         do {
> @@ -52,10 +68,11 @@ class UnixSocket {
>     // Receive a message in non-blocking mode. This call will open the socket 
> if
>     // it was not already open. The EINTR error code from the recv() libc 
> function
>     // is handled by retrying the recv() call in a loop. In case of other 
> errors,
> -  // the socket will be closed.
> +  // -1 is returned and errno contains one of the codes listed in the recv()
> +  // libc function. The socket will be closed in case the error was not 
> EINTR,
> +  // EAGAIN or EWOULDBLOCK.
>     ssize_t Recv(void* buffer, size_t length) {
> -    int sock = fd_;
> -    if (sock < 0) sock = Open();
> +    int sock = fd();
>       ssize_t result = -1;
>       if (sock >= 0) {
>         do {
> @@ -65,28 +82,26 @@ class UnixSocket {
>       }
>       return result;
>     }
> -  // Returns the current file descriptor for this UNIX socket, or -1 if the
> -  // socket is currently not open. Note that the Send() and Recv() methods 
> may
> -  // open and/or close the socket, and potentially the file descriptor will 
> be
> -  // different after a call to any of these two methods.
> -  int fd() const { return fd_; }
>   
>    protected:
>     explicit UnixSocket(const std::string& path);
> -  int Open();
> -  void Close();
>     virtual bool OpenHook(int sock);
>     virtual void CloseHook();
>     const struct sockaddr* addr() const {
>       return reinterpret_cast<const struct sockaddr*>(&addr_);
>     }
>     static socklen_t addrlen() { return sizeof(addr_); }
> +  int get_fd() const { return fd_; }
>     const char* path() const { return addr_.sun_path; }
>   
>    private:
> +  int Open();
> +  void Close();
> +
>     int fd_;
>     struct sockaddr_un addr_;
> -  Mutex mutex_;
> +  struct timespec last_failed_open_;
> +  int saved_errno_;
>   
>     DELETE_COPY_AND_MOVE_OPERATORS(UnixSocket);
>   };


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, SlashDot.org! http://sdm.link/slashdot
_______________________________________________
Opensaf-devel mailing list
Opensaf-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/opensaf-devel

Reply via email to