diff --git a/common/base/Makefile.mk b/common/base/Makefile.mk index 758bbc356f..73d8473cde 100644 --- a/common/base/Makefile.mk +++ b/common/base/Makefile.mk @@ -9,6 +9,11 @@ common_libolacommon_la_SOURCES += \ common/base/SysExits.cpp \ common/base/Version.cpp +if HAVE_STRERROR_R +common_libolacommon_la_SOURCES += common/base/StrError_R.cpp \ + common/base/StrError_R_XSI.cpp +endif + # TESTS ################################################## diff --git a/common/base/StrError_R.cpp b/common/base/StrError_R.cpp new file mode 100644 index 0000000000..b28153938d --- /dev/null +++ b/common/base/StrError_R.cpp @@ -0,0 +1,50 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * StrError_R.cpp + * Helper function for StrError_R_XSI(). + * Copyright (C) 2018 Shenghao Yang + */ + +#if HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +#include +#include + +#include +#include +#include + +#include "ola/base/StrError_R.h" +#include "ola/strings/Format.h" + +namespace ola { + +const int StrError_R_BufSize = 1024; + +std::string StrError_R(int errnum) { + // Pre-allocate to prevent re-allocation. + std::string out(StrError_R_BufSize, '\0'); + if (StrError_R_XSI(errnum, &(out[0]), out.size())) { + out.assign(std::string("errno = ") + ola::strings::IntToString(errnum)); + } else { + out.resize(strlen(&(out[0]))); + } + return out; +} + +} // namespace ola diff --git a/common/base/StrError_R_XSI.cpp b/common/base/StrError_R_XSI.cpp new file mode 100644 index 0000000000..22c6928e26 --- /dev/null +++ b/common/base/StrError_R_XSI.cpp @@ -0,0 +1,51 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * StrError_R_XSI.cpp + * Definition of strerror_r() that is XSI-compliant. + * This extra file is required because the header declaring the symbols + * for the C++ string library conflicts with the macros used to expose + * the XSI-compliant declaration of strerror_r(). + * Copyright (C) 2018 Shenghao Yang + */ + +#if HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +// Required for string.h to declare the standards-compliant version of +// strerror_r(), when compiling under glibc. Must come before the inclusion +// of string.h. +// These are conditional to avoid errors when not compiling with glibc, or +// when compiling with a compiler that does not define _POSIX_C_SOURCE. +// See strerror(3) as to why the macros are defined this way. +#ifdef _GNU_SOURCE +#undef _GNU_SOURCE +#endif + +#ifdef _POSIX_C_SOURCE +#undef _POSIX_C_SOURCE +#endif +#define _POSIX_C_SOURCE 200112L + +#include + +namespace ola { + +int StrError_R_XSI(int errnum, char* buf, size_t buflen) { + return strerror_r(errnum, buf, buflen); +} + +} // namespace ola diff --git a/configure.ac b/configure.ac index 4057663332..2bfc128e27 100644 --- a/configure.ac +++ b/configure.ac @@ -190,8 +190,8 @@ AC_FUNC_CLOSEDIR_VOID AC_FUNC_VPRINTF AC_CHECK_FUNCS([bzero gettimeofday memmove memset mkdir strdup strrchr \ if_nametoindex inet_ntoa inet_ntop inet_aton inet_pton select \ - socket strerror getifaddrs getloadavg getpwnam_r getpwuid_r \ - getgrnam_r getgrgid_r secure_getenv]) + socket strerror strerror_r getifaddrs getloadavg getpwnam_r \ + getpwuid_r getgrnam_r getgrgid_r secure_getenv]) AC_MSG_CHECKING(for readdir_r deprecation) old_cxxflags=$CXXFLAGS @@ -234,6 +234,9 @@ AM_CONDITIONAL(HAVE_EPOLL, test "${ax_cv_have_epoll}" = "yes") AC_CHECK_FUNCS([kqueue]) AM_CONDITIONAL(HAVE_KQUEUE, test "${ac_cv_func_kqueue}" = "yes") +# strerror_r +AM_CONDITIONAL([HAVE_STRERROR_R], [test "x$ac_cv_func_strerror_r" = "xyes"]) + # check if the compiler supports -rdynamic AC_MSG_CHECKING(for -rdynamic support) old_cppflags=$CPPFLAGS @@ -756,6 +759,31 @@ AC_ARG_ENABLE( [AS_HELP_STRING([--disable-doxygen-version], [Substitute the Doxygen version with latest, for the website])]) +# systemd support +AC_ARG_ENABLE( + [systemd], + [AS_HELP_STRING([--disable-systemd], [Disable systemd support])]) + +have_libsystemd="no" +AS_IF([test "x$enable_systemd" != xno], + [PKG_CHECK_MODULES([libsystemd], [libsystemd >= 38], [have_libsystemd=yes], + [AC_MSG_WARN([disabling systemd support: can't find libsystemd])]) + AC_CHECK_HEADER([systemd/sd-daemon.h], [], + [have_libsystemd=no + AC_MSG_WARN([disabling systemd support: can't find systemd/sd-daemon.h])]) + AS_IF([test "x$ac_cv_func_strerror_r" = xno], + [have_libsystemd=no + AC_MSG_WARN([disabling systemd support: can't find strerror_r])])]) + +AS_IF([test "x$have_libsystemd" = xyes], + [AC_DEFINE([HAVE_LIBSYSTEMD], [1], + [define if systemd support is wanted and libsystemd is present])]) + +AM_CONDITIONAL([HAVE_LIBSYSTEMD], [test "x$have_libsystemd" = xyes]) + +AS_IF([test "x$enable_systemd" = xyes && test "x$have_libsystemd" = xno], + [AC_MSG_ERROR([could not enable systemd support])]) + # UUCP Lock directory AC_ARG_WITH([uucp-lock], [AS_HELP_STRING([--with-uucp-lock], diff --git a/include/ola/base/Makefile.mk b/include/ola/base/Makefile.mk index d1761cad27..5811120207 100644 --- a/include/ola/base/Makefile.mk +++ b/include/ola/base/Makefile.mk @@ -9,5 +9,9 @@ olabaseinclude_HEADERS = \ include/ola/base/Macro.h \ include/ola/base/SysExits.h +if HAVE_STRERROR_R +olabaseinclude_HEADERS += include/ola/base/StrError_R.h +endif + nodist_olabaseinclude_HEADERS = \ include/ola/base/Version.h diff --git a/include/ola/base/StrError_R.h b/include/ola/base/StrError_R.h new file mode 100644 index 0000000000..5bd1f6719e --- /dev/null +++ b/include/ola/base/StrError_R.h @@ -0,0 +1,79 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * StrError_R.h + * Helper functions for strerror_r and a declaration of strerror_r that is + * XSI-compliant. + * Copyright (C) 2018 Shenghao Yang + */ + +/** + * @defgroup strerror Description of system error codes + * @brief Error descriptions + * @details Convenience functions to obtain descriptions of system error codes. + * The functions and variables in this group are only defined if \c strerror_r() + * is available. + */ + +/** + * @addtogroup strerror + * @{ + * @file StrError_R.h + * @} + */ + +#ifndef INCLUDE_OLA_BASE_STRERROR_R_H_ +#define INCLUDE_OLA_BASE_STRERROR_R_H_ + +#include + +namespace ola { + +/** + * @addtogroup strerror + * @{ + */ + +/** + * @brief Length of the internal buffer used for @ref StrError_R(). + * + * If the length of the system-provided error description exceeds the length + * of this buffer minus one, then the output of @ref StrError_R() will only + * include the numerical error value provided. + */ +extern const int StrError_R_BufSize; + +/** + * @brief XSI-compliant version of \c strerror_r(). + * + * @see https://linux.die.net/man/3/strerror for more details. + */ +int StrError_R_XSI(int errnum, char* buf, size_t buflen); + +/** + * @brief Convenience function that wraps @ref StrError_R_XSI(). + * + * @param errnum error value to generate the textual description for. + * @return Textual description of the error value. If the description is + * truncated due to an insufficient buffer length, the description will + * be in the form: "errno = errnum". + * @sa StrError_R_BufSize for information regarding truncation. + */ +std::string StrError_R(int errnum); + +/**@}*/ +} // namespace ola + +#endif // INCLUDE_OLA_BASE_STRERROR_R_H_ diff --git a/olad/Makefile.mk b/olad/Makefile.mk index 527bfc26c2..60c11bff28 100644 --- a/olad/Makefile.mk +++ b/olad/Makefile.mk @@ -42,6 +42,11 @@ ola_server_sources += olad/HttpServerActions.cpp \ ola_server_additional_libs += common/http/libolahttp.la endif +if HAVE_LIBSYSTEMD +ola_server_sources += olad/Systemd.cpp olad/Systemd.h +ola_server_additional_libs += $(libsystemd_LIBS) +endif + # lib olaserver lib_LTLIBRARIES += olad/libolaserver.la diff --git a/olad/OlaServer.cpp b/olad/OlaServer.cpp index 2921a6f996..bee60db22b 100644 --- a/olad/OlaServer.cpp +++ b/olad/OlaServer.cpp @@ -63,6 +63,10 @@ #include "olad/OladHTTPServer.h" #endif // HAVE_LIBMICROHTTPD +#ifdef HAVE_LIBSYSTEMD +#include "olad/Systemd.h" +#endif // HAVE_LIBSYSTEMD + DEFINE_s_uint16(rpc_port, r, ola::OlaServer::DEFAULT_RPC_PORT, "The port to listen for RPCs on. Defaults to 9010."); DEFINE_default_bool(register_with_dns_sd, true, @@ -472,9 +476,15 @@ bool OlaServer::InternalNewConnection( } void OlaServer::ReloadPluginsInternal() { +#ifdef HAVE_LIBSYSTEMD + ola::SystemdNotify(0, "RELOADING=1\nSTATUS=Reloading plugins\n"); +#endif // HAVE_LIBSYSTEMD OLA_INFO << "Reloading plugins"; StopPlugins(); m_plugin_manager->LoadAll(); +#ifdef HAVE_LIBSYSTEMD + ola::SystemdNotify(0, "READY=1\nSTATUS=Plugin reload complete\n"); +#endif // HAVE_LIBSYSTEMD } void OlaServer::UpdatePidStore(const RootPidStore *pid_store) { diff --git a/olad/Olad.cpp b/olad/Olad.cpp index cf1927a285..97e00026ad 100644 --- a/olad/Olad.cpp +++ b/olad/Olad.cpp @@ -36,6 +36,10 @@ // which needs to be after WinSock2.h, hence this order #include "olad/OlaDaemon.h" +#if HAVE_LIBSYSTEMD +#include "olad/Systemd.h" +#endif // HAVE_LIBSYSTEMD + #include "ola/Logging.h" #include "ola/base/Credentials.h" #include "ola/base/Flags.h" @@ -172,6 +176,18 @@ int main(int argc, char *argv[]) { ola::NewCallback(olad->GetOlaServer(), &ola::OlaServer::ReloadPlugins)); #endif // _WIN32 +#if HAVE_LIBSYSTEMD + if (ola::SystemdNotifyAvailable()) { + OLA_INFO << "Systemd notification socket address present, " + << "sending notifications."; + } else { + OLA_WARN << "Systemd notification socket address not present, " + << "not sending notifications."; + } + // Does not need to be guarded. sd_notify does its own internal check on + // the socket's presence, as well. + ola::SystemdNotify(0, "READY=1\nSTATUS=Startup complete\n"); +#endif // HAVE_LIBSYSTEMD olad->Run(); return ola::EXIT_OK; } diff --git a/olad/Systemd.cpp b/olad/Systemd.cpp new file mode 100644 index 0000000000..93a85cc007 --- /dev/null +++ b/olad/Systemd.cpp @@ -0,0 +1,49 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Systemd.cpp + * Provides wrapped access to systemd interfaces. + * Copyright (C) 2018 Shenghao Yang + */ + +#if HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +#if HAVE_LIBSYSTEMD +#include +#endif // HAVE_LIBSYSTEMD + +#include "ola/Logging.h" +#include "ola/base/StrError_R.h" + +#include "olad/Systemd.h" + +namespace ola { + +int SystemdNotify(int unset_environment, const char *state) { + int rtn = sd_notify(unset_environment, state); + if (rtn < 0) { + OLA_WARN << "Error sending notification to systemd: " + << ola::StrError_R(-rtn); + } + return rtn; +} + +bool SystemdNotifyAvailable() { + return (sd_notify(0, "") != 0); +} + +} // namespace ola diff --git a/olad/Systemd.h b/olad/Systemd.h new file mode 100644 index 0000000000..81255219bb --- /dev/null +++ b/olad/Systemd.h @@ -0,0 +1,46 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Systemd.h + * Interface to systemd functionality. + * Copyright (C) 2018 Shenghao Yang + */ + +#ifndef OLAD_SYSTEMD_H_ +#define OLAD_SYSTEMD_H_ + +namespace ola { + +/* + * @brief Notify systemd about daemon state changes. + * + * This function logs on any failure to queue notifications, but only if the + * notification socket environment variable is set. + * + * @param unset_environment whether to unset the notification socket environment + * variable so child processes cannot utilize it. + * @param state state block to pass to systemd. + * @return value returned from @ref sd_notify() + */ +int SystemdNotify(int unset_environment, const char *state); + +/* + * @brief Tests whether the systemd notification socket is available. + * @return @c true if the socket is available, @c false if not. + */ +bool SystemdNotifyAvailable(); + +} // namespace ola +#endif // OLAD_SYSTEMD_H_ diff --git a/systemd/olad.service b/systemd/olad.service new file mode 100644 index 0000000000..0a09d6283f --- /dev/null +++ b/systemd/olad.service @@ -0,0 +1,32 @@ +[Unit] +Description=Ola Daemon (User instance) +Documentation=https://docs.openlighting.org/ola/conf/ +Documentation=https://www.openlighting.org/ola/ +Documentation=https://github.com/OpenLightingProject/ola + +[Service] +# Task control options +Type=notify +ExecStart=/usr/bin/olad +ExecReload=/usr/bin/kill -HUP $MAINPID +NotifyAccess=main +Restart=on-failure + +# Exec Options +NoNewPrivileges=true +ProtectSystem=strict +ProtectHome=tmpfs +ProtectControlGroups=true +RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 +RestrictNamespaces=true +LockPersonality=true +MemoryDenyWriteExecute=true +SystemCallArchitectures=native + +# Directory specifications +ConfigurationDirectory="~/.ola/" +WorkingDirectory=~ + +[Install] +WantedBy=default.target +