0
0
Fork 0
mirror of https://github.com/netdata/netdata.git synced 2025-04-02 20:48:06 +00:00

Run the agent as a Windows service. ()

* 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
This commit is contained in:
vkalintiris 2024-07-02 12:19:20 +03:00 committed by GitHub
parent a38e6eef93
commit e99da8b64b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 616 additions and 87 deletions

3
.gitignore vendored
View file

@ -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

View file

@ -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)

View file

@ -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 \

View file

@ -0,0 +1,16 @@
#!/usr/bin/env bash
if [ "$#" -lt 1 ]; then
echo "Usage: $0 <command1> <command2> ... <commandN>"
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

View file

@ -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

View file

@ -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);
}

View file

@ -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;

View file

@ -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;
}
#ifndef OS_WINDOWS
int main(int argc, char *argv[])
{
int rc = netdata_main(argc, argv);
if (rc != 10)
return rc;
signals_handle();
// should never reach this point
// but we need it for rpmlint #2752
return 1;
}
#endif

View file

@ -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

252
src/daemon/winsvc.cc Normal file
View file

@ -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 <windows.h>
__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;
}
}

View file

@ -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] = {};

View file

@ -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

View file

@ -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"

View file

@ -224,24 +224,35 @@ 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));
}
static inline void spinlock_lock_internal(SPINLOCK *spinlock) {
#ifdef NETDATA_INTERNAL_CHECKS
size_t spins = 0;
#endif
#ifndef SPINLOCK_IMPL_WITH_MUTEX
static inline void spinlock_lock_internal(SPINLOCK *spinlock)
{
#ifdef NETDATA_INTERNAL_CHECKS
size_t spins = 0;
#endif
for(int i = 1;
__atomic_load_n(&spinlock->locked, __ATOMIC_RELAXED) ||
__atomic_test_and_set(&spinlock->locked, __ATOMIC_ACQUIRE)
; 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

View file

@ -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);

View file

@ -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);
}

View file

@ -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

View file

@ -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;
}

View file

@ -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);