diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-08-26 08:15:24 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-08-26 08:15:35 +0000 |
commit | f09848204fa5283d21ea43e262ee41aa578e1808 (patch) | |
tree | c62385d7adf209fa6a798635954d887f718fb3fb /src/daemon/winsvc.cc | |
parent | Releasing debian version 1.46.3-2. (diff) | |
download | netdata-f09848204fa5283d21ea43e262ee41aa578e1808.tar.xz netdata-f09848204fa5283d21ea43e262ee41aa578e1808.zip |
Merging upstream version 1.47.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/daemon/winsvc.cc')
-rw-r--r-- | src/daemon/winsvc.cc | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/src/daemon/winsvc.cc b/src/daemon/winsvc.cc new file mode 100644 index 000000000..9c5eb49ff --- /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 <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; + } +} |