nginx-util: fix issues and cleanup
authorPeter Stadler <redacted>
Tue, 26 May 2020 14:21:10 +0000 (16:21 +0200)
committerPeter Stadler <redacted>
Tue, 26 May 2020 16:36:03 +0000 (18:36 +0200)
* fix regex capture (to conform std)
* fix issues for Clang/libcxx (warnings/includes)
* fix CONFLICTS in the Makefile
* use /bin/sh in host scripts and shellcheck them
* add callback for setting arguments in ubus::call

Signed-off-by: Peter Stadler <redacted>
13 files changed:
net/nginx-util/Makefile
net/nginx-util/src/CMakeLists.txt
net/nginx-util/src/nginx-ssl-util.hpp
net/nginx-util/src/nginx-util.cpp
net/nginx-util/src/nginx-util.hpp
net/nginx-util/src/px5g-openssl.hpp
net/nginx-util/src/px5g.cpp
net/nginx-util/src/regex-pcre.hpp
net/nginx-util/src/test-nginx-util-root.sh
net/nginx-util/src/test-nginx-util.sh
net/nginx-util/src/test-px5g.sh
net/nginx-util/src/ubus-cxx.cpp [new file with mode: 0644]
net/nginx-util/src/ubus-cxx.hpp

index 5fd89878bcd285f63f8774f205f9d556bf2796d1..0d706d926b244cbae20f5430f3abcf38d969f5f9 100644 (file)
@@ -1,8 +1,8 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=nginx-util
-PKG_VERSION:=1.3
-PKG_RELEASE:=2
+PKG_VERSION:=1.4
+PKG_RELEASE:=1
 PKG_MAINTAINER:=Peter Stadler <peter.stadler@student.uibk.ac.at>
 
 include $(INCLUDE_DIR)/package.mk
@@ -11,6 +11,7 @@ include $(INCLUDE_DIR)/cmake.mk
 CMAKE_OPTIONS+= -DUBUS=y
 CMAKE_OPTIONS+= -DVERSION=$(PKG_VERSION)
 
+
 define Package/nginx-util/default
   SECTION:=net
   CATEGORY:=Network
@@ -19,63 +20,75 @@ define Package/nginx-util/default
   DEPENDS:=+libstdcpp +libubus +libubox +libpthread
 endef
 
+
 define Package/nginx-util
   $(Package/nginx-util/default)
   CONFLICTS:=nginx-ssl-util-nopcre nginx-ssl-util
 endef
 
+
 define Package/nginx-ssl-util/default
   $(Package/nginx-util/default)
   TITLE+= including SSL
   DEPENDS+= +libopenssl
-  CONFLICTS:=,nginx-util
+  CONFLICTS:=nginx-util,
 endef
 
+
 define Package/nginx-ssl-util
   $(Package/nginx-ssl-util/default)
   TITLE+= (using PCRE)
   DEPENDS+= +libpcre
-  CONFLICTS+= ,nginx-ssl-util-nopcre
+  CONFLICTS+= nginx-ssl-util-nopcre,
 endef
 
+
 define Package/nginx-ssl-util-nopcre
   $(Package/nginx-ssl-util/default)
   TITLE+= (using <regex>)
   CONFLICTS+= nginx-ssl-util
 endef
 
+
 define Package/nginx-util/description
   Utility that builds dynamically LAN listen directives for Nginx.
 endef
 
+
 Package/nginx-ssl-util/default/description = $(Package/nginx-util/description)\
   Furthermore, it manages SSL directives for its server parts and can create \
   corresponding (self-signed) certificates.
 
+
 Package/nginx-ssl-util/description = \
   $(Package/nginx-ssl-util/default/description) \
   It uses the PCRE library for performance.
 
+
 Package/nginx-ssl-util-nopcre/description = \
   $(Package/nginx-ssl-util/default/description) \
   It uses the standard regex library of C++.
 
+
 define Package/nginx-util/install
        $(INSTALL_DIR) $(1)/usr/bin
        $(INSTALL_BIN) $(PKG_BUILD_DIR)/nginx-util $(1)/usr/bin/nginx-util
 endef
 
+
 define Package/nginx-ssl-util/install
        $(INSTALL_DIR) $(1)/usr/bin
        $(INSTALL_BIN) $(PKG_BUILD_DIR)/nginx-ssl-util $(1)/usr/bin/nginx-util
 endef
 
+
 define Package/nginx-ssl-util-nopcre/install
        $(INSTALL_DIR) $(1)/usr/bin
        $(INSTALL_BIN) $(PKG_BUILD_DIR)/nginx-ssl-util-nopcre \
                $(1)/usr/bin/nginx-util
 endef
 
+
 $(eval $(call BuildPackage,nginx-util))
 $(eval $(call BuildPackage,nginx-ssl-util))
 $(eval $(call BuildPackage,nginx-ssl-util-nopcre))
index af1d67ea1ef1fcbf074e988cadc3933a7a254f6f..3b06c8537b1a45f1196a8308630aac36f373a2b6 100644 (file)
@@ -5,15 +5,8 @@ SET(CMAKE_CXX_STANDARD 17)
 
 INCLUDE(CheckFunctionExists)
 
-IF(UBUS)
-FIND_PATH(ubus_include_dir libubus.h)
-FIND_LIBRARY(ubox NAMES ubox)
-FIND_LIBRARY(ubus NAMES ubus)
-INCLUDE_DIRECTORIES(${ubus_include_dir})
-ENDIF()
-
 ADD_DEFINITIONS(-Os -Wall -Werror -Wextra -g3)
-ADD_DEFINITIONS(-Wno-unused-parameter -Wmissing-declarations)
+ADD_DEFINITIONS(-Wno-unused-parameter -Wmissing-declarations -Wshadow)
 
 SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
 
@@ -22,6 +15,14 @@ IF(UBUS)
 
 ADD_COMPILE_DEFINITIONS(VERSION=${VERSION})
 
+FIND_PATH(ubus_include_dir libubus.h)
+FIND_LIBRARY(ubus NAMES ubus)
+INCLUDE_DIRECTORIES(${ubus_include_dir})
+
+FIND_PATH(ubox_include_dir libubox/blobmsg.h)
+FIND_LIBRARY(ubox NAMES ubox)
+INCLUDE_DIRECTORIES(${ubox_include_dir})
+
 ADD_EXECUTABLE(nginx-util nginx-util.cpp)
 TARGET_COMPILE_DEFINITIONS(nginx-util PUBLIC -DNO_SSL)
 TARGET_LINK_LIBRARIES(nginx-util ${ubox} ${ubus} pthread)
@@ -38,6 +39,8 @@ INSTALL(TARGETS nginx-ssl-util-nopcre RUNTIME DESTINATION bin)
 
 ELSE()
 
+ADD_COMPILE_DEFINITIONS(VERSION=0)
+
 CONFIGURE_FILE(test-px5g.sh test-px5g.sh COPYONLY)
 CONFIGURE_FILE(test-nginx-util.sh test-nginx-util.sh COPYONLY)
 CONFIGURE_FILE(test-nginx-util-root.sh test-nginx-util-root.sh COPYONLY)
index 0fa7009ccbd84138c07935de2ca2b89b3806a6c8..895f9569f923c24af7413bc5a54793018f5a06fc 100644 (file)
@@ -1,8 +1,6 @@
 #ifndef __NGINX_SSL_UTIL_HPP
 #define __NGINX_SSL_UTIL_HPP
 
-#include <thread>
-
 #ifdef NO_PCRE
 #include <regex>
 namespace rgx = std;
@@ -147,7 +145,7 @@ constexpr auto _end = _Line{
 
 
 template<char clim='\0'>
-constexpr auto _capture = _Line{
+static constexpr auto _capture = _Line{
     [](const std::string & param, const std::string & /*begin*/) -> std::string
     { return '\'' + param + '\''; },
 
@@ -211,8 +209,8 @@ constexpr std::string_view _ssl_session_timeout = "ssl_session_timeout";
 // * Use constexpr---not available for strings or char * for now---look at lib.
 
 static const auto CRON_CMD = Line::build
-    < _space, _escape<NGINX_UTIL>, _space, _escape<ADD_SSL_FCT,'\''>, _space,
-        _capture<>, _newline >();
+    <_space, _escape<NGINX_UTIL>, _space, _escape<ADD_SSL_FCT,'\''>, _space,
+        _capture<>, _newline>();
 
 static const auto NGX_SERVER_NAME =
     Line::build<_begin, _escape<_server_name>, _space, _capture<';'>, _end>();
@@ -221,15 +219,15 @@ static const auto NGX_INCLUDE_LAN_LISTEN = Line::build
     <_begin, _escape<_include>, _space, _escape<LAN_LISTEN,'\''>, _end>();
 
 static const auto NGX_INCLUDE_LAN_LISTEN_DEFAULT = Line::build
-    < _begin, _escape<_include>, _space,
-        _escape<LAN_LISTEN_DEFAULT, '\''>, _end >();
+    <_begin, _escape<_include>, _space,
+        _escape<LAN_LISTEN_DEFAULT, '\''>, _end>();
 
 static const auto NGX_INCLUDE_LAN_SSL_LISTEN = Line::build
     <_begin, _escape<_include>, _space, _escape<LAN_SSL_LISTEN, '\''>, _end>();
 
 static const auto NGX_INCLUDE_LAN_SSL_LISTEN_DEFAULT = Line::build
-    < _begin, _escape<_include>, _space,
-        _escape<LAN_SSL_LISTEN_DEFAULT, '\''>, _end >();
+    <_begin, _escape<_include>, _space,
+        _escape<LAN_SSL_LISTEN_DEFAULT, '\''>, _end>();
 
 static const auto NGX_SSL_CRT = Line::build
     <_begin, _escape<_ssl_certificate>, _space, _capture<';'>, _end>();
@@ -346,7 +344,7 @@ void add_ssl_directives_to(const std::string & name, const bool isdefault)
     auto errmsg = std::string{"add_ssl_directives_to error: "};
     errmsg += "cannot add SSL directives to " + name + ".conf, missing: ";
     errmsg += NGX_SERVER_NAME.STR(name, "\n    ") + "\n";
-    throw std::runtime_error(errmsg.c_str());
+    throw std::runtime_error(errmsg);
 }
 
 
@@ -476,7 +474,7 @@ void use_cron_to_recreate_certificate(const std::string & name)
             std::string errmsg{"use_cron_to_recreate_certificate error: "};
             errmsg += "Cron unavailable to re-create the ssl certificate for ";
             errmsg += name + "\n";
-            throw std::runtime_error(errmsg.c_str());
+            throw std::runtime_error(errmsg);
         } // else active with or without instances:
 #endif
 
@@ -586,7 +584,7 @@ void del_ssl_directives_from(const std::string & name, const bool isdefault)
     auto errmsg = std::string{"del_ssl_directives_from error: "};
     errmsg += "cannot delete SSL directives from " + name + ".conf, missing: ";
     errmsg += NGX_SERVER_NAME.STR(name, "\n    ") + "\n";
-    throw std::runtime_error(errmsg.c_str());
+    throw std::runtime_error(errmsg);
 }
 
 
index 02aa7013ac21d8e9044a18ade93c9f6745759657..b0a724da6d337f7e72eeab5335368b3fcd8ad0b9 100644 (file)
@@ -1,3 +1,5 @@
+#include <iostream>
+
 #include "nginx-util.hpp"
 
 #ifndef NO_SSL
@@ -121,7 +123,7 @@ void get_env()
 auto main(int argc, char * argv[]) -> int
 {
     // TODO(pst): use std::span when available:
-    auto args = std::basic_string_view{argv, static_cast<size_t>(argc)};
+    auto args = std::basic_string_view<char *>{argv, static_cast<size_t>(argc)};
 
     auto cmds = std::array{
         std::array<std::string_view, 2>{"init_lan", ""},
index f3f14ffadbb6322aef5342f860c63170454ae01d..f4999e74ff3c2b378fd7f85583f48cd9f77450e8 100644 (file)
@@ -4,12 +4,14 @@
 #include <array>
 #include <cerrno>
 #include <cstdio>
+#include <cstdlib>
 #include <cstring>
 #include <fstream>
-#include <iostream>
 #include <string>
 #include <string_view>
+#include <thread>
 #include <unistd.h>
+#include <vector>
 
 #ifndef NO_UBUS
 #include "ubus-cxx.hpp"
@@ -61,15 +63,34 @@ void get_env();
 void write_file(const std::string_view & name, const std::string & str,
                 const std::ios_base::openmode flag)
 {
-    std::ofstream file(name.data(), flag);
-    if (!file.good()) {
-        throw std::ofstream::failure(
-            "write_file error: cannot open " + std::string{name});
+    auto tmp = std::string{name};
+
+    if ( (flag & std::ios::ate) == 0 && (flag & std::ios::app) == 0 ) {
+        tmp += ".tmp-XXXXXX";
+        auto fd = mkstemp(&tmp[0]);
+        if (fd==-1 || close(fd)!=0)
+        { throw std::runtime_error("write_file error: cannot access " + tmp); }
     }
 
-    file<<str<<std::flush;
+    try {
+        std::ofstream file(tmp.data(), flag);
+        if (!file.good()) {
+            throw std::ofstream::failure
+                ("write_file error: cannot open " + std::string{tmp});
+        }
 
-    file.close();
+        file<<str<<std::flush;
+
+        file.close();
+    } catch(...) {
+        if (tmp!=name) { remove(tmp.c_str()); } //remove can fail.
+        throw;
+    }
+
+    if (rename(tmp.c_str(), name.data()) != 0) {
+        throw std::runtime_error
+            ("write_file error: cannot move " + tmp + " to " + name.data());
+    }
 }
 
 
@@ -113,7 +134,7 @@ auto call(const char * program, S... args) -> pid_t
 
     std::string errmsg = "call error: cannot fork (";
     errmsg += std::to_string(errno) + "): " + std::strerror(errno);
-    throw std::runtime_error(errmsg.c_str());
+    throw std::runtime_error(errmsg);
 }
 
 
index 380eba335df53b2c98ae6a626df521f1f27b9b67..1b70dc5b0fa18c072c17879f175373ad824a96d0 100644 (file)
@@ -8,9 +8,11 @@
 #include <openssl/err.h>
 #include <openssl/pem.h>
 #include <openssl/rsa.h>
+#include <stdexcept>
 #include <string>
 #include <unistd.h>
 
+
 static constexpr auto rsa_min_modulus_bits = 512;
 
 using EVP_PKEY_ptr = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>;
@@ -52,11 +54,20 @@ inline auto print_error(const char * str, const size_t  /*len*/, void * errmsg)
 }
 
 
+// wrapper for clang-tidy:
+inline auto _BIO_new_fp(FILE * stream, const bool use_pem,
+                        const bool close=false) -> BIO *
+{
+    return BIO_new_fp( stream,  //NOLINTNEXTLINE(hicpp-signed-bitwise) macros:
+            (use_pem ? BIO_FP_TEXT : 0) | (close ? BIO_CLOSE : BIO_NOCLOSE) );
+}
+
+
 auto checkend(const std::string & crtpath,
               const time_t seconds, const bool use_pem) -> bool
 {
     BIO * bio = crtpath.empty() ?
-        BIO_new_fp(stdin, BIO_NOCLOSE | (use_pem ? BIO_FP_TEXT : 0)) :
+        _BIO_new_fp(stdin, use_pem) :
         BIO_new_file(crtpath.c_str(), (use_pem ? "r" : "rb"));
 
     X509 * x509 = nullptr;
@@ -71,7 +82,7 @@ auto checkend(const std::string & crtpath,
     if (x509==nullptr) {
         std::string errmsg{"checkend error: unable to load certificate\n"};
         ERR_print_errors_cb(print_error, &errmsg);
-        throw std::runtime_error(errmsg.c_str());
+        throw std::runtime_error(errmsg);
     }
 
     time_t checktime = time(nullptr) + seconds;
@@ -91,7 +102,7 @@ auto gen_eckey(const int curve) -> EVP_PKEY_ptr
         std::string errmsg{"gen_eckey error: cannot build group for curve id "};
         errmsg += std::to_string(curve) + "\n";
         ERR_print_errors_cb(print_error, &errmsg);
-        throw std::runtime_error(errmsg.c_str());
+        throw std::runtime_error(errmsg);
     }
 
     EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE);
@@ -115,18 +126,18 @@ auto gen_eckey(const int curve) -> EVP_PKEY_ptr
         std::string errmsg{"gen_eckey error: cannot build key with curve id "};
         errmsg += std::to_string(curve) + "\n";
         ERR_print_errors_cb(print_error, &errmsg);
-        throw std::runtime_error(errmsg.c_str());
+        throw std::runtime_error(errmsg);
     }
 
     EVP_PKEY_ptr pkey{EVP_PKEY_new(), ::EVP_PKEY_free};
 
-    auto tmp = static_cast<char *>(static_cast<void *>(eckey));
-
-    if (!EVP_PKEY_assign_EC_KEY(pkey.get(), tmp)) {
+    // EVP_PKEY_assign_EC_KEY is a macro casting eckey to char *:
+    //NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
+    if (!EVP_PKEY_assign_EC_KEY(pkey.get(), eckey)) {
         EC_KEY_free(eckey);
         std::string errmsg{"gen_eckey error: cannot assign EC key to EVP\n"};
         ERR_print_errors_cb(print_error, &errmsg);
-        throw std::runtime_error(errmsg.c_str());
+        throw std::runtime_error(errmsg);
     }
 
     return pkey;
@@ -139,14 +150,14 @@ auto gen_rsakey(const int keysize, const BN_ULONG exponent) -> EVP_PKEY_ptr
         std::string errmsg{"gen_rsakey error: RSA keysize ("};
         errmsg += std::to_string(keysize) + ") out of range [512..";
         errmsg += std::to_string(OPENSSL_RSA_MAX_MODULUS_BITS) + "]";
-        throw std::runtime_error(errmsg.c_str());
+        throw std::runtime_error(errmsg);
     }
     auto bignum = BN_new();
 
     if (bignum == nullptr) {
         std::string errmsg{"gen_rsakey error: cannot get big number struct\n"};
         ERR_print_errors_cb(print_error, &errmsg);
-        throw std::runtime_error(errmsg.c_str());
+        throw std::runtime_error(errmsg);
     }
 
     auto rsa = RSA_new();
@@ -167,18 +178,18 @@ auto gen_rsakey(const int keysize, const BN_ULONG exponent) -> EVP_PKEY_ptr
         errmsg += std::to_string(keysize) + " and exponent ";
         errmsg += std::to_string(exponent) + "\n";
         ERR_print_errors_cb(print_error, &errmsg);
-        throw std::runtime_error(errmsg.c_str());
+        throw std::runtime_error(errmsg);
     }
 
     EVP_PKEY_ptr pkey{EVP_PKEY_new(), ::EVP_PKEY_free};
 
-    auto tmp = static_cast<char *>(static_cast<void *>(rsa));
-
-    if (!EVP_PKEY_assign_RSA(pkey.get(), tmp)) {
+    // EVP_PKEY_assign_RSA is a macro casting rsa to char *:
+    //NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
+    if (!EVP_PKEY_assign_RSA(pkey.get(), rsa)) {
         RSA_free(rsa);
         std::string errmsg{"gen_rsakey error: cannot assign RSA key to EVP\n"};
         ERR_print_errors_cb(print_error, &errmsg);
-        throw std::runtime_error(errmsg.c_str());
+        throw std::runtime_error(errmsg);
     }
 
     return pkey;
@@ -190,20 +201,21 @@ void write_key(const EVP_PKEY_ptr & pkey,
 {
     BIO * bio = nullptr;
 
-    if (keypath.empty()) {
-        bio = BIO_new_fp( stdout, BIO_NOCLOSE | (use_pem ? BIO_FP_TEXT : 0) );
+    if (keypath.empty()) { bio = _BIO_new_fp(stdout, use_pem); }
 
-    else { // BIO_new_file(keypath.c_str(), (use_pem ? "w" : "wb") );
+    else { // BIO_new_file(keypath.c_str(), (use_pem ? "w" : "wb") );
 
         static constexpr auto mask = 0600;
         // auto fd = open(keypath.c_str(), O_WRONLY | O_CREAT | O_TRUNC, mask);
+        // creat has no cloexec, alt. triggers cppcoreguidelines-pro-type-vararg
+        //NOLINTNEXTLINE(android-cloexec-creat)
         auto fd = creat(keypath.c_str(), mask); // the same without va_args.
 
         if (fd >= 0) {
             auto fp = fdopen(fd, (use_pem ? "w" : "wb") );
 
             if (fp != nullptr) {
-                bio = BIO_new_fp(fp, BIO_CLOSE | (use_pem ? BIO_FP_TEXT : 0));
+                bio = _BIO_new_fp(fp, use_pem, true);
                 if (bio == nullptr) { fclose(fp); } // (fp owns fd)
             }
             else { close(fd); }
@@ -216,7 +228,7 @@ void write_key(const EVP_PKEY_ptr & pkey,
         errmsg += keypath.empty() ? "stdout" : keypath;
         errmsg += "\n";
         ERR_print_errors_cb(print_error, &errmsg);
-        throw std::runtime_error(errmsg.c_str());
+        throw std::runtime_error(errmsg);
     }
 
     int len = 0;
@@ -249,7 +261,7 @@ void write_key(const EVP_PKEY_ptr & pkey,
         errmsg += keypath.empty() ? "stdout" : keypath;
         errmsg += "\n";
         ERR_print_errors_cb(print_error, &errmsg);
-        throw std::runtime_error(errmsg.c_str());
+        throw std::runtime_error(errmsg);
     }
 }
 
@@ -265,7 +277,7 @@ auto subject2name(const std::string & subject) -> X509_NAME_ptr
     if (!name) {
         std::string errmsg{"subject2name error: cannot create X509 name \n"};
         ERR_print_errors_cb(print_error, &errmsg);
-        throw std::runtime_error(errmsg.c_str());
+        throw std::runtime_error(errmsg);
     }
 
     if (subject.empty()) { return name; }
@@ -287,19 +299,21 @@ auto subject2name(const std::string & subject) -> X509_NAME_ptr
             if (nid == NID_undef) {
                 // skip unknown entries (silently?).
             } else {
-                auto val = static_cast<const unsigned char *>(
-                                static_cast<const void *>(&subject[prev]) );
+                auto val = // X509_NAME_add_entry_by_NID wants it unsigned:
+                    //NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+                    reinterpret_cast<const unsigned char *>(&subject[prev]);
 
                 auto len = i - prev;
 
-                if ( X509_NAME_add_entry_by_NID(name.get(), nid, MBSTRING_ASC,
-                                                  val, len, -1, 0)
+                if ( X509_NAME_add_entry_by_NID(name.get(), nid,
+                        MBSTRING_ASC, //NOLINT(hicpp-signed-bitwise) is macro
+                        val, len, -1, 0)
                     == 0 )
                 {
                     std::string errmsg{"subject2name error: cannot add "};
                     errmsg += "/" + type +"="+ subject.substr(prev, len) +"\n";
                     ERR_print_errors_cb(print_error, &errmsg);
-                    throw std::runtime_error(errmsg.c_str());
+                    throw std::runtime_error(errmsg);
                 }
             }
             chr = '=';
@@ -320,7 +334,7 @@ void selfsigned(const EVP_PKEY_ptr & pkey, const int days,
     if (x509 == nullptr) {
         std::string errmsg{"selfsigned error: cannot create X509 structure\n"};
         ERR_print_errors_cb(print_error, &errmsg);
-        throw std::runtime_error(errmsg.c_str());
+        throw std::runtime_error(errmsg);
     }
 
     auto freeX509_and_throw = [&x509](const std::string & what)
@@ -329,7 +343,7 @@ void selfsigned(const EVP_PKEY_ptr & pkey, const int days,
         std::string errmsg{"selfsigned error: cannot set "};
         errmsg += what + " in X509 certificate\n";
         ERR_print_errors_cb(print_error, &errmsg);
-        throw std::runtime_error(errmsg.c_str());
+        throw std::runtime_error(errmsg);
     };
 
     if (X509_set_version(x509, 2) == 0) { freeX509_and_throw("version"); }
@@ -380,7 +394,7 @@ void selfsigned(const EVP_PKEY_ptr & pkey, const int days,
     }
 
     BIO * bio = crtpath.empty() ?
-        BIO_new_fp(stdout, BIO_NOCLOSE | (use_pem ? BIO_FP_TEXT : 0)) :
+        _BIO_new_fp(stdout, use_pem) :
         BIO_new_file(crtpath.c_str(), (use_pem ? "w" : "wb"));
 
     int len = 0;
@@ -399,7 +413,7 @@ void selfsigned(const EVP_PKEY_ptr & pkey, const int days,
         errmsg += crtpath.empty() ? "stdout" : crtpath;
         errmsg += "\n";
         ERR_print_errors_cb(print_error, &errmsg);
-        throw std::runtime_error(errmsg.c_str());
+        throw std::runtime_error(errmsg);
     }
 }
 
index 5ad13357bddc14230963beea525129c463834c18..e2a073d3599049fb52778c33bf2c9ef44726fdbd 100644 (file)
@@ -118,7 +118,7 @@ auto checkend(const argv_view & argv) -> int
             } catch (...) {
                 auto errmsg = std::string{"checkend error: invalid time "};
                 errmsg += argv[i];
-                std::throw_with_nested(std::runtime_error(errmsg.c_str()));
+                std::throw_with_nested(std::runtime_error(errmsg));
             }
 
             seconds = static_cast<time_t>(num);
@@ -126,7 +126,7 @@ auto checkend(const argv_view & argv) -> int
             if (num!=static_cast<intmax_t>(seconds)) {
                 auto errmsg = std::string{"checkend error: time too big "};
                 errmsg += argv[i];
-                throw std::runtime_error(errmsg.c_str());
+                throw std::runtime_error(errmsg);
             }
         }
     }
@@ -174,7 +174,7 @@ void eckey(const argv_view & argv)
             if (has_main_option) {
                 throw std::runtime_error
                     ("eckey error: more than one main option");
-            } // else:
+            } //else:
             has_main_option = true;
 
             curve = parse_curve(argv[i]);
@@ -223,7 +223,7 @@ void rsakey(const argv_view & argv)
 
             if (has_main_option) {
                 throw std::runtime_error("rsakey error: more than one keysize");
-            } // else:
+            } //else:
             has_main_option = true;
 
             try {
@@ -231,7 +231,7 @@ void rsakey(const argv_view & argv)
             } catch (...) {
                 std::string errmsg{"rsakey error: invalid keysize "};
                 errmsg += argv[i];
-                std::throw_with_nested(std::runtime_error(errmsg.c_str()));
+                std::throw_with_nested(std::runtime_error(errmsg));
             }
         }
     }
@@ -264,7 +264,7 @@ void selfsigned(const argv_view & argv)
             } catch (...) {
                 std::string errmsg{"selfsigned error: not a number for -days "};
                 errmsg += argv[i].substr(4);
-                std::throw_with_nested(std::runtime_error(errmsg.c_str()));
+                std::throw_with_nested(std::runtime_error(errmsg));
             }
         }
 
@@ -287,7 +287,7 @@ void selfsigned(const argv_view & argv)
                 } catch (...) {
                     std::string errmsg{"selfsigned error: invalid keysize "};
                     errmsg += argv[i].substr(4);
-                    std::throw_with_nested(std::runtime_error(errmsg.c_str()));
+                    std::throw_with_nested(std::runtime_error(errmsg));
                 }
             } else {
                 throw std::runtime_error("selfsigned error: invalid algorithm");
index 8408b60f407f4771d90d6f8480d6f2d4e56c3da4..bfd3a0534337f1bbc06752d4d3b0a36c5eddbab0 100644 (file)
@@ -1,5 +1,3 @@
-// implementing *some* <regex> functions using pcre for performance:
-
 #ifndef __REGEXP_PCRE_HPP
 #define __REGEXP_PCRE_HPP
 
@@ -11,6 +9,9 @@
 
 
 namespace rgx {
+/* partially implement the std::regex interface using PCRE for performance
+ * (=> pass "match" as non-const reference)
+ */
 
 
 namespace regex_constants {
@@ -130,8 +131,8 @@ class smatch {
 
     friend auto regex_search(std::string::const_iterator begin,
                              std::string::const_iterator end,
-                             smatch & match,
-                             const regex & rgx);
+                             smatch & match, //NOLINT(google-runtime-references)
+                             const regex & rgx); // match std::regex interface.
 
 
 private:
@@ -188,14 +189,15 @@ auto regex_replace(const std::string & subj,
                           const std::string & insert);
 
 
-inline auto regex_search(const std::string & subj, smatch & match,
-                         const regex & rgx);
+inline auto regex_search(const std::string & subj,
+                         smatch & match, //NOLINT(google-runtime-references)
+                         const regex & rgx); // match std::regex interface.
 
 
 auto regex_search(std::string::const_iterator begin,
                   std::string::const_iterator end,
-                  smatch & match,
-                  const regex & rgx);
+                  smatch & match, //NOLINT(google-runtime-references)
+                  const regex & rgx); // match std::regex interface.
 
 
 
@@ -260,12 +262,10 @@ auto smatch::format(const std::string & fmt) const {
         index = pos + 1;
 
         char chr = fmt[index++];
-        int n = 0;
-        static const auto BASE = 10;
         switch(chr) {
 
             case '&': // match
-                ret += this->str(0);
+                ret += str(0);
                 break;
 
             case '`': // prefix
@@ -276,14 +276,20 @@ auto smatch::format(const std::string & fmt) const {
                 ret.append(begin+vec[1], end);
                 break;
 
-            default: // number => submatch
-                while (isdigit(chr) != 0) {
-                    n = BASE*n + chr - '0';
-                    chr = fmt[index++];
-                }
-
-                ret += n>0 ? str(n) : std::string{"$"};
-
+            default:
+                if (isdigit(chr) != 0) { // one or two digits => submatch:
+                    int num = chr - '0';
+                    chr = fmt[index];
+                    if (isdigit(chr) != 0) { // second digit:
+                        ++index;
+                        static const auto base = 10;
+                        num = num*base + chr - '0';
+                    }
+                    ret += str(num);
+                    break;
+                } //else:
+
+                ret += '$';
                 [[fallthrough]];
 
             case '$': // escaped
index a6d78757cb43c62a8b1bcee758069f591062e39e..0ce8c94283d75cb031b674b67ece39586c680d34 100644 (file)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh
 
 PRINT_PASSED=2
 
@@ -37,7 +37,7 @@ setpoint_add_ssl() {
     local indent="\n$1"
     local name="$2"
     local default=""
-    [ "${name}" == "${LAN_NAME}" ] && default=".default"
+    [ "${name}" = "${LAN_NAME}" ] && default=".default"
     local prefix="${CONF_DIR}${name}"
 
     local CONF="$(grep -vE "$(_regex "${NGX_INCLUDE}" \
@@ -74,10 +74,17 @@ setpoint_add_ssl() {
 
 # ----------------------------------------------------------------------------
 
+test_setpoint() {
+    [ "$(cat "$1")" = "$2" ] && return
+    echo "$1:"; cat "$1"
+    echo "differs from setpoint:"; echo "$2"
+    [ "${PRINT_PASSED}" -gt 1 ] && pst_exit 1
+}
+
 
-function test() {
-    eval "$1 2>/dev/null >/dev/null "
-    if [ $? -eq $2 ]
+test() {
+    eval "$1 2>/dev/null >/dev/null"
+    if [ "$?" -eq "$2" ]
     then
         [ "${PRINT_PASSED}" -gt 0 ] \
         && printf "%-72s%-1s\n" "$1" "2>/dev/null >/dev/null (-> $2?) passed."
@@ -88,7 +95,7 @@ function test() {
 }
 
 
-[ "$PRINT_PASSED" -gt 0 ] && printf "\nTesting ${NGINX_UTIL} get_env ...\n"
+[ "$PRINT_PASSED" -gt 0 ] && printf "\nTesting %s get_env ...\n" "${NGINX_UTIL}"
 
 eval $("${NGINX_UTIL}" get_env)
 test '[ -n "${NGINX_CONF}" ]' 0
@@ -101,21 +108,21 @@ test '[ -n "${SSL_SESSION_TIMEOUT_ARG}" ]' 0
 test '[ -n "${ADD_SSL_FCT}" ]' 0
 
 
-[ "$PRINT_PASSED" -gt 0 ] && printf "\nPrepare files in ${CONF_DIR} ...\n"
+[ "$PRINT_PASSED" -gt 0 ] && printf "\nPrepare files in %s ...\n" "${CONF_DIR}"
 
-mkdir -p ${CONF_DIR}
+mkdir -p "${CONF_DIR}"
 
-cd ${CONF_DIR}
+cd "${CONF_DIR}" || exit 2
 
 NGX_INCLUDE="include '\$';"
 NGX_SERVER_NAME="server_name * '\$' *;"
 NGX_SSL_CRT="ssl_certificate '\$.crt';"
 NGX_SSL_KEY="ssl_certificate_key '\$.key';"
 NGX_SSL_SESSION_CACHE="ssl_session_cache '$(echo "${SSL_SESSION_CACHE_ARG}" \
-    | sed -E "s/$(__esc_regex ${LAN_NAME})/\$/")';"
+    | sed -E "s/$(__esc_regex "${LAN_NAME}")/\$/")';"
 NGX_SSL_SESSION_TIMEOUT="ssl_session_timeout '${SSL_SESSION_TIMEOUT_ARG}';"
 
-cat > ${LAN_NAME}.sans <<EOF
+cat > "${LAN_NAME}.sans" <<EOF
 # default_server for the LAN addresses getting the IPs by:
 # ifstatus lan | jsonfilter -e '@["ipv4-address","ipv6-address"].*.address'
 server {
@@ -196,56 +203,44 @@ EOF
 CONFS="${CONFS} tab:0"
 
 
-[ "$PRINT_PASSED" -gt 0 ] && printf "\nTesting ${NGINX_UTIL} init_lan ...\n"
+[ "$PRINT_PASSED" -gt 0 ] && printf "\nTesting %s init_lan ...\n" "${NGINX_UTIL}"
 
 mkdir -p "$(dirname "${LAN_LISTEN}")"
 
-cp ${LAN_NAME}.sans ${LAN_NAME}.conf
+cp "${LAN_NAME}.sans" "${LAN_NAME}.conf"
 
-test '${NGINX_UTIL} init_lan' 0
+test '"${NGINX_UTIL}" init_lan' 0
 
 
-[ "$PRINT_PASSED" -gt 0 ] && printf "\nSetup files in ${CONF_DIR} ...\n"
+[ "$PRINT_PASSED" -gt 0 ] && printf "\nSetup files in %s ...\n" "${CONF_DIR}"
 
 for conf in ${CONFS}
-do test 'setpoint_add_ssl "    " '${conf%:*} ${conf#*:}
+do test 'setpoint_add_ssl "    " '"${conf%:*}" "${conf#*:}"
 done
 
 test 'setpoint_add_ssl "\t" tab' 0 # fixes wrong indentation.
 
 
-[ "$PRINT_PASSED" -gt 0 ] && printf "\nTesting ${NGINX_UTIL} add_ssl ...\n"
+[ "$PRINT_PASSED" -gt 0 ] && printf "\nTesting %s add_ssl ...\n" "${NGINX_UTIL}"
 
 cp different_name.sans different_name.with
 
-test '[ "${ADD_SSL_FCT}" == "add_ssl" ] ' 0
+test '[ "${ADD_SSL_FCT}" = "add_ssl" ] ' 0
 
 for conf in ${CONFS}; do
-    name=${conf%:*}
-    cp ${name}.sans ${name}.conf
-    test '${NGINX_UTIL} add_ssl '${name} ${conf#*:}
-    (test '[ "$(cat '${name}'.conf)" == "$(cat '${name}'.with)" ]' 0 >/dev/null)
-    [ "$?" -gt 0 ] && {
-        echo "created ${name}.conf:"; cat "${name}.conf"
-        echo "differs from setpoint:"; cat "${name}.with"
-        exit 1
-    }
+    name="${conf%:*}"
+    cp "${name}.sans" "${name}.conf"
+    test '"${NGINX_UTIL}" add_ssl '"${name}" "${conf#*:}"
+    test_setpoint "${name}.conf" "$(cat "${name}.with")"
 done
 
-
-[ "$PRINT_PASSED" -gt 0 ] && printf "\nTesting ${NGINX_UTIL} del_ssl ...\n"
+[ "$PRINT_PASSED" -gt 0 ] && printf "\nTesting %s del_ssl ...\n" "${NGINX_UTIL}"
 
 sed -i "/server {/a\\    include '${LAN_LISTEN}';" minimal.sans
 
 for conf in ${CONFS}; do
-    name=${conf%:*}
-    cp ${name}.with ${name}.conf
-    test '${NGINX_UTIL} del_ssl '${name} ${conf#*:}
-    (test '[ "$(cat '${name}'.conf)" == "$(cat '${name}'.sans)" ]' 0 >/dev/null)
-    [ "$?" -gt 0 ] && {
-        echo "created ${name}.conf:"; cat "${name}.conf"
-        echo "differs from setpoint:"; cat "${name}.sans"
-        exit 1
-    }
+    name="${conf%:*}"
+    cp "${name}.with" "${name}.conf"
+    test '"${NGINX_UTIL}" del_ssl '"${name}" "${conf#*:}"
+    test_setpoint "${name}.conf" "$(cat "${name}.sans")"
 done
-
index 840f6f260158a7800fb121f6cd250e1bfd8a3284..787906350c8f655fe008641983c4b3b87e60c2ef 100755 (executable)
@@ -1,38 +1,48 @@
-#!/bin/bash
+#!/bin/sh
 
 printf "Initializing tests ...\n"
 
 fakechroot=""
 
-[ -x /usr/bin/fakechroot ] && fakechroot="/usr/bin/fakechroot" \
+[ -x "/usr/bin/fakechroot" ] && fakechroot="/usr/bin/fakechroot" \
 || [ "$(id -u)" -eq 0 ] || { \
     printf "Error: Testing needs fakechroot or whoami=root for chroot."
     return 1
 }
 
-TMPROOT=$(mktemp -d /tmp/test-nginx-util-XXXXXX)
+TMPROOT="$(mktemp -d "/tmp/test-nginx-util-XXXXXX")"
 
-ln -s /bin ${TMPROOT}/bin
+ln -s /bin "${TMPROOT}/bin"
 
-mkdir -p ${TMPROOT}/usr/bin/
+mkdir -p "${TMPROOT}/usr/bin/"
 
-cp ./test-nginx-util-root.sh ${TMPROOT}/usr/bin/
+cp "./test-nginx-util-root.sh" "${TMPROOT}/usr/bin/"
 
 
 printf "\n\n******* Testing nginx-ssl-util-noubus *******\n"
 
-cp ./nginx-ssl-util-noubus ${TMPROOT}/usr/bin/nginx-util
+cp "./nginx-ssl-util-noubus" "${TMPROOT}/usr/bin/nginx-util"
 
-${fakechroot} /bin/chroot ${TMPROOT} /bin/sh -c /usr/bin/test-nginx-util-root.sh
-
-echo $?
+"${fakechroot}" /bin/chroot "${TMPROOT}" \
+    /bin/sh -c "/usr/bin/test-nginx-util-root.sh" ||
+{
+    echo "!!! Error: $?"
+    rm -r "${TMPROOT}"
+    exit 1
+}
 
 
 printf "\n\n******* Testing nginx-ssl-util-nopcre-noubus *******\n"
 
-cp ./nginx-ssl-util-nopcre-noubus ${TMPROOT}/usr/bin/nginx-util
+cp "./nginx-ssl-util-nopcre-noubus" "${TMPROOT}/usr/bin/nginx-util"
 
-${fakechroot} /bin/chroot ${TMPROOT} /bin/sh -c /usr/bin/test-nginx-util-root.sh
+"${fakechroot}" /bin/chroot "${TMPROOT}" \
+    /bin/sh -c "/usr/bin/test-nginx-util-root.sh" ||
+{
+    echo "!!! Error: $?"
+    rm -r "${TMPROOT}"
+    exit 1
+}
 
 
-rm -r ${TMPROOT}
+rm -r "${TMPROOT}"
index 944464c2a0c0a44b6bf278bc1b526a7fac20e6eb..486b9ae98df161cbc627bf1d83a4f42fa87e8c46 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh
 
 PRINT_PASSED=2
 
@@ -8,7 +8,7 @@ OPENSSL_PEM="$(mktemp)"
 OPENSSL_DER="$(mktemp)"
 
 NONCE=$(dd if=/dev/urandom bs=1 count=4 2>/dev/null | hexdump -e '1/1 "%02x"')
-SUBJECT=/C="ZZ"/ST="Somewhere"/L="None"/O="OpenWrt'$NONCE'"/CN="OpenWrt"
+SUBJECT="/C=ZZ/ST=Somewhere/L=None/O=OpenWrt'$NONCE'/CN=OpenWrt"
 
 openssl req -x509 -nodes -days 1 -keyout /dev/null 2>/dev/null \
     -out "$OPENSSL_PEM" -subj "$SUBJECT" \
@@ -18,9 +18,9 @@ openssl req -x509 -nodes -days 1 -keyout /dev/null 2>/dev/null \
 || ( printf "error: generating DER certificate with openssl"; return 1)
 
 
-function test() {
+test() {
     eval "$1 >/dev/null "
-    if [ $? -eq $2 ]
+    if [ $? -eq "$2" ]
     then
         [ "${PRINT_PASSED}" -gt 0 ] \
         && printf "%-72s%-1s\n" "$1" ">/dev/null (-> $2?) passed."
@@ -130,8 +130,8 @@ test './px5g selfsigned -newkey rsa:666       | openssl x509 -checkend 0    ' 0
 test './px5g selfsigned -newkey ec            | openssl x509 -checkend 0    ' 0
 test './px5g selfsigned -newkey ec -pkeyopt ec_paramgen_curve:secp384r1 \
       | openssl x509 -checkend 0                                        ' 0
-test './px5g selfsigned -subj $SUBJECT | openssl x509 -noout \
-      -subject -nameopt compat | grep -q subject=$SUBJECT    2>/dev/null' 0
+test './px5g selfsigned -subj "$SUBJECT" | openssl x509 -noout \
+      -subject -nameopt compat | grep -q subject="$SUBJECT" 2>/dev/null' 0
 test './px5g selfsigned -out /dev/null -keyout /proc/self/fd/1 \
       | openssl rsa -check 2>/dev/null                                  ' 0
 
diff --git a/net/nginx-util/src/ubus-cxx.cpp b/net/nginx-util/src/ubus-cxx.cpp
new file mode 100644 (file)
index 0000000..7f65813
--- /dev/null
@@ -0,0 +1,148 @@
+#include <iostream>
+
+#include "ubus-cxx.hpp"
+
+
+inline void example_for_checking_if_there_is_a_key()
+{
+    if (ubus::call("service", "list").filter("cron")) {
+        std::cout<<"Cron is active (with or without instances) "<<std::endl;
+    }
+}
+
+
+inline void example_for_getting_values()
+{
+    auto lan_status = ubus::call("network.interface.lan", "status");
+    for (auto t : lan_status.filter("ipv6-address", "", "address")) {
+        //NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
+        auto x = const_cast<blob_attr *>(t);
+        std::cout<<"["<<blobmsg_get_string(x)<<"] ";
+    }
+    for (auto t : lan_status.filter("ipv4-address", "").filter("address")) {
+        //NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
+        auto x = const_cast<blob_attr *>(t);
+        std::cout<<blobmsg_get_string(x)<<" ";
+    }
+    std::cout<<std::endl;
+}
+
+
+inline void example_for_sending_message()
+{
+    auto set_arg = [](blob_buf * buf) -> int
+        { return blobmsg_add_string(buf, "config", "nginx"); };
+    for (auto t : ubus::call("uci", "get", set_arg).filter("values")) {
+        //NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
+        auto x = const_cast<blob_attr *>(t);
+        std::cout<<blobmsg_get_string(x)<<"\n";
+    }
+}
+
+
+inline void example_for_exploring()
+{
+    ubus::strings keys{"ipv4-address", "", ""};
+    for (auto t : ubus::call("network.interface.lan", "status").filter(keys)) {
+        //NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
+        auto x = const_cast<blob_attr *>(t);
+        std::cout<<blobmsg_name(x)<<": ";
+        switch (blob_id(x)) {
+            case BLOBMSG_TYPE_UNSPEC: std::cout<<"[unspecified]"; break;
+            case BLOBMSG_TYPE_ARRAY: std::cout<<"[array]"; break;
+            case BLOBMSG_TYPE_TABLE: std::cout<<"[table]"; break;
+            case BLOBMSG_TYPE_STRING: std::cout<<blobmsg_get_string(x); break;
+            case BLOBMSG_TYPE_INT64: std::cout<<blobmsg_get_u64(x); break;
+            case BLOBMSG_TYPE_INT32: std::cout<<blobmsg_get_u32(x); break;
+            case BLOBMSG_TYPE_INT16: std::cout<<blobmsg_get_u16(x); break;
+            case BLOBMSG_TYPE_BOOL: std::cout<<blobmsg_get_bool(x); break;
+            case BLOBMSG_TYPE_DOUBLE: std::cout<<blobmsg_get_double(x); break;
+            default: std::cout<<"[unknown]";
+        }
+        std::cout<<std::endl;
+    }
+}
+
+
+inline void example_for_recursive_exploring()
+{ // output like from the original ubus call:
+    const auto explore = [](auto message) -> void
+    {
+        auto end = message.end();
+        auto explore_internal =
+            [&end](auto & explore_ref, auto it, size_t depth=1) -> void
+        {
+            std::cout<<std::endl;
+            bool first = true;
+            for (; it!=end; ++it) {
+                //NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
+                auto attr = const_cast<blob_attr *>(*it);
+                if (first) { first = false; }
+                else { std::cout<<",\n"; }
+                std::cout<<std::string(depth, '\t');
+                std::string name = blobmsg_name(attr);
+                if (!name.empty()) {  std::cout<<"\""<<name<<"\": "; }
+                switch (blob_id(attr)) {
+                    case BLOBMSG_TYPE_UNSPEC: std::cout<<"(unspecified)"; break;
+                    case BLOBMSG_TYPE_ARRAY:
+                        std::cout<<"[";
+                        explore_ref(explore_ref, ubus::iterator{attr}, depth+1);
+                        std::cout<<"\n"<<std::string(depth, '\t')<<"]";
+                        break;
+                    case BLOBMSG_TYPE_TABLE:
+                        std::cout<<"{";
+                        explore_ref(explore_ref, ubus::iterator{attr}, depth+1);
+                        std::cout<<"\n"<<std::string(depth, '\t')<<"}";
+                        break;
+                    case BLOBMSG_TYPE_STRING:
+                        std::cout<<"\""<<blobmsg_get_string(attr)<<"\"";
+                        break;
+                    case BLOBMSG_TYPE_INT64:
+                        std::cout<<blobmsg_get_u64(attr);
+                        break;
+                    case BLOBMSG_TYPE_INT32:
+                        std::cout<<blobmsg_get_u32(attr);
+                        break;
+                    case BLOBMSG_TYPE_INT16:
+                        std::cout<<blobmsg_get_u16(attr);
+                        break;
+                    case BLOBMSG_TYPE_BOOL:
+                        std::cout<<(blobmsg_get_bool(attr) ? "true" : "false");
+                        break;
+                    case BLOBMSG_TYPE_DOUBLE:
+                        std::cout<<blobmsg_get_double(attr);
+                        break;
+                    default: std::cout<<"(unknown)"; break;
+                }
+            }
+        };
+        std::cout<<"{";
+        explore_internal(explore_internal, message.begin());
+        std::cout<<"\n}"<<std::endl;
+    };
+    explore(ubus::call("network.interface.lan", "status"));
+}
+
+
+auto main() -> int {
+
+    try {
+        example_for_checking_if_there_is_a_key();
+
+        example_for_getting_values();
+
+        example_for_sending_message();
+
+        example_for_exploring();
+
+        example_for_recursive_exploring();
+
+        return 0;
+    }
+
+    catch (const std::exception & e) { std::cerr<<e.what()<<std::endl; }
+
+    catch (...) { perror("main error"); }
+
+    return 1;
+}
index b1a62c6ce0b326158ff899eb40783bb63e50acb6..3c0851e2b30b54d84dbc1d53a1ad576ef58e170c 100644 (file)
 #ifndef _UBUS_CXX_HPP
 #define _UBUS_CXX_HPP
 
-extern "C" { //TODO(pst): remove when in upstream
-#include <libubus.h>
-}
 #include <cassert>
+#include <libubus.h>
 #include <memory>
 #include <mutex>
 #include <string>
 #include <utility>
 #include <vector>
+
 #ifndef NDEBUG
 #include <iostream>
 #endif
 
 
-// // example for checking if there is a key:
-// if (ubus::call("service", "list", 1000).filter("cron")) {
-//     std::cout<<"Cron is active (with or without instances) "<<std::endl;
-// }
-
-// // example for getting values:
-// auto lan_status = ubus::call("network.interface.lan", "status");
-// for (auto x : lan_status.filter("ipv6-address", "", "address")) {
-//     std::cout<<"["<<blobmsg_get_string(x)<<"] ";
-// }
-// for (auto x : lan_status.filter("ipv4-address", "").filter("address")) {
-//     std::cout<<blobmsg_get_string(x)<<" ";
-// }
-// std::cout<<std::endl;
-
-// // example for exploring:
-// ubus::strings keys{"ipv4-address", "", ""};
-// for (auto x : ubus::call("network.interface.lan", "status").filter(keys)) {
-//     std::cout<<blobmsg_name(x)<<": ";
-//     switch (blob_id(x)) {
-//         case BLOBMSG_TYPE_UNSPEC: std::cout<<"[unspecified]"; break;
-//         case BLOBMSG_TYPE_ARRAY: std::cout<<"[array]"; break;
-//         case BLOBMSG_TYPE_TABLE: std::cout<<"[table]"; break;
-//         case BLOBMSG_TYPE_STRING: std::cout<<blobmsg_get_string(x); break;
-//         case BLOBMSG_TYPE_INT64: std::cout<<blobmsg_get_u64(x); break;
-//         case BLOBMSG_TYPE_INT32: std::cout<<blobmsg_get_u32(x); break;
-//         case BLOBMSG_TYPE_INT16: std::cout<<blobmsg_get_u16(x); break;
-//         case BLOBMSG_TYPE_BOOL: std::cout<<blobmsg_get_bool(x); break;
-//         case BLOBMSG_TYPE_DOUBLE: std::cout<<blobmsg_get_double(x); break;
-//         default: std::cout<<"[unknown]";
-//     }
-//     std::cout<<std::endl;
-// }
-
-// // example for recursive exploring (output like from the original ubus call)
-// const auto explore = [](auto message) -> void
-// {
-//     auto end = message.end();
-//     auto explore_internal =
-//         [&end](auto & explore_ref, auto it, size_t depth=1) -> void
-//     {
-//         std::cout<<std::endl;
-//         bool first = true;
-//         for (; it!=end; ++it) {
-//             auto * attr = *it;
-//             if (first) { first = false; }
-//             else { std::cout<<",\n"; }
-//             std::cout<<std::string(depth, '\t');
-//             std::string name = blobmsg_name(attr);
-//             if (name != "") {  std::cout<<"\""<<name<<"\": "; }
-//             switch (blob_id(attr)) {
-//                 case BLOBMSG_TYPE_UNSPEC: std::cout<<"(unspecified)"; break;
-//                 case BLOBMSG_TYPE_ARRAY:
-//                     std::cout<<"[";
-//                     explore_ref(explore_ref, ubus::iterator{attr}, depth+1);
-//                     std::cout<<"\n"<<std::string(depth, '\t')<<"]";
-//                     break;
-//                 case BLOBMSG_TYPE_TABLE:
-//                     std::cout<<"{";
-//                     explore_ref(explore_ref, ubus::iterator{attr}, depth+1);
-//                     std::cout<<"\n"<<std::string(depth, '\t')<<"}";
-//                     break;
-//                 case BLOBMSG_TYPE_STRING:
-//                     std::cout<<"\""<<blobmsg_get_string(attr)<<"\"";
-//                     break;
-//                 case BLOBMSG_TYPE_INT64:
-//                     std::cout<<blobmsg_get_u64(attr);
-//                     break;
-//                 case BLOBMSG_TYPE_INT32:
-//                     std::cout<<blobmsg_get_u32(attr);
-//                     break;
-//                 case BLOBMSG_TYPE_INT16:
-//                     std::cout<<blobmsg_get_u16(attr);
-//                     break;
-//                 case BLOBMSG_TYPE_BOOL:
-//                     std::cout<<(blobmsg_get_bool(attr) ? "true" : "false");
-//                     break;
-//                 case BLOBMSG_TYPE_DOUBLE:
-//                     std::cout<<blobmsg_get_double(attr);
-//                     break;
-//                 default: std::cout<<"(unknown)"; break;
-//             }
-//         }
-//     };
-//     std::cout<<"{";
-//     explore_internal(explore_internal, message.begin());
-//     std::cout<<"\n}"<<std::endl;
-// };
-// explore(ubus::call("network.interface.lan", "status"));
-
-
-
 namespace ubus {
 
+static constexpr int call_timeout = 500;
+
 using msg_ptr = std::shared_ptr<const blob_attr>;
 
 using strings = std::vector<std::string>;
@@ -130,7 +39,7 @@ inline auto concat(strings dest, strings src, Strings ...more)
 template<class S, class ...Strings>
 inline auto concat(strings dest, S src, Strings ...more)
 {
-    dest.push_back(std::move(src));
+    dest.emplace_back(std::move(src));
     return concat(std::move(dest), std::move(more)...);
 }
 
@@ -186,7 +95,7 @@ public:
     }
 
 
-    inline iterator(iterator &&) = default;
+    inline iterator(iterator &&) noexcept = default;
 
 
     inline iterator(const iterator &) = delete;
@@ -208,7 +117,8 @@ public:
     auto operator++() -> iterator &;
 
 
-    inline ~iterator() { if (cur.get()==this) { static_cast<void>(cur.release()); } }
+    inline ~iterator()
+    { if (cur.get()==this) { static_cast<void>(cur.release()); } }
 
 };
 
@@ -225,8 +135,8 @@ private:
 
 public:
 
-    inline explicit message(msg_ptr  message, strings filter={""})
-    : msg{std::move(message)}, keys{std::move(filter)} {}
+    inline explicit message(msg_ptr message_ptr, strings filter={""})
+    : msg{std::move(message_ptr)}, keys{std::move(filter)} {}
 
 
     inline message(message &&) = default;
@@ -268,42 +178,41 @@ public:
 
 
 
-class ubus {
+class lock_shared_resources {
 
 private:
 
-    static std::mutex buffering;
+    static std::mutex inuse;
 
 
 public:
 
-    inline ubus() = delete;
 
+    inline lock_shared_resources() { inuse.lock(); }
 
-    inline ubus(ubus &&) = delete;
 
+    inline lock_shared_resources(lock_shared_resources &&) noexcept = default;
 
-    inline ubus(const ubus &) = delete;
 
+    inline lock_shared_resources(const lock_shared_resources &) = delete;
 
-    inline auto operator=(ubus &&) -> auto && = delete;
 
+    inline auto operator=(const lock_shared_resources &) -> auto & = delete;
 
-    inline auto operator=(const ubus &) -> auto & = delete;
 
+    inline auto operator=(lock_shared_resources &&) -> auto && = delete;
 
-    static auto get_context() -> ubus_context *
+
+    //NOLINTNEXTLINE(readability-convert-member-functions-to-static)
+    inline auto get_context() -> ubus_context * // is member to enforce inuse.
     {
         static auto ubus_freeing = [] (ubus_context * ctx) { ubus_free(ctx); };
         static std::unique_ptr<ubus_context, decltype(ubus_freeing)>
             lazy_ctx{ubus_connect(nullptr), ubus_freeing};
 
         if (!lazy_ctx) { // it could be available on a later call:
-            static std::mutex connecting;
 
-            connecting.lock();
-            if (!lazy_ctx) { lazy_ctx.reset(ubus_connect(nullptr)); }
-            connecting.unlock();
+            lazy_ctx.reset(ubus_connect(nullptr));
 
             if (!lazy_ctx) {
                 throw std::runtime_error("ubus error: cannot connect context");
@@ -314,7 +223,8 @@ public:
     }
 
 
-    static auto lock_and_get_shared_blob_buf() -> blob_buf *
+    //NOLINTNEXTLINE(readability-convert-member-functions-to-static)
+    inline auto get_blob_buf() -> blob_buf * // is member to enforce inuse.
     {
         static blob_buf buf;
 
@@ -322,28 +232,37 @@ public:
         static std::unique_ptr<blob_buf, decltype(blob_buf_freeing)>
                 created_to_free_on_the_end_of_life{&buf, blob_buf_freeing};
 
-        buffering.lock();
         blob_buf_init(&buf, 0);
+
         return &buf;
     }
 
 
-    static void unlock_shared_blob_buf() { buffering.unlock(); }
+    inline ~lock_shared_resources() { inuse.unlock(); }
 
+};
 
-    inline ~ubus() = delete;
 
-};
+template<class F>
+auto call(const char * path, const char * method, F set_arguments,
+          int timeout=call_timeout) -> message;
+
+
+inline auto call(const char * path, const char * method,
+                 int timeout=call_timeout) -> message
+{ return call(path, method, [](blob_buf * /*buf*/) { return 0; }, timeout); }
+
 
+inline auto call(const char * path, int timeout=call_timeout) -> message
+{ return call(path, "", timeout); }
 
-auto call(const char * path, const char * method="", int timeout=500);
 
 
 
 // ------------------------- implementation: ----------------------------------
 
 
-std::mutex ubus::buffering;
+std::mutex lock_shared_resources::inuse;
 
 
 inline auto iterator::operator++() -> iterator &
@@ -363,7 +282,10 @@ inline auto iterator::operator++() -> iterator &
             ++i;
 
             auto tmp = cur.release();
-            cur = std::unique_ptr<iterator>{new iterator(tmp)};
+
+            struct new_iterator : public iterator // use private constructor:
+            { explicit new_iterator(iterator * par) : iterator{par} {} };
+            cur = std::make_unique<new_iterator>(tmp);
 
         } else {
             while (true) {
@@ -392,31 +314,39 @@ inline auto iterator::operator++() -> iterator &
 }
 
 
-inline auto call(const char * path, const char * method, const int timeout)
+template<class F>
+inline auto call(const char * path, const char * method, F set_arguments,
+                 int timeout) -> message
 {
-    auto ctx = ubus::get_context();
+
+    auto shared = lock_shared_resources{};
+
+    auto ctx = shared.get_context();
 
     uint32_t id;
     int err = ubus_lookup_id(ctx, path, &id);
 
     if (err==0) { // call
-        ubus_request req{};
+        ubus_request request{};
 
-        auto buf = ubus::lock_and_get_shared_blob_buf();
-        err = ubus_invoke_async(ctx, id, method, buf->head, &req);
-        ubus::unlock_shared_blob_buf();
+        auto buf = shared.get_blob_buf();
+        err = set_arguments(buf);
+        if (err==0) {
+            err = ubus_invoke_async(ctx, id, method, buf->head, &request);
+        }
 
         if (err==0) {
 
-            msg_ptr msg;
+            msg_ptr message_ptr;
 
-            /* Cannot capture anything (msg), the lambda would be another type.
+            /* Cannot capture message_ptr, the lambda would be another type.
             * Pass a location where to save the message as priv pointer when
             * invoking and get it back here:
             */
-            req.priv = &msg;
+            request.priv = &message_ptr;
 
-            req.data_cb = [](ubus_request * req, int /*type*/, blob_attr * msg)
+            request.data_cb =
+                [](ubus_request * req, int /*type*/, blob_attr * msg)
             {
                 if (req==nullptr || msg==nullptr) { return; }
 
@@ -427,15 +357,15 @@ inline auto call(const char * path, const char * method, const int timeout)
                 if (!*saved) { throw std::bad_alloc(); }
             };
 
-            err = ubus_complete_request(ctx, &req, timeout);
+            err = ubus_complete_request(ctx, &request, timeout);
 
-            if (err==0) { return message{msg}; }
+            if (err==0) { return message{message_ptr}; }
         }
     }
 
     std::string errmsg = "ubus::call error: cannot invoke";
     errmsg +=  " (" + std::to_string(err) + ") " + path + " " + method;
-    throw std::runtime_error(errmsg.c_str());
+    throw std::runtime_error(errmsg);
 }
 
 
git clone https://git.99rst.org/PROJECT