diff --git a/.gitignore b/.gitignore index 54878dc7558ffb..4348244c5d569b 100644 --- a/.gitignore +++ b/.gitignore @@ -199,3 +199,6 @@ src/go/collectors/go.d.plugin/mocks/springboot2/.gradle/ src/go/collectors/go.d.plugin/mocks/tmp/* !src/go/collectors/go.d.plugin/mocks/tmp/.gitkeep src/go/collectors/go.d.plugin/vendor + +# ignore nsis installer +packaging/utils/netdata-installer.exe diff --git a/CMakeLists.txt b/CMakeLists.txt index a8a9105b5e5286..79ea95a606c9ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,6 +92,9 @@ set(OS_MACOS False) set(OS_WINDOWS False) set(ALLOW_PLATFORM_SENSITIVE_OPTIONS True) +set(NETDATA_RUNTIME_PREFIX "${CMAKE_INSTALL_PREFIX}") +set(BINDIR usr/sbin) + if("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") set(OS_MACOS True) find_library(IOKIT IOKit) @@ -111,6 +114,16 @@ elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") endif() elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "CYGWIN" OR "${CMAKE_SYSTEM_NAME}" STREQUAL "MSYS" OR "${CMAKE_SYSTEM_NAME}" STREQUAL "Windows") set(OS_WINDOWS True) + + if(NOT "${CMAKE_INSTALL_PREFIX}" STREQUAL "/opt/netdata") + message(FATAL_ERROR "CMAKE_INSTALL_PREFIX must be set to /opt/netdata, but it is set to ${CMAKE_INSTALL_PREFIX}") + endif() + + if(BUILD_FOR_PACKAGING) + set(NETDATA_RUNTIME_PREFIX "/") + endif() + + set(BINDIR usr/bin) add_definitions(-D_GNU_SOURCE) if($ENV{CLION_IDE}) @@ -1434,6 +1447,7 @@ elseif(OS_FREEBSD) elseif(OS_WINDOWS) list(APPEND NETDATA_FILES src/daemon/static_threads_windows.c + src/daemon/winsvc.cc ${WINDOWS_PLUGIN_FILES} ${INTERNAL_COLLECTORS_FILES} ) @@ -2279,7 +2293,7 @@ target_link_libraries(systemd-cat-native libnetdata) install(TARGETS systemd-cat-native COMPONENT netdata - DESTINATION usr/sbin) + DESTINATION "${BINDIR}") # # build log2journal @@ -2314,7 +2328,7 @@ if(PCRE2_FOUND) install(TARGETS log2journal COMPONENT netdata - DESTINATION usr/sbin) + DESTINATION "${BINDIR}") install(DIRECTORY src/collectors/log2journal/log2journal.d COMPONENT netdata @@ -2337,7 +2351,7 @@ target_link_libraries(netdatacli libnetdata) install(TARGETS netdatacli COMPONENT netdata - DESTINATION usr/sbin) + DESTINATION "${BINDIR}") # # Build go.d.plugin @@ -2354,17 +2368,16 @@ endif() # # Generate config file # - -if(NOT CMAKE_INSTALL_PREFIX STREQUAL "") - string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") +if(NOT NETDATA_RUNTIME_PREFIX STREQUAL "") + string(REGEX REPLACE "/$" "" NETDATA_RUNTIME_PREFIX "${NETDATA_RUNTIME_PREFIX}") endif() -set(CACHE_DIR "${CMAKE_INSTALL_PREFIX}/var/cache/netdata") -set(CONFIG_DIR "${CMAKE_INSTALL_PREFIX}/etc/netdata") -set(LIBCONFIG_DIR "${CMAKE_INSTALL_PREFIX}/usr/lib/netdata/conf.d") -set(LOG_DIR "${CMAKE_INSTALL_PREFIX}/var/log/netdata") -set(PLUGINS_DIR "${CMAKE_INSTALL_PREFIX}/usr/libexec/netdata/plugins.d") -set(VARLIB_DIR "${CMAKE_INSTALL_PREFIX}/var/lib/netdata") +set(CACHE_DIR "${NETDATA_RUNTIME_PREFIX}/var/cache/netdata") +set(CONFIG_DIR "${NETDATA_RUNTIME_PREFIX}/etc/netdata") +set(LIBCONFIG_DIR "${NETDATA_RUNTIME_PREFIX}/usr/lib/netdata/conf.d") +set(LOG_DIR "${NETDATA_RUNTIME_PREFIX}/var/log/netdata") +set(PLUGINS_DIR "${NETDATA_RUNTIME_PREFIX}/usr/libexec/netdata/plugins.d") +set(VARLIB_DIR "${NETDATA_RUNTIME_PREFIX}/var/lib/netdata") # A non-default value is only used when building Debian packages (/var/lib/netdata/www) if(NOT DEFINED WEB_DIR) @@ -2373,7 +2386,7 @@ else() string(REGEX REPLACE "^/" "" WEB_DIR "${WEB_DIR}") endif() set(WEB_DEST "${WEB_DIR}") -set(WEB_DIR "${CMAKE_INSTALL_PREFIX}/${WEB_DEST}") +set(WEB_DIR "${NETDATA_RUNTIME_PREFIX}/${WEB_DEST}") set(CONFIGURE_COMMAND "dummy-configure-command") @@ -2387,7 +2400,7 @@ configure_file(packaging/cmake/config.cmake.h.in config.h) # install # -install(TARGETS netdata COMPONENT netdata DESTINATION usr/sbin) +install(TARGETS netdata COMPONENT netdata DESTINATION "${BINDIR}") install(DIRECTORY COMPONENT netdata DESTINATION var/cache/netdata) install(DIRECTORY COMPONENT netdata DESTINATION var/log/netdata) @@ -2406,15 +2419,15 @@ install(DIRECTORY COMPONENT netdata DESTINATION usr/lib/netdata/conf.d/schema.d) install(DIRECTORY COMPONENT netdata DESTINATION usr/libexec/netdata/plugins.d) install(DIRECTORY COMPONENT netdata DESTINATION ${WEB_DEST}) -set(libsysdir_POST "${CMAKE_INSTALL_PREFIX}/usr/lib/netdata/system") -set(pkglibexecdir_POST "${CMAKE_INSTALL_PREFIX}/usr/libexec/netdata") -set(localstatedir_POST "${CMAKE_INSTALL_PREFIX}/var") -set(sbindir_POST "${CMAKE_INSTALL_PREFIX}/usr/sbin") -set(configdir_POST "${CMAKE_INSTALL_PREFIX}/etc/netdata") -set(libconfigdir_POST "${CMAKE_INSTALL_PREFIX}/usr/lib/netdata/conf.d") -set(cachedir_POST "${CMAKE_INSTALL_PREFIX}/var/cache/netdata") -set(registrydir_POST "${CMAKE_INSTALL_PREFIX}/var/lib/netdata/registry") -set(varlibdir_POST "${CMAKE_INSTALL_PREFIX}/var/lib/netdata") +set(libsysdir_POST "${NETDATA_RUNTIME_PREFIX}/usr/lib/netdata/system") +set(pkglibexecdir_POST "${NETDATA_RUNTIME_PREFIX}/usr/libexec/netdata") +set(localstatedir_POST "${NETDATA_RUNTIME_PREFIX}/var") +set(sbindir_POST "${NETDATA_RUNTIME_PREFIX}/${BINDIR}") +set(configdir_POST "${NETDATA_RUNTIME_PREFIX}/etc/netdata") +set(libconfigdir_POST "${NETDATA_RUNTIME_PREFIX}/usr/lib/netdata/conf.d") +set(cachedir_POST "${NETDATA_RUNTIME_PREFIX}/var/cache/netdata") +set(registrydir_POST "${NETDATA_RUNTIME_PREFIX}/var/lib/netdata/registry") +set(varlibdir_POST "${NETDATA_RUNTIME_PREFIX}/var/lib/netdata") set(netdata_user_POST "${NETDATA_USER}") # netdata-claim.sh @@ -2434,7 +2447,7 @@ configure_file(src/claim/netdata-claim.sh.in src/claim/netdata-claim.sh @ONLY) install(PROGRAMS ${CMAKE_BINARY_DIR}/src/claim/netdata-claim.sh COMPONENT netdata - DESTINATION usr/sbin) + DESTINATION "${BINDIR}") # # We don't check ENABLE_PLUGIN_CGROUP_NETWORK because rpm builds assume @@ -3026,4 +3039,71 @@ if(NOT OS_WINDOWS) DESTINATION ${WEB_DEST}/v0) endif() +if(OS_WINDOWS) + install(FILES /usr/bin/awk.exe + /usr/bin/bash.exe + /usr/bin/cat.exe + /usr/bin/chown.exe + /usr/bin/curl.exe + /usr/bin/env.exe + /usr/bin/grep.exe + /usr/bin/mkdir.exe + /usr/bin/openssl.exe + /usr/bin/rm.exe + /usr/bin/sed.exe + /usr/bin/sh.exe + /usr/bin/tail.exe + /usr/bin/tr.exe + /usr/bin/uuidgen.exe + /usr/bin/whoami.exe + DESTINATION "${BINDIR}") + + install(FILES /usr/bin/msys-2.0.dll + /usr/bin/msys-asn1-8.dll + /usr/bin/msys-brotlicommon-1.dll + /usr/bin/msys-brotlidec-1.dll + /usr/bin/msys-brotlienc-1.dll + /usr/bin/msys-com_err-1.dll + /usr/bin/msys-crypt-2.dll + /usr/bin/msys-crypto-3.dll + /usr/bin/msys-curl-4.dll + /usr/bin/msys-gcc_s-seh-1.dll + /usr/bin/msys-gmp-10.dll + /usr/bin/msys-gssapi-3.dll + /usr/bin/msys-hcrypto-4.dll + /usr/bin/msys-heimbase-1.dll + /usr/bin/msys-heimntlm-0.dll + /usr/bin/msys-hx509-5.dll + /usr/bin/msys-iconv-2.dll + /usr/bin/msys-idn2-0.dll + /usr/bin/msys-intl-8.dll + /usr/bin/msys-krb5-26.dll + /usr/bin/msys-lz4-1.dll + /usr/bin/msys-mpfr-6.dll + /usr/bin/msys-ncursesw6.dll + /usr/bin/msys-nghttp2-14.dll + /usr/bin/msys-pcre-1.dll + /usr/bin/msys-protobuf-32.dll + /usr/bin/msys-psl-5.dll + /usr/bin/msys-readline8.dll + /usr/bin/msys-roken-18.dll + /usr/bin/msys-sqlite3-0.dll + /usr/bin/msys-ssh2-1.dll + /usr/bin/msys-ssl-3.dll + /usr/bin/msys-stdc++-6.dll + /usr/bin/msys-unistring-5.dll + /usr/bin/msys-uuid-1.dll + /usr/bin/msys-uv-1.dll + /usr/bin/msys-wind-0.dll + /usr/bin/msys-z.dll + /usr/bin/msys-zstd-1.dll + DESTINATION "${BINDIR}") + + # Make bash & netdata happy + install(DIRECTORY DESTINATION tmp) + + # Make curl work with ssl + install(DIRECTORY /usr/ssl DESTINATION usr) +endif() + include(Packaging) diff --git a/packaging/utils/compile-on-windows.sh b/packaging/utils/compile-on-windows.sh index aa44771b8ef4dc..103052be497a40 100644 --- a/packaging/utils/compile-on-windows.sh +++ b/packaging/utils/compile-on-windows.sh @@ -16,7 +16,8 @@ install_dependencies() { msys/pcre2-devel mingw64/mingw-w64-x86_64-pcre2 ucrt64/mingw-w64-ucrt-x86_64-pcre2 \ msys/brotli-devel mingw64/mingw-w64-x86_64-brotli ucrt64/mingw-w64-ucrt-x86_64-brotli \ msys/ccache ucrt64/mingw-w64-ucrt-x86_64-ccache mingw64/mingw-w64-x86_64-ccache \ - mingw64/mingw-w64-x86_64-go ucrt64/mingw-w64-ucrt-x86_64-go + mingw64/mingw-w64-x86_64-go ucrt64/mingw-w64-ucrt-x86_64-go \ + mingw64/mingw-w64-x86_64-nsis } if [ "${1}" = "install" ] @@ -52,7 +53,9 @@ fi -G Ninja \ -DCMAKE_INSTALL_PREFIX="/opt/netdata" \ -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ - -DCMAKE_C_FLAGS="-O0 -ggdb -Wall -Wextra -Wno-char-subscripts -Wa,-mbig-obj -pipe -DNETDATA_INTERNAL_CHECKS=1 -D_FILE_OFFSET_BITS=64 -D__USE_MINGW_ANSI_STDIO=1" \ + -DCMAKE_C_FLAGS="-fstack-protector-all -O0 -ggdb -Wall -Wextra -Wno-char-subscripts -Wa,-mbig-obj -pipe -DNETDATA_INTERNAL_CHECKS=1 -D_FILE_OFFSET_BITS=64 -D__USE_MINGW_ANSI_STDIO=1" \ + -DBUILD_FOR_PACKAGING=On \ + -DUSE_MOLD=Off \ -DNETDATA_USER="${USER}" \ -DDEFAULT_FEATURE_STATE=Off \ -DENABLE_H2O=Off \ diff --git a/packaging/utils/find-dll-deps.sh b/packaging/utils/find-dll-deps.sh new file mode 100644 index 00000000000000..9f4fe384753d0b --- /dev/null +++ b/packaging/utils/find-dll-deps.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +if [ "$#" -lt 1 ]; then + echo "Usage: $0 ... " + exit 1 +fi + +results=() + +for arg in "$@"; do + while IFS= read -r line; do + results+=("$line") + done < <(ldd "$arg" | grep /usr/bin | awk '{ print $3 }') +done + +printf "%s\n" "${results[@]}" | sort | uniq diff --git a/packaging/utils/installer.nsi b/packaging/utils/installer.nsi index b78f52ffe2337e..10600f15ebf0a9 100644 --- a/packaging/utils/installer.nsi +++ b/packaging/utils/installer.nsi @@ -1,34 +1,58 @@ -Outfile "netdata-installer.exe" -InstallDir "C:\netdata" +!include "MUI2.nsh" +Name "Netdata" +Outfile "netdata-installer.exe" +InstallDir "$PROGRAMFILES\netdata" RequestExecutionLevel admin -Section - SetOutPath $INSTDIR - WriteUninstaller $INSTDIR\uninstaller.exe -SectionEnd +!define MUI_ABORTWARNING +!define MUI_UNABORTWARNING -Section "Install MSYS2 environment" - SetOutPath $TEMP +!insertmacro MUI_PAGE_WELCOME +!insertmacro MUI_PAGE_DIRECTORY +!insertmacro MUI_PAGE_INSTFILES +!insertmacro MUI_PAGE_FINISH - SetCompress off - File "C:\msys64\msys2-installer.exe" - nsExec::ExecToLog 'cmd.exe /C "$TEMP\msys2-installer.exe" in --confirm-command --accept-messages --root $INSTDIR' +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES +!insertmacro MUI_UNPAGE_FINISH - Delete "$TEMP\msys2-installer.exe" -SectionEnd - -Section "Install MSYS2 packages" - ExecWait '"$INSTDIR\usr\bin\bash.exe" -lc "pacman -S --noconfirm msys/libuv msys/protobuf"' -SectionEnd +!insertmacro MUI_LANGUAGE "English" Section "Install Netdata" - SetOutPath $INSTDIR\opt\netdata - + SetOutPath $INSTDIR SetCompress off + File /r "C:\msys64\opt\netdata\*.*" + + ClearErrors + ExecWait '"$SYSDIR\sc.exe" create Netdata binPath= "$INSTDIR\usr\bin\netdata.exe" start= delayed-auto' + IfErrors 0 +2 + DetailPrint "Warning: Failed to create Netdata service." + + ClearErrors + ExecWait '"$SYSDIR\sc.exe" description Netdata "Real-time system monitoring service"' + IfErrors 0 +2 + DetailPrint "Warning: Failed to add Netdata service description." + + ClearErrors + ExecWait '"$SYSDIR\sc.exe" start Netdata' + IfErrors 0 +2 + DetailPrint "Warning: Failed to start Netdata service." + + WriteUninstaller "$INSTDIR\Uninstall.exe" SectionEnd Section "Uninstall" - nsExec::ExecToLog 'cmd.exe /C "$INSTDIR\uninstall.exe" pr --confirm-command' + ClearErrors + ExecWait '"$SYSDIR\sc.exe" stop Netdata' + IfErrors 0 +2 + DetailPrint "Warning: Failed to stop Netdata service." + + ClearErrors + ExecWait '"$SYSDIR\sc.exe" delete Netdata' + IfErrors 0 +2 + DetailPrint "Warning: Failed to delete Netdata service." + + RMDir /r "$INSTDIR" SectionEnd diff --git a/src/daemon/analytics.c b/src/daemon/analytics.c index 33f6f357f68496..e9abf11c88a910 100644 --- a/src/daemon/analytics.c +++ b/src/daemon/analytics.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-or-later +#include "analytics.h" #include "common.h" #include "buildinfo.h" @@ -470,8 +471,6 @@ void analytics_alarms(void) */ void analytics_misc(void) { - spinlock_init(&analytics_data.spinlock); - #ifdef ENABLE_ACLK analytics_set_data(&analytics_data.netdata_host_cloud_available, "true"); analytics_set_data_str(&analytics_data.netdata_host_aclk_implementation, "Next Generation"); @@ -1081,3 +1080,8 @@ void analytics_statistic_send(const analytics_statistic_t *statistic) { freez(command_to_run); } + +void analytics_init(void) +{ + spinlock_init(&analytics_data.spinlock); +} diff --git a/src/daemon/analytics.h b/src/daemon/analytics.h index 501eb7b555f4ae..747cf6070ef3a7 100644 --- a/src/daemon/analytics.h +++ b/src/daemon/analytics.h @@ -86,6 +86,7 @@ void analytics_log_dashboard(void); void analytics_gather_mutable_meta_data(void); void analytics_report_oom_score(long long int score); void get_system_timezone(void); +void analytics_init(void); typedef struct { const char *action; diff --git a/src/daemon/main.c b/src/daemon/main.c index 2678d955c3ccaa..75fa13356e1794 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -496,6 +496,10 @@ void netdata_cleanup_and_exit(int ret, const char *action, const char *action_re watcher_shutdown_end(); watcher_thread_stop(); +#ifdef OS_WINDOWS + return; +#endif + #ifdef ENABLE_SENTRY if (ret) abort(); @@ -1460,7 +1464,11 @@ int unittest_prepare_rrd(char **user) { return 0; } -int main(int argc, char **argv) { +int netdata_main(int argc, char **argv) +{ + analytics_init(); + string_init(); + // initialize the system clocks clocks_init(); netdata_start_time = now_realtime_sec(); @@ -1471,11 +1479,16 @@ int main(int argc, char **argv) { int i; int config_loaded = 0; - int dont_fork = 0; bool close_open_fds = true; size_t default_stacksize; char *user = NULL; +#ifdef OS_WINDOWS + int dont_fork = 1; +#else + int dont_fork = 0; +#endif + static_threads = static_threads_get(); netdata_ready = false; @@ -2214,7 +2227,10 @@ int main(int argc, char **argv) { // fork the spawn server delta_startup_time("fork the spawn server"); + +#ifndef OS_WINDOWS spawn_init(); +#endif /* * Libuv uv_spawn() uses SIGCHLD internally: @@ -2357,22 +2373,21 @@ int main(int argc, char **argv) { } #endif - // ------------------------------------------------------------------------ - // initialize WebRTC - webrtc_initialize(); - // ------------------------------------------------------------------------ - // unblock signals - signals_unblock(); - // ------------------------------------------------------------------------ - // Handle signals + return 10; +} - signals_handle(); +#ifndef OS_WINDOWS +int main(int argc, char *argv[]) +{ + int rc = netdata_main(argc, argv); + if (rc != 10) + return rc; - // should never reach this point - // but we need it for rpmlint #2752 + signals_handle(); return 1; } +#endif diff --git a/src/daemon/static_threads.c b/src/daemon/static_threads.c index 2667c0fb5cee13..1a5f82f7f2dfe2 100644 --- a/src/daemon/static_threads.c +++ b/src/daemon/static_threads.c @@ -30,7 +30,7 @@ const struct netdata_static_thread static_threads_common[] = { .name = "HEALTH", .config_section = NULL, .config_name = NULL, - .enabled = 1, + .enabled = 0, .thread = NULL, .init_routine = NULL, .start_routine = health_main @@ -70,7 +70,11 @@ const struct netdata_static_thread static_threads_common[] = { .name = "PLUGINSD", .config_section = NULL, .config_name = NULL, +#ifdef OS_WINDOWS + .enabled = 0, +#else .enabled = 1, +#endif .thread = NULL, .init_routine = NULL, .start_routine = pluginsd_main diff --git a/src/daemon/winsvc.cc b/src/daemon/winsvc.cc new file mode 100644 index 00000000000000..cdf503256c88fe --- /dev/null +++ b/src/daemon/winsvc.cc @@ -0,0 +1,252 @@ +extern "C" { + +#include "daemon.h" +#include "libnetdata/libnetdata.h" + +int netdata_main(int argc, char *argv[]); +void signals_handle(void); + +} + +#include + +__attribute__((format(printf, 1, 2))) +static void netdata_service_log(const char *fmt, ...) +{ + char path[FILENAME_MAX + 1]; + snprintfz(path, FILENAME_MAX, "%s/service.log", LOG_DIR); + + FILE *fp = fopen(path, "a"); + if (fp == NULL) { + return; + } + + SYSTEMTIME time; + GetSystemTime(&time); + fprintf(fp, "%d:%d:%d - ", time.wHour, time.wMinute, time.wSecond); + + va_list args; + va_start(args, fmt); + vfprintf(fp, fmt, args); + va_end(args); + + fprintf(fp, "\n"); + + fflush(fp); + fclose(fp); +} + +static SERVICE_STATUS_HANDLE svc_status_handle = nullptr; +static SERVICE_STATUS svc_status = {}; + +static HANDLE svc_stop_event_handle = nullptr; + +static ND_THREAD *cleanup_thread = nullptr; + +static bool ReportSvcStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint, DWORD dwControlsAccepted) +{ + static DWORD dwCheckPoint = 1; + svc_status.dwCurrentState = dwCurrentState; + svc_status.dwWin32ExitCode = dwWin32ExitCode; + svc_status.dwWaitHint = dwWaitHint; + svc_status.dwControlsAccepted = dwControlsAccepted; + + if (dwCurrentState == SERVICE_RUNNING || dwCurrentState == SERVICE_STOPPED) + { + svc_status.dwCheckPoint = 0; + } + else + { + svc_status.dwCheckPoint = dwCheckPoint++; + } + + if (!SetServiceStatus(svc_status_handle, &svc_status)) { + netdata_service_log("@ReportSvcStatus: SetServiceStatusFailed (%d)", GetLastError()); + return false; + } + + return true; +} + +static HANDLE CreateEventHandle(const char *msg) +{ + HANDLE h = CreateEvent(NULL, TRUE, FALSE, NULL); + + if (!h) + { + netdata_service_log(msg); + + if (!ReportSvcStatus(SERVICE_STOPPED, GetLastError(), 1000, 0)) + { + netdata_service_log("Failed to set service status to stopped."); + } + + return NULL; + } + + return h; +} + +static void *call_netdata_cleanup(void *arg) +{ + UNUSED(arg); + + // Wait until we have to stop the service + netdata_service_log("Cleanup thread waiting for stop event..."); + WaitForSingleObject(svc_stop_event_handle, INFINITE); + + // Stop the agent + netdata_service_log("Running netdata cleanup..."); + netdata_cleanup_and_exit(0, NULL, NULL, NULL); + + // Close event handle + netdata_service_log("Closing stop event handle..."); + CloseHandle(svc_stop_event_handle); + + // Set status to stopped + netdata_service_log("Reporting the service as stopped..."); + ReportSvcStatus(SERVICE_STOPPED, 0, 0, 0); + + return nullptr; +} + +static void WINAPI ServiceControlHandler(DWORD controlCode) +{ + switch (controlCode) + { + case SERVICE_CONTROL_STOP: + { + if (svc_status.dwCurrentState != SERVICE_RUNNING) + return; + + // Set service status to stop-pending + netdata_service_log("Setting service status to stop-pending..."); + if (!ReportSvcStatus(SERVICE_STOP_PENDING, 0, 5000, 0)) + return; + + // Create cleanup thread + netdata_service_log("Creating cleanup thread..."); + char tag[NETDATA_THREAD_TAG_MAX + 1]; + snprintfz(tag, NETDATA_THREAD_TAG_MAX, "%s", "CLEANUP"); + cleanup_thread = nd_thread_create(tag, NETDATA_THREAD_OPTION_JOINABLE, call_netdata_cleanup, NULL); + + // Signal the stop request + netdata_service_log("Signalling the cleanup thread..."); + SetEvent(svc_stop_event_handle); + break; + } + case SERVICE_CONTROL_INTERROGATE: + { + ReportSvcStatus(svc_status.dwCurrentState, svc_status.dwWin32ExitCode, svc_status.dwWaitHint, svc_status.dwControlsAccepted); + break; + } + default: + break; + } +} + +void WINAPI ServiceMain(DWORD argc, LPSTR* argv) +{ + UNUSED(argc); + UNUSED(argv); + + // Create service status handle + netdata_service_log("Creating service status handle..."); + svc_status_handle = RegisterServiceCtrlHandler("Netdata", ServiceControlHandler); + if (!svc_status_handle) + { + netdata_service_log("@ServiceMain() - RegisterServiceCtrlHandler() failed..."); + return; + } + + // Set status to start-pending + netdata_service_log("Setting service status to start-pending..."); + svc_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + svc_status.dwServiceSpecificExitCode = 0; + svc_status.dwCheckPoint = 0; + if (!ReportSvcStatus(SERVICE_START_PENDING, 0, 5000, 0)) + { + netdata_service_log("Failed to set service status to start pending."); + return; + } + + // Create stop service event handle + netdata_service_log("Creating stop service event handle..."); + svc_stop_event_handle = CreateEventHandle("Failed to create stop event handle"); + if (!svc_stop_event_handle) + return; + + // Set status to running + netdata_service_log("Setting service status to running..."); + if (!ReportSvcStatus(SERVICE_RUNNING, 0, 5000, SERVICE_ACCEPT_STOP)) + { + netdata_service_log("Failed to set service status to running."); + return; + } + + // Run the agent + netdata_service_log("Running the agent..."); + netdata_main(argc, argv); + + netdata_service_log("Agent has been started..."); +} + +static bool update_path() { + const char *old_path = getenv("PATH"); + + if (!old_path) { + if (setenv("PATH", "/usr/bin", 1) != 0) { + netdata_service_log("Failed to set PATH to /usr/bin"); + return false; + } + + return true; + } + + size_t new_path_length = strlen(old_path) + strlen("/usr/bin") + 2; + char *new_path = (char *) callocz(new_path_length, sizeof(char)); + snprintfz(new_path, new_path_length, "/usr/bin:%s", old_path); + + if (setenv("PATH", new_path, 1) != 0) { + netdata_service_log("Failed to add /usr/bin to PATH"); + freez(new_path); + return false; + } + + freez(new_path); + return true; +} + +int main(int argc, char *argv[]) +{ + bool tty = isatty(fileno(stdout)) == 1; + + if (!update_path()) { + return 1; + } + + if (tty) + { + int rc = netdata_main(argc, argv); + if (rc != 10) + return rc; + + signals_handle(); + return 1; + } + else + { + SERVICE_TABLE_ENTRY serviceTable[] = { + { strdupz("Netdata"), ServiceMain }, + { nullptr, nullptr } + }; + + if (!StartServiceCtrlDispatcher(serviceTable)) + { + netdata_service_log("@main() - StartServiceCtrlDispatcher() failed..."); + return 1; + } + + return 0; + } +} diff --git a/src/database/rrdhost.c b/src/database/rrdhost.c index 6bf2c25518a6be..dd5f2a43e221db 100644 --- a/src/database/rrdhost.c +++ b/src/database/rrdhost.c @@ -935,7 +935,13 @@ void dbengine_init(char *hostname) { config_set_number(CONFIG_SECTION_DB, "dbengine tier 0 disk space MB", default_multidb_disk_quota_mb); } +#ifdef OS_WINDOWS + // FIXME: for whatever reason joining the initialization threads + // fails on Windows. + bool parallel_initialization = false; +#else bool parallel_initialization = (storage_tiers <= (size_t)get_netdata_cpus()) ? true : false; +#endif struct dbengine_initialization tiers_init[RRD_STORAGE_TIERS] = {}; diff --git a/src/libnetdata/clocks/clocks.c b/src/libnetdata/clocks/clocks.c index e1a3e64cb3610b..a6816896238583 100644 --- a/src/libnetdata/clocks/clocks.c +++ b/src/libnetdata/clocks/clocks.c @@ -368,6 +368,35 @@ usec_t heartbeat_next(heartbeat_t *hb, usec_t tick) { return dt; } +#ifdef OS_WINDOWS + +#include "windows.h" + +void sleep_usec_with_now(usec_t usec, usec_t started_ut) +{ + if (!started_ut) + started_ut = now_realtime_usec(); + + usec_t end_ut = started_ut + usec; + usec_t remaining_ut = usec; + + timeBeginPeriod(1); + + while (remaining_ut >= 1000) + { + DWORD sleep_ms = (DWORD) (remaining_ut / USEC_PER_MS); + Sleep(sleep_ms); + + usec_t now_ut = now_realtime_usec(); + if (now_ut >= end_ut) + break; + + remaining_ut = end_ut - now_ut; + } + + timeEndPeriod(1); +} +#else void sleep_usec_with_now(usec_t usec, usec_t started_ut) { // we expect microseconds (1.000.000 per second) // but timespec is nanoseconds (1.000.000.000 per second) @@ -411,6 +440,7 @@ void sleep_usec_with_now(usec_t usec, usec_t started_ut) { } } } +#endif static inline collected_number uptime_from_boottime(void) { #ifdef CLOCK_BOOTTIME_IS_AVAILABLE diff --git a/src/libnetdata/libnetdata.h b/src/libnetdata/libnetdata.h index 859f54cc33d32c..1c72e54106883c 100644 --- a/src/libnetdata/libnetdata.h +++ b/src/libnetdata/libnetdata.h @@ -451,7 +451,12 @@ typedef enum { } OPEN_FD_EXCLUDE; void for_each_open_fd(OPEN_FD_ACTION action, OPEN_FD_EXCLUDE excluded_fds); +#ifdef OS_WINDOWS +void netdata_cleanup_and_exit(int ret, const char *action, const char *action_result, const char *action_data); +#else void netdata_cleanup_and_exit(int ret, const char *action, const char *action_result, const char *action_data) NORETURN; +#endif + extern char *netdata_configured_host_prefix; #include "os/os.h" diff --git a/src/libnetdata/locks/locks.c b/src/libnetdata/locks/locks.c index d01ee29f143851..424b86ce9526f5 100644 --- a/src/libnetdata/locks/locks.c +++ b/src/libnetdata/locks/locks.c @@ -224,14 +224,24 @@ int __netdata_rwlock_trywrlock(netdata_rwlock_t *rwlock) { // spinlock implementation // https://www.youtube.com/watch?v=rmGJc9PXpuE&t=41s -void spinlock_init(SPINLOCK *spinlock) { +#ifdef SPINLOCK_IMPL_WITH_MUTEX +void spinlock_init(SPINLOCK *spinlock) +{ + netdata_mutex_init(&spinlock->inner); +} +#else +void spinlock_init(SPINLOCK *spinlock) +{ memset(spinlock, 0, sizeof(SPINLOCK)); } +#endif -static inline void spinlock_lock_internal(SPINLOCK *spinlock) { -#ifdef NETDATA_INTERNAL_CHECKS +#ifndef SPINLOCK_IMPL_WITH_MUTEX +static inline void spinlock_lock_internal(SPINLOCK *spinlock) +{ + #ifdef NETDATA_INTERNAL_CHECKS size_t spins = 0; -#endif + #endif for(int i = 1; __atomic_load_n(&spinlock->locked, __ATOMIC_RELAXED) || @@ -239,9 +249,10 @@ static inline void spinlock_lock_internal(SPINLOCK *spinlock) { ; i++ ) { -#ifdef NETDATA_INTERNAL_CHECKS + #ifdef NETDATA_INTERNAL_CHECKS spins++; -#endif + #endif + if(unlikely(i == 8)) { i = 0; tinysleep(); @@ -250,23 +261,29 @@ static inline void spinlock_lock_internal(SPINLOCK *spinlock) { // we have the lock -#ifdef NETDATA_INTERNAL_CHECKS + #ifdef NETDATA_INTERNAL_CHECKS spinlock->spins += spins; spinlock->locker_pid = gettid_cached(); -#endif + #endif nd_thread_spinlock_locked(); } +#endif // SPINLOCK_IMPL_WITH_MUTEX -static inline void spinlock_unlock_internal(SPINLOCK *spinlock) { -#ifdef NETDATA_INTERNAL_CHECKS +#ifndef SPINLOCK_IMPL_WITH_MUTEX +static inline void spinlock_unlock_internal(SPINLOCK *spinlock) +{ + #ifdef NETDATA_INTERNAL_CHECKS spinlock->locker_pid = 0; -#endif + #endif + __atomic_clear(&spinlock->locked, __ATOMIC_RELEASE); nd_thread_spinlock_unlocked(); } +#endif // SPINLOCK_IMPL_WITH_MUTEX +#ifndef SPINLOCK_IMPL_WITH_MUTEX static inline bool spinlock_trylock_internal(SPINLOCK *spinlock) { if(!__atomic_load_n(&spinlock->locked, __ATOMIC_RELAXED) && !__atomic_test_and_set(&spinlock->locked, __ATOMIC_ACQUIRE)) { @@ -277,36 +294,79 @@ static inline bool spinlock_trylock_internal(SPINLOCK *spinlock) { return false; } +#endif // SPINLOCK_IMPL_WITH_MUTEX +#ifdef SPINLOCK_IMPL_WITH_MUTEX +void spinlock_lock(SPINLOCK *spinlock) +{ + netdata_mutex_lock(&spinlock->inner); +} +#else void spinlock_lock(SPINLOCK *spinlock) { spinlock_lock_internal(spinlock); } +#endif +#ifdef SPINLOCK_IMPL_WITH_MUTEX +void spinlock_unlock(SPINLOCK *spinlock) +{ + netdata_mutex_unlock(&spinlock->inner); +} +#else void spinlock_unlock(SPINLOCK *spinlock) { spinlock_unlock_internal(spinlock); } +#endif +#ifdef SPINLOCK_IMPL_WITH_MUTEX +bool spinlock_trylock(SPINLOCK *spinlock) +{ + return netdata_mutex_trylock(&spinlock->inner) == 0; +} +#else bool spinlock_trylock(SPINLOCK *spinlock) { return spinlock_trylock_internal(spinlock); } +#endif +#ifdef SPINLOCK_IMPL_WITH_MUTEX +void spinlock_lock_cancelable(SPINLOCK *spinlock) +{ + netdata_mutex_lock(&spinlock->inner); +} +#else void spinlock_lock_cancelable(SPINLOCK *spinlock) { spinlock_lock_internal(spinlock); } +#endif +#ifdef SPINLOCK_IMPL_WITH_MUTEX +void spinlock_unlock_cancelable(SPINLOCK *spinlock) +{ + netdata_mutex_unlock(&spinlock->inner); +} +#else void spinlock_unlock_cancelable(SPINLOCK *spinlock) { spinlock_unlock_internal(spinlock); } +#endif +#ifdef SPINLOCK_IMPL_WITH_MUTEX +bool spinlock_trylock_cancelable(SPINLOCK *spinlock) +{ + return netdata_mutex_trylock(&spinlock->inner) == 0; +} +#else bool spinlock_trylock_cancelable(SPINLOCK *spinlock) { return spinlock_trylock_internal(spinlock); } +#endif // ---------------------------------------------------------------------------- // rw_spinlock implementation diff --git a/src/libnetdata/locks/locks.h b/src/libnetdata/locks/locks.h index d3873c295204af..c05c65fe2d5e4a 100644 --- a/src/libnetdata/locks/locks.h +++ b/src/libnetdata/locks/locks.h @@ -6,19 +6,34 @@ #include "../libnetdata.h" #include "../clocks/clocks.h" +// #ifdef OS_WINDOWS +// #define SPINLOCK_IMPL_WITH_MUTEX +// #endif + typedef pthread_mutex_t netdata_mutex_t; #define NETDATA_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER -typedef struct netdata_spinlock { - bool locked; -#ifdef NETDATA_INTERNAL_CHECKS - pid_t locker_pid; - size_t spins; +#ifdef SPINLOCK_IMPL_WITH_MUTEX + typedef struct netdata_spinlock + { + netdata_mutex_t inner; + } SPINLOCK; +#else + typedef struct netdata_spinlock + { + bool locked; + #ifdef NETDATA_INTERNAL_CHECKS + pid_t locker_pid; + size_t spins; + #endif + } SPINLOCK; #endif -} SPINLOCK; -#define NETDATA_SPINLOCK_INITIALIZER \ - { .locked = false } +#ifdef SPINLOCK_IMPL_WITH_MUTEX +#define NETDATA_SPINLOCK_INITIALIZER { .inner = PTHREAD_MUTEX_INITIALIZER } +#else +#define NETDATA_SPINLOCK_INITIALIZER { .locked = false } +#endif void spinlock_init(SPINLOCK *spinlock); void spinlock_lock(SPINLOCK *spinlock); diff --git a/src/libnetdata/string/string.c b/src/libnetdata/string/string.c index 94c11f4b9a2eb8..257a3cc4b91fbf 100644 --- a/src/libnetdata/string/string.c +++ b/src/libnetdata/string/string.c @@ -702,3 +702,8 @@ int string_unittest(size_t entries) { fprintf(stderr, "\n%zu errors found\n", errors); return errors ? 1 : 0; } + +void string_init(void) { + for (size_t i = 0; i != STRING_PARTITIONS; i++) + rw_spinlock_init(&string_base[i].spinlock); +} diff --git a/src/libnetdata/string/string.h b/src/libnetdata/string/string.h index f2ff9666ca548d..c44696be2561b7 100644 --- a/src/libnetdata/string/string.h +++ b/src/libnetdata/string/string.h @@ -34,4 +34,6 @@ void string_statistics(size_t *inserts, size_t *deletes, size_t *searches, size_ int string_unittest(size_t entries); +void string_init(void); + #endif diff --git a/src/libnetdata/threads/threads.c b/src/libnetdata/threads/threads.c index 0e12d173ec67b5..36c63f4e0fec68 100644 --- a/src/libnetdata/threads/threads.c +++ b/src/libnetdata/threads/threads.c @@ -418,12 +418,14 @@ bool nd_thread_signaled_to_cancel(void) { // ---------------------------------------------------------------------------- // nd_thread_join -void nd_thread_join(ND_THREAD *nti) { - if(!nti) return; +int nd_thread_join(ND_THREAD *nti) { + if(!nti) + return ESRCH; int ret = pthread_join(nti->thread, NULL); - if(ret != 0) - nd_log(NDLS_DAEMON, NDLP_WARNING, "cannot join thread. pthread_join() failed with code %d.", ret); + if(ret != 0) { + nd_log(NDLS_DAEMON, NDLP_WARNING, "cannot join thread. pthread_join() failed with code %d. (tag=%s)", ret, nti->tag); + } else { nd_thread_status_set(nti, NETDATA_THREAD_STATUS_JOINED); @@ -434,4 +436,6 @@ void nd_thread_join(ND_THREAD *nti) { freez(nti); } + + return ret; } diff --git a/src/libnetdata/threads/threads.h b/src/libnetdata/threads/threads.h index a7204e2a2ffbe4..0b54a5fc0b634c 100644 --- a/src/libnetdata/threads/threads.h +++ b/src/libnetdata/threads/threads.h @@ -70,7 +70,7 @@ void netdata_threads_init_after_fork(size_t stacksize); void netdata_threads_init_for_external_plugins(size_t stacksize); ND_THREAD *nd_thread_create(const char *tag, NETDATA_THREAD_OPTIONS options, void *(*start_routine) (void *), void *arg); -void nd_thread_join(ND_THREAD * nti); +int nd_thread_join(ND_THREAD * nti); ND_THREAD *nd_thread_self(void); bool nd_thread_is_me(ND_THREAD *nti);