master
cc 267 lines 7.1 KB
Raw
1 extern "C" {
2
3 #include "daemon.h"
4 #include "libnetdata/libnetdata.h"
5 #include "daemon/daemon-shutdown.h"
6
7 int netdata_main(int argc, char *argv[]);
8 void nd_process_signals(void);
9
10 }
11
12 __attribute__((format(printf, 1, 2)))
13 static void netdata_service_log(const char *fmt, ...)
14 {
15 char path[FILENAME_MAX + 1];
16 snprintfz(path, FILENAME_MAX, "%s/service.log", LOG_DIR);
17
18 FILE *fp = fopen(path, "a");
19 if (fp == NULL) {
20 return;
21 }
22
23 SYSTEMTIME time;
24 GetSystemTime(&time);
25 fprintf(fp, "%d:%d:%d - ", time.wHour, time.wMinute, time.wSecond);
26
27 va_list args;
28 va_start(args, fmt);
29 vfprintf(fp, fmt, args);
30 va_end(args);
31
32 fprintf(fp, "\n");
33
34 fflush(fp);
35 fclose(fp);
36 }
37
38 static SERVICE_STATUS_HANDLE svc_status_handle = nullptr;
39 static SERVICE_STATUS svc_status = {};
40
41 static HANDLE svc_stop_event_handle = nullptr;
42
43 static ND_THREAD *cleanup_thread = nullptr;
44
45 static bool ReportSvcStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint, DWORD dwControlsAccepted)
46 {
47 static DWORD dwCheckPoint = 1;
48 svc_status.dwCurrentState = dwCurrentState;
49 svc_status.dwWin32ExitCode = dwWin32ExitCode;
50 svc_status.dwWaitHint = dwWaitHint;
51 svc_status.dwControlsAccepted = dwControlsAccepted;
52
53 if (dwCurrentState == SERVICE_RUNNING || dwCurrentState == SERVICE_STOPPED)
54 {
55 svc_status.dwCheckPoint = 0;
56 }
57 else
58 {
59 svc_status.dwCheckPoint = dwCheckPoint++;
60 }
61
62 if (!SetServiceStatus(svc_status_handle, &svc_status)) {
63 netdata_service_log("@ReportSvcStatus: SetServiceStatusFailed (%d)", GetLastError());
64 return false;
65 }
66
67 return true;
68 }
69
70 static HANDLE CreateEventHandle(const char *msg)
71 {
72 HANDLE h = CreateEvent(NULL, TRUE, FALSE, NULL);
73
74 if (!h)
75 {
76 netdata_service_log("%s", msg);
77
78 if (!ReportSvcStatus(SERVICE_STOPPED, GetLastError(), 1000, 0))
79 {
80 netdata_service_log("Failed to set service status to stopped.");
81 }
82
83 return NULL;
84 }
85
86 return h;
87 }
88
89 static void call_netdata_cleanup(void *arg)
90 {
91 DWORD controlCode = *((DWORD *)arg);
92
93 // Wait until we have to stop the service
94 netdata_service_log("Cleanup thread waiting for stop event...");
95 WaitForSingleObject(svc_stop_event_handle, INFINITE);
96
97 // Stop the agent
98 netdata_service_log("Running netdata cleanup...");
99 EXIT_REASON reason;
100 switch(controlCode) {
101 case SERVICE_CONTROL_SHUTDOWN:
102 reason = (EXIT_REASON)(EXIT_REASON_SERVICE_STOP|EXIT_REASON_SYSTEM_SHUTDOWN);
103 break;
104
105 case SERVICE_CONTROL_STOP:
106 // fall-through
107
108 default:
109 reason = EXIT_REASON_SERVICE_STOP;
110 break;
111 }
112 netdata_exit_gracefully(reason, false);
113
114 // Close event handle
115 netdata_service_log("Closing stop event handle...");
116 CloseHandle(svc_stop_event_handle);
117
118 // Set status to stopped
119 netdata_service_log("Reporting the service as stopped...");
120 ReportSvcStatus(SERVICE_STOPPED, 0, 0, 0);
121 }
122
123 static void WINAPI ServiceControlHandler(DWORD controlCode)
124 {
125 switch (controlCode)
126 {
127 case SERVICE_CONTROL_SHUTDOWN:
128 case SERVICE_CONTROL_STOP:
129 {
130 if (svc_status.dwCurrentState != SERVICE_RUNNING)
131 return;
132
133 // Set service status to stop-pending
134 netdata_service_log("Setting service status to stop-pending...");
135 if (!ReportSvcStatus(SERVICE_STOP_PENDING, 0, 5000, 0))
136 return;
137
138 // Create cleanup thread
139 netdata_service_log("Creating cleanup thread...");
140 char tag[NETDATA_THREAD_TAG_MAX + 1];
141 snprintfz(tag, NETDATA_THREAD_TAG_MAX, "%s", "CLEANUP");
142 cleanup_thread = nd_thread_create(tag, NETDATA_THREAD_OPTION_DEFAULT, call_netdata_cleanup, &controlCode);
143
144 // Signal the stop request
145 netdata_service_log("Signalling the cleanup thread...");
146 SetEvent(svc_stop_event_handle);
147 break;
148 }
149 case SERVICE_CONTROL_INTERROGATE:
150 {
151 ReportSvcStatus(svc_status.dwCurrentState, svc_status.dwWin32ExitCode, svc_status.dwWaitHint, svc_status.dwControlsAccepted);
152 break;
153 }
154 default:
155 break;
156 }
157 }
158
159 void WINAPI ServiceMain(DWORD argc, LPSTR* argv)
160 {
161 UNUSED(argc);
162 UNUSED(argv);
163
164 // Create service status handle
165 netdata_service_log("Creating service status handle...");
166 svc_status_handle = RegisterServiceCtrlHandler("Netdata", ServiceControlHandler);
167 if (!svc_status_handle)
168 {
169 netdata_service_log("@ServiceMain() - RegisterServiceCtrlHandler() failed...");
170 return;
171 }
172
173 // Set status to start-pending
174 netdata_service_log("Setting service status to start-pending...");
175 svc_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
176 svc_status.dwServiceSpecificExitCode = 0;
177 svc_status.dwCheckPoint = 0;
178 if (!ReportSvcStatus(SERVICE_START_PENDING, 0, 5000, 0))
179 {
180 netdata_service_log("Failed to set service status to start pending.");
181 return;
182 }
183
184 // Create stop service event handle
185 netdata_service_log("Creating stop service event handle...");
186 svc_stop_event_handle = CreateEventHandle("Failed to create stop event handle");
187 if (!svc_stop_event_handle)
188 return;
189
190 // Set status to running
191 netdata_service_log("Setting service status to running...");
192 if (!ReportSvcStatus(SERVICE_RUNNING, 0, 5000, SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN))
193 {
194 netdata_service_log("Failed to set service status to running.");
195 return;
196 }
197
198 // Run the agent
199 netdata_service_log("Running the agent...");
200 netdata_main(argc, argv);
201
202 netdata_service_log("Agent has been started...");
203 }
204
205 static bool update_path() {
206 const char *old_path = getenv("PATH");
207
208 if (!old_path) {
209 if (setenv("PATH", "/usr/bin", 1) != 0) {
210 netdata_service_log("Failed to set PATH to /usr/bin");
211 return false;
212 }
213
214 return true;
215 }
216
217 size_t new_path_length = strlen(old_path) + strlen("/usr/bin") + 2;
218 char *new_path = (char *) callocz(new_path_length, sizeof(char));
219 snprintfz(new_path, new_path_length, "/usr/bin:%s", old_path);
220
221 if (setenv("PATH", new_path, 1) != 0) {
222 netdata_service_log("Failed to add /usr/bin to PATH");
223 freez(new_path);
224 return false;
225 }
226
227 freez(new_path);
228 return true;
229 }
230
231 int main(int argc, char *argv[])
232 {
233 #if defined(OS_WINDOWS) && defined(RUN_UNDER_CLION)
234 bool tty = true;
235 #else
236 bool tty = isatty(fileno(stdin)) == 1;
237 #endif
238
239 if (!update_path()) {
240 return 1;
241 }
242
243 if (tty)
244 {
245 int rc = netdata_main(argc, argv);
246 if (rc != 10)
247 return rc;
248
249 nd_process_signals();
250 return 1;
251 }
252 else
253 {
254 SERVICE_TABLE_ENTRY serviceTable[] = {
255 { strdupz("Netdata"), ServiceMain },
256 { nullptr, nullptr }
257 };
258
259 if (!StartServiceCtrlDispatcher(serviceTable))
260 {
261 netdata_service_log("@main() - StartServiceCtrlDispatcher() failed...");
262 return 1;
263 }
264
265 return 0;
266 }
267 }