osaf/libs/core/cplusplus/base/Makefile.am | 13 +- osaf/libs/core/cplusplus/base/buffer.h | 94 ++++++++ osaf/libs/core/cplusplus/base/log_message.cc | 143 ++++++++++++ osaf/libs/core/cplusplus/base/log_message.h | 188 ++++++++++++++++ osaf/libs/core/cplusplus/base/tests/Makefile.am | 12 +- osaf/libs/core/cplusplus/base/tests/log_message_test.cc | 112 +++++++++ osaf/libs/core/cplusplus/base/unix_client_socket.cc | 52 ++++ osaf/libs/core/cplusplus/base/unix_client_socket.h | 41 +++ osaf/libs/core/cplusplus/base/unix_server_socket.cc | 51 ++++ osaf/libs/core/cplusplus/base/unix_server_socket.h | 42 +++ osaf/libs/core/cplusplus/base/unix_socket.cc | 58 ++++ osaf/libs/core/cplusplus/base/unix_socket.h | 89 +++++++ 12 files changed, 888 insertions(+), 7 deletions(-)
Add support classes for using UNIX sockets, and for formatting log messages according to rfc5424. diff --git a/osaf/libs/core/cplusplus/base/Makefile.am b/osaf/libs/core/cplusplus/base/Makefile.am --- a/osaf/libs/core/cplusplus/base/Makefile.am +++ b/osaf/libs/core/cplusplus/base/Makefile.am @@ -23,10 +23,15 @@ DEFAULT_INCLUDES = SUBDIRS = tests noinst_HEADERS = \ + buffer.h \ getenv.h \ + log_message.h \ macros.h \ process.h \ - time.h + time.h \ + unix_client_socket.h \ + unix_server_socket.h \ + unix_socket.h noinst_LTLIBRARIES = libbase.la @@ -39,4 +44,8 @@ libbase_la_LDFLAGS = -static libbase_la_SOURCES = \ getenv.cc \ - process.cc + log_message.cc \ + process.cc \ + unix_client_socket.cc \ + unix_server_socket.cc \ + unix_socket.cc diff --git a/osaf/libs/core/cplusplus/base/buffer.h b/osaf/libs/core/cplusplus/base/buffer.h new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/buffer.h @@ -0,0 +1,94 @@ +/* -*- OpenSAF -*- + * + * (C) Copyright 2016 The OpenSAF Foundation + * + * 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. + * + * Author(s): Ericsson AB + * + */ + +#ifndef OSAF_LIBS_CORE_CPLUSPLUS_BASE_BUFFER_H_ +#define OSAF_LIBS_CORE_CPLUSPLUS_BASE_BUFFER_H_ + +#include <cstddef> +#include <cstdint> +#include <cstring> +#include "osaf/libs/core/cplusplus/base/macros.h" + +namespace base { + +// An output buffer that can be used to build e.g. a message to be sent over a +// network. +class Buffer { + public: + // Allocate a buffer with enough space to store @a capacity bytes of data. + explicit Buffer(size_t capacity) : + buffer_{new char[capacity]}, + capacity_{capacity}, + size_{0} { + } + ~Buffer() { + delete [] buffer_; + } + // Reset the write position to the start of the buffer. + void clear() { size_ = 0; } + // Returns true if the buffer is empty. + bool empty() const { return size_ == 0; } + // Returns a pointer to the start of the buffer. + const char* data() const { return buffer_; } + // Returns the number of bytes that have been written to the buffer. + size_t size() const { return size_; } + // Append a single character to the end of the buffer. + void AppendChar(char c) { + if (size_ != capacity_) buffer_[size_++] = c; + } + // This function is similar to AppendNumber(), except that leading zeros will + // be printed - i.e. this method implements a fixed field width. + void AppendFixedWidthNumber(uint32_t number, uint32_t power) { + for (;;) { + uint32_t digit = number / power; + AppendChar(digit + '0'); + if (power == 1) break; + number -= digit * power; + power /= 10; + } + } + // Append decimal @a number to the end of the buffer. The @a power parameter + // must be a power of ten, pointing to the highest possible digit in the + // decimal number. E.g. if @a power is 1000, then the @a number must not be + // greater than 9999. + void AppendNumber(uint32_t number, uint32_t power) { + while (power != 1 && power > number) power /= 10; + AppendFixedWidthNumber(number, power); + } + // Append a string of @a size characters to the end of the buffer. + void AppendString(const char* str, size_t size) { + size_t bytes_to_copy = capacity_ - size_; + if (size < bytes_to_copy) bytes_to_copy = size; + memcpy(buffer_ + size_, str, bytes_to_copy); + size_ += bytes_to_copy; + } + // Append a NUL-terminated string to the end of the buffer. + void AppendString(const char* str) { + AppendString(str, strlen(str)); + } + + private: + char* buffer_; + size_t capacity_; + size_t size_; + + DELETE_COPY_AND_MOVE_OPERATORS(Buffer); +}; + +} // namespace base + +#endif // OSAF_LIBS_CORE_CPLUSPLUS_BASE_BUFFER_H_ diff --git a/osaf/libs/core/cplusplus/base/log_message.cc b/osaf/libs/core/cplusplus/base/log_message.cc new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/log_message.cc @@ -0,0 +1,143 @@ +/* -*- OpenSAF -*- + * + * (C) Copyright 2016 The OpenSAF Foundation + * + * 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. + * + * Author(s): Ericsson AB + * + */ + +#include "osaf/libs/core/cplusplus/base/log_message.h" +#include <time.h> +#include <cstdint> +#include "osaf/libs/core/common/include/osaf_time.h" +#include "osaf/libs/core/cplusplus/base/buffer.h" +#include "osaf/libs/core/cplusplus/base/time.h" + +namespace base { + +const struct timespec LogMessage::kNullTime{0, -1}; + +void LogMessage::Write(Facility facility, Severity severity, + const struct timespec& time_stamp, + const HostName& host_name, + const AppName& app_name, + const ProcId& proc_id, + const MsgId& msg_id, + const StructuredElements& structured_elements, + const std::string& message, + Buffer* buffer) { + uint32_t priority = static_cast<uint32_t>(facility) * uint32_t{8} + + static_cast<uint32_t>(severity); + buffer->AppendChar('<'); + buffer->AppendNumber(priority, 100); + buffer->AppendString(">1 ", 3); + WriteTime(time_stamp, buffer); + buffer->AppendChar(' '); + buffer->AppendString(host_name.data(), host_name.size()); + buffer->AppendChar(' '); + buffer->AppendString(app_name.data(), app_name.size()); + buffer->AppendChar(' '); + buffer->AppendString(proc_id.data(), proc_id.size()); + buffer->AppendChar(' '); + buffer->AppendString(msg_id.data(), msg_id.size()); + buffer->AppendChar(' '); + if (structured_elements.empty()) { + buffer->AppendChar('-'); + } else { + for (const auto& elem : structured_elements) elem.Write(buffer); + } + if (!message.empty()) { + buffer->AppendChar(' '); + buffer->AppendString(message.data(), message.size()); + } +} + +void LogMessage::WriteTime(const struct timespec& ts, Buffer* buffer) { + struct tm local_time; + struct tm* local_ptr = localtime_r(&ts.tv_sec, &local_time); + if (ts.tv_nsec >= 0 && ts.tv_nsec < kNanosPerSec && local_ptr != nullptr) { + char time_zone[8]; + int16_t time_zone_offset = 0; + size_t result = strftime(time_zone, sizeof(time_zone), "%z", local_ptr); + if (result == 5) { + int hours = int{time_zone[1] - '0'} * 10 + int{time_zone[2] - '0'}; + int minutes = int{time_zone[3] - '0'} * 10 + int{time_zone[4] - '0'}; + time_zone_offset = 60 * hours + minutes; + if (time_zone[0] == '-') time_zone_offset = -time_zone_offset; + } + buffer->AppendFixedWidthNumber(local_ptr->tm_year + 1900, 1000); + buffer->AppendChar('-'); + buffer->AppendFixedWidthNumber(local_ptr->tm_mon + 1, 10); + buffer->AppendChar('-'); + buffer->AppendFixedWidthNumber(local_ptr->tm_mday, 10); + buffer->AppendChar('T'); + buffer->AppendFixedWidthNumber(local_ptr->tm_hour, 10); + buffer->AppendChar(':'); + buffer->AppendFixedWidthNumber(local_ptr->tm_min, 10); + buffer->AppendChar(':'); + buffer->AppendFixedWidthNumber(local_ptr->tm_min, 10); + uint32_t decimals = ts.tv_nsec / 1000; + if (decimals != 0) { + uint32_t power = 100000; + while (decimals % 10 == 0) { + decimals /= 10; + power /= 10; + } + buffer->AppendChar('.'); + buffer->AppendFixedWidthNumber(decimals, power); + } + if (time_zone_offset != 0) { + char sign; + int16_t offset; + if (time_zone_offset >= 0) { + sign = '+'; + offset = time_zone_offset; + } else { + sign = '-'; + offset = -time_zone_offset; + } + int16_t hours = offset / 60; + int16_t minutes = offset % 60; + buffer->AppendChar(sign); + buffer->AppendFixedWidthNumber(hours, 10); + buffer->AppendChar(':'); + buffer->AppendFixedWidthNumber(minutes, 10); + } else { + buffer->AppendChar('Z'); + } + } else { + buffer->AppendChar('-'); + } +} + +void LogMessage::Element::Write(Buffer* buffer) const { + buffer->AppendChar('['); + buffer->AppendString(id_.data(), id_.size()); + for (const Parameter& param : parameter_list_) { + buffer->AppendChar(' '); + param.Write(buffer); + } + buffer->AppendChar(']'); +} + +void LogMessage::Parameter::Write(Buffer* buffer) const { + buffer->AppendString(name_.data(), name_.size()); + buffer->AppendChar('='); + buffer->AppendChar('"'); + for (const char& c : value_) { + if (c == '"' || c == '\\' || c == ']') buffer->AppendChar('\\'); + buffer->AppendChar(c); + } + buffer->AppendChar('"'); +} + +} // namespace base diff --git a/osaf/libs/core/cplusplus/base/log_message.h b/osaf/libs/core/cplusplus/base/log_message.h new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/log_message.h @@ -0,0 +1,188 @@ +/* -*- OpenSAF -*- + * + * (C) Copyright 2016 The OpenSAF Foundation + * + * 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. + * + * Author(s): Ericsson AB + * + */ + +#ifndef OSAF_LIBS_CORE_CPLUSPLUS_BASE_LOG_MESSAGE_H_ +#define OSAF_LIBS_CORE_CPLUSPLUS_BASE_LOG_MESSAGE_H_ + +#include <list> +#include <set> +#include <string> + +namespace base { + +class Buffer; + +// The LogMessage class implements support for formatting log records according +// to RFC 5424 +class LogMessage { + public: + class String { + public: + String(const char* str, const std::string::size_type size) : + str_{size != 0 ? str : "-", size != 0 ? size : 1} {} + String(const std::string& str, const std::string::size_type max_size) : + str_{str.empty() ? std::string{"-"} : str.substr(0, max_size)} {} + bool empty() const { return str_.empty(); } + const char* c_str() const { return str_.c_str(); } + const char* data() const { return str_.data(); } + const std::string::size_type size() const { return str_.size(); } + bool operator<(const String& str) const { return str_ < str.str_; } + bool operator==(const String& str) const { return str_ == str.str_; } + protected: + constexpr static const bool IsPrintableAscii(char c) { + return c >= 33 && c <= 126; + } + std::string str_; + }; + // std::string extended with a maximum string length and a limitation that all + // characters must be printable ASCII. Empty strings are not allowed. When + // creating an object of this class, the string is truncated if it exceeds the + // maximum string length. Characters outside the range of printable ASCII will + // be replaced with underscore characters (_). If the string is empty, it will + // be replaced with a single dash (-). + template <std::string::size_type MaxSize> + class PrintableAscii : public String { + public: + explicit PrintableAscii(const std::string& str) : + String{str, MaxSize} { + for (char& c : str_) if (!IsPrintableAscii(c)) c = '_'; + } + }; + // std::string extended with a maximum string length of 32 characters and a + // limitation that all characters must be printable ASCII. Empty strings are + // not allowed. The characters =, ] and " are not allowed. When creating an + // object of this class, the string is truncated if it exceeds the maximum + // string length. Illegal characters will be replaced with underscore + // characters (_). If the string is empty, it will be replaced with a single + // dash (-). + class SdName : public String { + public: + constexpr static const std::string::size_type kMaxSize = 32; + explicit SdName(const std::string& sd_name) : + String{sd_name, kMaxSize} { + for (char& c : str_) { + if (!IsPrintableAscii(c) || c == '=' || c == ']' || c == '"') c = '_'; + } + } + }; + // Host name where the log record was created. Maximum length is 255 printable + // ASCII characters. + using HostName = PrintableAscii<255>; + // Name of the application that created the log record. Maximum length is 48 + // printable ASCII characters. + using AppName = PrintableAscii<48>; + // Process if of the application that created the log record. Maximum length + // is 128 printable ASCII characters. + using ProcId = PrintableAscii<128>; + // Message id of this log record. Maximum length is 32 printable ASCII + // characters. + using MsgId = PrintableAscii<32>; + // The facility that produced this log record. + enum class Facility { + kKern = 0, + kUser = 1, + kMail = 2, + kDaemon = 3, + kAuth = 4, + kSyslog = 5, + kLpr = 6, + kNews = 7, + kUucp = 8, + kCron = 9, + kAuthPriv = 10, + kFtp = 11, + kNtp = 12, + kAudit = 13, + kAlert = 14, + kClock = 15, + kLocal0 = 16, + kLocal1 = 17, + kLocal2 = 18, + kLocal3 = 19, + kLocal4 = 20, + kLocal5 = 21, + kLocal6 = 22, + kLocal7 = 23 + }; + // The severity level of this log record. + enum class Severity { + kEmerg = 0, + kAlert = 1, + kCrit = 2, + kErr = 3, + kWarning = 4, + kNotice = 5, + kInfo = 6, + kDebug = 7 + }; + // A parameter/value pair for a structued element + class Parameter { + public: + Parameter(const SdName& name, const std::string& value) : + name_{name}, + value_{value} {} + void Write(Buffer* buffer) const; + bool operator==(const Parameter& param) const { + return name_ == param.name_ && value_ == param.value_; + } + private: + SdName name_; + std::string value_; + }; + // A list of parameter/value pairs for a structued element + using ParameterList = std::list<Parameter>; + // A stuctured element consisting of an identifier and a list of + // parameter/value pairs. + class Element { + public: + Element(const SdName& id, + const ParameterList& parameter_list) : + id_{id}, + parameter_list_{parameter_list} {} + void Write(Buffer* buffer) const; + bool operator<(const Element& elem) const { return id_ < elem.id_; } + bool operator==(const Element& elem) const { + return id_ == elem.id_ && parameter_list_ == elem.parameter_list_; + } + private: + SdName id_; + ParameterList parameter_list_; + }; + // A set of stuctured elements. Each element must have a unique identifier. + using StructuredElements = std::set<Element>; + // kNullTime is intended to be used when the time the log record was generated + // is not known. + static const struct timespec kNullTime; + // Format a log record according to rfc5424 and write it to the provided + // buffer. + static void Write(Facility facility, Severity severity, + const struct timespec& time_stamp, + const HostName& host_name, + const AppName& app_name, + const ProcId& proc_id, + const MsgId& msg_id, + const StructuredElements& structured_elements, + const std::string& message, + Buffer* buffer); + + private: + static void WriteTime(const struct timespec& ts, Buffer* buffer); +}; + +} // namespace base + +#endif // OSAF_LIBS_CORE_CPLUSPLUS_BASE_LOG_MESSAGE_H_ diff --git a/osaf/libs/core/cplusplus/base/tests/Makefile.am b/osaf/libs/core/cplusplus/base/tests/Makefile.am --- a/osaf/libs/core/cplusplus/base/tests/Makefile.am +++ b/osaf/libs/core/cplusplus/base/tests/Makefile.am @@ -32,17 +32,19 @@ libbase_test_CPPFLAGS = \ libbase_test_LDFLAGS = \ -pthread -lrt \ $(top_builddir)/osaf/libs/core/cplusplus/base/libbase_la-getenv.o \ + $(top_builddir)/osaf/libs/core/cplusplus/base/libbase_la-log_message.o \ $(top_builddir)/osaf/libs/core/cplusplus/base/libbase_la-process.o libbase_test_SOURCES = \ + getenv_test.cc \ + log_message_test.cc \ + mock_logtrace.cc \ + mock_osaf_abort.cc \ + mock_osafassert.cc \ time_add_test.cc \ - time_subtract_test.cc \ time_compare_test.cc \ time_convert_test.cc \ - getenv_test.cc \ - mock_logtrace.cc \ - mock_osafassert.cc \ - mock_osaf_abort.cc + time_subtract_test.cc libbase_test_LDADD = \ $(GTEST_DIR)/lib/libgtest.la \ diff --git a/osaf/libs/core/cplusplus/base/tests/log_message_test.cc b/osaf/libs/core/cplusplus/base/tests/log_message_test.cc new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/tests/log_message_test.cc @@ -0,0 +1,112 @@ +/* -*- OpenSAF -*- + * + * (C) Copyright 2016 The OpenSAF Foundation + * + * 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. + * + * Author(s): Ericsson AB + * + */ + +#include <time.h> +#include <cstdlib> +#include <cstring> +#include "gtest/gtest.h" +#include "osaf/libs/core/cplusplus/base/buffer.h" +#include "osaf/libs/core/cplusplus/base/log_message.h" + +static const struct timespec time_stamp_1 = { 1475749599, 497243812 }; +static const struct timespec time_stamp_2 = { 1458469599, 97240812 }; + +static const char hello_world_text_1[] = "<133>1 2016-10-06T12:26:26.497243" + "+02:00 PL-42 log_message_test 24744 4711 " + "[elem1 param1=\"value1\" param2=\"value2\"][elem2] hello, world!"; + +static const char hello_world_text_2[] = "<191>1 2016-03-20T11:26:26.09724" + "+01:00 dark_star - _ 12345678901234567890123456789012 " + "[- -=\"\" a_b_c=\"d=e\\]f\"][_] test "; + +static const char null_text[] = "<0>1 - - - - - -"; + +TEST(LogMessageTest, WriteHelloWorld1) { + setenv("TZ", "Europe/Stockholm", 1); + tzset(); + base::Buffer buf{256}; + base::LogMessage::Write( + base::LogMessage::Facility::kLocal0, + base::LogMessage::Severity::kNotice, + time_stamp_1, + base::LogMessage::HostName{"PL-42"}, + base::LogMessage::AppName{"log_message_test"}, + base::LogMessage::ProcId{"24744"}, + base::LogMessage::MsgId{"4711"}, + {{base::LogMessage::SdName{"elem1"}, + {base::LogMessage::Parameter{base::LogMessage::SdName{"param1"}, + "value1"}, + base::LogMessage::Parameter{base::LogMessage::SdName{"param2"}, + "value2"}}}, + {base::LogMessage::SdName{"elem2"}, + {}}}, + "hello, world!", + &buf); + EXPECT_EQ(buf.size(), sizeof(hello_world_text_1) - 1); + EXPECT_EQ(memcmp(buf.data(), + hello_world_text_1, + sizeof(hello_world_text_1) - 1), 0); +} + +TEST(LogMessageTest, WriteHelloWorld2) { + setenv("TZ", "Europe/Stockholm", 1); + tzset(); + base::Buffer buf{256}; + base::LogMessage::Write( + base::LogMessage::Facility::kLocal7, + base::LogMessage::Severity::kDebug, + time_stamp_2, + base::LogMessage::HostName{"dark star"}, + base::LogMessage::AppName{""}, + base::LogMessage::ProcId{"_"}, + base::LogMessage::MsgId{"123456789012345678901234567890123"}, + {{base::LogMessage::SdName{""}, + {base::LogMessage::Parameter{base::LogMessage::SdName{""}, + ""}, + base::LogMessage::Parameter{base::LogMessage::SdName{"a=b]c"}, + "d=e]f"}}}, + {base::LogMessage::SdName{"_"}, + {}}}, + " test ", + &buf); + EXPECT_EQ(buf.size(), sizeof(hello_world_text_2) - 1); + EXPECT_EQ(memcmp(buf.data(), + hello_world_text_2, + sizeof(hello_world_text_2) - 1), 0); +} + +TEST(LogMessageTest, WriteNullMessage) { + setenv("TZ", "Europe/Stockholm", 1); + tzset(); + base::Buffer buf(256); + base::LogMessage::Write( + base::LogMessage::Facility::kKern, + base::LogMessage::Severity::kEmerg, + base::LogMessage::kNullTime, + base::LogMessage::HostName{""}, + base::LogMessage::AppName{""}, + base::LogMessage::ProcId{""}, + base::LogMessage::MsgId{""}, + {}, + "", + &buf); + + EXPECT_EQ(buf.size(), sizeof(null_text) - 1); + EXPECT_EQ(memcmp(buf.data(), + null_text, + sizeof(null_text) - 1), 0); +} diff --git a/osaf/libs/core/cplusplus/base/unix_client_socket.cc b/osaf/libs/core/cplusplus/base/unix_client_socket.cc new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/unix_client_socket.cc @@ -0,0 +1,52 @@ +/* -*- OpenSAF -*- + * + * (C) Copyright 2016 The OpenSAF Foundation + * + * 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. + * + * Author(s): Ericsson AB + * + */ + +#include "osaf/libs/core/cplusplus/base/unix_client_socket.h" +#include <sys/socket.h> +#include <time.h> +#include <cerrno> + +namespace base { + +UnixClientSocket::UnixClientSocket(const std::string& path) : + UnixSocket{path} { +} + +UnixClientSocket::~UnixClientSocket() { +} + +void UnixClientSocket::Open() { + if (fd() < 0) { + UnixSocket::Open(); + if (fd() >= 0) { + int result; + int e; + do { + result = connect(fd(), 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)); + if (result != 0) Close(); + } + } +} + +} // namespace base diff --git a/osaf/libs/core/cplusplus/base/unix_client_socket.h b/osaf/libs/core/cplusplus/base/unix_client_socket.h new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/unix_client_socket.h @@ -0,0 +1,41 @@ +/* -*- OpenSAF -*- + * + * (C) Copyright 2016 The OpenSAF Foundation + * + * 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. + * + * Author(s): Ericsson AB + * + */ + +#ifndef OSAF_LIBS_CORE_CPLUSPLUS_BASE_UNIX_CLIENT_SOCKET_H_ +#define OSAF_LIBS_CORE_CPLUSPLUS_BASE_UNIX_CLIENT_SOCKET_H_ + +#include <string> +#include "osaf/libs/core/cplusplus/base/unix_socket.h" + +namespace base { + +// A class implementing a non-blocking UNIX domain client socket. +class UnixClientSocket : public UnixSocket { + public: + // Set the path name for this client socket. Note that this call does not + // create the socket - you need to call Send() or Recv() for that to happen. + explicit UnixClientSocket(const std::string& path); + // Closes the client socket if it was open, but does not delete the socket + // from the file system. + virtual ~UnixClientSocket(); + protected: + virtual void Open(); +}; + +} // namespace base + +#endif // OSAF_LIBS_CORE_CPLUSPLUS_BASE_UNIX_CLIENT_SOCKET_H_ diff --git a/osaf/libs/core/cplusplus/base/unix_server_socket.cc b/osaf/libs/core/cplusplus/base/unix_server_socket.cc new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/unix_server_socket.cc @@ -0,0 +1,51 @@ +/* -*- OpenSAF -*- + * + * (C) Copyright 2016 The OpenSAF Foundation + * + * 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. + * + * Author(s): Ericsson AB + * + */ + +#include "osaf/libs/core/cplusplus/base/unix_server_socket.h" +#include <sys/socket.h> +#include <unistd.h> +#include <cerrno> + +namespace base { + +UnixServerSocket::UnixServerSocket(const std::string& path) : + UnixSocket{path} { +} + +UnixServerSocket::~UnixServerSocket() { +} + +void UnixServerSocket::Open() { + if (fd() < 0) { + UnixSocket::Open(); + if (fd() >= 0) { + int result = bind(fd(), addr(), addrlen()); + if (result != 0) Close(); + } + } +} + +void UnixServerSocket::Close() { + if (fd() >= 0) { + int e = errno; + UnixSocket::Close(); + unlink(path()); + errno = e; + } +} + +} // namespace base diff --git a/osaf/libs/core/cplusplus/base/unix_server_socket.h b/osaf/libs/core/cplusplus/base/unix_server_socket.h new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/unix_server_socket.h @@ -0,0 +1,42 @@ +/* -*- OpenSAF -*- + * + * (C) Copyright 2016 The OpenSAF Foundation + * + * 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. + * + * Author(s): Ericsson AB + * + */ + +#ifndef OSAF_LIBS_CORE_CPLUSPLUS_BASE_UNIX_SERVER_SOCKET_H_ +#define OSAF_LIBS_CORE_CPLUSPLUS_BASE_UNIX_SERVER_SOCKET_H_ + +#include <string> +#include "osaf/libs/core/cplusplus/base/unix_socket.h" + +namespace base { + +// A class implementing a non-blocking UNIX domain server socket. +class UnixServerSocket : public UnixSocket { + public: + // Set the path name for this server socket. Note that this call does not + // create the socket - you need to call Send() or Recv() for that to happen. + explicit UnixServerSocket(const std::string& path); + // Closes the server socket if it was open, and deletes the socket from the + // file system. + virtual ~UnixServerSocket(); + protected: + virtual void Open(); + virtual void Close(); +}; + +} // namespace base + +#endif // OSAF_LIBS_CORE_CPLUSPLUS_BASE_UNIX_SERVER_SOCKET_H_ diff --git a/osaf/libs/core/cplusplus/base/unix_socket.cc b/osaf/libs/core/cplusplus/base/unix_socket.cc new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/unix_socket.cc @@ -0,0 +1,58 @@ +/* -*- OpenSAF -*- + * + * (C) Copyright 2016 The OpenSAF Foundation + * + * 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. + * + * Author(s): Ericsson AB + * + */ + +#include "osaf/libs/core/cplusplus/base/unix_socket.h" +#include <sys/socket.h> +#include <unistd.h> +#include <cstring> + +namespace base { + +UnixSocket::UnixSocket(const std::string& path) : + fd_{-1}, + addr_{AF_UNIX, {}} { + if (path.size() < sizeof(addr_.sun_path)) { + memcpy(addr_.sun_path, path.c_str(), path.size() + 1); + } else { + addr_.sun_path[0] = '\0'; + } +} + +void UnixSocket::Open() { + if (fd_ < 0) { + if (addr_.sun_path[0] != '\0') { + fd_ = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); + } else { + errno = ENAMETOOLONG; + } + } +} + +UnixSocket::~UnixSocket() { + Close(); +} + +void UnixSocket::Close() { + if (fd_ >= 0) { + int e = errno; + close(fd_); + errno = e; + fd_ = -1; + } +} + +} // namespace base diff --git a/osaf/libs/core/cplusplus/base/unix_socket.h b/osaf/libs/core/cplusplus/base/unix_socket.h new file mode 100644 --- /dev/null +++ b/osaf/libs/core/cplusplus/base/unix_socket.h @@ -0,0 +1,89 @@ +/* -*- OpenSAF -*- + * + * (C) Copyright 2016 The OpenSAF Foundation + * + * 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. + * + * Author(s): Ericsson AB + * + */ + +#ifndef OSAF_LIBS_CORE_CPLUSPLUS_BASE_UNIX_SOCKET_H_ +#define OSAF_LIBS_CORE_CPLUSPLUS_BASE_UNIX_SOCKET_H_ + +#include <sys/socket.h> +#include <sys/un.h> +#include <cerrno> +#include <string> +#include "osaf/libs/core/cplusplus/base/macros.h" + +namespace base { + +// A class implementing non-blocking operations on a UNIX domain socket. +class UnixSocket { + public: + // Close the socket. + virtual ~UnixSocket(); + // 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. + ssize_t Send(const void* buffer, size_t length) { + if (fd_ < 0) Open(); + ssize_t result = -1; + if (fd_ >= 0) { + do { + result = send(fd_, buffer, length, MSG_NOSIGNAL); + } while (result < 0 && errno == EINTR); + if (result < 0 && (errno != EAGAIN && errno != EWOULDBLOCK)) Close(); + } + return result; + } + // 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. + ssize_t Recv(void* buffer, size_t length) { + if (fd_ < 0) Open(); + ssize_t result = -1; + if (fd_ >= 0) { + do { + result = recv(fd_, buffer, length, 0); + } while (result < 0 && errno == EINTR); + if (result < 0 && (errno != EAGAIN && errno != EWOULDBLOCK)) Close(); + } + 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); + virtual void Open(); + virtual void Close(); + const struct sockaddr* addr() const { + return reinterpret_cast<const struct sockaddr*>(&addr_); + } + static socklen_t addrlen() { return sizeof(addr_); } + const char* path() const { return addr_.sun_path; } + + private: + int fd_; + struct sockaddr_un addr_; + + DELETE_COPY_AND_MOVE_OPERATORS(UnixSocket); +}; + +} // namespace base + +#endif // OSAF_LIBS_CORE_CPLUSPLUS_BASE_UNIX_SOCKET_H_ ------------------------------------------------------------------------------ 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