diff --git a/CMakeLists.txt b/CMakeLists.txt index 256f54c..40e6261 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,6 +77,7 @@ add_executable( owfms src/framework/StorageClass.h src/framework/ow_constants.h src/framework/WebSocketClientNotifications.h + src/framework/MicroServiceErrorHandler.h src/RESTObjects/RESTAPI_SecurityObjects.h src/RESTObjects/RESTAPI_SecurityObjects.cpp src/RESTObjects/RESTAPI_ProvObjects.cpp src/RESTObjects/RESTAPI_ProvObjects.h src/RESTObjects/RESTAPI_GWobjects.h src/RESTObjects/RESTAPI_GWobjects.cpp diff --git a/Dockerfile b/Dockerfile index 777e4a8..45a76f1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,18 +1,17 @@ -ARG ALPINE_VERSION=3.16.2 +ARG DEBIAN_VERSION=11.4-slim ARG POCO_VERSION=poco-tip-v1 ARG FMTLIB_VERSION=9.0.0 ARG CPPKAFKA_VERSION=tip-v1 ARG JSON_VALIDATOR_VERSION=2.1.0 ARG AWS_SDK_VERSION=1.9.315 -FROM alpine:$ALPINE_VERSION AS build-base +FROM debian:$DEBIAN_VERSION AS build-base -RUN apk add --update --no-cache \ +RUN apt-get update && apt-get install --no-install-recommends -y \ make cmake g++ git \ - unixodbc-dev postgresql-dev mariadb-dev \ - librdkafka-dev boost-dev openssl-dev \ - zlib-dev nlohmann-json \ - curl-dev + unixodbc-dev libpq-dev libmariadb-dev libmariadbclient-dev-compat \ + librdkafka-dev libboost-all-dev libssl-dev \ + zlib1g-dev nlohmann-json3-dev ca-certificates libcurl4-openssl-dev FROM build-base AS poco-build @@ -113,21 +112,21 @@ RUN cmake .. \ -DBUILD_SHARED_LIBS=ON RUN cmake --build . --config Release -j8 -FROM alpine:$ALPINE_VERSION +FROM debian:$DEBIAN_VERSION ENV OWFMS_USER=owfms \ OWFMS_ROOT=/owfms-data \ OWFMS_CONFIG=/owfms-data -RUN addgroup -S "$OWFMS_USER" && \ - adduser -S -G "$OWFMS_USER" "$OWFMS_USER" +RUN useradd "$OWFMS_USER" RUN mkdir /openwifi RUN mkdir -p "$OWFMS_ROOT" "$OWFMS_CONFIG" && \ chown "$OWFMS_USER": "$OWFMS_ROOT" "$OWFMS_CONFIG" -RUN apk add --update --no-cache librdkafka su-exec gettext ca-certificates bash jq curl \ - mariadb-connector-c libpq unixodbc postgresql-client +RUN apt-get update && apt-get install --no-install-recommends -y \ + librdkafka++1 gosu gettext ca-certificates bash jq curl wget \ + libmariadb-dev-compat libpq5 unixodbc COPY readiness_check /readiness_check COPY test_scripts/curl/cli /cli @@ -136,7 +135,7 @@ COPY owfms.properties.tmpl / COPY docker-entrypoint.sh / COPY wait-for-postgres.sh / RUN wget https://raw.githubusercontent.com/Telecominfraproject/wlan-cloud-ucentral-deploy/main/docker-compose/certs/restapi-ca.pem \ - -O /usr/local/share/ca-certificates/restapi-ca-selfsigned.pem + -O /usr/local/share/ca-certificates/restapi-ca-selfsigned.crt COPY --from=owfms-build /owfms/cmake-build/owfms /openwifi/owfms COPY --from=cppkafka-build /cppkafka/cmake-build/src/lib/* /usr/local/lib @@ -145,6 +144,8 @@ COPY --from=aws-sdk-cpp-build /aws-sdk-cpp/cmake-build/aws-cpp-sdk-core/libaws-c COPY --from=aws-sdk-cpp-build /aws-sdk-cpp/cmake-build/aws-cpp-sdk-s3/libaws-cpp-sdk-s3.so /usr/local/lib COPY --from=aws-sdk-cpp-build /aws-sdk-cpp/cmake-build/aws-cpp-sdk-sns/libaws-cpp-sdk-sns.so /usr/local/lib +RUN ldconfig + EXPOSE 16004 17004 16104 ENTRYPOINT ["/docker-entrypoint.sh"] diff --git a/build b/build index 3cacc0b..19c7bdb 100644 --- a/build +++ b/build @@ -1 +1 @@ -12 \ No newline at end of file +16 \ No newline at end of file diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 755510e..c237f97 100755 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash set -e if [ "$SELFSIGNED_CERTS" = 'true' ]; then @@ -52,7 +52,7 @@ if [ "$1" = '/openwifi/owfms' -a "$(id -u)" = '0' ]; then if [ "$RUN_CHOWN" = 'true' ]; then chown -R "$OWFMS_USER": "$OWFMS_ROOT" "$OWFMS_CONFIG" fi - exec su-exec "$OWFMS_USER" "$@" + exec gosu "$OWFMS_USER" "$@" fi exec "$@" diff --git a/src/framework/MicroService.h b/src/framework/MicroService.h index 890fc12..cb17123 100644 --- a/src/framework/MicroService.h +++ b/src/framework/MicroService.h @@ -96,8 +96,10 @@ using namespace std::chrono_literals; #include "Poco/NObserver.h" #include "Poco/Net/SocketNotification.h" #include "Poco/Base64Decoder.h" +#include "Poco/ThreadLocal.h" #include "cppkafka/cppkafka.h" +#include "framework/MicroServiceErrorHandler.h" #include "framework/OpenWifiTypes.h" #include "framework/KafkaTopics.h" #include "framework/ow_constants.h" @@ -667,6 +669,19 @@ namespace OpenWifi::RESTAPI_utils { namespace OpenWifi::Utils { + inline bool NormalizeMac(std::string & Mac) { + Poco::replaceInPlace(Mac,":",""); + Poco::replaceInPlace(Mac,"-",""); + if(Mac.size()!=12) + return false; + for(const auto &i:Mac) { + if(!std::isxdigit(i)) + return false; + } + Poco::toLowerInPlace(Mac); + return true; + } + inline void SetThreadName(const char *name) { #ifdef __linux__ Poco::Thread::current()->setName(name); @@ -1346,28 +1361,6 @@ namespace OpenWifi { Poco::ExpireLRUCache Cache_{Size,Expiry}; }; - class MyErrorHandler : public Poco::ErrorHandler { - public: - explicit MyErrorHandler(Poco::Util::Application &App) : App_(App) {} - inline void exception(const Poco::Exception & E) { - Poco::Thread * CurrentThread = Poco::Thread::current(); - App_.logger().log(E); - poco_error(App_.logger(), fmt::format("Exception occurred in {}",CurrentThread->getName())); - } - - inline void exception(const std::exception & E) { - Poco::Thread * CurrentThread = Poco::Thread::current(); - poco_warning(App_.logger(), fmt::format("std::exception in {}: {}",CurrentThread->getName(),E.what())); - } - - inline void exception() { - Poco::Thread * CurrentThread = Poco::Thread::current(); - poco_warning(App_.logger(), fmt::format("exception in {}",CurrentThread->getName())); - } - private: - Poco::Util::Application &App_; - }; - class BusEventManager : public Poco::Runnable { public: explicit BusEventManager(Poco::Logger &L) : Logger_(L) { @@ -3082,7 +3075,7 @@ namespace OpenWifi { private: std::vector> RESTServers_; - Poco::ThreadPool Pool_{"x-rest",4,128}; + Poco::ThreadPool Pool_{"x-rest",32,128}; RESTAPI_GenericServer Server_; RESTAPI_ExtServer() noexcept: @@ -3099,15 +3092,16 @@ namespace OpenWifi { inline Poco::Net::HTTPRequestHandler *createRequestHandler(const Poco::Net::HTTPServerRequest &Request) override { try { Poco::URI uri(Request.getURI()); - Utils::SetThreadName(fmt::format("x-rest:{}",TransactionId_).c_str()); - return RESTAPI_ExtServer()->CallServer(uri.getPath(), TransactionId_++); + auto TID = NextTransactionId_++; + Utils::SetThreadName(fmt::format("x-rest:{}",TID).c_str()); + return RESTAPI_ExtServer()->CallServer(uri.getPath(), TID); } catch (...) { } return nullptr; } private: - static inline std::atomic_uint64_t TransactionId_ = 1; + static inline std::atomic_uint64_t NextTransactionId_ = 1; }; class LogMuxer : public Poco::Channel { @@ -3215,7 +3209,7 @@ namespace OpenWifi { const Poco::ThreadPool & Pool() { return Pool_; } private: std::vector> RESTServers_; - Poco::ThreadPool Pool_{"i-rest",4,96}; + Poco::ThreadPool Pool_{"i-rest",32,96}; RESTAPI_GenericServer Server_; RESTAPI_IntServer() noexcept: @@ -3230,12 +3224,13 @@ namespace OpenWifi { public: inline IntRequestHandlerFactory() = default; inline Poco::Net::HTTPRequestHandler *createRequestHandler(const Poco::Net::HTTPServerRequest &Request) override { - Utils::SetThreadName(fmt::format("i-rest:{}",TransactionId_).c_str()); + auto TID=NextTransactionId_++; + Utils::SetThreadName(fmt::format("i-rest:{}",TID).c_str()); Poco::URI uri(Request.getURI()); - return RESTAPI_IntServer()->CallServer(uri.getPath(), TransactionId_); + return RESTAPI_IntServer()->CallServer(uri.getPath(), TID); } private: - static inline std::atomic_uint64_t TransactionId_ = 1; + static inline std::atomic_uint64_t NextTransactionId_ = 1; }; struct MicroServiceMeta { @@ -3576,7 +3571,7 @@ namespace OpenWifi { auto LoggingDestination = MicroService::instance().ConfigGetString("logging.type", "file"); auto LoggingFormat = MicroService::instance().ConfigGetString("logging.format", - "%Y-%m-%d %H:%M:%S %s: [%p] %t"); + "%Y-%m-%d %H:%M:%S.%i %s: [%p][thr:%I] %t"); if (LoggingDestination == "console") { Poco::AutoPtr Console(new Poco::ConsoleChannel); Poco::AutoPtr Async(new Poco::AsyncChannel(Console)); @@ -3963,8 +3958,6 @@ namespace OpenWifi { } Poco::Net::HTTPServerParams::Ptr Params = new Poco::Net::HTTPServerParams; - Params->setMaxThreads(50); - Params->setMaxQueued(200); Params->setKeepAlive(true); Params->setName("ws:xrest"); @@ -3999,8 +3992,6 @@ namespace OpenWifi { } auto Params = new Poco::Net::HTTPServerParams; - Params->setMaxThreads(50); - Params->setMaxQueued(200); Params->setKeepAlive(true); Params->setName("ws:irest"); @@ -4020,7 +4011,7 @@ namespace OpenWifi { } inline int MicroService::main([[maybe_unused]] const ArgVec &args) { - MyErrorHandler ErrorHandler(*this); + MicroServiceErrorHandler ErrorHandler(*this); Poco::ErrorHandler::set(&ErrorHandler); if (!HelpRequested_) { @@ -5100,7 +5091,7 @@ namespace OpenWifi { auto Op = flags & Poco::Net::WebSocket::FRAME_OP_BITMASK; if (n == 0) { - poco_warning(Logger(),Poco::format("CLOSE(%s): %s UI Client is closing WS connection.", Id_, UserName_)); + poco_debug(Logger(),fmt::format("CLOSE({}): {} UI Client is closing WS connection.", Id_, UserName_)); return delete this; } @@ -5113,7 +5104,7 @@ namespace OpenWifi { case Poco::Net::WebSocket::FRAME_OP_PONG: { } break; case Poco::Net::WebSocket::FRAME_OP_CLOSE: { - poco_warning(Logger(),Poco::format("CLOSE(%s): %s UI Client is closing WS connection.", Id_, UserName_)); + poco_debug(Logger(),fmt::format("CLOSE({}): {} UI Client is closing WS connection.", Id_, UserName_)); Done = true; } break; case Poco::Net::WebSocket::FRAME_OP_TEXT: { @@ -5126,7 +5117,7 @@ namespace OpenWifi { AuthClient()->IsAuthorized(Tokens[1], UserInfo_, 0, Expired, Contacted)) { Authenticated_ = true; UserName_ = UserInfo_.userinfo.email; - poco_warning(Logger(),Poco::format("START(%s): %s UI Client is starting WS connection.", Id_, UserName_)); + poco_debug(Logger(),fmt::format("START({}): {} UI Client is starting WS connection.", Id_, UserName_)); std::string S{"Welcome! Bienvenue! Bienvenidos!"}; WS_->sendFrame(S.c_str(), S.size()); WebSocketClientServer()->SetUser(Id_, UserInfo_.userinfo.email); @@ -5212,7 +5203,6 @@ namespace OpenWifi { Poco::Net::ErrorNotification>(*this,&WebSocketClient::OnSocketError)); (*WS_).shutdown(); (*WS_).close(); - WebSocketClientServer()->UnRegister(Id_); } catch(...) { } diff --git a/src/framework/MicroServiceErrorHandler.h b/src/framework/MicroServiceErrorHandler.h new file mode 100644 index 0000000..05d75d1 --- /dev/null +++ b/src/framework/MicroServiceErrorHandler.h @@ -0,0 +1,169 @@ +// +// Created by stephane bourque on 2022-09-29. +// + +#pragma once + +#include "fmt/format.h" +#include "Poco/Util/Application.h" +#include "Poco/ErrorHandler.h" +#include "Poco/Net/NetException.h" +#include "Poco/Net/SSLException.h" +#include "Poco/JSON/Template.h" +#include "Poco/Thread.h" + +namespace OpenWifi { + + class MicroServiceErrorHandler : public Poco::ErrorHandler { + public: + explicit MicroServiceErrorHandler(Poco::Util::Application &App) : App_(App) { + } + + inline void exception(const Poco::Exception & Base) override { + try { + if(Poco::Thread::current()!= nullptr) { + t_name = Poco::Thread::current()->getName(); + t_id = Poco::Thread::current()->id(); + } else { + t_name = "startup_code"; + t_id = 0; + } + + App_.logger().log(Base); + Base.rethrow(); + + } catch (const Poco::Net::InvalidCertificateException &E) { + poco_error(App_.logger(), fmt::format("Poco::Net::InvalidCertificateException thr_name={} thr_id={} code={} text={} msg={} what={}", + t_name, t_id, E.code(), + E.displayText(), + E.message(), + E.what())); + } catch (const Poco::Net::InvalidSocketException &E) { + poco_error(App_.logger(), fmt::format("Poco::Net::InvalidSocketException thr_name={} thr_id={} code={} text={} msg={} what={}", + t_name, t_id, E.code(), + E.displayText(), + E.message(), + E.what())); + } catch (const Poco::Net::WebSocketException &E) { + poco_error(App_.logger(), fmt::format("Poco::Net::WebSocketException thr_name={} thr_id={} code={} text={} msg={} what={}", + t_name, t_id, E.code(), + E.displayText(), + E.message(), + E.what())); + } catch (const Poco::Net::ConnectionResetException &E) { + poco_error(App_.logger(), fmt::format("Poco::Net::ConnectionResetException thr_name={} thr_id={} code={} text={} msg={} what={}", + t_name, t_id, E.code(), + E.displayText(), + E.message(), + E.what())); + } catch (const Poco::Net::CertificateValidationException &E) { + poco_error(App_.logger(), fmt::format("Poco::Net::CertificateValidationException thr_name={} thr_id={} code={} text={} msg={} what={}", + t_name, t_id, E.code(), + E.displayText(), + E.message(), + E.what())); + } catch (const Poco::Net::SSLConnectionUnexpectedlyClosedException &E) { + poco_error(App_.logger(), fmt::format("Poco::Net::SSLConnectionUnexpectedlyClosedException thr_name={} thr_id={} code={} text={} msg={} what={}", + t_name, t_id, E.code(), + E.displayText(), + E.message(), + E.what())); + } catch (const Poco::Net::SSLContextException &E) { + poco_error(App_.logger(), fmt::format("Poco::Net::SSLContextException thr_name={} thr_id={} code={} text={} msg={} what={}", + t_name, t_id, E.code(), + E.displayText(), + E.message(), + E.what())); + } catch (const Poco::Net::SSLException &E) { + poco_error(App_.logger(), fmt::format("Poco::Net::SSLException thr_name={} thr_id={} code={} text={} msg={} what={}", + t_name, t_id, E.code(), + E.displayText(), + E.message(), + E.what())); + + } catch (const Poco::Net::InvalidAddressException &E) { + poco_error(App_.logger(), fmt::format("Poco::Net::InvalidAddressException thr_name={} thr_id={} code={} text={} msg={} what={}", + t_name, t_id, E.code(), + E.displayText(), + E.message(), + E.what())); + + } catch (const Poco::Net::NetException &E) { + poco_error(App_.logger(), fmt::format("Poco::Net::NetException thr_name={} thr_id={} code={} text={} msg={} what={}", + t_name, t_id, E.code(), + E.displayText(), + E.message(), + E.what())); + + } catch (const Poco::IOException &E) { + poco_error(App_.logger(), fmt::format("Poco::IOException thr_name={} thr_id={} code={} text={} msg={} what={}", + t_name, t_id, E.code(), + E.displayText(), + E.message(), + E.what())); + } catch (const Poco::RuntimeException &E) { + poco_error(App_.logger(), fmt::format("Poco::RuntimeException thr_name={} thr_id={} code={} text={} msg={} what={}", + t_name, t_id, E.code(), + E.displayText(), + E.message(), + E.what())); + } catch (const Poco::JSON::JSONTemplateException &E) { + poco_error(App_.logger(), fmt::format("Poco::JSON::JSONTemplateException thr_name={} thr_id={} code={} text={} msg={} what={}", + t_name, t_id, E.code(), + E.displayText(), + E.message(), + E.what())); + } catch (const Poco::JSON::JSONException &E) { + poco_error(App_.logger(), fmt::format("Poco::JSON::JSONException thr_name={} thr_id={} code={} text={} msg={} what={}", + t_name, t_id, E.code(), + E.displayText(), + E.message(), + E.what())); + } catch (const Poco::ApplicationException &E) { + poco_error(App_.logger(), fmt::format("Poco::ApplicationException thr_name={} thr_id={} code={} text={} msg={} what={}", + t_name, t_id, E.code(), + E.displayText(), + E.message(), + E.what())); + } catch (const Poco::Exception &E) { + poco_error(App_.logger(), fmt::format("Poco::Exception thr_name={} thr_id={} code={} text={} msg={} what={}", + t_name, t_id, E.code(), + E.displayText(), + E.message(), + E.what())); + } catch (...) { + poco_error(App_.logger(), fmt::format("Poco:Generic thr_name={}",t_name, t_id)); + } + } + + inline void exception(const std::exception & E) override { + if(Poco::Thread::current()!= nullptr) { + t_name = Poco::Thread::current()->getName(); + t_id = Poco::Thread::current()->id(); + } else { + t_name = "startup_code"; + t_id = 0; + } + poco_warning(App_.logger(), fmt::format("std::exception in {}: {} thr_id={}", + t_name,E.what(), + t_id)); + } + + inline void exception() override { + if(Poco::Thread::current()!= nullptr) { + t_name = Poco::Thread::current()->getName(); + t_id = Poco::Thread::current()->id(); + } else { + t_name = "startup_code"; + t_id = 0; + } + poco_warning(App_.logger(), fmt::format("generic exception in {} thr_id={}", + t_name, t_id)); + } + private: + Poco::Util::Application &App_; + std::string t_name; + int t_id=0; + }; + +} \ No newline at end of file diff --git a/wait-for-postgres.sh b/wait-for-postgres.sh index 288d616..59ed58c 100755 --- a/wait-for-postgres.sh +++ b/wait-for-postgres.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # wait-for-postgres.sh set -e @@ -20,7 +20,7 @@ if [ "$1" = '/openwifi/owfms' -a "$(id -u)" = '0' ]; then if [ "$RUN_CHOWN" = 'true' ]; then chown -R "$OWFMS_USER": "$OWFMS_ROOT" "$OWFMS_CONFIG" fi - exec su-exec "$OWFMS_USER" "$@" + exec gosu "$OWFMS_USER" "$@" fi exec "$@"