@cryptotaxi247 / netdata-1 / commits / e99da8b64

Run the agent as a Windows service. (#17766)

* Run the agent as a Windows service. This commit contains the boilerplate code for running the agent as a Windows service. We start the agent's main as a separate thread, although this is not strictly required based on my experiments. We need similar logic for calling netdata's exit function when someone wants to stop the agent. However, at this point we need to resolve the issue of gaps when running the agent as a service. It seems that sleeping for one second with `sleep(1)`, actually sleeps for 2 to 4 seconds on my setup. Once we resolve this, the work that remains concerns packaging: ie. installing the binaries at the proper places so that the relevant DLLs are found. To test this PR you need to: - Build the agent: ./packaging/utils/compile-on-windows.sh - Install the files: `ninja -C build/ install` - Copy the main binary: `cp ./build/netdata /usr/bin/` - (Only once) Create the netdata service: `sc.exe config Netdata binPath="C:\msys64\usr\bin\netdata"` - Start the service: `sc.exe start Netdata` A couple notes: - The health and the spawn client have been disabled for the time being. They will be re-enabled once we finish the agent-as-service issue and the packaging. - Last time I checked, the agent crashes after a while when using dbengine. In order to have something that works correctly, you should specify memory-mode ram in your netdata.conf. * Add windows version for sleep_usec_with_now * Split install prefix from runtime prefix These paths are always the same for non-Windows systems. On Windows, RFS is the top-level installation path. With the current setup, Netdata will be installed at C:\msys64\opt\netdata at packaging time. However, the layout of the application means that when the agent starts, it'll look as if everything was installed at /. * Do not use mold linker on Windows. * Use modern UI for installer. * Make the service delayed-auto * Use mutexes instead of spinlocks. * Update service handling logic. * Add proper ifdefs for spinlock implementation. * Initialize analytics spinlock * Add a macro to build the agent as regular cli tool. * Add makensis dependency * Let installer know it's installing Netdata. * Disable pluginsd on Windows When pluginsd is enabled, the agent freezes approximately 20% of the time during startup. * Add service description. * Return pthread_join result * Print tag when we fail to join a thread. * Do not use mutexes instead of spinlocks. * Assorted changes to service/main code. * Rework service functions. With the current implementation we are not getting any MUTEX_LOCK errors and thread joining succeeds. The only case where joining fails is the parallel initialization of dbengine threads, which we can easily avoid by serializing the initialization step. * Rework main functions This will allow someone to run the agent either as a service or as a command-line tool. * Change runtime prefix only when building for packaging. * Install binaries and dlls. * Make netdata claiming through UI work correctly. * Fix netdata path

vkalintiris committed Jul 2, 2024 at 12:19 UTC e99da8b64b1588a4ccf19f89953ee83c49584bb7
19 files changed +615 -86
.gitignore
+3
@@ -199,3 +199,6 @@ src/go/collectors/go.d.plugin/mocks/springboot2/.gradle/
199 src/go/collectors/go.d.plugin/mocks/tmp/*
200 !src/go/collectors/go.d.plugin/mocks/tmp/.gitkeep
201 src/go/collectors/go.d.plugin/vendor
202 +
203 +# ignore nsis installer
204 +packaging/utils/netdata-installer.exe
CMakeLists.txt
+104 -24
@@ -92,6 +92,9 @@ set(OS_MACOS False)
92 set(OS_WINDOWS False)
93 set(ALLOW_PLATFORM_SENSITIVE_OPTIONS True)
94
95 +set(NETDATA_RUNTIME_PREFIX "${CMAKE_INSTALL_PREFIX}")
96 +set(BINDIR usr/sbin)
97 +
98 if("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")
99 set(OS_MACOS True)
100 find_library(IOKIT IOKit)
@@ -111,6 +114,16 @@ elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
114 endif()
115 elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "CYGWIN" OR "${CMAKE_SYSTEM_NAME}" STREQUAL "MSYS" OR "${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")
116 set(OS_WINDOWS True)
117 +
118 + if(NOT "${CMAKE_INSTALL_PREFIX}" STREQUAL "/opt/netdata")
119 + message(FATAL_ERROR "CMAKE_INSTALL_PREFIX must be set to /opt/netdata, but it is set to ${CMAKE_INSTALL_PREFIX}")
120 + endif()
121 +
122 + if(BUILD_FOR_PACKAGING)
123 + set(NETDATA_RUNTIME_PREFIX "/")
124 + endif()
125 +
126 + set(BINDIR usr/bin)
127 add_definitions(-D_GNU_SOURCE)
128
129 if($ENV{CLION_IDE})
@@ -1434,6 +1447,7 @@ elseif(OS_FREEBSD)
1447 elseif(OS_WINDOWS)
1448 list(APPEND NETDATA_FILES
1449 src/daemon/static_threads_windows.c
1450 + src/daemon/winsvc.cc
1451 ${WINDOWS_PLUGIN_FILES}
1452 ${INTERNAL_COLLECTORS_FILES}
1453 )
@@ -2279,7 +2293,7 @@ target_link_libraries(systemd-cat-native libnetdata)
2293
2294 install(TARGETS systemd-cat-native
2295 COMPONENT netdata
2282 - DESTINATION usr/sbin)
2296 + DESTINATION "${BINDIR}")
2297
2298 #
2299 # build log2journal
@@ -2314,7 +2328,7 @@ if(PCRE2_FOUND)
2328
2329 install(TARGETS log2journal
2330 COMPONENT netdata
2317 - DESTINATION usr/sbin)
2331 + DESTINATION "${BINDIR}")
2332
2333 install(DIRECTORY src/collectors/log2journal/log2journal.d
2334 COMPONENT netdata
@@ -2337,7 +2351,7 @@ target_link_libraries(netdatacli libnetdata)
2351
2352 install(TARGETS netdatacli
2353 COMPONENT netdata
2340 - DESTINATION usr/sbin)
2354 + DESTINATION "${BINDIR}")
2355
2356 #
2357 # Build go.d.plugin
@@ -2354,17 +2368,16 @@ endif()
2368 #
2369 # Generate config file
2370 #
2357 -
2358 -if(NOT CMAKE_INSTALL_PREFIX STREQUAL "")
2359 - string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}")
2371 +if(NOT NETDATA_RUNTIME_PREFIX STREQUAL "")
2372 + string(REGEX REPLACE "/$" "" NETDATA_RUNTIME_PREFIX "${NETDATA_RUNTIME_PREFIX}")
2373 endif()
2374
2362 -set(CACHE_DIR "${CMAKE_INSTALL_PREFIX}/var/cache/netdata")
2363 -set(CONFIG_DIR "${CMAKE_INSTALL_PREFIX}/etc/netdata")
2364 -set(LIBCONFIG_DIR "${CMAKE_INSTALL_PREFIX}/usr/lib/netdata/conf.d")
2365 -set(LOG_DIR "${CMAKE_INSTALL_PREFIX}/var/log/netdata")
2366 -set(PLUGINS_DIR "${CMAKE_INSTALL_PREFIX}/usr/libexec/netdata/plugins.d")
2367 -set(VARLIB_DIR "${CMAKE_INSTALL_PREFIX}/var/lib/netdata")
2375 +set(CACHE_DIR "${NETDATA_RUNTIME_PREFIX}/var/cache/netdata")
2376 +set(CONFIG_DIR "${NETDATA_RUNTIME_PREFIX}/etc/netdata")
2377 +set(LIBCONFIG_DIR "${NETDATA_RUNTIME_PREFIX}/usr/lib/netdata/conf.d")
2378 +set(LOG_DIR "${NETDATA_RUNTIME_PREFIX}/var/log/netdata")
2379 +set(PLUGINS_DIR "${NETDATA_RUNTIME_PREFIX}/usr/libexec/netdata/plugins.d")
2380 +set(VARLIB_DIR "${NETDATA_RUNTIME_PREFIX}/var/lib/netdata")
2381
2382 # A non-default value is only used when building Debian packages (/var/lib/netdata/www)
2383 if(NOT DEFINED WEB_DIR)
@@ -2373,7 +2386,7 @@ else()
2386 string(REGEX REPLACE "^/" "" WEB_DIR "${WEB_DIR}")
2387 endif()
2388 set(WEB_DEST "${WEB_DIR}")
2376 -set(WEB_DIR "${CMAKE_INSTALL_PREFIX}/${WEB_DEST}")
2389 +set(WEB_DIR "${NETDATA_RUNTIME_PREFIX}/${WEB_DEST}")
2390
2391
2392 set(CONFIGURE_COMMAND "dummy-configure-command")
@@ -2387,7 +2400,7 @@ configure_file(packaging/cmake/config.cmake.h.in config.h)
2400 # install
2401 #
2402
2390 -install(TARGETS netdata COMPONENT netdata DESTINATION usr/sbin)
2403 +install(TARGETS netdata COMPONENT netdata DESTINATION "${BINDIR}")
2404
2405 install(DIRECTORY COMPONENT netdata DESTINATION var/cache/netdata)
2406 install(DIRECTORY COMPONENT netdata DESTINATION var/log/netdata)
@@ -2406,15 +2419,15 @@ install(DIRECTORY COMPONENT netdata DESTINATION usr/lib/netdata/conf.d/schema.d)
2419 install(DIRECTORY COMPONENT netdata DESTINATION usr/libexec/netdata/plugins.d)
2420 install(DIRECTORY COMPONENT netdata DESTINATION ${WEB_DEST})
2421
2409 -set(libsysdir_POST "${CMAKE_INSTALL_PREFIX}/usr/lib/netdata/system")
2410 -set(pkglibexecdir_POST "${CMAKE_INSTALL_PREFIX}/usr/libexec/netdata")
2411 -set(localstatedir_POST "${CMAKE_INSTALL_PREFIX}/var")
2412 -set(sbindir_POST "${CMAKE_INSTALL_PREFIX}/usr/sbin")
2413 -set(configdir_POST "${CMAKE_INSTALL_PREFIX}/etc/netdata")
2414 -set(libconfigdir_POST "${CMAKE_INSTALL_PREFIX}/usr/lib/netdata/conf.d")
2415 -set(cachedir_POST "${CMAKE_INSTALL_PREFIX}/var/cache/netdata")
2416 -set(registrydir_POST "${CMAKE_INSTALL_PREFIX}/var/lib/netdata/registry")
2417 -set(varlibdir_POST "${CMAKE_INSTALL_PREFIX}/var/lib/netdata")
2422 +set(libsysdir_POST "${NETDATA_RUNTIME_PREFIX}/usr/lib/netdata/system")
2423 +set(pkglibexecdir_POST "${NETDATA_RUNTIME_PREFIX}/usr/libexec/netdata")
2424 +set(localstatedir_POST "${NETDATA_RUNTIME_PREFIX}/var")
2425 +set(sbindir_POST "${NETDATA_RUNTIME_PREFIX}/${BINDIR}")
2426 +set(configdir_POST "${NETDATA_RUNTIME_PREFIX}/etc/netdata")
2427 +set(libconfigdir_POST "${NETDATA_RUNTIME_PREFIX}/usr/lib/netdata/conf.d")
2428 +set(cachedir_POST "${NETDATA_RUNTIME_PREFIX}/var/cache/netdata")
2429 +set(registrydir_POST "${NETDATA_RUNTIME_PREFIX}/var/lib/netdata/registry")
2430 +set(varlibdir_POST "${NETDATA_RUNTIME_PREFIX}/var/lib/netdata")
2431 set(netdata_user_POST "${NETDATA_USER}")
2432
2433 # netdata-claim.sh
@@ -2434,7 +2447,7 @@ configure_file(src/claim/netdata-claim.sh.in src/claim/netdata-claim.sh @ONLY)
2447 install(PROGRAMS
2448 ${CMAKE_BINARY_DIR}/src/claim/netdata-claim.sh
2449 COMPONENT netdata
2437 - DESTINATION usr/sbin)
2450 + DESTINATION "${BINDIR}")
2451
2452 #
2453 # We don't check ENABLE_PLUGIN_CGROUP_NETWORK because rpm builds assume
@@ -3026,4 +3039,71 @@ if(NOT OS_WINDOWS)
3039 DESTINATION ${WEB_DEST}/v0)
3040 endif()
3041
3042 +if(OS_WINDOWS)
3043 + install(FILES /usr/bin/awk.exe
3044 + /usr/bin/bash.exe
3045 + /usr/bin/cat.exe
3046 + /usr/bin/chown.exe
3047 + /usr/bin/curl.exe
3048 + /usr/bin/env.exe
3049 + /usr/bin/grep.exe
3050 + /usr/bin/mkdir.exe
3051 + /usr/bin/openssl.exe
3052 + /usr/bin/rm.exe
3053 + /usr/bin/sed.exe
3054 + /usr/bin/sh.exe
3055 + /usr/bin/tail.exe
3056 + /usr/bin/tr.exe
3057 + /usr/bin/uuidgen.exe
3058 + /usr/bin/whoami.exe
3059 + DESTINATION "${BINDIR}")
3060 +
3061 + install(FILES /usr/bin/msys-2.0.dll
3062 + /usr/bin/msys-asn1-8.dll
3063 + /usr/bin/msys-brotlicommon-1.dll
3064 + /usr/bin/msys-brotlidec-1.dll
3065 + /usr/bin/msys-brotlienc-1.dll
3066 + /usr/bin/msys-com_err-1.dll
3067 + /usr/bin/msys-crypt-2.dll
3068 + /usr/bin/msys-crypto-3.dll
3069 + /usr/bin/msys-curl-4.dll
3070 + /usr/bin/msys-gcc_s-seh-1.dll
3071 + /usr/bin/msys-gmp-10.dll
3072 + /usr/bin/msys-gssapi-3.dll
3073 + /usr/bin/msys-hcrypto-4.dll
3074 + /usr/bin/msys-heimbase-1.dll
3075 + /usr/bin/msys-heimntlm-0.dll
3076 + /usr/bin/msys-hx509-5.dll
3077 + /usr/bin/msys-iconv-2.dll
3078 + /usr/bin/msys-idn2-0.dll
3079 + /usr/bin/msys-intl-8.dll
3080 + /usr/bin/msys-krb5-26.dll
3081 + /usr/bin/msys-lz4-1.dll
3082 + /usr/bin/msys-mpfr-6.dll
3083 + /usr/bin/msys-ncursesw6.dll
3084 + /usr/bin/msys-nghttp2-14.dll
3085 + /usr/bin/msys-pcre-1.dll
3086 + /usr/bin/msys-protobuf-32.dll
3087 + /usr/bin/msys-psl-5.dll
3088 + /usr/bin/msys-readline8.dll
3089 + /usr/bin/msys-roken-18.dll
3090 + /usr/bin/msys-sqlite3-0.dll
3091 + /usr/bin/msys-ssh2-1.dll
3092 + /usr/bin/msys-ssl-3.dll
3093 + /usr/bin/msys-stdc++-6.dll
3094 + /usr/bin/msys-unistring-5.dll
3095 + /usr/bin/msys-uuid-1.dll
3096 + /usr/bin/msys-uv-1.dll
3097 + /usr/bin/msys-wind-0.dll
3098 + /usr/bin/msys-z.dll
3099 + /usr/bin/msys-zstd-1.dll
3100 + DESTINATION "${BINDIR}")
3101 +
3102 + # Make bash & netdata happy
3103 + install(DIRECTORY DESTINATION tmp)
3104 +
3105 + # Make curl work with ssl
3106 + install(DIRECTORY /usr/ssl DESTINATION usr)
3107 +endif()
3108 +
3109 include(Packaging)
packaging/utils/compile-on-windows.sh
+5 -2
@@ -16,7 +16,8 @@ install_dependencies() {
16 msys/pcre2-devel mingw64/mingw-w64-x86_64-pcre2 ucrt64/mingw-w64-ucrt-x86_64-pcre2 \
17 msys/brotli-devel mingw64/mingw-w64-x86_64-brotli ucrt64/mingw-w64-ucrt-x86_64-brotli \
18 msys/ccache ucrt64/mingw-w64-ucrt-x86_64-ccache mingw64/mingw-w64-x86_64-ccache \
19 - mingw64/mingw-w64-x86_64-go ucrt64/mingw-w64-ucrt-x86_64-go
19 + mingw64/mingw-w64-x86_64-go ucrt64/mingw-w64-ucrt-x86_64-go \
20 + mingw64/mingw-w64-x86_64-nsis
21 }
22
23 if [ "${1}" = "install" ]
@@ -52,7 +53,9 @@ fi
53 -G Ninja \
54 -DCMAKE_INSTALL_PREFIX="/opt/netdata" \
55 -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \
55 - -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" \
56 + -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" \
57 + -DBUILD_FOR_PACKAGING=On \
58 + -DUSE_MOLD=Off \
59 -DNETDATA_USER="${USER}" \
60 -DDEFAULT_FEATURE_STATE=Off \
61 -DENABLE_H2O=Off \
packaging/utils/find-dll-deps.sh new
+16
@@ -0,0 +1,16 @@
1 +#!/usr/bin/env bash
2 +
3 +if [ "$#" -lt 1 ]; then
4 + echo "Usage: $0 <command1> <command2> ... <commandN>"
5 + exit 1
6 +fi
7 +
8 +results=()
9 +
10 +for arg in "$@"; do
11 + while IFS= read -r line; do
12 + results+=("$line")
13 + done < <(ldd "$arg" | grep /usr/bin | awk '{ print $3 }')
14 +done
15 +
16 +printf "%s\n" "${results[@]}" | sort | uniq
packaging/utils/installer.nsi
+44 -20
@@ -1,34 +1,58 @@
1 -Outfile "netdata-installer.exe"
2 -InstallDir "C:\netdata"
1 +!include "MUI2.nsh"
2
3 +Name "Netdata"
4 +Outfile "netdata-installer.exe"
5 +InstallDir "$PROGRAMFILES\netdata"
6 RequestExecutionLevel admin
7
6 -Section
7 - SetOutPath $INSTDIR
8 - WriteUninstaller $INSTDIR\uninstaller.exe
9 -SectionEnd
8 +!define MUI_ABORTWARNING
9 +!define MUI_UNABORTWARNING
10
11 -Section "Install MSYS2 environment"
12 - SetOutPath $TEMP
11 +!insertmacro MUI_PAGE_WELCOME
12 +!insertmacro MUI_PAGE_DIRECTORY
13 +!insertmacro MUI_PAGE_INSTFILES
14 +!insertmacro MUI_PAGE_FINISH
15
14 - SetCompress off
15 - File "C:\msys64\msys2-installer.exe"
16 - nsExec::ExecToLog 'cmd.exe /C "$TEMP\msys2-installer.exe" in --confirm-command --accept-messages --root $INSTDIR'
16 +!insertmacro MUI_UNPAGE_CONFIRM
17 +!insertmacro MUI_UNPAGE_INSTFILES
18 +!insertmacro MUI_UNPAGE_FINISH
19
18 - Delete "$TEMP\msys2-installer.exe"
19 -SectionEnd
20 -
21 -Section "Install MSYS2 packages"
22 - ExecWait '"$INSTDIR\usr\bin\bash.exe" -lc "pacman -S --noconfirm msys/libuv msys/protobuf"'
23 -SectionEnd
20 +!insertmacro MUI_LANGUAGE "English"
21
22 Section "Install Netdata"
26 - SetOutPath $INSTDIR\opt\netdata
27 -
23 + SetOutPath $INSTDIR
24 SetCompress off
25 +
26 File /r "C:\msys64\opt\netdata\*.*"
27 +
28 + ClearErrors
29 + ExecWait '"$SYSDIR\sc.exe" create Netdata binPath= "$INSTDIR\usr\bin\netdata.exe" start= delayed-auto'
30 + IfErrors 0 +2
31 + DetailPrint "Warning: Failed to create Netdata service."
32 +
33 + ClearErrors
34 + ExecWait '"$SYSDIR\sc.exe" description Netdata "Real-time system monitoring service"'
35 + IfErrors 0 +2
36 + DetailPrint "Warning: Failed to add Netdata service description."
37 +
38 + ClearErrors
39 + ExecWait '"$SYSDIR\sc.exe" start Netdata'
40 + IfErrors 0 +2
41 + DetailPrint "Warning: Failed to start Netdata service."
42 +
43 + WriteUninstaller "$INSTDIR\Uninstall.exe"
44 SectionEnd
45
46 Section "Uninstall"
33 - nsExec::ExecToLog 'cmd.exe /C "$INSTDIR\uninstall.exe" pr --confirm-command'
47 + ClearErrors
48 + ExecWait '"$SYSDIR\sc.exe" stop Netdata'
49 + IfErrors 0 +2
50 + DetailPrint "Warning: Failed to stop Netdata service."
51 +
52 + ClearErrors
53 + ExecWait '"$SYSDIR\sc.exe" delete Netdata'
54 + IfErrors 0 +2
55 + DetailPrint "Warning: Failed to delete Netdata service."
56 +
57 + RMDir /r "$INSTDIR"
58 SectionEnd
src/daemon/analytics.c
+6 -2
@@ -1,5 +1,6 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 +#include "analytics.h"
4 #include "common.h"
5 #include "buildinfo.h"
6
@@ -470,8 +471,6 @@ void analytics_alarms(void)
471 */
472 void analytics_misc(void)
473 {
473 - spinlock_init(&analytics_data.spinlock);
474 -
474 #ifdef ENABLE_ACLK
475 analytics_set_data(&analytics_data.netdata_host_cloud_available, "true");
476 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) {
1080
1081 freez(command_to_run);
1082 }
1083 +
1084 +void analytics_init(void)
1085 +{
1086 + spinlock_init(&analytics_data.spinlock);
1087 +}
src/daemon/analytics.h
+1
@@ -86,6 +86,7 @@ void analytics_log_dashboard(void);
86 void analytics_gather_mutable_meta_data(void);
87 void analytics_report_oom_score(long long int score);
88 void get_system_timezone(void);
89 +void analytics_init(void);
90
91 typedef struct {
92 const char *action;
src/daemon/main.c
+28 -13
@@ -496,6 +496,10 @@ void netdata_cleanup_and_exit(int ret, const char *action, const char *action_re
496 watcher_shutdown_end();
497 watcher_thread_stop();
498
499 +#ifdef OS_WINDOWS
500 + return;
501 +#endif
502 +
503 #ifdef ENABLE_SENTRY
504 if (ret)
505 abort();
@@ -1460,7 +1464,11 @@ int unittest_prepare_rrd(char **user) {
1464 return 0;
1465 }
1466
1463 -int main(int argc, char **argv) {
1467 +int netdata_main(int argc, char **argv)
1468 +{
1469 + analytics_init();
1470 + string_init();
1471 +
1472 // initialize the system clocks
1473 clocks_init();
1474 netdata_start_time = now_realtime_sec();
@@ -1471,11 +1479,16 @@ int main(int argc, char **argv) {
1479
1480 int i;
1481 int config_loaded = 0;
1474 - int dont_fork = 0;
1482 bool close_open_fds = true;
1483 size_t default_stacksize;
1484 char *user = NULL;
1485
1486 +#ifdef OS_WINDOWS
1487 + int dont_fork = 1;
1488 +#else
1489 + int dont_fork = 0;
1490 +#endif
1491 +
1492 static_threads = static_threads_get();
1493
1494 netdata_ready = false;
@@ -2214,7 +2227,10 @@ int main(int argc, char **argv) {
2227
2228 // fork the spawn server
2229 delta_startup_time("fork the spawn server");
2230 +
2231 +#ifndef OS_WINDOWS
2232 spawn_init();
2233 +#endif
2234
2235 /*
2236 * Libuv uv_spawn() uses SIGCHLD internally:
@@ -2357,22 +2373,21 @@ int main(int argc, char **argv) {
2373 }
2374 #endif
2375
2360 - // ------------------------------------------------------------------------
2361 - // initialize WebRTC
2362 -
2376 webrtc_initialize();
2377
2365 - // ------------------------------------------------------------------------
2366 - // unblock signals
2367 -
2378 signals_unblock();
2379
2370 - // ------------------------------------------------------------------------
2371 - // Handle signals
2380 + return 10;
2381 +}
2382
2373 - signals_handle();
2383 +#ifndef OS_WINDOWS
2384 +int main(int argc, char *argv[])
2385 +{
2386 + int rc = netdata_main(argc, argv);
2387 + if (rc != 10)
2388 + return rc;
2389
2375 - // should never reach this point
2376 - // but we need it for rpmlint #2752
2390 + signals_handle();
2391 return 1;
2392 }
2393 +#endif
src/daemon/static_threads.c
+5 -1
@@ -30,7 +30,7 @@ const struct netdata_static_thread static_threads_common[] = {
30 .name = "HEALTH",
31 .config_section = NULL,
32 .config_name = NULL,
33 - .enabled = 1,
33 + .enabled = 0,
34 .thread = NULL,
35 .init_routine = NULL,
36 .start_routine = health_main
@@ -70,7 +70,11 @@ const struct netdata_static_thread static_threads_common[] = {
70 .name = "PLUGINSD",
71 .config_section = NULL,
72 .config_name = NULL,
73 +#ifdef OS_WINDOWS
74 + .enabled = 0,
75 +#else
76 .enabled = 1,
77 +#endif
78 .thread = NULL,
79 .init_routine = NULL,
80 .start_routine = pluginsd_main
src/daemon/winsvc.cc new
+252
@@ -0,0 +1,252 @@
1 +extern "C" {
2 +
3 +#include "daemon.h"
4 +#include "libnetdata/libnetdata.h"
5 +
6 +int netdata_main(int argc, char *argv[]);
7 +void signals_handle(void);
8 +
9 +}
10 +
11 +#include <windows.h>
12 +
13 +__attribute__((format(printf, 1, 2)))
14 +static void netdata_service_log(const char *fmt, ...)
15 +{
16 + char path[FILENAME_MAX + 1];
17 + snprintfz(path, FILENAME_MAX, "%s/service.log", LOG_DIR);
18 +
19 + FILE *fp = fopen(path, "a");
20 + if (fp == NULL) {
21 + return;
22 + }
23 +
24 + SYSTEMTIME time;
25 + GetSystemTime(&time);
26 + fprintf(fp, "%d:%d:%d - ", time.wHour, time.wMinute, time.wSecond);
27 +
28 + va_list args;
29 + va_start(args, fmt);
30 + vfprintf(fp, fmt, args);
31 + va_end(args);
32 +
33 + fprintf(fp, "\n");
34 +
35 + fflush(fp);
36 + fclose(fp);
37 +}
38 +
39 +static SERVICE_STATUS_HANDLE svc_status_handle = nullptr;
40 +static SERVICE_STATUS svc_status = {};
41 +
42 +static HANDLE svc_stop_event_handle = nullptr;
43 +
44 +static ND_THREAD *cleanup_thread = nullptr;
45 +
46 +static bool ReportSvcStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint, DWORD dwControlsAccepted)
47 +{
48 + static DWORD dwCheckPoint = 1;
49 + svc_status.dwCurrentState = dwCurrentState;
50 + svc_status.dwWin32ExitCode = dwWin32ExitCode;
51 + svc_status.dwWaitHint = dwWaitHint;
52 + svc_status.dwControlsAccepted = dwControlsAccepted;
53 +
54 + if (dwCurrentState == SERVICE_RUNNING || dwCurrentState == SERVICE_STOPPED)
55 + {
56 + svc_status.dwCheckPoint = 0;
57 + }
58 + else
59 + {
60 + svc_status.dwCheckPoint = dwCheckPoint++;
61 + }
62 +
63 + if (!SetServiceStatus(svc_status_handle, &svc_status)) {
64 + netdata_service_log("@ReportSvcStatus: SetServiceStatusFailed (%d)", GetLastError());
65 + return false;
66 + }
67 +
68 + return true;
69 +}
70 +
71 +static HANDLE CreateEventHandle(const char *msg)
72 +{
73 + HANDLE h = CreateEvent(NULL, TRUE, FALSE, NULL);
74 +
75 + if (!h)
76 + {
77 + netdata_service_log(msg);
78 +
79 + if (!ReportSvcStatus(SERVICE_STOPPED, GetLastError(), 1000, 0))
80 + {
81 + netdata_service_log("Failed to set service status to stopped.");
82 + }
83 +
84 + return NULL;
85 + }
86 +
87 + return h;
88 +}
89 +
90 +static void *call_netdata_cleanup(void *arg)
91 +{
92 + UNUSED(arg);
93 +
94 + // Wait until we have to stop the service
95 + netdata_service_log("Cleanup thread waiting for stop event...");
96 + WaitForSingleObject(svc_stop_event_handle, INFINITE);
97 +
98 + // Stop the agent
99 + netdata_service_log("Running netdata cleanup...");
100 + netdata_cleanup_and_exit(0, NULL, NULL, NULL);
101 +
102 + // Close event handle
103 + netdata_service_log("Closing stop event handle...");
104 + CloseHandle(svc_stop_event_handle);
105 +
106 + // Set status to stopped
107 + netdata_service_log("Reporting the service as stopped...");
108 + ReportSvcStatus(SERVICE_STOPPED, 0, 0, 0);
109 +
110 + return nullptr;
111 +}
112 +
113 +static void WINAPI ServiceControlHandler(DWORD controlCode)
114 +{
115 + switch (controlCode)
116 + {
117 + case SERVICE_CONTROL_STOP:
118 + {
119 + if (svc_status.dwCurrentState != SERVICE_RUNNING)
120 + return;
121 +
122 + // Set service status to stop-pending
123 + netdata_service_log("Setting service status to stop-pending...");
124 + if (!ReportSvcStatus(SERVICE_STOP_PENDING, 0, 5000, 0))
125 + return;
126 +
127 + // Create cleanup thread
128 + netdata_service_log("Creating cleanup thread...");
129 + char tag[NETDATA_THREAD_TAG_MAX + 1];
130 + snprintfz(tag, NETDATA_THREAD_TAG_MAX, "%s", "CLEANUP");
131 + cleanup_thread = nd_thread_create(tag, NETDATA_THREAD_OPTION_JOINABLE, call_netdata_cleanup, NULL);
132 +
133 + // Signal the stop request
134 + netdata_service_log("Signalling the cleanup thread...");
135 + SetEvent(svc_stop_event_handle);
136 + break;
137 + }
138 + case SERVICE_CONTROL_INTERROGATE:
139 + {
140 + ReportSvcStatus(svc_status.dwCurrentState, svc_status.dwWin32ExitCode, svc_status.dwWaitHint, svc_status.dwControlsAccepted);
141 + break;
142 + }
143 + default:
144 + break;
145 + }
146 +}
147 +
148 +void WINAPI ServiceMain(DWORD argc, LPSTR* argv)
149 +{
150 + UNUSED(argc);
151 + UNUSED(argv);
152 +
153 + // Create service status handle
154 + netdata_service_log("Creating service status handle...");
155 + svc_status_handle = RegisterServiceCtrlHandler("Netdata", ServiceControlHandler);
156 + if (!svc_status_handle)
157 + {
158 + netdata_service_log("@ServiceMain() - RegisterServiceCtrlHandler() failed...");
159 + return;
160 + }
161 +
162 + // Set status to start-pending
163 + netdata_service_log("Setting service status to start-pending...");
164 + svc_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
165 + svc_status.dwServiceSpecificExitCode = 0;
166 + svc_status.dwCheckPoint = 0;
167 + if (!ReportSvcStatus(SERVICE_START_PENDING, 0, 5000, 0))
168 + {
169 + netdata_service_log("Failed to set service status to start pending.");
170 + return;
171 + }
172 +
173 + // Create stop service event handle
174 + netdata_service_log("Creating stop service event handle...");
175 + svc_stop_event_handle = CreateEventHandle("Failed to create stop event handle");
176 + if (!svc_stop_event_handle)
177 + return;
178 +
179 + // Set status to running
180 + netdata_service_log("Setting service status to running...");
181 + if (!ReportSvcStatus(SERVICE_RUNNING, 0, 5000, SERVICE_ACCEPT_STOP))
182 + {
183 + netdata_service_log("Failed to set service status to running.");
184 + return;
185 + }
186 +
187 + // Run the agent
188 + netdata_service_log("Running the agent...");
189 + netdata_main(argc, argv);
190 +
191 + netdata_service_log("Agent has been started...");
192 +}
193 +
194 +static bool update_path() {
195 + const char *old_path = getenv("PATH");
196 +
197 + if (!old_path) {
198 + if (setenv("PATH", "/usr/bin", 1) != 0) {
199 + netdata_service_log("Failed to set PATH to /usr/bin");
200 + return false;
201 + }
202 +
203 + return true;
204 + }
205 +
206 + size_t new_path_length = strlen(old_path) + strlen("/usr/bin") + 2;
207 + char *new_path = (char *) callocz(new_path_length, sizeof(char));
208 + snprintfz(new_path, new_path_length, "/usr/bin:%s", old_path);
209 +
210 + if (setenv("PATH", new_path, 1) != 0) {
211 + netdata_service_log("Failed to add /usr/bin to PATH");
212 + freez(new_path);
213 + return false;
214 + }
215 +
216 + freez(new_path);
217 + return true;
218 +}
219 +
220 +int main(int argc, char *argv[])
221 +{
222 + bool tty = isatty(fileno(stdout)) == 1;
223 +
224 + if (!update_path()) {
225 + return 1;
226 + }
227 +
228 + if (tty)
229 + {
230 + int rc = netdata_main(argc, argv);
231 + if (rc != 10)
232 + return rc;
233 +
234 + signals_handle();
235 + return 1;
236 + }
237 + else
238 + {
239 + SERVICE_TABLE_ENTRY serviceTable[] = {
240 + { strdupz("Netdata"), ServiceMain },
241 + { nullptr, nullptr }
242 + };
243 +
244 + if (!StartServiceCtrlDispatcher(serviceTable))
245 + {
246 + netdata_service_log("@main() - StartServiceCtrlDispatcher() failed...");
247 + return 1;
248 + }
249 +
250 + return 0;
251 + }
252 +}
src/database/rrdhost.c
+6
@@ -935,7 +935,13 @@ void dbengine_init(char *hostname) {
935 config_set_number(CONFIG_SECTION_DB, "dbengine tier 0 disk space MB", default_multidb_disk_quota_mb);
936 }
937
938 +#ifdef OS_WINDOWS
939 + // FIXME: for whatever reason joining the initialization threads
940 + // fails on Windows.
941 + bool parallel_initialization = false;
942 +#else
943 bool parallel_initialization = (storage_tiers <= (size_t)get_netdata_cpus()) ? true : false;
944 +#endif
945
946 struct dbengine_initialization tiers_init[RRD_STORAGE_TIERS] = {};
947
src/libnetdata/clocks/clocks.c
+30
@@ -368,6 +368,35 @@ usec_t heartbeat_next(heartbeat_t *hb, usec_t tick) {
368 return dt;
369 }
370
371 +#ifdef OS_WINDOWS
372 +
373 +#include "windows.h"
374 +
375 +void sleep_usec_with_now(usec_t usec, usec_t started_ut)
376 +{
377 + if (!started_ut)
378 + started_ut = now_realtime_usec();
379 +
380 + usec_t end_ut = started_ut + usec;
381 + usec_t remaining_ut = usec;
382 +
383 + timeBeginPeriod(1);
384 +
385 + while (remaining_ut >= 1000)
386 + {
387 + DWORD sleep_ms = (DWORD) (remaining_ut / USEC_PER_MS);
388 + Sleep(sleep_ms);
389 +
390 + usec_t now_ut = now_realtime_usec();
391 + if (now_ut >= end_ut)
392 + break;
393 +
394 + remaining_ut = end_ut - now_ut;
395 + }
396 +
397 + timeEndPeriod(1);
398 +}
399 +#else
400 void sleep_usec_with_now(usec_t usec, usec_t started_ut) {
401 // we expect microseconds (1.000.000 per second)
402 // 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) {
440 }
441 }
442 }
443 +#endif
444
445 static inline collected_number uptime_from_boottime(void) {
446 #ifdef CLOCK_BOOTTIME_IS_AVAILABLE
src/libnetdata/libnetdata.h
+5
@@ -451,7 +451,12 @@ typedef enum {
451 } OPEN_FD_EXCLUDE;
452 void for_each_open_fd(OPEN_FD_ACTION action, OPEN_FD_EXCLUDE excluded_fds);
453
454 +#ifdef OS_WINDOWS
455 +void netdata_cleanup_and_exit(int ret, const char *action, const char *action_result, const char *action_data);
456 +#else
457 void netdata_cleanup_and_exit(int ret, const char *action, const char *action_result, const char *action_data) NORETURN;
458 +#endif
459 +
460 extern char *netdata_configured_host_prefix;
461
462 #include "os/os.h"
src/libnetdata/locks/locks.c
+71 -11
@@ -224,14 +224,24 @@ int __netdata_rwlock_trywrlock(netdata_rwlock_t *rwlock) {
224 // spinlock implementation
225 // https://www.youtube.com/watch?v=rmGJc9PXpuE&t=41s
226
227 -void spinlock_init(SPINLOCK *spinlock) {
227 +#ifdef SPINLOCK_IMPL_WITH_MUTEX
228 +void spinlock_init(SPINLOCK *spinlock)
229 +{
230 + netdata_mutex_init(&spinlock->inner);
231 +}
232 +#else
233 +void spinlock_init(SPINLOCK *spinlock)
234 +{
235 memset(spinlock, 0, sizeof(SPINLOCK));
236 }
237 +#endif
238
231 -static inline void spinlock_lock_internal(SPINLOCK *spinlock) {
232 -#ifdef NETDATA_INTERNAL_CHECKS
239 +#ifndef SPINLOCK_IMPL_WITH_MUTEX
240 +static inline void spinlock_lock_internal(SPINLOCK *spinlock)
241 +{
242 + #ifdef NETDATA_INTERNAL_CHECKS
243 size_t spins = 0;
234 -#endif
244 + #endif
245
246 for(int i = 1;
247 __atomic_load_n(&spinlock->locked, __ATOMIC_RELAXED) ||
@@ -239,9 +249,10 @@ static inline void spinlock_lock_internal(SPINLOCK *spinlock) {
249 ; i++
250 ) {
251
242 -#ifdef NETDATA_INTERNAL_CHECKS
252 + #ifdef NETDATA_INTERNAL_CHECKS
253 spins++;
244 -#endif
254 + #endif
255 +
256 if(unlikely(i == 8)) {
257 i = 0;
258 tinysleep();
@@ -250,23 +261,29 @@ static inline void spinlock_lock_internal(SPINLOCK *spinlock) {
261
262 // we have the lock
263
253 -#ifdef NETDATA_INTERNAL_CHECKS
264 + #ifdef NETDATA_INTERNAL_CHECKS
265 spinlock->spins += spins;
266 spinlock->locker_pid = gettid_cached();
256 -#endif
267 + #endif
268
269 nd_thread_spinlock_locked();
270 }
271 +#endif // SPINLOCK_IMPL_WITH_MUTEX
272
261 -static inline void spinlock_unlock_internal(SPINLOCK *spinlock) {
262 -#ifdef NETDATA_INTERNAL_CHECKS
273 +#ifndef SPINLOCK_IMPL_WITH_MUTEX
274 +static inline void spinlock_unlock_internal(SPINLOCK *spinlock)
275 +{
276 + #ifdef NETDATA_INTERNAL_CHECKS
277 spinlock->locker_pid = 0;
264 -#endif
278 + #endif
279 +
280 __atomic_clear(&spinlock->locked, __ATOMIC_RELEASE);
281
282 nd_thread_spinlock_unlocked();
283 }
284 +#endif // SPINLOCK_IMPL_WITH_MUTEX
285
286 +#ifndef SPINLOCK_IMPL_WITH_MUTEX
287 static inline bool spinlock_trylock_internal(SPINLOCK *spinlock) {
288 if(!__atomic_load_n(&spinlock->locked, __ATOMIC_RELAXED) &&
289 !__atomic_test_and_set(&spinlock->locked, __ATOMIC_ACQUIRE)) {
@@ -277,36 +294,79 @@ static inline bool spinlock_trylock_internal(SPINLOCK *spinlock) {
294
295 return false;
296 }
297 +#endif // SPINLOCK_IMPL_WITH_MUTEX
298
299 +#ifdef SPINLOCK_IMPL_WITH_MUTEX
300 +void spinlock_lock(SPINLOCK *spinlock)
301 +{
302 + netdata_mutex_lock(&spinlock->inner);
303 +}
304 +#else
305 void spinlock_lock(SPINLOCK *spinlock)
306 {
307 spinlock_lock_internal(spinlock);
308 }
309 +#endif
310
311 +#ifdef SPINLOCK_IMPL_WITH_MUTEX
312 +void spinlock_unlock(SPINLOCK *spinlock)
313 +{
314 + netdata_mutex_unlock(&spinlock->inner);
315 +}
316 +#else
317 void spinlock_unlock(SPINLOCK *spinlock)
318 {
319 spinlock_unlock_internal(spinlock);
320 }
321 +#endif
322
323 +#ifdef SPINLOCK_IMPL_WITH_MUTEX
324 +bool spinlock_trylock(SPINLOCK *spinlock)
325 +{
326 + return netdata_mutex_trylock(&spinlock->inner) == 0;
327 +}
328 +#else
329 bool spinlock_trylock(SPINLOCK *spinlock)
330 {
331 return spinlock_trylock_internal(spinlock);
332 }
333 +#endif
334
335 +#ifdef SPINLOCK_IMPL_WITH_MUTEX
336 +void spinlock_lock_cancelable(SPINLOCK *spinlock)
337 +{
338 + netdata_mutex_lock(&spinlock->inner);
339 +}
340 +#else
341 void spinlock_lock_cancelable(SPINLOCK *spinlock)
342 {
343 spinlock_lock_internal(spinlock);
344 }
345 +#endif
346
347 +#ifdef SPINLOCK_IMPL_WITH_MUTEX
348 +void spinlock_unlock_cancelable(SPINLOCK *spinlock)
349 +{
350 + netdata_mutex_unlock(&spinlock->inner);
351 +}
352 +#else
353 void spinlock_unlock_cancelable(SPINLOCK *spinlock)
354 {
355 spinlock_unlock_internal(spinlock);
356 }
357 +#endif
358
359 +#ifdef SPINLOCK_IMPL_WITH_MUTEX
360 +bool spinlock_trylock_cancelable(SPINLOCK *spinlock)
361 +{
362 + return netdata_mutex_trylock(&spinlock->inner) == 0;
363 +}
364 +#else
365 bool spinlock_trylock_cancelable(SPINLOCK *spinlock)
366 {
367 return spinlock_trylock_internal(spinlock);
368 }
369 +#endif
370
371 // ----------------------------------------------------------------------------
372 // rw_spinlock implementation
src/libnetdata/locks/locks.h
+23 -8
@@ -6,19 +6,34 @@
6 #include "../libnetdata.h"
7 #include "../clocks/clocks.h"
8
9 +// #ifdef OS_WINDOWS
10 +// #define SPINLOCK_IMPL_WITH_MUTEX
11 +// #endif
12 +
13 typedef pthread_mutex_t netdata_mutex_t;
14 #define NETDATA_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
15
12 -typedef struct netdata_spinlock {
13 - bool locked;
14 -#ifdef NETDATA_INTERNAL_CHECKS
15 - pid_t locker_pid;
16 - size_t spins;
16 +#ifdef SPINLOCK_IMPL_WITH_MUTEX
17 + typedef struct netdata_spinlock
18 + {
19 + netdata_mutex_t inner;
20 + } SPINLOCK;
21 +#else
22 + typedef struct netdata_spinlock
23 + {
24 + bool locked;
25 + #ifdef NETDATA_INTERNAL_CHECKS
26 + pid_t locker_pid;
27 + size_t spins;
28 + #endif
29 + } SPINLOCK;
30 #endif
18 -} SPINLOCK;
31
20 -#define NETDATA_SPINLOCK_INITIALIZER \
21 - { .locked = false }
32 +#ifdef SPINLOCK_IMPL_WITH_MUTEX
33 +#define NETDATA_SPINLOCK_INITIALIZER { .inner = PTHREAD_MUTEX_INITIALIZER }
34 +#else
35 +#define NETDATA_SPINLOCK_INITIALIZER { .locked = false }
36 +#endif
37
38 void spinlock_init(SPINLOCK *spinlock);
39 void spinlock_lock(SPINLOCK *spinlock);
src/libnetdata/string/string.c
+5
@@ -702,3 +702,8 @@ int string_unittest(size_t entries) {
702 fprintf(stderr, "\n%zu errors found\n", errors);
703 return errors ? 1 : 0;
704 }
705 +
706 +void string_init(void) {
707 + for (size_t i = 0; i != STRING_PARTITIONS; i++)
708 + rw_spinlock_init(&string_base[i].spinlock);
709 +}
src/libnetdata/string/string.h
+2
@@ -34,4 +34,6 @@ void string_statistics(size_t *inserts, size_t *deletes, size_t *searches, size_
34
35 int string_unittest(size_t entries);
36
37 +void string_init(void);
38 +
39 #endif
src/libnetdata/threads/threads.c
+8 -4
@@ -418,12 +418,14 @@ bool nd_thread_signaled_to_cancel(void) {
418 // ----------------------------------------------------------------------------
419 // nd_thread_join
420
421 -void nd_thread_join(ND_THREAD *nti) {
422 - if(!nti) return;
421 +int nd_thread_join(ND_THREAD *nti) {
422 + if(!nti)
423 + return ESRCH;
424
425 int ret = pthread_join(nti->thread, NULL);
425 - if(ret != 0)
426 - nd_log(NDLS_DAEMON, NDLP_WARNING, "cannot join thread. pthread_join() failed with code %d.", ret);
426 + if(ret != 0) {
427 + nd_log(NDLS_DAEMON, NDLP_WARNING, "cannot join thread. pthread_join() failed with code %d. (tag=%s)", ret, nti->tag);
428 + }
429 else {
430 nd_thread_status_set(nti, NETDATA_THREAD_STATUS_JOINED);
431
@@ -434,4 +436,6 @@ void nd_thread_join(ND_THREAD *nti) {
436
437 freez(nti);
438 }
439 +
440 + return ret;
441 }
src/libnetdata/threads/threads.h
+1 -1
@@ -70,7 +70,7 @@ void netdata_threads_init_after_fork(size_t stacksize);
70 void netdata_threads_init_for_external_plugins(size_t stacksize);
71
72 ND_THREAD *nd_thread_create(const char *tag, NETDATA_THREAD_OPTIONS options, void *(*start_routine) (void *), void *arg);
73 -void nd_thread_join(ND_THREAD * nti);
73 +int nd_thread_join(ND_THREAD * nti);
74 ND_THREAD *nd_thread_self(void);
75 bool nd_thread_is_me(ND_THREAD *nti);
76