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
+}