master
c 288 lines 8.78 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "windows_plugin.h"
4 #include "windows-internals.h"
5
6 // Service data
7 struct win_service {
8 char *service_name;
9 DWORD pid;
10 bool present;
11
12 RRDSET *st_service_state;
13 RRDDIM *rd_service_state_running;
14 RRDDIM *rd_service_state_stopped;
15 RRDDIM *rd_service_state_start_pending;
16 RRDDIM *rd_service_state_stop_pending;
17 RRDDIM *rd_service_state_continue_pending;
18 RRDDIM *rd_service_state_pause_pending;
19 RRDDIM *rd_service_state_paused;
20 RRDDIM *rd_service_state_unknown;
21
22 COUNTER_DATA ServiceState;
23 };
24
25 static DICTIONARY *win_services = NULL;
26
27 static void win_service_cleanup(struct win_service *s)
28 {
29 if (s->st_service_state)
30 rrdset_is_obsolete___safe_from_collector_thread(s->st_service_state);
31 freez(s->service_name);
32 s->service_name = NULL;
33 }
34
35 void dict_win_service_insert_cb(const DICTIONARY_ITEM *item, void *value, void *data __maybe_unused)
36 {
37 struct win_service *ptr = value;
38 const char *name = dictionary_acquired_item_name((DICTIONARY_ITEM *)item);
39
40 ptr->service_name = strdupz(name);
41 }
42
43 void dict_win_service_delete_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused)
44 {
45 struct win_service *s = value;
46 win_service_cleanup(s);
47 }
48
49 void do_GetServicesStatus_cleanup(void)
50 {
51 if (win_services) {
52 dictionary_destroy(win_services);
53 win_services = NULL;
54 }
55 }
56
57 static void initialize(void)
58 {
59 win_services = dictionary_create_advanced(
60 DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct win_service));
61
62 dictionary_register_insert_callback(win_services, dict_win_service_insert_cb, NULL);
63 dictionary_register_delete_callback(win_services, dict_win_service_delete_cb, NULL);
64 }
65
66 static int dict_win_services_mark_missing_cb(
67 const DICTIONARY_ITEM *item __maybe_unused,
68 void *value,
69 void *data __maybe_unused)
70 {
71 struct win_service *p = value;
72 p->present = false;
73 return 1;
74 }
75
76 static BOOL fill_dictionary_with_content()
77 {
78 PVOID buffer = NULL;
79 DWORD bytes_needed = 0;
80
81 LPENUM_SERVICE_STATUS_PROCESS service, services;
82 DWORD total_services = 0;
83 SC_HANDLE ndSCMH = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT);
84 if (unlikely(!ndSCMH)) {
85 return FALSE;
86 }
87
88 // Query to obtain overall information
89 BOOL ret = EnumServicesStatusEx(
90 ndSCMH,
91 SC_ENUM_PROCESS_INFO,
92 SERVICE_WIN32,
93 SERVICE_STATE_ALL,
94 (LPBYTE)buffer,
95 bytes_needed,
96 (LPDWORD)&bytes_needed,
97 (LPDWORD)&total_services,
98 NULL,
99 NULL);
100
101 if (ret) {
102 dictionary_walkthrough_write(win_services, dict_win_services_mark_missing_cb, NULL);
103 // This only happens if there are truly 0 services in the system (a valid edge case).
104 goto endServiceCollection;
105 }
106
107 if (GetLastError() != ERROR_MORE_DATA) {
108 nd_log(NDLS_COLLECTORS, NDLP_ERR, "Failed to query service status (error: %lu)", GetLastError());
109 goto endServiceCollection;
110 }
111
112 buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bytes_needed);
113 if (!buffer) {
114 nd_log(NDLS_COLLECTORS, NDLP_ERR, "Failed to allocate memory for service enumeration");
115 ret = FALSE;
116 goto endServiceCollection;
117 }
118
119 if (!EnumServicesStatusEx(
120 ndSCMH,
121 SC_ENUM_PROCESS_INFO,
122 SERVICE_WIN32,
123 SERVICE_STATE_ALL,
124 (LPBYTE)buffer,
125 bytes_needed,
126 &bytes_needed,
127 &total_services,
128 NULL,
129 NULL)) {
130 nd_log(NDLS_COLLECTORS, NDLP_ERR, "Failed to enumerate services (error: %lu)", GetLastError());
131 goto endServiceCollection;
132 }
133
134 dictionary_walkthrough_write(win_services, dict_win_services_mark_missing_cb, NULL);
135 services = (LPENUM_SERVICE_STATUS_PROCESS)buffer;
136
137 for (ULONG i = 0; i < total_services; i++) {
138 service = &services[i];
139 if (!service->lpServiceName || !*service->lpServiceName)
140 continue;
141
142 struct win_service *p = dictionary_set(win_services, service->lpServiceName, NULL, sizeof(*p));
143 if (!p)
144 continue;
145
146 p->ServiceState.current.Data = service->ServiceStatusProcess.dwCurrentState;
147 p->pid = service->ServiceStatusProcess.dwProcessId;
148 p->present = true;
149 }
150
151 ret = TRUE;
152
153 endServiceCollection:
154 if (buffer)
155 HeapFree(GetProcessHeap(), 0, buffer);
156
157 CloseServiceHandle(ndSCMH);
158 return ret;
159 }
160
161 static RRDDIM *win_service_select_dim(struct win_service *p, uint32_t selector)
162 {
163 // Values defined according to https://learn.microsoft.com/en-us/windows/win32/api/winsvc/ns-winsvc-service_status
164 switch (selector) {
165 case 1:
166 return p->rd_service_state_stopped;
167 case 2:
168 return p->rd_service_state_start_pending;
169 case 3:
170 return p->rd_service_state_stop_pending;
171 case 4:
172 return p->rd_service_state_running;
173 case 5:
174 return p->rd_service_state_continue_pending;
175 case 6:
176 return p->rd_service_state_pause_pending;
177 case 7:
178 return p->rd_service_state_paused;
179 case 8:
180 default:
181 return p->rd_service_state_unknown;
182 }
183 }
184
185 static int dict_win_services_charts_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data)
186 {
187 struct win_service *p = value;
188 int *update_every = data;
189
190 if (!p->st_service_state) {
191 char id[RRD_ID_LENGTH_MAX + 1];
192 snprintfz(id, RRD_ID_LENGTH_MAX, "service_%s_state", p->service_name);
193 netdata_fix_chart_name(id);
194 p->st_service_state = rrdset_create_localhost(
195 "service",
196 id,
197 NULL,
198 "service",
199 "windows.service_state",
200 "Service state",
201 "state",
202 PLUGIN_WINDOWS_NAME,
203 "PerflibService",
204 PRIO_SERVICE_STATE,
205 *update_every,
206 RRDSET_TYPE_LINE);
207
208 if (unlikely(!p->st_service_state))
209 return 1;
210
211 p->rd_service_state_running = rrddim_add(p->st_service_state, "running", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
212
213 p->rd_service_state_stopped = rrddim_add(p->st_service_state, "stopped", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
214
215 p->rd_service_state_start_pending =
216 rrddim_add(p->st_service_state, "start_pending", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
217
218 p->rd_service_state_stop_pending =
219 rrddim_add(p->st_service_state, "stop_pending", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
220
221 p->rd_service_state_continue_pending =
222 rrddim_add(p->st_service_state, "continue_pending", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
223
224 p->rd_service_state_pause_pending =
225 rrddim_add(p->st_service_state, "pause_pending", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
226
227 p->rd_service_state_paused = rrddim_add(p->st_service_state, "paused", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
228
229 p->rd_service_state_unknown = rrddim_add(p->st_service_state, "unknown", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
230
231 rrdlabels_add(p->st_service_state->rrdlabels, "service", p->service_name, RRDLABEL_SRC_AUTO);
232 }
233
234 #define NETDATA_WINDOWS_SERVICE_STATE_TOTAL_STATES (8)
235 uint32_t current_state = (uint32_t)p->ServiceState.current.Data;
236 for (uint32_t i = 1; i <= NETDATA_WINDOWS_SERVICE_STATE_TOTAL_STATES; i++) {
237 RRDDIM *dim = win_service_select_dim(p, i);
238 if (!dim)
239 continue;
240 uint32_t chart_value = (current_state == i) ? 1 : 0;
241
242 rrddim_set_by_pointer(p->st_service_state, dim, (collected_number)chart_value);
243 }
244
245 rrdset_done(p->st_service_state);
246
247 return 1;
248 }
249
250 static int dict_win_services_delete_missing_cb(const DICTIONARY_ITEM *item, void *value, void *data __maybe_unused)
251 {
252 struct win_service *p = value;
253
254 if (!p->present)
255 dictionary_del(win_services, dictionary_acquired_item_name(item));
256
257 return 1;
258 }
259
260 int do_GetServicesStatus(int update_every, usec_t dt __maybe_unused)
261 {
262 #define NETDATA_SERVICE_MAX_TRY (5)
263 static int limit = 0;
264 static bool initialized = false;
265
266 if (unlikely(!initialized)) {
267 initialize();
268 initialized = true;
269 }
270
271 if (!fill_dictionary_with_content()) {
272 if (++limit == NETDATA_SERVICE_MAX_TRY) {
273 nd_log(
274 NDLS_COLLECTORS,
275 NDLP_ERR,
276 "Disabling thread after %u consecutive tries to open Service Management.",
277 NETDATA_SERVICE_MAX_TRY);
278 return -1;
279 }
280 return 0;
281 }
282
283 limit = 0;
284 dictionary_walkthrough_write(win_services, dict_win_services_delete_missing_cb, NULL);
285 dictionary_sorted_walkthrough_read(win_services, dict_win_services_charts_cb, &update_every);
286
287 return 0;
288 }