Commit 02844e598f7b93c7b40eeeb8da7b5b37b29980a0

Authored by Peter M. Groen
1 parent 3894f4d8

Added test to library

.gitignore 0 → 100644
  1 +/build/
... ...
CMakeLists.txt 0 → 100644
  1 +cmake_minimum_required(VERSION 3.30)
  2 +project(daemonbase VERSION 0.1 LANGUAGES CXX)
  3 +include_guard(GLOBAL)
  4 +
  5 +if(EXISTS ${CMAKE_SOURCE_DIR}/cmake)
  6 + # Use the cmake directives for compilers etc...
  7 + LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
  8 +else()
  9 + message("Cmake source dir : ${CMAKE_SOURCE_DIR}")
  10 +endif()
  11 +
  12 +include_directories( SYSTEM
  13 + ${CMAKE_CURRENT_SOURCE_DIR}/include
  14 +)
  15 +
  16 +include(compiler)
  17 +
  18 +set(SRC_LIST
  19 + ${CMAKE_CURRENT_SOURCE_DIR}/include/daemonbase.h
  20 + ${CMAKE_CURRENT_SOURCE_DIR}/include/daemonconfig.h
  21 + ${CMAKE_CURRENT_SOURCE_DIR}/include/daemonlog.h
  22 +)
  23 +
  24 +include(library)
  25 +add_libraries()
  26 +
  27 +include(installation)
  28 +install_component()
  29 +
  30 +include(packaging)
  31 +package_component()
  32 +
  33 +link_directories(
  34 + ${CMAKE_BINARY_DIR}/lib
  35 +)
  36 +
  37 +set_target_properties(${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE CXX)
  38 +
  39 +add_subdirectory(test)
0 40 \ No newline at end of file
... ...
cmake/COPYING.OSDEV 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
0 22 \ No newline at end of file
... ...
cmake/FindGMock.cmake 0 → 100644
  1 +#.rst:
  2 +# FindGMock
  3 +# ---------
  4 +#
  5 +# Locate the Google C++ Mocking Framework.
  6 +#
  7 +# Defines the following variables:
  8 +#
  9 +# ::
  10 +#
  11 +# GMOCK_FOUND - Found the Google Testing framework
  12 +# GMOCK_INCLUDE_DIRS - Include directories
  13 +#
  14 +#
  15 +#
  16 +# Also defines the gmock source path
  17 +#
  18 +# ::
  19 +#
  20 +# GMOCK_SOURCE_DIR - directory containing the gmock sources
  21 +#
  22 +#
  23 +#
  24 +# Accepts the following variables as input:
  25 +#
  26 +# ::
  27 +#
  28 +# GMOCK_ROOT - (as a CMake or environment variable)
  29 +# The root directory of the gmock install prefix
  30 +#
  31 +#
  32 +#
  33 +#
  34 +# Example Usage:
  35 +#
  36 +# ::
  37 +#
  38 +# enable_testing()
  39 +# find_package(GMock REQUIRED)
  40 +# include_directories(${GMOCK_INCLUDE_DIRS})
  41 +#
  42 +#
  43 +#
  44 +# ::
  45 +#
  46 +# add_executable(foo ${GMOCK_SOURCE_DIR}/gmock-all.cc foo.cc)
  47 +# target_link_libraries(foo)
  48 +#
  49 +#
  50 +
  51 +find_path(GMOCK_INCLUDE_DIR gmock/gmock.h
  52 + HINTS
  53 + $ENV{GMOCK_ROOT}/include
  54 + ${GMOCK_ROOT}/include
  55 +)
  56 +mark_as_advanced(GMOCK_INCLUDE_DIR)
  57 +
  58 +find_path(GMOCK_SOURCE_DIR gmock-all.cc
  59 + HINTS
  60 + $ENV{GMOCK_ROOT}/src/gmock
  61 + ${GMOCK_ROOT}/src/gmock
  62 + PATHS
  63 + ${GMOCK_INCLUDE_DIR}/../src/gmock
  64 +)
  65 +mark_as_advanced(GMOCK_SOURCE_DIR)
  66 +
  67 +FIND_PACKAGE_HANDLE_STANDARD_ARGS(GMock DEFAULT_MSG GMOCK_INCLUDE_DIR GMOCK_SOURCE_DIR)
  68 +
  69 +if(GMOCK_FOUND)
  70 + set(GMOCK_INCLUDE_DIRS ${GMOCK_INCLUDE_DIR})
  71 +endif()
  72 +
... ...
cmake/FindJsoncpp.cmake 0 → 100644
  1 +# Locate jsoncpp
  2 +#
  3 +# This module defines
  4 +# JSONCPP_FOUND, if false, do not try to link to jsoncpp
  5 +# JSONCPP_LIBRARY, where to find jsoncpp
  6 +# JSONCPP_INCLUDE_DIR, where to find json.h
  7 +#
  8 +# By default, the dynamic libraries of jsoncpp will be found. To find the static ones instead,
  9 +# you must set the JSONCPP_STATIC_LIBRARY variable to TRUE before calling find_package(Jsoncpp ...).
  10 +#
  11 +# If jsoncpp is not installed in a standard path, you can use the JSONCPP_DIR CMake variable
  12 +# to tell CMake where jsoncpp is.
  13 +
  14 +set(JSONCPP_FOUND 0)
  15 +
  16 +# attempt to find static library first if this is set
  17 +if(JSONCPP_STATIC_LIBRARY)
  18 + set(JSONCPP_STATIC libjsoncpp.a)
  19 +endif()
  20 +
  21 +# find the jsoncpp include directory
  22 +find_path(JSONCPP_INCLUDE_DIR json/json.h
  23 + PATHS
  24 + ~/Library/Frameworks/jsoncpp/include/
  25 + /Library/Frameworks/jsoncpp/include/
  26 + /usr/local/include/
  27 + /usr/include/
  28 + /sw/jsoncpp/ # Fink
  29 + /opt/local/jsoncpp/ # DarwinPorts
  30 + /opt/csw/jsoncpp/ # Blastwave
  31 + /opt/jsoncpp/
  32 + ${JSONCPP_DIR}/include/
  33 + PATH_SUFFIXES jsoncpp)
  34 +
  35 +# find the jsoncpp library
  36 +find_library(JSONCPP_LIBRARY
  37 + NAMES ${JSONCPP_STATIC} jsoncpp
  38 + PATH_SUFFIXES lib64 lib
  39 + PATHS ~/Library/Frameworks
  40 + /Library/Frameworks
  41 + /usr/local
  42 + /usr
  43 + /sw
  44 + /opt/local
  45 + /opt/csw
  46 + /opt
  47 + ${JSONCPP_DIR}/lib)
  48 +
  49 +# handle the QUIETLY and REQUIRED arguments and set JSONCPP_FOUND to TRUE if all listed variables are TRUE
  50 +include(FindPackageHandleStandardArgs)
  51 +FIND_PACKAGE_HANDLE_STANDARD_ARGS(JSONCPP DEFAULT_MSG JSONCPP_INCLUDE_DIR JSONCPP_LIBRARY)
  52 +mark_as_advanced(JSONCPP_INCLUDE_DIR JSONCPP_LIBRARY)
  53 +message("JSONCPP_FOUND = ${JSONCPP_FOUND}, includedir = ${JSONCPP_INCLUDE_DIR}, lib = ${JSONCPP_LIBRARY}")
... ...
cmake/FindYamlCpp.cmake 0 → 100644
  1 +# Locate yaml-cpp
  2 +#
  3 +# This module defines
  4 +# YAMLCPP_FOUND, if false, do not try to link to yaml-cpp
  5 +# YAMLCPP_LIBRARY, where to find yaml-cpp
  6 +# YAMLCPP_INCLUDE_DIR, where to find yaml.h
  7 +#
  8 +# By default, the dynamic libraries of yaml-cpp will be found. To find the static ones instead,
  9 +# you must set the YAMLCPP_STATIC_LIBRARY variable to TRUE before calling find_package(YamlCpp ...).
  10 +#
  11 +# If yaml-cpp is not installed in a standard path, you can use the YAMLCPP_DIR CMake variable
  12 +# to tell CMake where yaml-cpp is.
  13 +
  14 +# attempt to find static library first if this is set
  15 +if(YAMLCPP_STATIC_LIBRARY)
  16 + set(YAMLCPP_STATIC libyaml-cpp.a)
  17 +endif()
  18 +
  19 +# find the yaml-cpp include directory
  20 +find_path(YAMLCPP_INCLUDE_DIR yaml-cpp/yaml.h
  21 + PATH_SUFFIXES include
  22 + PATHS
  23 + ~/Library/Frameworks/yaml-cpp/include/
  24 + /Library/Frameworks/yaml-cpp/include/
  25 + /usr/local/include/
  26 + /usr/include/
  27 + /sw/yaml-cpp/ # Fink
  28 + /opt/local/yaml-cpp/ # DarwinPorts
  29 + /opt/csw/yaml-cpp/ # Blastwave
  30 + /opt/yaml-cpp/
  31 + ${YAMLCPP_DIR}/include/)
  32 +
  33 +# find the yaml-cpp library
  34 +find_library(YAMLCPP_LIBRARY
  35 + NAMES ${YAMLCPP_STATIC} yaml-cpp
  36 + PATH_SUFFIXES lib64 lib
  37 + PATHS ~/Library/Frameworks
  38 + /Library/Frameworks
  39 + /usr/local
  40 + /usr
  41 + /sw
  42 + /opt/local
  43 + /opt/csw
  44 + /opt
  45 + ${YAMLCPP_DIR}/lib)
  46 +
  47 +# handle the QUIETLY and REQUIRED arguments and set YAMLCPP_FOUND to TRUE if all listed variables are TRUE
  48 +include(FindPackageHandleStandardArgs)
  49 +FIND_PACKAGE_HANDLE_STANDARD_ARGS(YAMLCPP DEFAULT_MSG YAMLCPP_INCLUDE_DIR YAMLCPP_LIBRARY)
  50 +mark_as_advanced(YAMLCPP_INCLUDE_DIR YAMLCPP_LIBRARY)
... ...
cmake/application.cmake 0 → 100644
  1 +# @brief Adds an executable target and performs related actions,
  2 +# such as verioning, binary dir and configuration.
  3 +# @note The default binary directory is PROJECT_BINARY_DIR, but can be overridden by specifying the ${PROJECT_NAME}_CURRENT_BINARY_DIR.
  4 +function(add_application)
  5 +
  6 +message( STATUS "${PROJECT_NAME} linking libraries : ${ARGN}")
  7 +
  8 +# Use PROJECT_BINARY_DIR by default, but override if necessary.
  9 +set(CURRENT_PROJECT_BINARY_DIR ${PROJECT_BINARY_DIR})
  10 +if (${PROJECT_NAME}_CURRENT_BINARY_DIR)
  11 + set(CURRENT_PROJECT_BINARY_DIR ${${PROJECT_NAME}_CURRENT_BINARY_DIR})
  12 +endif()
  13 +message(STATUS "CURRENT_PROJECT_BINARY_DIR : ${CURRENT_PROJECT_BINARY_DIR}")
  14 +
  15 +include_directories(${HSOA_VERSION_INCLUDE_DIR})
  16 +
  17 +add_executable( ${PROJECT_NAME}
  18 + ${HSOA_VERSION_SRC_FILE}
  19 + ${SRC_LIST}
  20 +)
  21 +
  22 +target_link_libraries( ${PROJECT_NAME}
  23 + ${ARGN}
  24 +)
  25 +
  26 +set_target_properties( ${PROJECT_NAME}
  27 + PROPERTIES
  28 + VERSION ${PROJECT_VERSION}
  29 + RUNTIME_OUTPUT_DIRECTORY ${CURRENT_PROJECT_BINARY_DIR}/bin
  30 +)
  31 +
  32 +# Copy the testconfig to the build dir, so the binaries are testable from the build dir.
  33 +# The created packages will not contain the testconfig but the production config instead. See installation.cmake.
  34 +if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/testconfig/)
  35 + file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/testconfig/" DESTINATION "${CURRENT_PROJECT_BINARY_DIR}/etc")
  36 +endif()
  37 +
  38 +endfunction()
... ...
cmake/application.post_install.inc.cmake.in 0 → 100644
  1 +# Make sure the mLogic group exists
  2 +MLOGIC_GROUP=mlogic
  3 +groupadd --force $MLOGIC_GROUP
  4 +# Create the user
  5 +appuserid=$(id -u @PROJECT_NAME@)
  6 +if [ ! $appuserid ] ; then
  7 + useradd --groups $MLOGIC_GROUP --no-create-home @PROJECT_NAME@
  8 +fi
  9 +# Prevent direct login
  10 +passwd --lock @PROJECT_NAME@
  11 +# Change group ownership of log folder
  12 +chgrp $MLOGIC_GROUP @PROJCOMP_LOG_INSTALL_DIR@
  13 +# Change group ownership of run folder
  14 +chgrp $MLOGIC_GROUP @PROJCOMP_RUN_INSTALL_DIR@
... ...
cmake/application.post_uninstall.inc.cmake.in 0 → 100644
  1 +# Remove the user
  2 +appuserid=$(id -u @PROJECT_NAME@)
  3 +if [ $appuserid ] ; then
  4 + userdel --remove --force @PROJECT_NAME@
  5 +fi
... ...
cmake/artifacts.cmake 0 → 100644
  1 +# @brief Tries to find the module with find_package, and uses CMAKE_INSTALL_PREFIX (see documentation).
  2 +# If the module can't be found, includes the directory by the specified module path.
  3 +# @param module The name of the module on which a dependency is added
  4 +# @param modulepath The relative path of the module wrt the current project
  5 +# @param moduleincludepath [optional] The relative path to the include directory
  6 +# @note Needs to be a macro because of the scope of the variables that are set by find_package.
  7 +macro(depend_module module modulepath)
  8 + message(STATUS "module : ${module}")
  9 + message(STATUS "modulepath : ${modulepath}")
  10 +
  11 + set ( optional_macro_args ${ARGN} )
  12 + list ( LENGTH optional_macro_args num_optional_args )
  13 + if ( ${num_optional_args} GREATER 0 )
  14 + list ( GET optional_macro_args 0 moduleincludepath )
  15 + else()
  16 + set ( moduleincludepath include )
  17 + endif()
  18 + message(STATUS "moduleincludepath : ${moduleincludepath}")
  19 +
  20 + find_package(${module} CONFIG QUIET)
  21 + message( STATUS "${module}_FOUND: ${${module}_FOUND}" )
  22 +
  23 + string( TOUPPER ${module} MODULE_NAME_UPPER )
  24 +
  25 + # check if the constructed variable name exists and if so check also the content.
  26 + if ( NOT ${${module}_FOUND} )
  27 + if ( NOT TARGET ${module} )
  28 + message( STATUS "Adding subdirectory ${modulepath}/${module} as subdirectory ${CMAKE_CURRENT_LIST_DIR}/staging/${module}." )
  29 + add_subdirectory(${modulepath}/${module} staging/${module})
  30 + endif()
  31 +
  32 + set( ${MODULE_NAME_UPPER}_INCLUDE_DIR ${modulepath}/${module}/${moduleincludepath} CACHE STRING "${module} include path" FORCE )
  33 + set( ${MODULE_NAME_UPPER}_LIB_DIR "${CMAKE_CURRENT_BINARY_DIR}" CACHE STRING "${module} library path" FORCE )
  34 + set( ${MODULE_NAME_UPPER}_LIB_NAME "${module}" CACHE STRING "${module} library name" FORCE )
  35 + endif()
  36 +
  37 + message( STATUS "${MODULE_NAME_UPPER}_INCLUDE_DIR: ${${MODULE_NAME_UPPER}_INCLUDE_DIR}" )
  38 + message( STATUS "${MODULE_NAME_UPPER}_LIB_DIR: ${${MODULE_NAME_UPPER}_LIB_DIR}" )
  39 + message( STATUS "${MODULE_NAME_UPPER}_LIB_NAME: ${${MODULE_NAME_UPPER}_LIB_NAME}" )
  40 +endmacro()
... ...
cmake/compiler.cmake 0 → 100644
  1 +include(CheckCXXCompilerFlag)
  2 +
  3 +set(PLATFORM_RELEASE "")
  4 +function(platformRelease)
  5 + set(PR, "")
  6 + execute_process(COMMAND /bin/bash -c "if [ -e /etc/redhat-release ]; then cat /etc/redhat-release | tr -d '\n'; fi" OUTPUT_VARIABLE PR)
  7 + message(STATUS "Platform is ${PR}")
  8 + set(PLATFORM_RELEASE "${PR}" PARENT_SCOPE)
  9 +endfunction(platformRelease)
  10 +
  11 +# The target architecture (-march, -mtune) is assumed to be the architecture that was used to build gcc.
  12 +# If cross-compilation is required, this should be specified as cmake arguments.
  13 +
  14 +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wshadow" )
  15 +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wnon-virtual-dtor -Woverloaded-virtual -Winit-self -Wuninitialized -Wunused -Wcast-qual" )
  16 +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-long-long" )
  17 +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wold-style-cast" )
  18 +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=return-type" )
  19 +
  20 +if(CMAKE_SYSTEM_PROCESSOR MATCHES "^x86_64")
  21 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-noexcept-type" )
  22 +endif()
  23 +
  24 +if(CMAKE_COMPILER_IS_GNUCC
  25 + AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9
  26 + AND NOT APPLE)
  27 + # Mac OSX doesn't seem to honour -isystem, so not for Mac.
  28 + # Also, GCC 4.8 complains about boost
  29 + MESSAGE(STATUS "Don't treat warnings as errors")
  30 +else()
  31 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror" )
  32 +endif()
  33 +if(BUILD_WITH_PROFILING)
  34 + message(STATUS "Profiling enabled")
  35 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg" )
  36 +else()
  37 + message(STATUS "Profiling disabled")
  38 +endif()
  39 +
  40 +# Allow linking into a dynamic library
  41 +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC" )
  42 +
  43 +# Use RelWithDebInfo as default build type
  44 +if (NOT CMAKE_BUILD_TYPE)
  45 + set(CMAKE_BUILD_TYPE "RelWithDebInfo")
  46 +endif()
  47 +message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
  48 +
  49 +if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
  50 + set(NOASSERTS 1) # Disable asserts as well
  51 + # -O3 causes weird crashes on win32 so we force -O2 for release builds also for linux builds
  52 + string(REGEX REPLACE "-O3" "-O2" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
  53 + string(REGEX REPLACE "-O3" "-O2" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
  54 + if(MINGW)
  55 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s")
  56 + endif()
  57 +else()
  58 + # Let Qt track the use of QSharedPointer
  59 + set(CMAKE_CXX_CFLAGS "${CMAKE_CXX_FLAGS} -DQT_SHAREDPOINTER_TRACK_POINTERS")
  60 + # If we're debugging, remove -O2 completely
  61 + string(REGEX REPLACE "-O2" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
  62 + string(REGEX REPLACE "-O2" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
  63 + string(REGEX REPLACE "-O2" "" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
  64 + string(REGEX REPLACE "-O2" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
  65 +endif()
  66 +
  67 +# Disable asserts with -DNOASSERTS=1 as a CMake command-line parameter
  68 +if(NOASSERTS)
  69 + add_definitions(-DQT_NO_DEBUG)
  70 + add_definitions(-DNDEBUG)
  71 + message(STATUS "Asserts have been disabled")
  72 +endif(NOASSERTS)
  73 +
  74 +if(APPLE)
  75 + # AVX instructions currently cause assembler errors, so disabling them
  76 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -w -mno-avx -mmacosx-version-min=10.7 -stdlib=libc++ ")
  77 + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -w -mno-avx -mmacosx-version-min=10.7")
  78 +endif(APPLE)
  79 +
  80 +if(UNIX)
  81 + if(BUILD_WITH_COVERAGE)
  82 + # Specifically enable coverate info, since ccache can't cache with
  83 + # profiling enabled, seriously hurting build-times in Debug-mode
  84 + message(STATUS "Generate coverage info")
  85 + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage")
  86 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage")
  87 + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage")
  88 + if (NOT TARGET RemoveGCDA)
  89 + add_custom_target(RemoveGCDA ALL
  90 + COMMAND find . -name "*.gcda" -delete
  91 + COMMENT "Removing gcda files")
  92 + endif(NOT TARGET RemoveGCDA)
  93 +
  94 + if(NOT NOASSERTS)
  95 + add_definitions(-DQT_SHAREDPOINTER_TRACK_POINTERS)
  96 + endif(NOT NOASSERTS)
  97 + else()
  98 + message(STATUS "Building without coverage info")
  99 + endif()
  100 +endif(UNIX)
  101 +
  102 +if(UNIX AND NOT APPLE)
  103 + # needed for making qwt happy
  104 + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lrt" )
  105 +endif(UNIX AND NOT APPLE)
  106 +
  107 +if(MINGW)
  108 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mwindows" )
  109 +endif(MINGW)
  110 +
  111 +CHECK_CXX_COMPILER_FLAG( -fstack-protector-all result )
  112 +if(result)
  113 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector-all")
  114 +endif(result)
  115 +
  116 +CHECK_CXX_COMPILER_FLAG( -fthreadsafe-statics THREADSAFE_RESULT)
  117 +if( NOT THREADSAFE_RESULT)
  118 + message(FATAL_ERROR, "Compiler does not support threadsafe statics variables in methods")
  119 +endif()
  120 +# We use static variables in method scope, they must be threadsafe, this is on by default since gcc 4 but this flag is a check
  121 +# If the compiler does not support this then build will fail.
  122 +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fthreadsafe-statics")
  123 +
  124 +if(CMAKE_COMPILER_IS_GNUCXX)
  125 + # Check on which architecture we are. ARM doesn't support mfpmath
  126 + if(CMAKE_SYSTEM_PROCESSOR MATCHES "^x86_64")
  127 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpmath=sse" )
  128 + endif()
  129 +
  130 + MESSAGE(STATUS "Checking the c++ Standard supported by the compiler" )
  131 + # Check which version of c++ we can use. We drill down from c++17 to c96
  132 + CHECK_CXX_COMPILER_FLAG( -std=c++17 cxxresult )
  133 + if( NOT cxxresult )
  134 + CHECK_CXX_COMPILER_FLAG( -std=c++14 cxxresult )
  135 + if( NOT cxxresult )
  136 + CHECK_CXX_COMPILER_FLAG( -std=c++11 cxxresult )
  137 + if( NOT cxxresult )
  138 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
  139 + else()
  140 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
  141 + endif()
  142 + else()
  143 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
  144 + endif()
  145 + else()
  146 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
  147 + endif()
  148 + MESSAGE(STATUS "Compiling for ${CMAKE_CXX_FLAGS}")
  149 +
  150 + # -Wzero-as-null-pointer-constant is disabled for now, since the Qt 4.8.4
  151 + # macro's produce a bucketload of these warnings. Might be useful later on.
  152 +# CHECK_CXX_COMPILER_FLAG( -Wzero-as-null-pointer-constant cxxresult )
  153 +# if(cxxresult)
  154 +# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wzero-as-null-pointer-constant")
  155 +# endif(cxxresult)
  156 + # Leave out deprecation warnings, mostly generated by CppUnit
  157 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations")
  158 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Weffc++")
  159 +
  160 +endif(CMAKE_COMPILER_IS_GNUCXX)
  161 +
  162 +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  163 + MESSAGE(STATUS "Enabling Clang flags")
  164 + # We enable all warnings and disable what we don't need
  165 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Weverything")
  166 + # Warns if items need to be re-aligned to 4-byte boundaries. Since we use
  167 + # booleans in our code, these warnings are all over the place. If
  168 + # memory-usage becomes an issue, we can pack all booleans together using
  169 + # this warning. However, it's currently not practical to eliminate them all.
  170 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-padded")
  171 + # We use a lot of static objects, which get cleaned up at exit by their destructors.
  172 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-exit-time-destructors")
  173 + # Static objects tend to have global constructors
  174 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-global-constructors")
  175 + # We mix int / unsigned int a lot, so disabled it for the moment
  176 + #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-sign-conversion")
  177 + # Warning caused by the Qt translations
  178 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-prototypes")
  179 + # We standardize on C++11, so don't bother us with C++98
  180 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-c++98-compat -Wno-c++98-compat-pedantic")
  181 + # We explicitly want to use the switch(){default: break;} construction
  182 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-switch-enum")
  183 + # Q_OBJECT does an "int i = i;"...
  184 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-self-assign")
  185 + # Compile for C++11
  186 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
  187 + # Don't complain about unused arguments, since this also triggers warnings
  188 + # about include-directories that are unused...
  189 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Qunused-arguments")
  190 + # Don't complain about fall-throughs in switch/case statements
  191 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-implicit-fallthrough")
  192 + # Since "Q_ASSERT(false)" creates a not of noise, disable it when asserts are active
  193 + if(NOT NOASSERTS)
  194 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unreachable-code")
  195 + endif(NOT NOASSERTS)
  196 + # There is no way to avoid this warning right now, so we disable it
  197 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-weak-template-vtables")
  198 + # Warning caused by Qt resource files, so disabling it
  199 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-variable-declarations")
  200 + # WORKAROUND: Somehow Qt forgets that Clang is a C++11 compiler
  201 + ADD_DEFINITIONS("-DQ_COMPILER_INITIALIZER_LISTS")
  202 + # Clang 3.3 doesn't know Doxygens' "\test" and "\retval" tags, so disabling
  203 + # check for unknown tags for now
  204 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-documentation-unknown-command")
  205 +
  206 + if(CMAKE_CXX_COMPILER_VERSION MATCHES "3\\.4\\.2")
  207 + message(STATUS "compiler is clang version 3.4.2")
  208 + platformRelease()
  209 + if("${PLATFORM_RELEASE}" MATCHES "CentOS Linux release 7\\.2\\..+")
  210 + # Override the gnu minor version only for this specific combination of platform and clang version
  211 + set(CMAKE_CXX_FLAGS "-U__GNUC_MINOR__ -D__GNUC_MINOR__=3 ${CMAKE_CXX_FLAGS}")
  212 + message(STATUS "Overriding clang gnu minor version to 3 so that several libstd c++11 features like tuple can be used")
  213 + endif("${PLATFORM_RELEASE}" MATCHES "CentOS Linux release 7\\.2\\..+")
  214 + endif(CMAKE_CXX_COMPILER_VERSION MATCHES "3\\.4\\.2")
  215 +endif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  216 +
  217 +if(APPLE)
  218 + set(WHOLE_ARCHIVE_ON "-all_load")
  219 + set(WHOLE_ARCHIVE_OFF "")
  220 +else()
  221 + set(WHOLE_ARCHIVE_ON "-Wl,-whole-archive")
  222 + set(WHOLE_ARCHIVE_OFF "-Wl,-no-whole-archive")
  223 +endif()
... ...
cmake/config.cmake.in 0 → 100644
  1 +@PACKAGE_INIT@
  2 +
  3 +string( TOUPPER @PROJECT_NAME@ PROJECT_NAME_UPPER )
  4 +
  5 +string( CONCAT MY_PACKAGE_INCLUDE_DIR ${PROJECT_NAME_UPPER} "_INCLUDE_DIR" )
  6 +set_and_check(${MY_PACKAGE_INCLUDE_DIR} "@PACKAGE_PROJCOMP_INCLUDE_INSTALL_DIR@")
  7 +message( STATUS "${MY_PACKAGE_INCLUDE_DIR}: ${${MY_PACKAGE_INCLUDE_DIR}}" )
  8 +
  9 +string( CONCAT MY_PACKAGE_LIB_DIR ${PROJECT_NAME_UPPER} "_LIB_DIR" )
  10 +set_and_check(${MY_PACKAGE_LIB_DIR} "@PACKAGE_PROJCOMP_LIB_INSTALL_DIR@")
  11 +message( STATUS "${MY_PACKAGE_LIB_DIR}: ${${MY_PACKAGE_LIB_DIR}}" )
  12 +
  13 +string( CONCAT MY_PACKAGE_CMAKE_DIR ${PROJECT_NAME_UPPER} "_CMAKE_DIR" )
  14 +set_and_check(${MY_PACKAGE_CMAKE_DIR} "@PACKAGE_PROJCOMP_CMAKE_INSTALL_DIR@")
  15 +message( STATUS "${MY_PACKAGE_CMAKE_DIR}: ${${MY_PACKAGE_CMAKE_DIR}}" )
  16 +
  17 +string( CONCAT MY_PACKAGE_LIB_NAME ${PROJECT_NAME_UPPER} "_LIB_NAME" )
  18 +set(${MY_PACKAGE_LIB_NAME} "@PROJECT_NAME@")
  19 +message( STATUS "${MY_PACKAGE_LIB_NAME}: ${${MY_PACKAGE_LIB_NAME}}" )
  20 +
  21 +check_required_components("@PROJECT_NAME@")
  22 +#message( STATUS "@PROJECT_NAME@_FOUND: ${${@PROJECT_NAME@}_FOUND}" )
... ...
cmake/installation.cmake 0 → 100644
  1 +set( INSTALLATION_CURRENT_CMAKE_DIR ${CMAKE_CURRENT_LIST_DIR} )
  2 +
  3 +# Layout
  4 +set(USR_LOCAL_DIR "/usr/local" CACHE STRING "" FORCE)
  5 +set(LD_SO_CONF_D_DIR "/etc/ld.so.conf.d" CACHE STRING "" FORCE)
  6 +set(SYSTEMD_CONFIG_DIR "/etc/systemd/system" CACHE STRING "" FORCE)
  7 +set(VAR_LOG_DIR "/var/log" CACHE STRING "" FORCE)
  8 +set(VAR_RUN_DIR "/var/run" CACHE STRING "" FORCE)
  9 +set(PROJCOMP_LOG_INSTALL_DIR "${VAR_LOG_DIR}/${PROJECT_NAME}")
  10 +set(PROJCOMP_RUN_INSTALL_DIR "${VAR_RUN_DIR}/${PROJECT_NAME}")
  11 +set(PROJCOMP_BIN_INSTALL_DIR "${PROJECT_NAME}/bin")
  12 +set(PROJCOMP_LIB_INSTALL_DIR "lib")
  13 +set(PROJCOMP_INCLUDE_INSTALL_DIR "${PROJECT_NAME}/include")
  14 +set(PROJCOMP_ETC_INSTALL_DIR "${PROJECT_NAME}/etc")
  15 +set(PROJCOMP_CMAKE_INSTALL_DIR "${PROJECT_NAME}/cmake")
  16 +set(PROJCOMP_COMPONENT_NAME "${REPOSITORY_PACKAGE_NAME}-${PROJECT_NAME}")
  17 +set(PROJCOMP_COMPONENT_NAME_DEVEL "${PROJCOMP_COMPONENT_NAME}-devel")
  18 +
  19 +# @brief Installs a library component and performs related actions.
  20 +function(install_component)
  21 +
  22 +set(GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
  23 +
  24 +# Configuration
  25 +string(TOLOWER ${PROJECT_NAME} MODULE_NAME_LOWER)
  26 +set(PROJECT_CONFIG "${GENERATED_DIR}/${MODULE_NAME_LOWER}-config.cmake")
  27 +
  28 +# Generate the cmake config file
  29 +include(CMakePackageConfigHelpers)
  30 +configure_package_config_file(
  31 + "${INSTALLATION_CURRENT_CMAKE_DIR}/config.cmake.in"
  32 + "${PROJECT_CONFIG}"
  33 + INSTALL_DESTINATION "${PROJCOMP_CMAKE_INSTALL_DIR}"
  34 + PATH_VARS PROJCOMP_INCLUDE_INSTALL_DIR PROJCOMP_LIB_INSTALL_DIR PROJCOMP_CMAKE_INSTALL_DIR
  35 +)
  36 +
  37 +# Install license file
  38 +if (EXISTS "${INSTALLATION_CURRENT_CMAKE_DIR}/COPYING.OSDEV")
  39 + install(
  40 + FILES "${INSTALLATION_CURRENT_CMAKE_DIR}/COPYING.OSDEV"
  41 + DESTINATION ${PROJCOMP_LIB_INSTALL_DIR}
  42 + COMPONENT "${PROJCOMP_COMPONENT_NAME}"
  43 + )
  44 +endif()
  45 +
  46 +# Install targets
  47 +install(
  48 + TARGETS ${PROJECT_NAME}
  49 + DESTINATION ${PROJCOMP_LIB_INSTALL_DIR}
  50 + COMPONENT "${PROJCOMP_COMPONENT_NAME}"
  51 +)
  52 +
  53 +# Configure and install dynamic linker runtime bindings (ld.so.conf.d file)
  54 +configure_file(
  55 + "${INSTALLATION_CURRENT_CMAKE_DIR}/ld.so.conf.d.cmake.in"
  56 + "${GENERATED_DIR}/${PROJECT_NAME}.conf"
  57 +)
  58 +install(
  59 + FILES "${GENERATED_DIR}/${PROJECT_NAME}.conf"
  60 + DESTINATION ${LD_SO_CONF_D_DIR}
  61 + COMPONENT "${PROJCOMP_COMPONENT_NAME}"
  62 +)
  63 +
  64 +# Configure and set cpack post install script
  65 +configure_file(
  66 + "${INSTALLATION_CURRENT_CMAKE_DIR}/library.post_install.inc.cmake.in"
  67 + "${GENERATED_DIR}/library.post_install.inc"
  68 +)
  69 +# Set the per-component post install script file.
  70 +set(CPACK_RPM_${PROJCOMP_COMPONENT_NAME}_POST_INSTALL_SCRIPT_FILE "${GENERATED_DIR}/library.post_install.inc" CACHE STRING "${PROJECT_NAME} post_install script" FORCE)
  71 +
  72 +# Headers
  73 +# At the moment, we anticipate the headers to be located according to either 1) or 2).
  74 +
  75 +# 1) Apply glob style pattern for those projects that have the header files in the root folder of the repository (such as opcua_model).
  76 +file (GLOB PACKAGE_HEADERS "${CMAKE_CURRENT_LIST_DIR}/*.h*")
  77 +install(
  78 + FILES ${PACKAGE_HEADERS}
  79 + DESTINATION ${PROJCOMP_INCLUDE_INSTALL_DIR}
  80 + COMPONENT "${PROJCOMP_COMPONENT_NAME_DEVEL}"
  81 +)
  82 +# 2) Copy the include dir for those projects that have the header files in include/mlogic (such as opc_utils).
  83 +if (EXISTS ${CMAKE_CURRENT_LIST_DIR}/include/mlogic)
  84 + install(
  85 + DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/include/mlogic
  86 + DESTINATION ${PROJCOMP_INCLUDE_INSTALL_DIR}
  87 + COMPONENT "${PROJCOMP_COMPONENT_NAME_DEVEL}"
  88 + )
  89 +endif()
  90 +
  91 +# Install cmake project config
  92 +install(
  93 + FILES ${PROJECT_CONFIG}
  94 + DESTINATION ${PROJCOMP_CMAKE_INSTALL_DIR}
  95 + COMPONENT "${PROJCOMP_COMPONENT_NAME_DEVEL}"
  96 +)
  97 +
  98 +# The -devel package depends on the runtime package
  99 +set(CPACK_RPM_${PROJCOMP_COMPONENT_NAME_DEVEL}_PACKAGE_REQUIRES "${PROJCOMP_COMPONENT_NAME} = ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}-${CURRENT_PROJECT_VERSION_RELEASENR}" CACHE STRING "${PROJCOMP_COMPONENT_NAME_DEVEL} dependency" FORCE )
  100 +# Clear the dependencies for the runtime package, as cpack erroneously added unexpected dependencies (hints towards the previously built package)
  101 +set(CPACK_RPM_${PROJCOMP_COMPONENT_NAME}_PACKAGE_REQUIRES "" CACHE STRING "${PROJCOMP_COMPONENT_NAME} dependencies" FORCE )
  102 +
  103 +endfunction()
  104 +
  105 +# @brief Installs the executable binary and related items
  106 +# (such as systemd service files, log folder, post [un]install scripts)
  107 +# @param INSTALL_SYSTEMD_SERVICE [optional] Boolean to indicate whether the systemd unit files must be installed. Optional. The default is ON.
  108 +function(install_application)
  109 +
  110 +set ( optional_macro_args ${ARGN} )
  111 +list ( LENGTH optional_macro_args num_optional_args )
  112 +if ( ${num_optional_args} GREATER 0 )
  113 + list ( GET optional_macro_args 0 INSTALL_SYSTEMD_SERVICE )
  114 +endif()
  115 +
  116 +if(NOT DEFINED INSTALL_SYSTEMD_SERVICE)
  117 + set(INSTALL_SYSTEMD_SERVICE "ON")
  118 +endif()
  119 +
  120 +message(STATUS "INSTALL_SYSTEMD_SERVICE: ${INSTALL_SYSTEMD_SERVICE}")
  121 +
  122 +set(GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
  123 +
  124 +# Install license file
  125 +if (EXISTS "${INSTALLATION_CURRENT_CMAKE_DIR}/COPYING.OSDEV")
  126 + install(
  127 + FILES "${INSTALLATION_CURRENT_CMAKE_DIR}/COPYING.OSDEV"
  128 + DESTINATION ${PROJCOMP_BIN_INSTALL_DIR}
  129 + COMPONENT "${PROJCOMP_COMPONENT_NAME}"
  130 + )
  131 +endif()
  132 +
  133 +# Install binary file
  134 +install(
  135 + TARGETS ${PROJECT_NAME}
  136 + DESTINATION ${PROJCOMP_BIN_INSTALL_DIR}
  137 + COMPONENT "${PROJCOMP_COMPONENT_NAME}"
  138 +)
  139 +
  140 +# Install configuration
  141 +if (EXISTS ${CMAKE_CURRENT_LIST_DIR}/config/)
  142 + install(
  143 + DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/config/
  144 + DESTINATION ${PROJCOMP_ETC_INSTALL_DIR}
  145 + COMPONENT "${PROJCOMP_COMPONENT_NAME}"
  146 + )
  147 +endif()
  148 +
  149 +if (INSTALL_SYSTEMD_SERVICE STREQUAL "ON")
  150 + # Configure and install systemd unit file
  151 + configure_file(
  152 + "${INSTALLATION_CURRENT_CMAKE_DIR}/service.cmake.in"
  153 + "${GENERATED_DIR}/${PROJECT_NAME}.service"
  154 + )
  155 + install(
  156 + FILES "${GENERATED_DIR}/${PROJECT_NAME}.service"
  157 + DESTINATION ${SYSTEMD_CONFIG_DIR}
  158 + COMPONENT "${PROJCOMP_COMPONENT_NAME}"
  159 + )
  160 +endif()
  161 +
  162 +# Install log folder
  163 +install(
  164 + DIRECTORY
  165 + DESTINATION ${PROJCOMP_LOG_INSTALL_DIR}
  166 + DIRECTORY_PERMISSIONS
  167 + OWNER_WRITE OWNER_READ OWNER_EXECUTE
  168 + GROUP_WRITE GROUP_READ GROUP_EXECUTE SETGID
  169 + # No permissions for WORLD
  170 + COMPONENT "${PROJCOMP_COMPONENT_NAME}"
  171 +)
  172 +
  173 +# Install run folder
  174 +install(
  175 + DIRECTORY
  176 + DESTINATION ${PROJCOMP_RUN_INSTALL_DIR}
  177 + DIRECTORY_PERMISSIONS
  178 + OWNER_WRITE OWNER_READ OWNER_EXECUTE
  179 + GROUP_WRITE GROUP_READ GROUP_EXECUTE
  180 + # No permissions for WORLD
  181 + COMPONENT "${PROJCOMP_COMPONENT_NAME}"
  182 +)
  183 +
  184 +# Configure and set cpack post install script
  185 +configure_file(
  186 + "${INSTALLATION_CURRENT_CMAKE_DIR}/application.post_install.inc.cmake.in"
  187 + "${GENERATED_DIR}/application.post_install.inc"
  188 +)
  189 +# Set the per-component post install script file.
  190 +set(CPACK_RPM_${PROJCOMP_COMPONENT_NAME}_POST_INSTALL_SCRIPT_FILE "${GENERATED_DIR}/application.post_install.inc" CACHE STRING "${PROJECT_NAME} post_install script" FORCE)
  191 +
  192 +endfunction()
... ...
cmake/ld.so.conf.d.cmake.in 0 → 100644
  1 +@CMAKE_INSTALL_PREFIX@/@PROJCOMP_LIB_INSTALL_DIR@/
... ...
cmake/library.cmake 0 → 100644
  1 +# @brief Adds a single shared or static library target and performs related actions,
  2 +# such as target properties and some packaging variables.
  3 +# @note The default binary directory is CMAKE_BINARY_DIR, but can be overridden by specifying the ${PROJECT_NAME}_CURRENT_BINARY_DIR.
  4 +function(add_libraries)
  5 +
  6 +message( STATUS "${PROJECT_NAME} linking libraries : ${ARGN}")
  7 +
  8 +# Use CMAKE_BINARY_DIR by default, but override if necessary.
  9 +set(CURRENT_PROJECT_BINARY_DIR ${CMAKE_BINARY_DIR})
  10 +if (${PROJECT_NAME}_CURRENT_BINARY_DIR)
  11 + set(CURRENT_PROJECT_BINARY_DIR ${${PROJECT_NAME}_CURRENT_BINARY_DIR})
  12 +endif()
  13 +message(STATUS "CURRENT_PROJECT_BINARY_DIR : ${CURRENT_PROJECT_BINARY_DIR}")
  14 +
  15 +if( ${BUILD_STATIC} )
  16 + if(${BUILD_STATIC} STREQUAL "ON")
  17 + set(SHARED_OR_STATIC "STATIC")
  18 + else()
  19 + set(SHARED_OR_STATIC "SHARED")
  20 + endif()
  21 +else()
  22 + set(SHARED_OR_STATIC "SHARED")
  23 +endif()
  24 +
  25 +add_library( ${PROJECT_NAME} ${SHARED_OR_STATIC}
  26 + ${SRC_LIST}
  27 +)
  28 +
  29 +target_link_libraries( ${PROJECT_NAME}
  30 + ${ARGN}
  31 +)
  32 +
  33 +set_target_properties( ${PROJECT_NAME}
  34 + PROPERTIES
  35 + VERSION ${PROJECT_VERSION}
  36 + SOVERSION ${PROJECT_VERSION_MAJOR}
  37 + LIBRARY_OUTPUT_DIRECTORY ${CURRENT_PROJECT_BINARY_DIR}/lib
  38 + ARCHIVE_OUTPUT_DIRECTORY ${CURRENT_PROJECT_BINARY_DIR}/lib
  39 +)
  40 +
  41 +endfunction()
... ...
cmake/library.post_install.inc.cmake.in 0 → 100644
  1 +# Update the dynamic linker runtime bindings.
  2 +VAR1="/sbin/ldconfig"
  3 +VAR2=`which ldconfig`
  4 +
  5 +if [ -e $VAR1 ]; then
  6 + $VAR1
  7 +elif [ ! -z $VAR2 ]; then
  8 + $VAR2
  9 +else
  10 + VAR3=`find / -name ldconfig -type f`
  11 + if [ -e $VAR3 ]; then
  12 + $VAR3
  13 + else
  14 + echo "ldconfig not found!!!"
  15 + fi
  16 +fi
... ...
cmake/packaging.cmake 0 → 100644
  1 +set( PACKAGING_CURRENT_CMAKE_DIR ${CMAKE_CURRENT_LIST_DIR} )
  2 +
  3 +# @brief Creates component packages. Can be executed by calling "make package".
  4 +function(package_component)
  5 +
  6 +# Determine build architecture
  7 +execute_process(
  8 + COMMAND uname -m
  9 + COMMAND tr -d '\n'
  10 + OUTPUT_VARIABLE ARCHITECTURE
  11 +)
  12 +
  13 +# Set package name
  14 +# Known issue: Due to the way cpack works (only evaluated once), the PROJECT_NAME will be the last package to
  15 +# call this function. We worked around this by including packaging.cmake only from 1 location in the repository.
  16 +# However, in a superbuild (i.e. from an mlogic parent directory building all repositories at once),
  17 +# this will yield an erroneous package name (usually datacollector, as that is the last project to be evaluated).
  18 +# In order to prevent dynamic package names caused by different build directories,
  19 +# The package name is set to a fixed value. The actual name of the rpm and package will be determined by
  20 +# other variable values such as the component name and version, leading to uniquely identifiable packages.
  21 +set(CPACK_PACKAGE_NAME osdev)
  22 +
  23 +# Set package version numbers
  24 +set(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}")
  25 +set(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}")
  26 +set(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}")
  27 +# The release number is not part of CPACK_PACKAGE_VERSION and is dealt with seperately (see below)
  28 +set(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
  29 +message( STATUS "Packaging ${CPACK_PACKAGE_NAME} ${CPACK_PACKAGE_VERSION} for ${ARCHITECTURE}.")
  30 +
  31 +set(CPACK_PACKAGE_RELEASE "1")
  32 +if (CURRENT_PROJECT_VERSION_RELEASENR)
  33 + set(CPACK_PACKAGE_RELEASE ${CURRENT_PROJECT_VERSION_RELEASENR})
  34 +endif()
  35 +
  36 +# Build CPack driven installer packages
  37 +include(InstallRequiredSystemLibraries)
  38 +
  39 +# This doesn't seem to work, only the specific archive variables (_RPM_, _DEB_) seem to work
  40 +#set(CPACK_ARCHIVE_COMPONENT_INSTALL ON)
  41 +set(CPACK_COMPONENTS_IGNORE_GROUPS 1)
  42 +# Enable the line below if the repository being built has only 1 target that's installed
  43 +# (No longer necessary, as we now have at least 2 components: runtime and development components.)
  44 +#set(CPACK_COMPONENTS_ALL ${CPACK_PACKAGE_NAME} ${CPACK_PACKAGE_NAME}-devel)
  45 +
  46 +# Enable DESTDIR and copy CMAKE_INSTALL_PREFIX, which requires disabling rpm relocatability
  47 +set(CPACK_SET_DESTDIR 1)
  48 +set(CPACK_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})
  49 +set(CPACK_PACKAGE_RELOCATABLE OFF)
  50 +
  51 +# Set package metadata
  52 +if (EXISTS "${PACKAGING_CURRENT_CMAKE_DIR}/../../../COPYING.OSDEV")
  53 + set(CPACK_RESOURCE_FILE_LICENSE "${PACKAGING_CURRENT_CMAKE_DIR}/../../../COPYING.OSDEV")
  54 +endif()
  55 +if (EXISTS "${PACKAGING_CURRENT_CMAKE_DIR}/../../../README.md")
  56 + set(CPACK_RESOURCE_FILE_README "${PACKAGING_CURRENT_CMAKE_DIR}/../../../README.md")
  57 +endif()
  58 +set(CPACK_SYSTEM_NAME "${ARCHITECTURE}")
  59 +set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}.${CURRENT_PROJECT_VERSION_LETTER}-${CPACK_PACKAGE_RELEASE}.${ARCHITECTURE}")
  60 +
  61 +# RPM specific fields
  62 +set(CPACK_RPM_PACKAGE_ARCHITECTURE "${ARCHITECTURE}")
  63 +set(CPACK_RPM_COMPONENT_INSTALL ON)
  64 +set(CPACK_RPM_PACKAGE_GROUP "${CPACK_PACKAGE_NAME}-${REPOSITORY_PACKAGE_NAME}")
  65 +# Unfortunately, CPACK_RPM_PACKAGE_RELEASE isn't inherited from CPACK_PACKAGE_RELEASE automatically.
  66 +set(CPACK_RPM_PACKAGE_RELEASE ${CPACK_PACKAGE_RELEASE})
  67 +set(CPACK_RPM_PACKAGE_VENDOR ${CURRENT_PROJECT_MANUFACTURER_CODE})
  68 +set(CPACK_RPM_PACKAGE_SUMMARY "${CPACK_PACKAGE_NAME} package")
  69 +set(CPACK_RPM_PACKAGE_DESCRIPTION "${CPACK_RPM_PACKAGE_SUMMARY}")
  70 +set(CPACK_RPM_PACKAGE_LICENSE "${CURRENT_PROJECT_MANUFACTURER_CODE}")
  71 +
  72 +# Select CPack generators
  73 +set(CPACK_GENERATOR "RPM")
  74 +
  75 +# Exclude certain system directories from the rpm
  76 +set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION /usr;/etc;/etc/systemd;/var;${USR_LOCAL_DIR};${LD_SO_CONF_D_DIR};${SYSTEMD_CONFIG_DIR};${VAR_LOG_DIR};${VAR_RUN_DIR})
  77 +
  78 +# This line should come last
  79 +include(CPack)
  80 +
  81 +endfunction(package_component)
... ...
cmake/projectheader.cmake 0 → 100644
  1 +# @brief Defines the project name, version and binary dir.
  2 +# @param CURRENT_PROJECT_NAME The name of the project to define.
  3 +# @param CURRENT_PROJECT_BINARY_DIR [optional] Override for the default project binary dir (PROJECT_BINARY_DIR for executables, CMAKE_BINARY_DIR for libraries).
  4 +macro ( project_header CURRENT_PROJECT_NAME )
  5 +
  6 +# Determine the version and fill the following variables :
  7 +# SOVERSION : The tag from the git-repository. Not necessarily a number-only string.
  8 +find_package(Git QUIET)
  9 +
  10 +if(GIT_FOUND)
  11 + if( EXISTS "${PROJECT_SOURCE_DIR}/.git")
  12 + execute_process(COMMAND ${GIT_EXECUTABLE} describe --abbrev=0 --tags
  13 + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
  14 + OUTPUT_VARIABLE CURRENT_PROJECT_VERSION
  15 + OUTPUT_STRIP_TRAILING_WHITESPACE
  16 + RESULT_VARIABLE GIT_SUBMOD_RESULT)
  17 +
  18 + # Exit code will be 128 if no tags are present
  19 + if( ${GIT_SUBMOD_RESULT} EQUAL 128 )
  20 + set(CURRENT_PROJECT_VERSION "0.0.1")
  21 + endif()
  22 +
  23 +
  24 +
  25 + message( STATUS "================================================================" )
  26 + message( STATUS "Found the following tag : ${CURRENT_PROJECT_VERSION}")
  27 + message( STATUS "================================================================" )
  28 + else()
  29 + message( STATUS ".git directory does not exists..")
  30 + message( STATUS "Project directory : ${PROJECT_SOURCE_DIR}")
  31 + endif()
  32 +else()
  33 + message( STATUS "git-command not found....")
  34 + message( FATAL "Unable to determine the version of the software.")
  35 +endif()
  36 +
  37 +
  38 +message( STATUS "" )
  39 +message( STATUS "================================================================" )
  40 +message( STATUS "Creating Makefile of ${CURRENT_PROJECT_NAME}" )
  41 +message( STATUS "================================================================" )
  42 +message( STATUS "CURRENT_PROJECT_VERSION: ${CURRENT_PROJECT_VERSION}" )
  43 +
  44 +set(SOVERSION "${CURRENT_PROJECT_VERSION}")
  45 +set(VERSION "${CURRENT_PROJECT_VERSION}")
  46 +
  47 +project(${CURRENT_PROJECT_NAME} VERSION ${CURRENT_PROJECT_VERSION})
  48 +set_property(GLOBAL PROPERTY GLOBAL_DEPENDS_NO_CYCLES 1)
  49 +
  50 +set ( optional_macro_args ${ARGN} )
  51 +list ( LENGTH optional_macro_args num_optional_args )
  52 +if ( ${num_optional_args} GREATER 0 )
  53 + # Set project binary dir override to the specified value
  54 + list ( GET optional_macro_args 0 CURRENT_PROJECT_BINARY_DIR )
  55 + set ( ${PROJECT_NAME}_CURRENT_BINARY_DIR ${CURRENT_PROJECT_BINARY_DIR} CACHE STRING "${PROJECT_NAME} binary dir" FORCE )
  56 +endif()
  57 +
  58 +endmacro()
... ...
cmake/qtmoc.cmake 0 → 100644
  1 +# @brief Creates the qt5 mocs for the specified header files.
  2 +# @param SRC_LIST The current source list of the project, to which to add the created moc files.
  3 +# @param MOC_LIST The list of header files for which to create qt5 mocs.
  4 +macro(create_mocs SRC_LIST MOC_LIST)
  5 +
  6 +message( STATUS "${PROJECT_NAME} Creating mocs for: ${ARGN}")
  7 +
  8 +set( MOCABLE_LIST
  9 + ${ARGN}
  10 +)
  11 +
  12 +# Empty the MOC_LIST variable
  13 +set( ${MOC_LIST}
  14 +)
  15 +
  16 +# Create the MOC_LIST
  17 +QT6_WRAP_CPP( ${MOC_LIST} ${MOCABLE_LIST} )
  18 +
  19 +# Append SRC_LIST with MOC_LIST
  20 +list ( APPEND SRC_LIST
  21 + ${${MOC_LIST}}
  22 +)
  23 +
  24 +message( STATUS "${PROJECT_NAME} MOC_LIST: ${${MOC_LIST}}")
  25 +message( STATUS "${PROJECT_NAME} SRC_LIST: ${${SRC_LIST}}")
  26 +
  27 +set_source_files_properties(
  28 + ${${MOC_LIST}}
  29 + PROPERTIES
  30 + COMPILE_FLAGS -Wno-undefined-reinterpret-cast
  31 +)
  32 +
  33 +endmacro()
... ...
cmake/qtuic.cmake 0 → 100644
  1 +# @brief Creates the qt5 ui_ headers for the specified designer files.
  2 +# @param SRC_LIST The current source list of the project, to which to add the created moc files.
  3 +# @param UIC_LIST The list of designer files for which to create qt5 headers.
  4 +macro(create_ui SRC_LIST UIC_LIST)
  5 +
  6 +message( STATUS "${PROJECT_NAME} Creating headers for: ${ARGN}")
  7 +
  8 +set( UICABLE_LIST
  9 + ${ARGN}
  10 +)
  11 +
  12 +# Empty the UIC_LIST variable
  13 +set( ${UIC_LIST}
  14 +)
  15 +
  16 +# Create the UIC_LIST
  17 +QT5_WRAP_UI( ${UIC_LIST} ${UICABLE_LIST} )
  18 +
  19 +# Append SRC_LIST with UIC_LIST
  20 +list ( APPEND SRC_LIST
  21 + ${${UIC_LIST}}
  22 +)
  23 +
  24 +# Avoid warnings by including a generated header file.
  25 +include_directories( ${SYSTEMORNOT}
  26 + ${CMAKE_CURRENT_BINARY_DIR}
  27 + ${CMAKE_SOURCE_DIR}
  28 +)
  29 +
  30 +message( STATUS "${PROJECT_NAME} UIC_LIST: ${${UIC_LIST}}")
  31 +message( STATUS "${PROJECT_NAME} SRC_LIST: ${${SRC_LIST}}")
  32 +
  33 +set_source_files_properties(
  34 + ${${UIC_LIST}}
  35 + PROPERTIES
  36 + COMPILE_FLAGS -Wno-undefined-reinterpret-cast
  37 +)
  38 +
  39 +endmacro()
... ...
cmake/service.cmake.in 0 → 100644
  1 +[Unit]
  2 +Description=Mlogic @PROJECT_NAME@
  3 +After=network.target
  4 +
  5 +[Service]
  6 +WorkingDirectory=@CMAKE_INSTALL_PREFIX@/@PROJECT_NAME@/
  7 +ExecStart=@CMAKE_INSTALL_PREFIX@/@PROJCOMP_BIN_INSTALL_DIR@/@PROJECT_NAME@
  8 +User=@PROJECT_NAME@
  9 +
  10 +[Install]
  11 +WantedBy=multi-user.target
... ...
cmake/sync.sh 0 → 100755
  1 +#!/bin/bash
  2 +
  3 +# $1: cmake-file-filename
  4 +function sync-file()
  5 +{
  6 + echo ---------- Syncing $1 started
  7 +
  8 + cp -f ../../mlogic_support/cmake/$1 $1
  9 + if [ $? -ne 0 ] ; then
  10 + echo ---------- Syncing $1 failed
  11 + return 1
  12 + fi
  13 +
  14 + echo ---------- Syncing $1 finished
  15 +}
  16 +
  17 +# We don't copy installation.cmake, because the includes files are in the src folder.
  18 +# datacollector is different in this respect from the other repositories.
  19 +MLOGIC_CMAKE_FILES="artifacts.cmake
  20 +compiler.cmake
  21 +config.cmake.in
  22 +FindGMock.cmake
  23 +library.cmake
  24 +packaging.cmake
  25 +projectheader.cmake
  26 +qtmoc.cmake"
  27 +
  28 +# Process the cmake files
  29 +cd $(dirname $(readlink -f $0)) || exit 1
  30 +for cmake_file in ${MLOGIC_CMAKE_FILES}
  31 +do
  32 + sync-file ${cmake_file}
  33 +done
... ...
cmake/targetprops.cmake 0 → 100644
  1 +set_target_properties( ${PROJECT_NAME} PROPERTIES
  2 + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
  3 + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
  4 + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/archive
  5 + LINKER_LANGUAGE CXX
  6 +)
  7 +
... ...
include/daemonbase.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ****************************************************************************/
  22 +#pragma once
  23 +
  24 +#include <cstdlib>
  25 +#include <iostream>
  26 +#include <sys/types.h>
  27 +#include <unistd.h>
  28 +#include <cerrno>
  29 +#include <cstring>
  30 +#include <string>
  31 +#include <csignal>
  32 +#include <sys/stat.h>
  33 +#include <stdexcept>
  34 +#include <chrono>
  35 +#include <thread>
  36 +#include <atomic>
  37 +#include <condition_variable>
  38 +
  39 +#include "daemonlog.h"
  40 +#include "daemonconfig.h"
  41 +
  42 +namespace osdev::components::daemon
  43 +{
  44 +class DaemonBase
  45 +{
  46 +private:
  47 + static DaemonBase *instance;
  48 +
  49 +public:
  50 + /**
  51 + * Construct a new daemon process.
  52 + * @note: only one daemon per application is possible.
  53 + * @param name: name of daemon process
  54 + * @param cwd: daemon current working directory, root "/" directory by default.
  55 + * @param update_duration: duration to sleep before waking up the on_update() callback every time, deafult 10 seconds.
  56 + */
  57 + DaemonBase(const std::string &name,
  58 + const std::string &cwd = "/",
  59 + const std::chrono::high_resolution_clock::duration &update_duration = std::chrono::seconds(10))
  60 + : m_name(name)
  61 + , m_cwd(cwd)
  62 + , m_update_duration(update_duration)
  63 + , m_is_running(false)
  64 + , m_exit_code(EXIT_SUCCESS)
  65 + {
  66 + if (instance)
  67 + {
  68 + daemonlog::error("Only one daemon instance is possible.");
  69 + std::exit(EXIT_FAILURE);
  70 + }
  71 + instance = this;
  72 + }
  73 +
  74 + DaemonBase()
  75 + : m_name("<unknown_daemon>")
  76 + , m_cwd("/")
  77 + , m_update_duration(std::chrono::seconds(10))
  78 + , m_is_running(false)
  79 + {
  80 + if(instance)
  81 + {
  82 + daemonlog::error("Only one daemon instance is possible.");
  83 + std::exit(EXIT_FAILURE);
  84 + }
  85 + instance = this;
  86 + }
  87 +
  88 + void run(int argc, char *argv[])
  89 + {
  90 + if (m_is_running.load())
  91 + {
  92 + daemonlog::error("Daemon '" + m_name + "' is already running.");
  93 + return;
  94 + }
  95 +
  96 + // Get config file path from cmd args passed by ExecStart=/usr/bin/my_daemon --config /etc/my_daemon/my_daemon.conf
  97 + // since we need it for a reload./
  98 + for (std::int32_t i = 0; i < argc; i++)
  99 + {
  100 + if (!std::strcmp(argv[i], "--config"))
  101 + {
  102 + if (i + 1 < argc)
  103 + {
  104 + m_config_file = argv[i + 1];
  105 + }
  106 + else
  107 + {
  108 + daemonlog::error("Missing config file. Did ytou forget to specify a config file in your .serve file's ExecStart ?");
  109 + }
  110 + break;
  111 + }
  112 + }
  113 +
  114 + // daemonize this program by forking the parent process.
  115 + daemonize();
  116 +
  117 + // Mark as running (better to have it before on_start() as a user may call stop() inside on_start()).
  118 + m_is_running = true;
  119 + on_start(daemonconfig::from_file(m_config_file));
  120 + while (m_is_running.load())
  121 + {
  122 + on_update();
  123 +
  124 + // On long sleeps, if we want to exit we need a condition_variable to wake up the thread from sleep to carry on exiting.
  125 + std::unique_lock<std::mutex> lock(m_mutex);
  126 + m_update_cv.wait_for(lock, m_update_duration, [this]()
  127 + {
  128 + return !m_is_running.load();
  129 + });
  130 + }
  131 + on_stop();
  132 + }
  133 +
  134 + void stop(std::int32_t code = EXIT_SUCCESS)
  135 + {
  136 + m_exit_code = code;
  137 + m_is_running.store(false);
  138 + m_update_cv.notify_all();
  139 + }
  140 +
  141 + virtual ~DaemonBase()
  142 + {
  143 + daemonlog::shutdown();
  144 + // Terminate the child process when the daemon completes (loop stopped)
  145 + // @note that calling std::exit() inside the run function will not call DTor,
  146 + std::exit(m_exit_code);
  147 + }
  148 +
  149 + // Getters & Setters
  150 + void set_update_duration(const std::chrono::high_resolution_clock::duration &duration) noexcept
  151 + { m_update_duration = duration; }
  152 +
  153 + const std::chrono::high_resolution_clock::duration& get_update_duration() const noexcept
  154 + { return m_update_duration; }
  155 +
  156 + void set_name(const std::string &daemon_name) noexcept
  157 + { m_name = daemon_name; }
  158 +
  159 + const std::string& get_name() const noexcept
  160 + { return m_name; }
  161 +
  162 + void set_cwd(const std::string &current_working_dir) noexcept
  163 + {
  164 + // Change the current working directory to a directory guaranteed to exist, provided by the user.
  165 + if (chdir(current_working_dir.c_str()) < 0)
  166 + {
  167 + daemonlog::error("Could not change current working directory to'"
  168 + + current_working_dir + "': "
  169 + + std::string(std::strerror(errno)));
  170 + return;
  171 + }
  172 + m_cwd = current_working_dir;
  173 + }
  174 +
  175 + const std::string& get_cwd() const noexcept { return m_cwd; }
  176 +
  177 + pid_t get_pid() const noexcept { return m_pid; }
  178 + pid_t get_sid() const noexcept { return m_sid; }
  179 +
  180 +protected: // Callbacks
  181 + /**
  182 + * @brief Called once on daemon starts
  183 + * @scenarios:
  184 + * - when system starts
  185 + * - when you run `$ systemctl start your_daemon` manually
  186 + * @param cfg: Installed daemon config file
  187 + * Initialize your code here...
  188 + */
  189 + virtual void on_start(const daemonconfig &cfg) = 0;
  190 +
  191 + /**
  192 + * @brief Called every DURATION which was set by set_update_duration(DURATION).
  193 + * Update your code here...
  194 + */
  195 + virtual void on_update() = 0;
  196 +
  197 + /**
  198 + * @brief Called once before daemon is about to exit.
  199 + * @scenarios:
  200 + * - when you call stop(ewxit_code)
  201 + * - when you run `$ systemctl stop your_daemon` manually
  202 + * - when the system kills your daemon for some reason
  203 + * Cleanup your code here...
  204 + */
  205 + virtual void on_stop() = 0;
  206 +
  207 + /**
  208 + * @brief Called once when daemon's config or service files are updated.
  209 + * @scenarios:
  210 + * - when you run `$systemctl daemon-reload` after you have changed your .conf or .service files
  211 + * (after reinstalling your daemon with `$ sudo make install` for example)
  212 + * Reinitialize your code here...
  213 + */
  214 + virtual void on_reload(const daemonconfig &cfg) = 0;
  215 +
  216 +private:
  217 + static void signal_handler(std::int32_t sig)
  218 + {
  219 + daemonlog::info("Signal " + std::to_string(sig) + " received.");
  220 +
  221 + switch(sig)
  222 + {
  223 + // daemon.service handler : ExecStop=/bin/kill -s SIGTERM $MAINPID
  224 + // When daemon is stopped, system sends SIGTERM first.
  225 + // If daemon didn't respond during 90 seconds, it will send a SIGKILL signal
  226 + case SIGTERM:
  227 + case SIGKILL:
  228 + {
  229 + instance->stop();
  230 + break;
  231 + }
  232 + // daemon.service handler : ExecReload=/bin/kill -S SIGHUB $MAINPID
  233 + // When a daemon is reloaded due updates in .service or .conf, system sends SIGHUP signal.
  234 + case SIGHUP:
  235 + {
  236 + instance->on_reload(daemonconfig::from_file(instance->m_config_file));
  237 + break;
  238 + }
  239 + default:
  240 + {
  241 + break;
  242 + }
  243 + }
  244 + }
  245 +
  246 + /**
  247 + * @brief Daemonize this program
  248 + * @note: It is also possible to use glibc function daemon()
  249 + * at this point, but it is useful to customize your daemon.
  250 + * Like for example handle signals, set working directory...
  251 + */
  252 + void daemonize()
  253 + {
  254 + // Fork off the parent process (https://linux.die.net/man/3/fork)
  255 + m_pid = fork();
  256 + // Success: The parent process continues with a PID > 0
  257 + if (m_pid > 0)
  258 + {
  259 + std::exit(EXIT_SUCCESS);
  260 + }
  261 + else if (m_pid < 0)
  262 + {
  263 + // An error occurred. A process ID lower than 0 indicates a failure in either process
  264 + std::exit(EXIT_FAILURE);
  265 + }
  266 + // The parent process has now terminated, and the forked child process will continue
  267 + // (the pid of the child process was 0)
  268 +
  269 + // Since the child process is a daemon, the unask needs to be set so files and logs can be written
  270 + umask(0);
  271 +
  272 + // Initialize syslog for this daemon, here it's a good place to do so.
  273 + daemonlog::init(m_name);
  274 +
  275 + // On success: The child process becomes session leader. Generate a session ID for the child process.
  276 + m_sid = setsid();
  277 + if (m_sid < 0)
  278 + {
  279 + daemonlog::error("Could not set SID to child process: " + std::string(std::strerror(errno)));
  280 + std::exit(EXIT_FAILURE);
  281 + }
  282 +
  283 + // Ignore the Child terminated or stopped signal.
  284 + std::signal(SIGCHLD, SIG_IGN);
  285 +
  286 + // Set signal handlers to detect daemon interrupt, restart..
  287 + std::signal(SIGHUP, signal_handler);
  288 +
  289 + // When a sudo systemctl stop my_daemon is ran, by default, a SIGTERM is sent,
  290 + // followed by 90 seconds of waiting followed by a SIGKILL
  291 + std::signal(SIGTERM, signal_handler);
  292 + std::signal(SIGKILL, signal_handler);
  293 +
  294 + // Change the current working directory to a directory guaranteed to exist, procided by the user
  295 + if (chdir(m_cwd.c_str()) < 0)
  296 + {
  297 + daemonlog::error("Could not change current working directory to `" + m_cwd + "': " + std::string(std::strerror(errno)));
  298 + std::exit(EXIT_FAILURE);
  299 + }
  300 +
  301 + // A daemon cannot use the terminal, so close standard file descriptors for security reasons
  302 + close(STDIN_FILENO);
  303 + close(STDOUT_FILENO);
  304 + close(STDERR_FILENO);
  305 + }
  306 +
  307 + // Member variables
  308 + pid_t m_pid;
  309 + pid_t m_sid;
  310 + std::string m_config_file;
  311 + std::string m_name;
  312 + std::string m_cwd;
  313 + std::chrono::high_resolution_clock::duration m_update_duration;
  314 + std::atomic<bool> m_is_running;
  315 + std::condition_variable m_update_cv;
  316 + std::mutex m_mutex;
  317 + std::int32_t m_exit_code;
  318 +
  319 +};
  320 +
  321 +DaemonBase* DaemonBase::instance = nullptr;
  322 +
  323 +} /* End namespace osdev::components::daemon */
0 324 \ No newline at end of file
... ...
include/daemonconfig.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ****************************************************************************/
  22 +#pragma once
  23 +
  24 +#include <fstream>
  25 +#include <cstring>
  26 +#include <string>
  27 +#include <vector>
  28 +#include <sstream>
  29 +#include <iostream>
  30 +#include <algorithm>
  31 +#include <map>
  32 +
  33 +#include "daemonlog.h"
  34 +
  35 +// TODO: Do a better config parsing.
  36 +namespace osdev::components::daemon
  37 +{
  38 + struct daemonconfig
  39 + {
  40 + std::map<std::string, std::string> values;
  41 +
  42 + std::string get(const std::string &key) const
  43 + {
  44 + auto it = values.find(key);
  45 + if (it != values.end())
  46 + {
  47 + return it->second;
  48 + }
  49 + return "";
  50 + }
  51 +
  52 + static daemonconfig from_file(const std::string &filename)
  53 + {
  54 + daemonconfig cfg;
  55 + if (filename.empty())
  56 + {
  57 + return cfg;
  58 + }
  59 +
  60 + auto split = [](const std::string &str, char delim)
  61 + {
  62 + std::vector<std::string> parts;
  63 + std::stringstream oss(str);
  64 + std::string part;
  65 + while (std::getline(oss, part, delim))
  66 + {
  67 + parts.push_back(part);
  68 + }
  69 + return parts;
  70 + };
  71 +
  72 + auto trim = [](std::string &s)
  73 + {
  74 + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch){ return !std::isspace(ch);}));
  75 + s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch){return !std::isspace(ch);}).base(), s.end());
  76 + };
  77 +
  78 + std::ifstream ifs{filename};
  79 + std::string line;
  80 +
  81 + while(std::getline(ifs, line))
  82 + {
  83 + trim(line);
  84 + if (line.empty()) // skip empty lines
  85 + {
  86 + continue;
  87 + }
  88 +
  89 + if (line[0] == '#') // skip comments
  90 + {
  91 + continue;
  92 + }
  93 +
  94 + auto parts = split(line, '=');
  95 + std::string key = parts[0];
  96 + std::string value = parts[1];
  97 + trim(key);
  98 + trim(value);
  99 + cfg.values[key] = value;
  100 + }
  101 + ifs.close();
  102 + return cfg;
  103 + }
  104 + };
  105 +}
0 106 \ No newline at end of file
... ...
include/daemonlog.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ****************************************************************************/
  22 +#pragma once
  23 +
  24 +#include <sys/syslog.h>
  25 +#include <syslog.h>
  26 +#include <cstdio>
  27 +#include <string>
  28 +
  29 +namespace osdev::components::daemon {
  30 +
  31 +class daemonlog
  32 +{
  33 +public:
  34 + /**
  35 + * Initialize the logger
  36 + * @param daemon_name
  37 + */
  38 + static void init(const std::string &daemon_name)
  39 + {
  40 + // m_daemon_name = daemon_name;
  41 + openlog(daemon_name.c_str(), LOG_PID, LOG_DAEMON);
  42 + }
  43 +
  44 + /**
  45 + * main logger with priority LOG_X
  46 + * @param message
  47 + * @param priority
  48 + */
  49 + static void log(const std::string &message, std::int32_t priority)
  50 + {
  51 + syslog(priority, "%s", message.c_str());
  52 + }
  53 +
  54 + /**
  55 + * debug-level messages
  56 + * @param message
  57 + */
  58 + static void debug(const std::string &message)
  59 + {
  60 + log(message, LOG_DEBUG);
  61 + }
  62 +
  63 + /**
  64 + * informational
  65 + * @param message
  66 + */
  67 + static void info(const std::string &message)
  68 + {
  69 + log(message, LOG_INFO);
  70 + }
  71 +
  72 + /**
  73 + * normal but significant condition
  74 + * @param message
  75 + */
  76 + static void notice(const std::string &message)
  77 + {
  78 + log(message, LOG_NOTICE);
  79 + }
  80 +
  81 + /**
  82 + * Warning conditions
  83 + * @param message
  84 + */
  85 + static void warning(const std::string &message)
  86 + {
  87 + log(message, LOG_WARNING);
  88 + }
  89 +
  90 + /**
  91 + * error conditions
  92 + * @param message
  93 + */
  94 + static void error(const std::string &message)
  95 + {
  96 + log(message, LOG_ERR);
  97 + }
  98 +
  99 + /**
  100 + * critical conditions
  101 + * @param message
  102 + */
  103 + static void critical(const std::string &message)
  104 + {
  105 + log(message, LOG_CRIT);
  106 + }
  107 +
  108 + /**
  109 + * action must be taken immediately
  110 + * @param message
  111 + */
  112 + static void alert(const std::string &message)
  113 + {
  114 + log(message, LOG_ALERT);
  115 + }
  116 +
  117 + /**
  118 + * system is unusable
  119 + * @param message
  120 + */
  121 + static void emergency(const std::string &message)
  122 + {
  123 + log(message, LOG_EMERG);
  124 + }
  125 +
  126 + /**
  127 + * shutdown the logger
  128 + */
  129 + static void shutdown()
  130 + {
  131 + closelog();
  132 + }
  133 +
  134 +private:
  135 + static std::string priority_str(std::int32_t priority)
  136 + {
  137 + switch(priority)
  138 + {
  139 + case LOG_EMERG: return "emergency";
  140 + case LOG_ALERT: return "alert";
  141 + case LOG_CRIT: return "critical";
  142 + case LOG_ERR: return "error";
  143 + case LOG_WARNING: return "warning";
  144 + case LOG_NOTICE: return "notice";
  145 + case LOG_INFO: return "info";
  146 + case LOG_DEBUG: return "debug";
  147 + default: return "unknown_priority";
  148 + }
  149 + }
  150 +
  151 + std::string m_daemon_name;
  152 +
  153 +};
  154 +
  155 +// std::string osdev::components::daemon::m_daemon_name{};
  156 +
  157 +}
0 158 \ No newline at end of file
... ...
test/CMakeLists.txt 0 → 100644
  1 +#
  2 +# Don't call this file directly from cmake.
  3 +# TRhis file is included from the upper directory.
  4 +#
  5 +# Build rules for the daemon library
  6 +
  7 +add_executable(MyExampleDaemon
  8 + MyDaemonExample.cpp
  9 +)
  10 +
  11 +target_include_directories(MyExampleDaemon PRIVATE
  12 + ${CMAKE_CURRENT_SOURCE_DIR}
  13 + ../include
  14 +)
  15 +
  16 +target_link_libraries(MyExampleDaemon PRIVATE
  17 + daemonbase
  18 +)
0 19 \ No newline at end of file
... ...
test/MyDaemonExample.cpp 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ****************************************************************************/
  22 +#include "daemonbase.h"
  23 +#include <cstdlib>
  24 +
  25 +using namespace osdev::components::daemon;
  26 +using namespace std::chrono_literals;
  27 +
  28 +class MyExampleDaemon : public DaemonBase
  29 +{
  30 +public:
  31 + void on_start(const daemonconfig &config) override
  32 + {
  33 + /// Called once after daemon starts automatically with system startup
  34 + /// or when you manually call `$ system,ctl start MyExampleDaemon`
  35 +
  36 + /// Initialize your code here...
  37 +
  38 +
  39 +
  40 + daemonlog::info("MyExampleDaemon::on_start(): MyExampleDaemon version: " + config.get("version") + " started successfully!");
  41 + }
  42 +
  43 + void on_update() override
  44 + {
  45 + /// Called every DURATION set in set_update_duration()...
  46 +
  47 + /// Update your code here
  48 +
  49 + daemonlog::info("MyExampleDaemon::on_update()");
  50 + }
  51 +
  52 + void on_stop() override
  53 + {
  54 + /// Called once before daemon is about to exit with system shutdown or when you manually call `$ systemctl stop MyExampleDaemon`
  55 + /// Cleanup your code here...
  56 +
  57 + daemonlog::info("MyExampleDaemon::on_stop()");
  58 + }
  59 +
  60 + void on_reload(const daemonconfig &cfg) override
  61 + {
  62 + /// Called once after your daemon's config fil is updated then reloaded with `$ systemctl reload MyExampleDaemon`
  63 + /// Handle your config updates here...
  64 +
  65 + daemonlog::info("MyExampleDaemon::on_reload(): new daemon version from updated config: " + cfg.get("version"));
  66 + }
  67 +};
  68 +
  69 +
  70 +int main(int argc, char *argv[])
  71 +{
  72 + MyExampleDaemon oDaemon; // Create the daemon instance.
  73 +
  74 + oDaemon.set_name("MyAweSomeExampleDaemon"); // Set daemon name to identify logs in syslog
  75 + oDaemon.set_update_duration(3s); // Set duration to sleep before triggering the on_update callback 3 seconds.
  76 + oDaemon.set_cwd("/root"); // set daemon's current working directory to roots home-folder
  77 + oDaemon.run(argc, argv); // run the daemon
  78 +
  79 + return(EXIT_SUCCESS); // Close the main process now the child process is running.
  80 +}
0 81 \ No newline at end of file
... ...