0
0
Fork 0
mirror of https://github.com/netdata/netdata.git synced 2025-04-14 01:29:11 +00:00
netdata_netdata/src/daemon/winsvc.cc
Costa Tsaousis 166e9caffa
spawn server (Windows support for external plugins) ()
* listening ipv6 sockets may be both ipv4 and ipv6, depending on the IPV6_ONLY flag

* working libmnl ipv46 detection and added latency and retransmits from TCP_INFO

* fix aggregations for rtt and retrans

* code cleanup

* code cleanup

* code cleanup again

* restore field renames

* count namespaces

* run namespaces in parallel

* add libmnl to buildinfo

* lock around safe_fork()

* libmnl ports are in network byte order

* posix spawn server for both executables and callback functions

* local-sockets and network-viewer using the new spawn server

* cleanup spawn servers sockets

* spawn server stdin and stdout are linked to /dev/null

* no need for spinlock in spawn server

* empty all parameters

* new spawn server is now used for plugins.d plugins

* fix for environ

* claiming script runs via the new spawn server

* tc.plugin uses the new spawn server

* analytics, buildinfo and cgroups.plugin use the new spawn server

* cgroup-discovery uses the new spawn server

* added ability to wait or kill spawned processes

* removed old spawn server and now alert notifications use the new one

* remove left-overs

* hide spawn server internals; started working on windows version of the spawn server

* fixes for windows

* more windows work

* more work on windows

* added debug log to spawn server

* fix compilation warnings

* enable static threads on windows

* running external plugins

* working spawn server on windows

* spawn server logs to collectoers.log

* log windows last error together with errno

* log updates

* cleanup

* decode_argv does not add an empty parameter

* removed debug log

* removed debug return

* rework on close_range()

* eliminate the need for waitid()

* clear errno on the signal handler

* added universal os_setproctitle() call to support FreeBSD too

* os_get_pid_max() for windows and macos

* isolate pids array from the rest of the code in apps.plugin so that it can be turned to a hashtable
2024-07-10 14:23:29 +03:00

252 lines
6.6 KiB
C++

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(stdin)) == 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;
}
}