@cryptotaxi247 / netdata-1 / commits / 640f5f35d

Windows Events Improvements 2 (#18563)

* NULL values are also valid for numbers * remove defective log from static threaded web server * fix coloring of rows based on severity * add a message when there is no message at all * make task visible * remove duplicate field definitions * switched to windows native spawn server * fix discrepancy in lazy loading of xml

Costa Tsaousis committed Sep 16, 2024 at 19:18 UTC 640f5f35da8d8b1e2df5af56c2cbe4e2ccdfa38f
9 files changed +1094 -1028
src/collectors/windows-events.plugin/windows-events-query.c
+6 -14
@@ -108,31 +108,23 @@ cleanup:
108
109 static bool wevt_get_field_from_events_log(
110 WEVT_LOG *log, PROVIDER_META_HANDLE *p, EVT_HANDLE event_handle,
111 - TXT_UTF8 *dst, EVT_FORMAT_MESSAGE_FLAGS flags, const char *empty) {
111 + TXT_UTF8 *dst, EVT_FORMAT_MESSAGE_FLAGS flags) {
112
113 dst->src = TXT_SOURCE_EVENT_LOG;
114
115 if(wevt_get_message_unicode(&log->ops.unicode, publisher_handle(p), event_handle, 0, flags))
116 return wevt_str_unicode_to_utf8(dst, &log->ops.unicode);
117
118 - if(empty) {
119 - txt_utf8_resize(dst, 128, false);
120 - dst->used = snprintfz(dst->data, dst->size, "%s", empty);
121 - }
122 - else
123 - wevt_utf8_empty(dst);
124 -
118 + wevt_utf8_empty(dst);
119 return false;
120 }
121
122 bool wevt_get_event_utf8(WEVT_LOG *log, PROVIDER_META_HANDLE *p, EVT_HANDLE event_handle, TXT_UTF8 *dst) {
129 - return wevt_get_field_from_events_log(
130 - log, p, event_handle, dst, EvtFormatMessageEvent, "No event message for this event.");
123 + return wevt_get_field_from_events_log(log, p, event_handle, dst, EvtFormatMessageEvent);
124 }
125
126 bool wevt_get_xml_utf8(WEVT_LOG *log, PROVIDER_META_HANDLE *p, EVT_HANDLE event_handle, TXT_UTF8 *dst) {
134 - return wevt_get_field_from_events_log(
135 - log, p, event_handle, dst, EvtFormatMessageXml, "No XML in this event entry.");
127 + return wevt_get_field_from_events_log(log, p, event_handle, dst, EvtFormatMessageXml);
128 }
129
130 static inline void wevt_event_done(WEVT_LOG *log) {
@@ -160,7 +152,7 @@ static void wevt_get_field_from_cache(
152 if (field_cache_get(cache_type, provider, value, dst))
153 return;
154
163 - wevt_get_field_from_events_log(log, h, log->bookmark, dst, flags, NULL);
155 + wevt_get_field_from_events_log(log, h, log->bookmark, dst, flags);
156 field_cache_set(cache_type, provider, value, dst);
157 }
158
@@ -442,7 +434,7 @@ bool wevt_get_next_event_one(WEVT_LOG *log, WEVT_EVENT *ev, bool full) {
434 wevt_field_get_sid(&content[FIELD_USER_ID], &log->ops.user);
435
436 PROVIDER_META_HANDLE *h = log->publisher =
445 - publisher_get(ev->provider, log->ops.content.data[FIELD_PROVIDER_NAME].StringVal);
437 + publisher_get(ev->provider, content[FIELD_PROVIDER_NAME].StringVal);
438
439 wevt_get_level(log, ev, h);
440 wevt_get_task(log, ev, h);
src/collectors/windows-events.plugin/windows-events-query.h
+18
@@ -132,26 +132,41 @@ static inline void wevt_variant_resize(WEVT_VARIANT *v, size_t required_size) {
132 }
133
134 static inline uint8_t wevt_field_get_uint8(EVT_VARIANT *ev) {
135 + if((ev->Type & EVT_VARIANT_TYPE_MASK) == EvtVarTypeNull)
136 + return 0;
137 +
138 fatal_assert((ev->Type & EVT_VARIANT_TYPE_MASK) == EvtVarTypeByte);
139 return ev->ByteVal;
140 }
141
142 static inline uint16_t wevt_field_get_uint16(EVT_VARIANT *ev) {
143 + if((ev->Type & EVT_VARIANT_TYPE_MASK) == EvtVarTypeNull)
144 + return 0;
145 +
146 fatal_assert((ev->Type & EVT_VARIANT_TYPE_MASK) == EvtVarTypeUInt16);
147 return ev->UInt16Val;
148 }
149
150 static inline uint32_t wevt_field_get_uint32(EVT_VARIANT *ev) {
151 + if((ev->Type & EVT_VARIANT_TYPE_MASK) == EvtVarTypeNull)
152 + return 0;
153 +
154 fatal_assert((ev->Type & EVT_VARIANT_TYPE_MASK) == EvtVarTypeUInt32);
155 return ev->UInt32Val;
156 }
157
158 static inline uint64_t wevt_field_get_uint64(EVT_VARIANT *ev) {
159 + if((ev->Type & EVT_VARIANT_TYPE_MASK) == EvtVarTypeNull)
160 + return 0;
161 +
162 fatal_assert((ev->Type & EVT_VARIANT_TYPE_MASK) == EvtVarTypeUInt64);
163 return ev->UInt64Val;
164 }
165
166 static inline uint64_t wevt_field_get_uint64_hex(EVT_VARIANT *ev) {
167 + if((ev->Type & EVT_VARIANT_TYPE_MASK) == EvtVarTypeNull)
168 + return 0;
169 +
170 fatal_assert((ev->Type & EVT_VARIANT_TYPE_MASK) == EvtVarTypeHexInt64);
171 return ev->UInt64Val;
172 }
@@ -179,6 +194,9 @@ static inline bool wevt_field_get_sid(EVT_VARIANT *ev, TXT_UTF8 *dst) {
194 }
195
196 static inline uint64_t wevt_field_get_filetime_to_ns(EVT_VARIANT *ev) {
197 + if((ev->Type & EVT_VARIANT_TYPE_MASK) == EvtVarTypeNull)
198 + return 0;
199 +
200 fatal_assert((ev->Type & EVT_VARIANT_TYPE_MASK) == EvtVarTypeFileTime);
201 return os_windows_ulonglong_to_unix_epoch_ns(ev->FileTimeVal);
202 }
src/collectors/windows-events.plugin/windows-events.c
+100 -43
@@ -151,7 +151,7 @@ static inline WEVT_QUERY_STATUS check_stop(const bool *cancelled, const usec_t *
151 }
152
153 FACET_ROW_SEVERITY wevt_levelid_to_facet_severity(FACETS *facets __maybe_unused, FACET_ROW *row, void *data __maybe_unused) {
154 - FACET_ROW_KEY_VALUE *levelid_rkv = dictionary_get(row->dict, WEVT_FIELD_LEVEL);
154 + FACET_ROW_KEY_VALUE *levelid_rkv = dictionary_get(row->dict, WEVT_FIELD_LEVEL "ID");
155 if(!levelid_rkv || levelid_rkv->empty)
156 return FACET_ROW_SEVERITY_NORMAL;
157
@@ -172,6 +172,7 @@ FACET_ROW_SEVERITY wevt_levelid_to_facet_severity(FACETS *facets __maybe_unused,
172 }
173
174 struct wevt_bin_data {
175 + bool rendered;
176 WEVT_LOG *log;
177 EVT_HANDLE bookmark;
178 PROVIDER_META_HANDLE *publisher;
@@ -187,10 +188,11 @@ static void wevt_cleanup_bin_data(void *data) {
188 freez(d);
189 }
190
190 -static inline void wevt_facets_register_bin_data(WEVT_LOG *log, FACETS *facets) {
191 +static inline void wevt_facets_register_bin_data(WEVT_LOG *log, FACETS *facets, WEVT_EVENT *ev) {
192 struct wevt_bin_data *d = mallocz(sizeof(struct wevt_bin_data));
193
194 d->log = log;
195 + d->rendered = false;
196
197 // take the bookmark
198 d->bookmark = log->bookmark; log->bookmark = NULL;
@@ -201,6 +203,14 @@ static inline void wevt_facets_register_bin_data(WEVT_LOG *log, FACETS *facets)
203 facets_row_bin_data_set(facets, wevt_cleanup_bin_data, d);
204 }
205
206 +static void wevt_lazy_loading_event_and_xml(struct wevt_bin_data *d, FACET_ROW *row __maybe_unused) {
207 + if(d->rendered) return;
208 +
209 + wevt_get_xml_utf8(d->log, d->publisher, d->bookmark, &d->log->ops.xml);
210 + wevt_get_event_utf8(d->log, d->publisher, d->bookmark, &d->log->ops.event);
211 + d->rendered = true;
212 +}
213 +
214 static void wevt_render_xml(
215 FACETS *facets,
216 BUFFER *json_array,
@@ -214,7 +224,7 @@ static void wevt_render_xml(
224 return;
225 }
226
217 - wevt_get_xml_utf8(d->log, d->publisher, d->bookmark, &d->log->ops.xml);
227 + wevt_lazy_loading_event_and_xml(d, row);
228 buffer_json_add_array_item_string(json_array, d->log->ops.xml.data);
229 }
230
@@ -231,11 +241,78 @@ static void wevt_render_message(
241 return;
242 }
243
234 - wevt_get_event_utf8(d->log, d->publisher, d->bookmark, &d->log->ops.event);
235 - buffer_json_add_array_item_string(json_array, d->log->ops.event.data);
244 + wevt_lazy_loading_event_and_xml(d, row);
245 +
246 + if(d->log->ops.event.used <= 1) {
247 + TXT_UTF8 *xml = &d->log->ops.xml;
248 +
249 + buffer_flush(rkv->wb);
250 +
251 + bool added_message = false;
252 + if(xml->used > 1) {
253 + const char *message_path[] = {
254 + "RenderingInfo",
255 + "Message",
256 + NULL};
257 +
258 + added_message = buffer_xml_extract_and_print_value(
259 + rkv->wb,
260 + xml->data, xml->used - 1,
261 + NULL,
262 + message_path);
263 + }
264 +
265 + if(!added_message) {
266 + const FACET_ROW_KEY_VALUE *event_id_rkv = dictionary_get(row->dict, WEVT_FIELD_EVENTID);
267 + if (event_id_rkv && buffer_strlen(event_id_rkv->wb)) {
268 + buffer_fast_strcat(rkv->wb, "Event ", 6);
269 + buffer_fast_strcat(rkv->wb, buffer_tostring(event_id_rkv->wb), buffer_strlen(event_id_rkv->wb));
270 + } else
271 + buffer_strcat(rkv->wb, "Unknown Event ");
272 +
273 + const FACET_ROW_KEY_VALUE *provider_rkv = dictionary_get(row->dict, WEVT_FIELD_PROVIDER);
274 + if (provider_rkv && buffer_strlen(provider_rkv->wb)) {
275 + buffer_fast_strcat(rkv->wb, " of ", 4);
276 + buffer_fast_strcat(rkv->wb, buffer_tostring(provider_rkv->wb), buffer_strlen(provider_rkv->wb));
277 + buffer_putc(rkv->wb, '.');
278 + } else
279 + buffer_strcat(rkv->wb, "of unknown Provider.");
280 + }
281 +
282 + if(xml->used > 1) {
283 + const char *event_path[] = {
284 + "EventData",
285 + NULL
286 + };
287 + bool added_event_data = buffer_extract_and_print_xml(
288 + rkv->wb,
289 + xml->data, xml->used - 1,
290 + "\n\nRelated event data:\n",
291 + event_path);
292 +
293 + const char *user_path[] = {
294 + "UserData",
295 + NULL
296 + };
297 + bool added_user_data = buffer_extract_and_print_xml(
298 + rkv->wb,
299 + xml->data, xml->used - 1,
300 + "\n\nRelated user data:\n",
301 + user_path);
302 +
303 + if(!added_event_data && !added_user_data)
304 + buffer_strcat(rkv->wb, " Without any related data.");
305 + }
306 +
307 + buffer_json_add_array_item_string(json_array, buffer_tostring(rkv->wb));
308 + }
309 + else
310 + buffer_json_add_array_item_string(json_array, d->log->ops.event.data);
311 }
312
313 static void wevt_register_fields(LOGS_QUERY_STATUS *lqs) {
314 + // the order of the fields here, controls the order of the fields at the table presented
315 +
316 FACETS *facets = lqs->facets;
317 LOGS_QUERY_REQUEST *rq = &lqs->rq;
318
@@ -247,18 +324,15 @@ static void wevt_register_fields(LOGS_QUERY_STATUS *lqs) {
324
325 facets_register_key_name(
326 facets, WEVT_FIELD_CHANNEL,
250 - rq->default_facet |
251 - FACET_KEY_OPTION_FTS);
327 + rq->default_facet | FACET_KEY_OPTION_FTS);
328
329 facets_register_key_name(
330 facets, WEVT_FIELD_PROVIDER,
255 - rq->default_facet |
256 - FACET_KEY_OPTION_VISIBLE | FACET_KEY_OPTION_FTS);
331 + rq->default_facet | FACET_KEY_OPTION_VISIBLE | FACET_KEY_OPTION_FTS);
332
333 facets_register_key_name(
334 facets, WEVT_FIELD_SOURCE,
260 - rq->default_facet |
261 - FACET_KEY_OPTION_FTS);
335 + rq->default_facet | FACET_KEY_OPTION_FTS);
336
337 facets_register_key_name(
338 facets, WEVT_FIELD_USER,
@@ -287,15 +361,11 @@ static void wevt_register_fields(LOGS_QUERY_STATUS *lqs) {
361
362 facets_register_key_name(
363 facets, WEVT_FIELD_TASK,
290 - rq->default_facet | FACET_KEY_OPTION_FTS);
291 -
292 - facets_register_key_name(
293 - facets, WEVT_FIELD_PROCESSID,
294 - rq->default_facet | FACET_KEY_OPTION_FTS);
364 + rq->default_facet | FACET_KEY_OPTION_FTS | FACET_KEY_OPTION_VISIBLE);
365
366 facets_register_key_name(
297 - facets, WEVT_FIELD_THREADID,
298 - rq->default_facet | FACET_KEY_OPTION_FTS);
367 + facets, WEVT_FIELD_TASK "ID",
368 + FACET_KEY_OPTION_NONE);
369
370 facets_register_key_name(
371 facets, WEVT_FIELD_OPCODE,
@@ -305,14 +375,6 @@ static void wevt_register_fields(LOGS_QUERY_STATUS *lqs) {
375 facets, WEVT_FIELD_OPCODE "ID",
376 FACET_KEY_OPTION_NONE);
377
308 - facets_register_key_name(
309 - facets, WEVT_FIELD_TASK,
310 - rq->default_facet | FACET_KEY_OPTION_FTS);
311 -
312 - facets_register_key_name(
313 - facets, WEVT_FIELD_TASK "ID",
314 - FACET_KEY_OPTION_NONE);
315 -
378 facets_register_key_name(
379 facets, WEVT_FIELD_KEYWORDS,
380 rq->default_facet | FACET_KEY_OPTION_FTS);
@@ -372,7 +434,7 @@ static const char *source_to_str(TXT_UTF8 *txt) {
434 }
435 #endif
436
375 -static inline size_t wevt_process_event(WEVT_LOG *log, FACETS *facets, LOGS_QUERY_SOURCE *src, usec_t *msg_ut __maybe_unused, WEVT_EVENT *e) {
437 +static inline size_t wevt_process_event(WEVT_LOG *log, FACETS *facets, LOGS_QUERY_SOURCE *src, usec_t *msg_ut __maybe_unused, WEVT_EVENT *ev) {
438 size_t len, bytes = log->ops.content.used;
439
440 if(log->ops.provider.used > 1) {
@@ -404,7 +466,7 @@ static inline size_t wevt_process_event(WEVT_LOG *log, FACETS *facets, LOGS_QUER
466
467 {
468 static __thread char event_record_id_str[UINT64_MAX_LENGTH];
407 - len = print_uint64(event_record_id_str, e->id);
469 + len = print_uint64(event_record_id_str, ev->id);
470 bytes += len;
471 facets_add_key_value_length(
472 facets, WEVT_FIELD_EVENTRECORDID, sizeof(WEVT_FIELD_EVENTRECORDID) - 1,
@@ -455,25 +517,25 @@ static inline size_t wevt_process_event(WEVT_LOG *log, FACETS *facets, LOGS_QUER
517
518 {
519 static __thread char event_id_str[UINT64_MAX_LENGTH];
458 - len = print_uint64(event_id_str, e->event_id);
520 + len = print_uint64(event_id_str, ev->event_id);
521 bytes += len;
522 facets_add_key_value_length(
523 facets, WEVT_FIELD_EVENTID, sizeof(WEVT_FIELD_EVENTID) - 1,
524 event_id_str, len);
525 }
526
465 - if(e->process_id) {
527 + if(ev->process_id) {
528 static __thread char process_id_str[UINT64_MAX_LENGTH];
467 - len = print_uint64(process_id_str, e->process_id);
529 + len = print_uint64(process_id_str, ev->process_id);
530 bytes += len;
531 facets_add_key_value_length(
532 facets, WEVT_FIELD_PROCESSID, sizeof(WEVT_FIELD_PROCESSID) - 1,
533 process_id_str, len);
534 }
535
474 - if(e->thread_id) {
536 + if(ev->thread_id) {
537 static __thread char thread_id_str[UINT64_MAX_LENGTH];
476 - len = print_uint64(thread_id_str, e->thread_id);
538 + len = print_uint64(thread_id_str, ev->thread_id);
539 bytes += len;
540 facets_add_key_value_length(
541 facets, WEVT_FIELD_THREADID, sizeof(WEVT_FIELD_THREADID) - 1,
@@ -482,7 +544,7 @@ static inline size_t wevt_process_event(WEVT_LOG *log, FACETS *facets, LOGS_QUER
544
545 {
546 static __thread char str[UINT64_MAX_LENGTH];
485 - len = print_uint64(str, e->level);
547 + len = print_uint64(str, ev->level);
548 bytes += len;
549 facets_add_key_value_length(
550 facets, WEVT_FIELD_LEVEL "ID", sizeof(WEVT_FIELD_LEVEL) + 2 - 1, str, len);
@@ -490,7 +552,7 @@ static inline size_t wevt_process_event(WEVT_LOG *log, FACETS *facets, LOGS_QUER
552
553 {
554 static __thread char str[UINT64_HEX_MAX_LENGTH];
493 - len = print_uint64_hex_full(str, e->keywords);
555 + len = print_uint64_hex_full(str, ev->keywords);
556 bytes += len;
557 facets_add_key_value_length(
558 facets, WEVT_FIELD_KEYWORDS "ID", sizeof(WEVT_FIELD_KEYWORDS) + 2 - 1, str, len);
@@ -498,7 +560,7 @@ static inline size_t wevt_process_event(WEVT_LOG *log, FACETS *facets, LOGS_QUER
560
561 {
562 static __thread char str[UINT64_MAX_LENGTH];
501 - len = print_uint64(str, e->opcode);
563 + len = print_uint64(str, ev->opcode);
564 bytes += len;
565 facets_add_key_value_length(
566 facets, WEVT_FIELD_OPCODE "ID", sizeof(WEVT_FIELD_OPCODE) + 2 - 1, str, len);
@@ -506,18 +568,13 @@ static inline size_t wevt_process_event(WEVT_LOG *log, FACETS *facets, LOGS_QUER
568
569 {
570 static __thread char str[UINT64_MAX_LENGTH];
509 - len = print_uint64(str, e->task);
571 + len = print_uint64(str, ev->task);
572 bytes += len;
573 facets_add_key_value_length(
574 facets, WEVT_FIELD_TASK "ID", sizeof(WEVT_FIELD_TASK) + 2 - 1, str, len);
575 }
576
515 -// bytes += log->ops.xml.used * 2;
516 -// facets_add_key_value_length(
517 -// facets, WEVT_FIELD_XML, sizeof(WEVT_FIELD_XML) - 1,
518 -// log->ops.xml.data, log->ops.xml.used - 1);
519 -
520 - wevt_facets_register_bin_data(log, facets);
577 + wevt_facets_register_bin_data(log, facets, ev);
578
579 #ifdef NETDATA_INTERNAL_CHECKS
580 facets_add_key_value(facets, "z_level_source", source_to_str(&log->ops.level));
src/libnetdata/spawn_server/spawn_library.c
+51 -51
@@ -1,51 +1,51 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "spawn_library.h"
4 -
5 -BUFFER *argv_to_cmdline_buffer(const char **argv) {
6 - BUFFER *wb = buffer_create(0, NULL);
7 -
8 - for(size_t i = 0; argv[i] ;i++) {
9 - const char *s = argv[i];
10 - size_t len = strlen(s);
11 - buffer_need_bytes(wb, len * 2 + 1);
12 -
13 - bool needs_quotes = false;
14 - for(const char *c = s; !needs_quotes && *c ; c++) {
15 - switch(*c) {
16 - case ' ':
17 - case '\v':
18 - case '\t':
19 - case '\n':
20 - case '"':
21 - needs_quotes = true;
22 - break;
23 -
24 - default:
25 - break;
26 - }
27 - }
28 -
29 - if(needs_quotes && buffer_strlen(wb))
30 - buffer_strcat(wb, " \"");
31 - else if(buffer_strlen(wb))
32 - buffer_putc(wb, ' ');
33 -
34 - for(const char *c = s; *c ; c++) {
35 - switch(*c) {
36 - case '"':
37 - buffer_putc(wb, '\\');
38 - // fall through
39 -
40 - default:
41 - buffer_putc(wb, *c);
42 - break;
43 - }
44 - }
45 -
46 - if(needs_quotes)
47 - buffer_strcat(wb, "\"");
48 - }
49 -
50 - return wb;
51 -}
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "spawn_library.h"
4 +
5 +BUFFER *argv_to_cmdline_buffer(const char **argv) {
6 + BUFFER *wb = buffer_create(0, NULL);
7 +
8 + for(size_t i = 0; argv[i] ;i++) {
9 + const char *s = argv[i];
10 + size_t len = strlen(s);
11 + buffer_need_bytes(wb, len * 2 + 1);
12 +
13 + bool needs_quotes = false;
14 + for(const char *c = s; !needs_quotes && *c ; c++) {
15 + switch(*c) {
16 + case ' ':
17 + case '\v':
18 + case '\t':
19 + case '\n':
20 + case '"':
21 + needs_quotes = true;
22 + break;
23 +
24 + default:
25 + break;
26 + }
27 + }
28 +
29 + if(needs_quotes && buffer_strlen(wb))
30 + buffer_strcat(wb, " \"");
31 + else if(buffer_strlen(wb))
32 + buffer_putc(wb, ' ');
33 +
34 + for(const char *c = s; *c ; c++) {
35 + switch(*c) {
36 + case '"':
37 + buffer_putc(wb, '\\');
38 + // fall through
39 +
40 + default:
41 + buffer_putc(wb, *c);
42 + break;
43 + }
44 + }
45 +
46 + if(needs_quotes)
47 + buffer_strcat(wb, "\"");
48 + }
49 +
50 + return wb;
51 +}
src/libnetdata/spawn_server/spawn_library.h
+10 -10
@@ -1,10 +1,10 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#ifndef NETDATA_SPAWN_LIBRARY_H
4 -#define NETDATA_SPAWN_LIBRARY_H
5 -
6 -#include "../libnetdata.h"
7 -
8 -BUFFER *argv_to_cmdline_buffer(const char **argv);
9 -
10 -#endif //NETDATA_SPAWN_LIBRARY_H
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_SPAWN_LIBRARY_H
4 +#define NETDATA_SPAWN_LIBRARY_H
5 +
6 +#include "../libnetdata.h"
7 +
8 +BUFFER *argv_to_cmdline_buffer(const char **argv);
9 +
10 +#endif //NETDATA_SPAWN_LIBRARY_H
src/libnetdata/spawn_server/spawn_server_internals.h
+97 -97
@@ -1,97 +1,97 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#ifndef NETDATA_SPAWN_SERVER_INTERNALS_H
4 -#define NETDATA_SPAWN_SERVER_INTERNALS_H
5 -
6 -#include "../libnetdata.h"
7 -#include "spawn_server.h"
8 -#include "spawn_library.h"
9 -
10 -#if defined(OS_WINDOWS)
11 -// #define SPAWN_SERVER_VERSION_WINDOWS 1
12 -// #define SPAWN_SERVER_VERSION_UV 1
13 -#define SPAWN_SERVER_VERSION_POSIX_SPAWN 1
14 -#else
15 -#define SPAWN_SERVER_VERSION_NOFORK 1
16 -// #define SPAWN_SERVER_VERSION_UV 1
17 -// #define SPAWN_SERVER_VERSION_POSIX_SPAWN 1
18 -#endif
19 -
20 -#if defined(SPAWN_SERVER_VERSION_WINDOWS)
21 -#include <windows.h>
22 -#include <io.h>
23 -#include <fcntl.h>
24 -#include <process.h>
25 -#include <tlhelp32.h>
26 -#include <tchar.h>
27 -#include <sys/cygwin.h>
28 -#endif
29 -
30 -struct spawn_server {
31 - size_t id;
32 - size_t request_id;
33 - const char *name;
34 -
35 -#if defined(SPAWN_SERVER_VERSION_UV)
36 - uv_loop_t *loop;
37 - uv_thread_t thread;
38 - uv_async_t async;
39 - bool stopping;
40 -
41 - SPINLOCK spinlock;
42 - struct work_item *work_queue;
43 -#endif
44 -
45 -#if defined(SPAWN_SERVER_VERSION_NOFORK)
46 - SPAWN_SERVER_OPTIONS options;
47 -
48 - ND_UUID magic; // for authorizing requests, the client needs to know our random UUID
49 - // it is ignored for PING requests
50 -
51 - int pipe[2];
52 - int sock; // the listening socket of the server
53 - pid_t server_pid;
54 - char *path;
55 - spawn_request_callback_t cb;
56 -
57 - int argc;
58 - const char **argv;
59 -#endif
60 -
61 -#if defined(SPAWN_SERVER_VERSION_POSIX_SPAWN)
62 -#endif
63 -
64 -#if defined(SPAWN_SERVER_VERSION_WINDOWS)
65 -#endif
66 -};
67 -
68 -struct spawn_instance {
69 - size_t request_id;
70 - int sock;
71 - int write_fd;
72 - int read_fd;
73 - pid_t child_pid;
74 -
75 -#if defined(SPAWN_SERVER_VERSION_UV)
76 - uv_process_t process;
77 - int exit_code;
78 - uv_sem_t sem;
79 -#endif
80 -
81 -#if defined(SPAWN_SERVER_VERSION_NOFORK)
82 -#endif
83 -
84 -#if defined(SPAWN_SERVER_VERSION_POSIX_SPAWN)
85 - const char *cmdline;
86 - bool exited;
87 - int waitpid_status;
88 - struct spawn_instance *prev, *next;
89 -#endif
90 -
91 -#if defined(SPAWN_SERVER_VERSION_WINDOWS)
92 - HANDLE process_handle;
93 - DWORD dwProcessId;
94 -#endif
95 -};
96 -
97 -#endif //NETDATA_SPAWN_SERVER_INTERNALS_H
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_SPAWN_SERVER_INTERNALS_H
4 +#define NETDATA_SPAWN_SERVER_INTERNALS_H
5 +
6 +#include "../libnetdata.h"
7 +#include "spawn_server.h"
8 +#include "spawn_library.h"
9 +
10 +#if defined(OS_WINDOWS)
11 +#define SPAWN_SERVER_VERSION_WINDOWS 1
12 +// #define SPAWN_SERVER_VERSION_UV 1
13 +// #define SPAWN_SERVER_VERSION_POSIX_SPAWN 1
14 +#else
15 +#define SPAWN_SERVER_VERSION_NOFORK 1
16 +// #define SPAWN_SERVER_VERSION_UV 1
17 +// #define SPAWN_SERVER_VERSION_POSIX_SPAWN 1
18 +#endif
19 +
20 +#if defined(SPAWN_SERVER_VERSION_WINDOWS)
21 +#include <windows.h>
22 +#include <io.h>
23 +#include <fcntl.h>
24 +#include <process.h>
25 +#include <tlhelp32.h>
26 +#include <tchar.h>
27 +#include <sys/cygwin.h>
28 +#endif
29 +
30 +struct spawn_server {
31 + size_t id;
32 + size_t request_id;
33 + const char *name;
34 +
35 +#if defined(SPAWN_SERVER_VERSION_UV)
36 + uv_loop_t *loop;
37 + uv_thread_t thread;
38 + uv_async_t async;
39 + bool stopping;
40 +
41 + SPINLOCK spinlock;
42 + struct work_item *work_queue;
43 +#endif
44 +
45 +#if defined(SPAWN_SERVER_VERSION_NOFORK)
46 + SPAWN_SERVER_OPTIONS options;
47 +
48 + ND_UUID magic; // for authorizing requests, the client needs to know our random UUID
49 + // it is ignored for PING requests
50 +
51 + int pipe[2];
52 + int sock; // the listening socket of the server
53 + pid_t server_pid;
54 + char *path;
55 + spawn_request_callback_t cb;
56 +
57 + int argc;
58 + const char **argv;
59 +#endif
60 +
61 +#if defined(SPAWN_SERVER_VERSION_POSIX_SPAWN)
62 +#endif
63 +
64 +#if defined(SPAWN_SERVER_VERSION_WINDOWS)
65 +#endif
66 +};
67 +
68 +struct spawn_instance {
69 + size_t request_id;
70 + int sock;
71 + int write_fd;
72 + int read_fd;
73 + pid_t child_pid;
74 +
75 +#if defined(SPAWN_SERVER_VERSION_UV)
76 + uv_process_t process;
77 + int exit_code;
78 + uv_sem_t sem;
79 +#endif
80 +
81 +#if defined(SPAWN_SERVER_VERSION_NOFORK)
82 +#endif
83 +
84 +#if defined(SPAWN_SERVER_VERSION_POSIX_SPAWN)
85 + const char *cmdline;
86 + bool exited;
87 + int waitpid_status;
88 + struct spawn_instance *prev, *next;
89 +#endif
90 +
91 +#if defined(SPAWN_SERVER_VERSION_WINDOWS)
92 + HANDLE process_handle;
93 + DWORD dwProcessId;
94 +#endif
95 +};
96 +
97 +#endif //NETDATA_SPAWN_SERVER_INTERNALS_H
src/libnetdata/spawn_server/spawn_server_libuv.c
+395 -395
@@ -1,395 +1,395 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "spawn_server_internals.h"
4 -
5 -#if defined(SPAWN_SERVER_VERSION_UV)
6 -
7 -int spawn_server_instance_read_fd(SPAWN_INSTANCE *si) { return si->read_fd; }
8 -int spawn_server_instance_write_fd(SPAWN_INSTANCE *si) { return si->write_fd; }
9 -void spawn_server_instance_read_fd_unset(SPAWN_INSTANCE *si) { si->read_fd = -1; }
10 -void spawn_server_instance_write_fd_unset(SPAWN_INSTANCE *si) { si->write_fd = -1; }
11 -pid_t spawn_server_instance_pid(SPAWN_INSTANCE *si) { return uv_process_get_pid(&si->process); }
12 -
13 -typedef struct work_item {
14 - int stderr_fd;
15 - const char **argv;
16 - uv_sem_t sem;
17 - SPAWN_INSTANCE *instance;
18 - struct work_item *prev;
19 - struct work_item *next;
20 -} work_item;
21 -
22 -int uv_errno_to_errno(int uv_err) {
23 - switch (uv_err) {
24 - case 0: return 0;
25 - case UV_E2BIG: return E2BIG;
26 - case UV_EACCES: return EACCES;
27 - case UV_EADDRINUSE: return EADDRINUSE;
28 - case UV_EADDRNOTAVAIL: return EADDRNOTAVAIL;
29 - case UV_EAFNOSUPPORT: return EAFNOSUPPORT;
30 - case UV_EAGAIN: return EAGAIN;
31 - case UV_EAI_ADDRFAMILY: return EAI_ADDRFAMILY;
32 - case UV_EAI_AGAIN: return EAI_AGAIN;
33 - case UV_EAI_BADFLAGS: return EAI_BADFLAGS;
34 -#if defined(EAI_CANCELED)
35 - case UV_EAI_CANCELED: return EAI_CANCELED;
36 -#endif
37 - case UV_EAI_FAIL: return EAI_FAIL;
38 - case UV_EAI_FAMILY: return EAI_FAMILY;
39 - case UV_EAI_MEMORY: return EAI_MEMORY;
40 - case UV_EAI_NODATA: return EAI_NODATA;
41 - case UV_EAI_NONAME: return EAI_NONAME;
42 - case UV_EAI_OVERFLOW: return EAI_OVERFLOW;
43 - case UV_EAI_SERVICE: return EAI_SERVICE;
44 - case UV_EAI_SOCKTYPE: return EAI_SOCKTYPE;
45 - case UV_EALREADY: return EALREADY;
46 - case UV_EBADF: return EBADF;
47 - case UV_EBUSY: return EBUSY;
48 - case UV_ECANCELED: return ECANCELED;
49 - case UV_ECHARSET: return EILSEQ; // No direct mapping, using EILSEQ
50 - case UV_ECONNABORTED: return ECONNABORTED;
51 - case UV_ECONNREFUSED: return ECONNREFUSED;
52 - case UV_ECONNRESET: return ECONNRESET;
53 - case UV_EDESTADDRREQ: return EDESTADDRREQ;
54 - case UV_EEXIST: return EEXIST;
55 - case UV_EFAULT: return EFAULT;
56 - case UV_EFBIG: return EFBIG;
57 - case UV_EHOSTUNREACH: return EHOSTUNREACH;
58 - case UV_EINTR: return EINTR;
59 - case UV_EINVAL: return EINVAL;
60 - case UV_EIO: return EIO;
61 - case UV_EISCONN: return EISCONN;
62 - case UV_EISDIR: return EISDIR;
63 - case UV_ELOOP: return ELOOP;
64 - case UV_EMFILE: return EMFILE;
65 - case UV_EMSGSIZE: return EMSGSIZE;
66 - case UV_ENAMETOOLONG: return ENAMETOOLONG;
67 - case UV_ENETDOWN: return ENETDOWN;
68 - case UV_ENETUNREACH: return ENETUNREACH;
69 - case UV_ENFILE: return ENFILE;
70 - case UV_ENOBUFS: return ENOBUFS;
71 - case UV_ENODEV: return ENODEV;
72 - case UV_ENOENT: return ENOENT;
73 - case UV_ENOMEM: return ENOMEM;
74 - case UV_ENONET: return ENONET;
75 - case UV_ENOSPC: return ENOSPC;
76 - case UV_ENOSYS: return ENOSYS;
77 - case UV_ENOTCONN: return ENOTCONN;
78 - case UV_ENOTDIR: return ENOTDIR;
79 - case UV_ENOTEMPTY: return ENOTEMPTY;
80 - case UV_ENOTSOCK: return ENOTSOCK;
81 - case UV_ENOTSUP: return ENOTSUP;
82 - case UV_ENOTTY: return ENOTTY;
83 - case UV_ENXIO: return ENXIO;
84 - case UV_EPERM: return EPERM;
85 - case UV_EPIPE: return EPIPE;
86 - case UV_EPROTO: return EPROTO;
87 - case UV_EPROTONOSUPPORT: return EPROTONOSUPPORT;
88 - case UV_EPROTOTYPE: return EPROTOTYPE;
89 - case UV_ERANGE: return ERANGE;
90 - case UV_EROFS: return EROFS;
91 - case UV_ESHUTDOWN: return ESHUTDOWN;
92 - case UV_ESPIPE: return ESPIPE;
93 - case UV_ESRCH: return ESRCH;
94 - case UV_ETIMEDOUT: return ETIMEDOUT;
95 - case UV_ETXTBSY: return ETXTBSY;
96 - case UV_EXDEV: return EXDEV;
97 - default: return EINVAL; // Use EINVAL for unknown libuv errors
98 - }
99 -}
100 -
101 -static void posix_unmask_sigchld_on_thread(void) {
102 - sigset_t sigset;
103 - sigemptyset(&sigset); // Initialize the signal set to empty
104 - sigaddset(&sigset, SIGCHLD); // Add SIGCHLD to the set
105 -
106 - if(pthread_sigmask(SIG_UNBLOCK, &sigset, NULL) != 0)
107 - netdata_log_error("SPAWN SERVER: cannot unmask SIGCHLD");
108 -}
109 -
110 -static void server_thread(void *arg) {
111 - SPAWN_SERVER *server = (SPAWN_SERVER *)arg;
112 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
113 - "SPAWN SERVER: started");
114 -
115 - // this thread needs to process SIGCHLD (by libuv)
116 - // otherwise the on_exit() callback is never run
117 - posix_unmask_sigchld_on_thread();
118 -
119 - // run the event loop
120 - uv_run(server->loop, UV_RUN_DEFAULT);
121 -
122 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
123 - "SPAWN SERVER: ended");
124 -}
125 -
126 -static void on_process_exit(uv_process_t *req, int64_t exit_status, int term_signal) {
127 - SPAWN_INSTANCE *si = (SPAWN_INSTANCE *)req->data;
128 - si->exit_code = (int)(term_signal ? term_signal : exit_status << 8);
129 - uv_close((uv_handle_t *)req, NULL); // Properly close the process handle
130 -
131 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
132 - "SPAWN SERVER: process with pid %d exited with code %d and term_signal %d",
133 - si->child_pid, (int)exit_status, term_signal);
134 -
135 - uv_sem_post(&si->sem); // Signal that the process has exited
136 -}
137 -
138 -static SPAWN_INSTANCE *spawn_process_with_libuv(uv_loop_t *loop, int stderr_fd, const char **argv) {
139 - SPAWN_INSTANCE *si = NULL;
140 - bool si_sem_init = false;
141 -
142 - int stdin_pipe[2] = { -1, -1 };
143 - int stdout_pipe[2] = { -1, -1 };
144 -
145 - if (pipe(stdin_pipe) == -1) {
146 - nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: stdin pipe() failed");
147 - goto cleanup;
148 - }
149 -
150 - if (pipe(stdout_pipe) == -1) {
151 - nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: stdout pipe() failed");
152 - goto cleanup;
153 - }
154 -
155 - si = callocz(1, sizeof(SPAWN_INSTANCE));
156 - si->exit_code = -1;
157 -
158 - if (uv_sem_init(&si->sem, 0)) {
159 - nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: uv_sem_init() failed");
160 - goto cleanup;
161 - }
162 - si_sem_init = true;
163 -
164 - uv_stdio_container_t stdio[3] = { 0 };
165 - stdio[0].flags = UV_INHERIT_FD;
166 - stdio[0].data.fd = stdin_pipe[PIPE_READ];
167 - stdio[1].flags = UV_INHERIT_FD;
168 - stdio[1].data.fd = stdout_pipe[PIPE_WRITE];
169 - stdio[2].flags = UV_INHERIT_FD;
170 - stdio[2].data.fd = stderr_fd;
171 -
172 - uv_process_options_t options = { 0 };
173 - options.stdio_count = 3;
174 - options.stdio = stdio;
175 - options.exit_cb = on_process_exit;
176 - options.file = argv[0];
177 - options.args = (char **)argv;
178 - options.env = (char **)environ;
179 -
180 - // uv_spawn() does not close all other open file descriptors
181 - // we have to close them manually
182 - int fds[3] = { stdio[0].data.fd, stdio[1].data.fd, stdio[2].data.fd };
183 - os_close_all_non_std_open_fds_except(fds, 3, CLOSE_RANGE_CLOEXEC);
184 -
185 - int rc = uv_spawn(loop, &si->process, &options);
186 - if (rc) {
187 - errno = uv_errno_to_errno(rc);
188 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
189 - "SPAWN SERVER: uv_spawn() failed with error %s, %s",
190 - uv_err_name(rc), uv_strerror(rc));
191 - goto cleanup;
192 - }
193 -
194 - // Successfully spawned
195 -
196 - // get the pid of the process spawned
197 - si->child_pid = uv_process_get_pid(&si->process);
198 -
199 - // on_process_exit() needs this to find the si
200 - si->process.data = si;
201 -
202 - nd_log(NDLS_COLLECTORS, NDLP_INFO,
203 - "SPAWN SERVER: process created with pid %d", si->child_pid);
204 -
205 - // close the child sides of the pipes
206 - close(stdin_pipe[PIPE_READ]);
207 - si->write_fd = stdin_pipe[PIPE_WRITE];
208 - si->read_fd = stdout_pipe[PIPE_READ];
209 - close(stdout_pipe[PIPE_WRITE]);
210 -
211 - return si;
212 -
213 -cleanup:
214 - if(stdin_pipe[PIPE_READ] != -1) close(stdin_pipe[PIPE_READ]);
215 - if(stdin_pipe[PIPE_WRITE] != -1) close(stdin_pipe[PIPE_WRITE]);
216 - if(stdout_pipe[PIPE_READ] != -1) close(stdout_pipe[PIPE_READ]);
217 - if(stdout_pipe[PIPE_WRITE] != -1) close(stdout_pipe[PIPE_WRITE]);
218 - if(si) {
219 - if(si_sem_init)
220 - uv_sem_destroy(&si->sem);
221 -
222 - freez(si);
223 - }
224 - return NULL;
225 -}
226 -
227 -static void async_callback(uv_async_t *handle) {
228 - nd_log(NDLS_COLLECTORS, NDLP_INFO, "SPAWN SERVER: dequeue commands started");
229 - SPAWN_SERVER *server = (SPAWN_SERVER *)handle->data;
230 -
231 - // Check if the server is stopping
232 - if (__atomic_load_n(&server->stopping, __ATOMIC_RELAXED)) {
233 - nd_log(NDLS_COLLECTORS, NDLP_INFO, "SPAWN SERVER: stopping...");
234 - uv_stop(server->loop);
235 - return;
236 - }
237 -
238 - work_item *item;
239 - spinlock_lock(&server->spinlock);
240 - while (server->work_queue) {
241 - item = server->work_queue;
242 - DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(server->work_queue, item, prev, next);
243 - spinlock_unlock(&server->spinlock);
244 -
245 - item->instance = spawn_process_with_libuv(server->loop, item->stderr_fd, item->argv);
246 - uv_sem_post(&item->sem);
247 -
248 - spinlock_lock(&server->spinlock);
249 - }
250 - spinlock_unlock(&server->spinlock);
251 -
252 - nd_log(NDLS_COLLECTORS, NDLP_INFO, "SPAWN SERVER: dequeue commands done");
253 -}
254 -
255 -
256 -SPAWN_SERVER* spawn_server_create(SPAWN_SERVER_OPTIONS options __maybe_unused, const char *name, spawn_request_callback_t cb __maybe_unused, int argc __maybe_unused, const char **argv __maybe_unused) {
257 - SPAWN_SERVER* server = callocz(1, sizeof(SPAWN_SERVER));
258 - spinlock_init(&server->spinlock);
259 -
260 - if (name)
261 - server->name = strdupz(name);
262 - else
263 - server->name = strdupz("unnamed");
264 -
265 - server->loop = callocz(1, sizeof(uv_loop_t));
266 - if (uv_loop_init(server->loop)) {
267 - nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN PARENT: uv_loop_init() failed");
268 - freez(server->loop);
269 - freez((void *)server->name);
270 - freez(server);
271 - return NULL;
272 - }
273 -
274 - if (uv_async_init(server->loop, &server->async, async_callback)) {
275 - nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN PARENT: uv_async_init() failed");
276 - uv_loop_close(server->loop);
277 - freez(server->loop);
278 - freez((void *)server->name);
279 - freez(server);
280 - return NULL;
281 - }
282 - server->async.data = server;
283 -
284 - if (uv_thread_create(&server->thread, server_thread, server)) {
285 - nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN PARENT: uv_thread_create() failed");
286 - uv_close((uv_handle_t*)&server->async, NULL);
287 - uv_loop_close(server->loop);
288 - freez(server->loop);
289 - freez((void *)server->name);
290 - freez(server);
291 - return NULL;
292 - }
293 -
294 - return server;
295 -}
296 -
297 -static void close_handle(uv_handle_t* handle, void* arg __maybe_unused) {
298 - if (!uv_is_closing(handle)) {
299 - uv_close(handle, NULL);
300 - }
301 -}
302 -
303 -void spawn_server_destroy(SPAWN_SERVER *server) {
304 - if (!server) return;
305 -
306 - __atomic_store_n(&server->stopping, true, __ATOMIC_RELAXED);
307 -
308 - // Trigger the async callback to stop the event loop
309 - uv_async_send(&server->async);
310 -
311 - // Wait for the server thread to finish
312 - uv_thread_join(&server->thread);
313 -
314 - uv_stop(server->loop);
315 - uv_close((uv_handle_t*)&server->async, NULL);
316 -
317 - // Walk through and close any remaining handles
318 - uv_walk(server->loop, close_handle, NULL);
319 -
320 - uv_loop_close(server->loop);
321 - freez(server->loop);
322 - freez((void *)server->name);
323 - freez(server);
324 -}
325 -
326 -SPAWN_INSTANCE* spawn_server_exec(SPAWN_SERVER *server, int stderr_fd __maybe_unused, int custom_fd __maybe_unused, const char **argv, const void *data __maybe_unused, size_t data_size __maybe_unused, SPAWN_INSTANCE_TYPE type) {
327 - if (type != SPAWN_INSTANCE_TYPE_EXEC)
328 - return NULL;
329 -
330 - work_item item = { 0 };
331 - item.stderr_fd = stderr_fd;
332 - item.argv = argv;
333 -
334 - if (uv_sem_init(&item.sem, 0)) {
335 - nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN PARENT: uv_sem_init() failed");
336 - return NULL;
337 - }
338 -
339 - spinlock_lock(&server->spinlock);
340 - // item is in the stack, but the server will remove it before sending to us
341 - // the semaphore, so it is safe to have the item in the stack.
342 - work_item *item_ptr = &item;
343 - DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(server->work_queue, item_ptr, prev, next);
344 - spinlock_unlock(&server->spinlock);
345 -
346 - uv_async_send(&server->async);
347 -
348 - nd_log(NDLS_COLLECTORS, NDLP_INFO, "SPAWN PARENT: queued command");
349 -
350 - // Wait for the command to be executed
351 - uv_sem_wait(&item.sem);
352 - uv_sem_destroy(&item.sem);
353 -
354 - if (!item.instance) {
355 - nd_log(NDLS_COLLECTORS, NDLP_INFO, "SPAWN PARENT: process failed to be started");
356 - return NULL;
357 - }
358 -
359 - nd_log(NDLS_COLLECTORS, NDLP_INFO, "SPAWN PARENT: process started");
360 -
361 - return item.instance;
362 -}
363 -
364 -int spawn_server_exec_kill(SPAWN_SERVER *server __maybe_unused, SPAWN_INSTANCE *si) {
365 - if(!si) return -1;
366 -
367 - // close all pipe descriptors to force the child to exit
368 - if(si->read_fd != -1) { close(si->read_fd); si->read_fd = -1; }
369 - if(si->write_fd != -1) { close(si->write_fd); si->write_fd = -1; }
370 -
371 - if (uv_process_kill(&si->process, SIGTERM)) {
372 - nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN PARENT: uv_process_kill() failed");
373 - return -1;
374 - }
375 -
376 - return spawn_server_exec_wait(server, si);
377 -}
378 -
379 -int spawn_server_exec_wait(SPAWN_SERVER *server __maybe_unused, SPAWN_INSTANCE *si) {
380 - if (!si) return -1;
381 -
382 - // close all pipe descriptors to force the child to exit
383 - if(si->read_fd != -1) { close(si->read_fd); si->read_fd = -1; }
384 - if(si->write_fd != -1) { close(si->write_fd); si->write_fd = -1; }
385 -
386 - // Wait for the process to exit
387 - uv_sem_wait(&si->sem);
388 - int exit_code = si->exit_code;
389 -
390 - uv_sem_destroy(&si->sem);
391 - freez(si);
392 - return exit_code;
393 -}
394 -
395 -#endif
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "spawn_server_internals.h"
4 +
5 +#if defined(SPAWN_SERVER_VERSION_UV)
6 +
7 +int spawn_server_instance_read_fd(SPAWN_INSTANCE *si) { return si->read_fd; }
8 +int spawn_server_instance_write_fd(SPAWN_INSTANCE *si) { return si->write_fd; }
9 +void spawn_server_instance_read_fd_unset(SPAWN_INSTANCE *si) { si->read_fd = -1; }
10 +void spawn_server_instance_write_fd_unset(SPAWN_INSTANCE *si) { si->write_fd = -1; }
11 +pid_t spawn_server_instance_pid(SPAWN_INSTANCE *si) { return uv_process_get_pid(&si->process); }
12 +
13 +typedef struct work_item {
14 + int stderr_fd;
15 + const char **argv;
16 + uv_sem_t sem;
17 + SPAWN_INSTANCE *instance;
18 + struct work_item *prev;
19 + struct work_item *next;
20 +} work_item;
21 +
22 +int uv_errno_to_errno(int uv_err) {
23 + switch (uv_err) {
24 + case 0: return 0;
25 + case UV_E2BIG: return E2BIG;
26 + case UV_EACCES: return EACCES;
27 + case UV_EADDRINUSE: return EADDRINUSE;
28 + case UV_EADDRNOTAVAIL: return EADDRNOTAVAIL;
29 + case UV_EAFNOSUPPORT: return EAFNOSUPPORT;
30 + case UV_EAGAIN: return EAGAIN;
31 + case UV_EAI_ADDRFAMILY: return EAI_ADDRFAMILY;
32 + case UV_EAI_AGAIN: return EAI_AGAIN;
33 + case UV_EAI_BADFLAGS: return EAI_BADFLAGS;
34 +#if defined(EAI_CANCELED)
35 + case UV_EAI_CANCELED: return EAI_CANCELED;
36 +#endif
37 + case UV_EAI_FAIL: return EAI_FAIL;
38 + case UV_EAI_FAMILY: return EAI_FAMILY;
39 + case UV_EAI_MEMORY: return EAI_MEMORY;
40 + case UV_EAI_NODATA: return EAI_NODATA;
41 + case UV_EAI_NONAME: return EAI_NONAME;
42 + case UV_EAI_OVERFLOW: return EAI_OVERFLOW;
43 + case UV_EAI_SERVICE: return EAI_SERVICE;
44 + case UV_EAI_SOCKTYPE: return EAI_SOCKTYPE;
45 + case UV_EALREADY: return EALREADY;
46 + case UV_EBADF: return EBADF;
47 + case UV_EBUSY: return EBUSY;
48 + case UV_ECANCELED: return ECANCELED;
49 + case UV_ECHARSET: return EILSEQ; // No direct mapping, using EILSEQ
50 + case UV_ECONNABORTED: return ECONNABORTED;
51 + case UV_ECONNREFUSED: return ECONNREFUSED;
52 + case UV_ECONNRESET: return ECONNRESET;
53 + case UV_EDESTADDRREQ: return EDESTADDRREQ;
54 + case UV_EEXIST: return EEXIST;
55 + case UV_EFAULT: return EFAULT;
56 + case UV_EFBIG: return EFBIG;
57 + case UV_EHOSTUNREACH: return EHOSTUNREACH;
58 + case UV_EINTR: return EINTR;
59 + case UV_EINVAL: return EINVAL;
60 + case UV_EIO: return EIO;
61 + case UV_EISCONN: return EISCONN;
62 + case UV_EISDIR: return EISDIR;
63 + case UV_ELOOP: return ELOOP;
64 + case UV_EMFILE: return EMFILE;
65 + case UV_EMSGSIZE: return EMSGSIZE;
66 + case UV_ENAMETOOLONG: return ENAMETOOLONG;
67 + case UV_ENETDOWN: return ENETDOWN;
68 + case UV_ENETUNREACH: return ENETUNREACH;
69 + case UV_ENFILE: return ENFILE;
70 + case UV_ENOBUFS: return ENOBUFS;
71 + case UV_ENODEV: return ENODEV;
72 + case UV_ENOENT: return ENOENT;
73 + case UV_ENOMEM: return ENOMEM;
74 + case UV_ENONET: return ENONET;
75 + case UV_ENOSPC: return ENOSPC;
76 + case UV_ENOSYS: return ENOSYS;
77 + case UV_ENOTCONN: return ENOTCONN;
78 + case UV_ENOTDIR: return ENOTDIR;
79 + case UV_ENOTEMPTY: return ENOTEMPTY;
80 + case UV_ENOTSOCK: return ENOTSOCK;
81 + case UV_ENOTSUP: return ENOTSUP;
82 + case UV_ENOTTY: return ENOTTY;
83 + case UV_ENXIO: return ENXIO;
84 + case UV_EPERM: return EPERM;
85 + case UV_EPIPE: return EPIPE;
86 + case UV_EPROTO: return EPROTO;
87 + case UV_EPROTONOSUPPORT: return EPROTONOSUPPORT;
88 + case UV_EPROTOTYPE: return EPROTOTYPE;
89 + case UV_ERANGE: return ERANGE;
90 + case UV_EROFS: return EROFS;
91 + case UV_ESHUTDOWN: return ESHUTDOWN;
92 + case UV_ESPIPE: return ESPIPE;
93 + case UV_ESRCH: return ESRCH;
94 + case UV_ETIMEDOUT: return ETIMEDOUT;
95 + case UV_ETXTBSY: return ETXTBSY;
96 + case UV_EXDEV: return EXDEV;
97 + default: return EINVAL; // Use EINVAL for unknown libuv errors
98 + }
99 +}
100 +
101 +static void posix_unmask_sigchld_on_thread(void) {
102 + sigset_t sigset;
103 + sigemptyset(&sigset); // Initialize the signal set to empty
104 + sigaddset(&sigset, SIGCHLD); // Add SIGCHLD to the set
105 +
106 + if(pthread_sigmask(SIG_UNBLOCK, &sigset, NULL) != 0)
107 + netdata_log_error("SPAWN SERVER: cannot unmask SIGCHLD");
108 +}
109 +
110 +static void server_thread(void *arg) {
111 + SPAWN_SERVER *server = (SPAWN_SERVER *)arg;
112 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
113 + "SPAWN SERVER: started");
114 +
115 + // this thread needs to process SIGCHLD (by libuv)
116 + // otherwise the on_exit() callback is never run
117 + posix_unmask_sigchld_on_thread();
118 +
119 + // run the event loop
120 + uv_run(server->loop, UV_RUN_DEFAULT);
121 +
122 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
123 + "SPAWN SERVER: ended");
124 +}
125 +
126 +static void on_process_exit(uv_process_t *req, int64_t exit_status, int term_signal) {
127 + SPAWN_INSTANCE *si = (SPAWN_INSTANCE *)req->data;
128 + si->exit_code = (int)(term_signal ? term_signal : exit_status << 8);
129 + uv_close((uv_handle_t *)req, NULL); // Properly close the process handle
130 +
131 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
132 + "SPAWN SERVER: process with pid %d exited with code %d and term_signal %d",
133 + si->child_pid, (int)exit_status, term_signal);
134 +
135 + uv_sem_post(&si->sem); // Signal that the process has exited
136 +}
137 +
138 +static SPAWN_INSTANCE *spawn_process_with_libuv(uv_loop_t *loop, int stderr_fd, const char **argv) {
139 + SPAWN_INSTANCE *si = NULL;
140 + bool si_sem_init = false;
141 +
142 + int stdin_pipe[2] = { -1, -1 };
143 + int stdout_pipe[2] = { -1, -1 };
144 +
145 + if (pipe(stdin_pipe) == -1) {
146 + nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: stdin pipe() failed");
147 + goto cleanup;
148 + }
149 +
150 + if (pipe(stdout_pipe) == -1) {
151 + nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: stdout pipe() failed");
152 + goto cleanup;
153 + }
154 +
155 + si = callocz(1, sizeof(SPAWN_INSTANCE));
156 + si->exit_code = -1;
157 +
158 + if (uv_sem_init(&si->sem, 0)) {
159 + nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: uv_sem_init() failed");
160 + goto cleanup;
161 + }
162 + si_sem_init = true;
163 +
164 + uv_stdio_container_t stdio[3] = { 0 };
165 + stdio[0].flags = UV_INHERIT_FD;
166 + stdio[0].data.fd = stdin_pipe[PIPE_READ];
167 + stdio[1].flags = UV_INHERIT_FD;
168 + stdio[1].data.fd = stdout_pipe[PIPE_WRITE];
169 + stdio[2].flags = UV_INHERIT_FD;
170 + stdio[2].data.fd = stderr_fd;
171 +
172 + uv_process_options_t options = { 0 };
173 + options.stdio_count = 3;
174 + options.stdio = stdio;
175 + options.exit_cb = on_process_exit;
176 + options.file = argv[0];
177 + options.args = (char **)argv;
178 + options.env = (char **)environ;
179 +
180 + // uv_spawn() does not close all other open file descriptors
181 + // we have to close them manually
182 + int fds[3] = { stdio[0].data.fd, stdio[1].data.fd, stdio[2].data.fd };
183 + os_close_all_non_std_open_fds_except(fds, 3, CLOSE_RANGE_CLOEXEC);
184 +
185 + int rc = uv_spawn(loop, &si->process, &options);
186 + if (rc) {
187 + errno = uv_errno_to_errno(rc);
188 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
189 + "SPAWN SERVER: uv_spawn() failed with error %s, %s",
190 + uv_err_name(rc), uv_strerror(rc));
191 + goto cleanup;
192 + }
193 +
194 + // Successfully spawned
195 +
196 + // get the pid of the process spawned
197 + si->child_pid = uv_process_get_pid(&si->process);
198 +
199 + // on_process_exit() needs this to find the si
200 + si->process.data = si;
201 +
202 + nd_log(NDLS_COLLECTORS, NDLP_INFO,
203 + "SPAWN SERVER: process created with pid %d", si->child_pid);
204 +
205 + // close the child sides of the pipes
206 + close(stdin_pipe[PIPE_READ]);
207 + si->write_fd = stdin_pipe[PIPE_WRITE];
208 + si->read_fd = stdout_pipe[PIPE_READ];
209 + close(stdout_pipe[PIPE_WRITE]);
210 +
211 + return si;
212 +
213 +cleanup:
214 + if(stdin_pipe[PIPE_READ] != -1) close(stdin_pipe[PIPE_READ]);
215 + if(stdin_pipe[PIPE_WRITE] != -1) close(stdin_pipe[PIPE_WRITE]);
216 + if(stdout_pipe[PIPE_READ] != -1) close(stdout_pipe[PIPE_READ]);
217 + if(stdout_pipe[PIPE_WRITE] != -1) close(stdout_pipe[PIPE_WRITE]);
218 + if(si) {
219 + if(si_sem_init)
220 + uv_sem_destroy(&si->sem);
221 +
222 + freez(si);
223 + }
224 + return NULL;
225 +}
226 +
227 +static void async_callback(uv_async_t *handle) {
228 + nd_log(NDLS_COLLECTORS, NDLP_INFO, "SPAWN SERVER: dequeue commands started");
229 + SPAWN_SERVER *server = (SPAWN_SERVER *)handle->data;
230 +
231 + // Check if the server is stopping
232 + if (__atomic_load_n(&server->stopping, __ATOMIC_RELAXED)) {
233 + nd_log(NDLS_COLLECTORS, NDLP_INFO, "SPAWN SERVER: stopping...");
234 + uv_stop(server->loop);
235 + return;
236 + }
237 +
238 + work_item *item;
239 + spinlock_lock(&server->spinlock);
240 + while (server->work_queue) {
241 + item = server->work_queue;
242 + DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(server->work_queue, item, prev, next);
243 + spinlock_unlock(&server->spinlock);
244 +
245 + item->instance = spawn_process_with_libuv(server->loop, item->stderr_fd, item->argv);
246 + uv_sem_post(&item->sem);
247 +
248 + spinlock_lock(&server->spinlock);
249 + }
250 + spinlock_unlock(&server->spinlock);
251 +
252 + nd_log(NDLS_COLLECTORS, NDLP_INFO, "SPAWN SERVER: dequeue commands done");
253 +}
254 +
255 +
256 +SPAWN_SERVER* spawn_server_create(SPAWN_SERVER_OPTIONS options __maybe_unused, const char *name, spawn_request_callback_t cb __maybe_unused, int argc __maybe_unused, const char **argv __maybe_unused) {
257 + SPAWN_SERVER* server = callocz(1, sizeof(SPAWN_SERVER));
258 + spinlock_init(&server->spinlock);
259 +
260 + if (name)
261 + server->name = strdupz(name);
262 + else
263 + server->name = strdupz("unnamed");
264 +
265 + server->loop = callocz(1, sizeof(uv_loop_t));
266 + if (uv_loop_init(server->loop)) {
267 + nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN PARENT: uv_loop_init() failed");
268 + freez(server->loop);
269 + freez((void *)server->name);
270 + freez(server);
271 + return NULL;
272 + }
273 +
274 + if (uv_async_init(server->loop, &server->async, async_callback)) {
275 + nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN PARENT: uv_async_init() failed");
276 + uv_loop_close(server->loop);
277 + freez(server->loop);
278 + freez((void *)server->name);
279 + freez(server);
280 + return NULL;
281 + }
282 + server->async.data = server;
283 +
284 + if (uv_thread_create(&server->thread, server_thread, server)) {
285 + nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN PARENT: uv_thread_create() failed");
286 + uv_close((uv_handle_t*)&server->async, NULL);
287 + uv_loop_close(server->loop);
288 + freez(server->loop);
289 + freez((void *)server->name);
290 + freez(server);
291 + return NULL;
292 + }
293 +
294 + return server;
295 +}
296 +
297 +static void close_handle(uv_handle_t* handle, void* arg __maybe_unused) {
298 + if (!uv_is_closing(handle)) {
299 + uv_close(handle, NULL);
300 + }
301 +}
302 +
303 +void spawn_server_destroy(SPAWN_SERVER *server) {
304 + if (!server) return;
305 +
306 + __atomic_store_n(&server->stopping, true, __ATOMIC_RELAXED);
307 +
308 + // Trigger the async callback to stop the event loop
309 + uv_async_send(&server->async);
310 +
311 + // Wait for the server thread to finish
312 + uv_thread_join(&server->thread);
313 +
314 + uv_stop(server->loop);
315 + uv_close((uv_handle_t*)&server->async, NULL);
316 +
317 + // Walk through and close any remaining handles
318 + uv_walk(server->loop, close_handle, NULL);
319 +
320 + uv_loop_close(server->loop);
321 + freez(server->loop);
322 + freez((void *)server->name);
323 + freez(server);
324 +}
325 +
326 +SPAWN_INSTANCE* spawn_server_exec(SPAWN_SERVER *server, int stderr_fd __maybe_unused, int custom_fd __maybe_unused, const char **argv, const void *data __maybe_unused, size_t data_size __maybe_unused, SPAWN_INSTANCE_TYPE type) {
327 + if (type != SPAWN_INSTANCE_TYPE_EXEC)
328 + return NULL;
329 +
330 + work_item item = { 0 };
331 + item.stderr_fd = stderr_fd;
332 + item.argv = argv;
333 +
334 + if (uv_sem_init(&item.sem, 0)) {
335 + nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN PARENT: uv_sem_init() failed");
336 + return NULL;
337 + }
338 +
339 + spinlock_lock(&server->spinlock);
340 + // item is in the stack, but the server will remove it before sending to us
341 + // the semaphore, so it is safe to have the item in the stack.
342 + work_item *item_ptr = &item;
343 + DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(server->work_queue, item_ptr, prev, next);
344 + spinlock_unlock(&server->spinlock);
345 +
346 + uv_async_send(&server->async);
347 +
348 + nd_log(NDLS_COLLECTORS, NDLP_INFO, "SPAWN PARENT: queued command");
349 +
350 + // Wait for the command to be executed
351 + uv_sem_wait(&item.sem);
352 + uv_sem_destroy(&item.sem);
353 +
354 + if (!item.instance) {
355 + nd_log(NDLS_COLLECTORS, NDLP_INFO, "SPAWN PARENT: process failed to be started");
356 + return NULL;
357 + }
358 +
359 + nd_log(NDLS_COLLECTORS, NDLP_INFO, "SPAWN PARENT: process started");
360 +
361 + return item.instance;
362 +}
363 +
364 +int spawn_server_exec_kill(SPAWN_SERVER *server __maybe_unused, SPAWN_INSTANCE *si) {
365 + if(!si) return -1;
366 +
367 + // close all pipe descriptors to force the child to exit
368 + if(si->read_fd != -1) { close(si->read_fd); si->read_fd = -1; }
369 + if(si->write_fd != -1) { close(si->write_fd); si->write_fd = -1; }
370 +
371 + if (uv_process_kill(&si->process, SIGTERM)) {
372 + nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN PARENT: uv_process_kill() failed");
373 + return -1;
374 + }
375 +
376 + return spawn_server_exec_wait(server, si);
377 +}
378 +
379 +int spawn_server_exec_wait(SPAWN_SERVER *server __maybe_unused, SPAWN_INSTANCE *si) {
380 + if (!si) return -1;
381 +
382 + // close all pipe descriptors to force the child to exit
383 + if(si->read_fd != -1) { close(si->read_fd); si->read_fd = -1; }
384 + if(si->write_fd != -1) { close(si->write_fd); si->write_fd = -1; }
385 +
386 + // Wait for the process to exit
387 + uv_sem_wait(&si->sem);
388 + int exit_code = si->exit_code;
389 +
390 + uv_sem_destroy(&si->sem);
391 + freez(si);
392 + return exit_code;
393 +}
394 +
395 +#endif
src/libnetdata/spawn_server/spawn_server_windows.c
+417 -417
@@ -1,417 +1,417 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "spawn_server_internals.h"
4 -
5 -#if defined(SPAWN_SERVER_VERSION_WINDOWS)
6 -
7 -int spawn_server_instance_read_fd(SPAWN_INSTANCE *si) { return si->read_fd; }
8 -int spawn_server_instance_write_fd(SPAWN_INSTANCE *si) { return si->write_fd; }
9 -void spawn_server_instance_read_fd_unset(SPAWN_INSTANCE *si) { si->read_fd = -1; }
10 -void spawn_server_instance_write_fd_unset(SPAWN_INSTANCE *si) { si->write_fd = -1; }
11 -
12 -pid_t spawn_server_instance_pid(SPAWN_INSTANCE *si) {
13 - if(si->child_pid != -1)
14 - return si->child_pid;
15 -
16 - return (pid_t)si->dwProcessId;
17 -}
18 -
19 -static void update_cygpath_env(void) {
20 - static volatile bool done = false;
21 -
22 - if(done) return;
23 - done = true;
24 -
25 - char win_path[MAX_PATH];
26 -
27 - // Convert Cygwin root path to Windows path
28 - cygwin_conv_path(CCP_POSIX_TO_WIN_A, "/", win_path, sizeof(win_path));
29 -
30 - nd_setenv("NETDATA_CYGWIN_BASE_PATH", win_path, 1);
31 -
32 - nd_log(NDLS_COLLECTORS, NDLP_INFO, "Cygwin/MSYS2 base path set to '%s'", win_path);
33 -}
34 -
35 -SPAWN_SERVER* spawn_server_create(SPAWN_SERVER_OPTIONS options __maybe_unused, const char *name, spawn_request_callback_t cb __maybe_unused, int argc __maybe_unused, const char **argv __maybe_unused) {
36 - update_cygpath_env();
37 -
38 - SPAWN_SERVER* server = callocz(1, sizeof(SPAWN_SERVER));
39 - if(name)
40 - server->name = strdupz(name);
41 - else
42 - server->name = strdupz("unnamed");
43 - return server;
44 -}
45 -
46 -void spawn_server_destroy(SPAWN_SERVER *server) {
47 - if (server) {
48 - freez((void *)server->name);
49 - freez(server);
50 - }
51 -}
52 -
53 -static BUFFER *argv_to_windows(const char **argv) {
54 - BUFFER *wb = buffer_create(0, NULL);
55 -
56 - // argv[0] is the path
57 - char b[strlen(argv[0]) * 2 + 1024];
58 - cygwin_conv_path(CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE, argv[0], b, sizeof(b));
59 -
60 - for(size_t i = 0; argv[i] ;i++) {
61 - const char *s = (i == 0) ? b : argv[i];
62 - size_t len = strlen(s);
63 - buffer_need_bytes(wb, len * 2 + 1);
64 -
65 - bool needs_quotes = false;
66 - for(const char *c = s; !needs_quotes && *c ; c++) {
67 - switch(*c) {
68 - case ' ':
69 - case '\v':
70 - case '\t':
71 - case '\n':
72 - case '"':
73 - needs_quotes = true;
74 - break;
75 -
76 - default:
77 - break;
78 - }
79 - }
80 -
81 - if(buffer_strlen(wb)) {
82 - if (needs_quotes)
83 - buffer_strcat(wb, " \"");
84 - else
85 - buffer_putc(wb, ' ');
86 - }
87 -
88 - for(const char *c = s; *c ; c++) {
89 - switch(*c) {
90 - case '"':
91 - buffer_putc(wb, '\\');
92 - // fall through
93 -
94 - default:
95 - buffer_putc(wb, *c);
96 - break;
97 - }
98 - }
99 -
100 - if(needs_quotes)
101 - buffer_strcat(wb, "\"");
102 - }
103 -
104 - return wb;
105 -}
106 -
107 -int set_fd_blocking(int fd) {
108 - int flags = fcntl(fd, F_GETFL, 0);
109 - if (flags == -1) {
110 - nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN PARENT: fcntl(F_GETFL) failed");
111 - return -1;
112 - }
113 -
114 - flags &= ~O_NONBLOCK;
115 - if (fcntl(fd, F_SETFL, flags) == -1) {
116 - nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN PARENT: fcntl(F_SETFL) failed");
117 - return -1;
118 - }
119 -
120 - return 0;
121 -}
122 -
123 -//static void print_environment_block(char *env_block) {
124 -// if (env_block == NULL) {
125 -// fprintf(stderr, "Environment block is NULL\n");
126 -// return;
127 -// }
128 -//
129 -// char *env = env_block;
130 -// while (*env) {
131 -// fprintf(stderr, "ENVIRONMENT: %s\n", env);
132 -// // Move to the next string in the block
133 -// env += strlen(env) + 1;
134 -// }
135 -//}
136 -
137 -SPAWN_INSTANCE* spawn_server_exec(SPAWN_SERVER *server, int stderr_fd, int custom_fd __maybe_unused, const char **argv, const void *data __maybe_unused, size_t data_size __maybe_unused, SPAWN_INSTANCE_TYPE type) {
138 - static SPINLOCK spinlock = NETDATA_SPINLOCK_INITIALIZER;
139 -
140 - if (type != SPAWN_INSTANCE_TYPE_EXEC)
141 - return NULL;
142 -
143 - int pipe_stdin[2] = { -1, -1 }, pipe_stdout[2] = { -1, -1 };
144 -
145 - errno_clear();
146 -
147 - SPAWN_INSTANCE *instance = callocz(1, sizeof(*instance));
148 - instance->request_id = __atomic_add_fetch(&server->request_id, 1, __ATOMIC_RELAXED);
149 -
150 - CLEAN_BUFFER *wb = argv_to_windows(argv);
151 - char *command = (char *)buffer_tostring(wb);
152 -
153 - if (pipe(pipe_stdin) == -1) {
154 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
155 - "SPAWN PARENT: Cannot create stdin pipe() for request No %zu, command: %s",
156 - instance->request_id, command);
157 - goto cleanup;
158 - }
159 -
160 - if (pipe(pipe_stdout) == -1) {
161 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
162 - "SPAWN PARENT: Cannot create stdout pipe() for request No %zu, command: %s",
163 - instance->request_id, command);
164 - goto cleanup;
165 - }
166 -
167 - // Ensure pipes are in blocking mode
168 - if (set_fd_blocking(pipe_stdin[PIPE_READ]) == -1 || set_fd_blocking(pipe_stdin[PIPE_WRITE]) == -1 ||
169 - set_fd_blocking(pipe_stdout[PIPE_READ]) == -1 || set_fd_blocking(pipe_stdout[PIPE_WRITE]) == -1) {
170 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
171 - "SPAWN PARENT: Failed to set blocking I/O on pipes for request No %zu, command: %s",
172 - instance->request_id, command);
173 - }
174 -
175 - // do not run multiple times this section
176 - // to prevent handles leaking
177 - spinlock_lock(&spinlock);
178 -
179 - // Convert POSIX file descriptors to Windows handles
180 - HANDLE stdin_read_handle = (HANDLE)_get_osfhandle(pipe_stdin[PIPE_READ]);
181 - HANDLE stdout_write_handle = (HANDLE)_get_osfhandle(pipe_stdout[PIPE_WRITE]);
182 - HANDLE stderr_handle = (HANDLE)_get_osfhandle(stderr_fd);
183 -
184 - if (stdin_read_handle == INVALID_HANDLE_VALUE || stdout_write_handle == INVALID_HANDLE_VALUE || stderr_handle == INVALID_HANDLE_VALUE) {
185 - spinlock_unlock(&spinlock);
186 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
187 - "SPAWN PARENT: Invalid handle value(s) for request No %zu, command: %s",
188 - instance->request_id, command);
189 - goto cleanup;
190 - }
191 -
192 - // Set handle inheritance
193 - if (!SetHandleInformation(stdin_read_handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT) ||
194 - !SetHandleInformation(stdout_write_handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT) ||
195 - !SetHandleInformation(stderr_handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) {
196 - spinlock_unlock(&spinlock);
197 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
198 - "SPAWN PARENT: Cannot set handle(s) inheritance for request No %zu, command: %s",
199 - instance->request_id, command);
200 - goto cleanup;
201 - }
202 -
203 - // Set up the STARTUPINFO structure
204 - STARTUPINFO si;
205 - PROCESS_INFORMATION pi;
206 - ZeroMemory(&si, sizeof(si));
207 - si.cb = sizeof(si);
208 - si.dwFlags = STARTF_USESTDHANDLES;
209 - si.hStdInput = stdin_read_handle;
210 - si.hStdOutput = stdout_write_handle;
211 - si.hStdError = stderr_handle;
212 -
213 - // Retrieve the current environment block
214 - char* env_block = GetEnvironmentStrings();
215 -// print_environment_block(env_block);
216 -
217 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
218 - "SPAWN PARENT: Running request No %zu, command: '%s'",
219 - instance->request_id, command);
220 -
221 - int fds[3] = { pipe_stdin[PIPE_READ], pipe_stdout[PIPE_WRITE], stderr_fd };
222 - os_close_all_non_std_open_fds_except(fds, 3, CLOSE_RANGE_CLOEXEC);
223 -
224 - // Spawn the process
225 - errno_clear();
226 - if (!CreateProcess(NULL, command, NULL, NULL, TRUE, 0, env_block, NULL, &si, &pi)) {
227 - spinlock_unlock(&spinlock);
228 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
229 - "SPAWN PARENT: cannot CreateProcess() for request No %zu, command: %s",
230 - instance->request_id, command);
231 - goto cleanup;
232 - }
233 -
234 - FreeEnvironmentStrings(env_block);
235 -
236 - // When we create a process with the CreateProcess function, it returns two handles:
237 - // - one for the process (pi.hProcess) and
238 - // - one for the primary thread of the new process (pi.hThread).
239 - // Both of these handles need to be explicitly closed when they are no longer needed.
240 - CloseHandle(pi.hThread);
241 -
242 - // end of the critical section
243 - spinlock_unlock(&spinlock);
244 -
245 - // Close unused pipe ends
246 - close(pipe_stdin[PIPE_READ]); pipe_stdin[PIPE_READ] = -1;
247 - close(pipe_stdout[PIPE_WRITE]); pipe_stdout[PIPE_WRITE] = -1;
248 -
249 - // Store process information in instance
250 - instance->dwProcessId = pi.dwProcessId;
251 - instance->child_pid = cygwin_winpid_to_pid((pid_t)pi.dwProcessId);
252 - instance->process_handle = pi.hProcess;
253 -
254 - // Convert handles to POSIX file descriptors
255 - instance->write_fd = pipe_stdin[PIPE_WRITE];
256 - instance->read_fd = pipe_stdout[PIPE_READ];
257 -
258 - errno_clear();
259 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
260 - "SPAWN PARENT: created process for request No %zu, pid %d (winpid %d), command: %s",
261 - instance->request_id, (int)instance->child_pid, (int)pi.dwProcessId, command);
262 -
263 - return instance;
264 -
265 - cleanup:
266 - if (pipe_stdin[PIPE_READ] >= 0) close(pipe_stdin[PIPE_READ]);
267 - if (pipe_stdin[PIPE_WRITE] >= 0) close(pipe_stdin[PIPE_WRITE]);
268 - if (pipe_stdout[PIPE_READ] >= 0) close(pipe_stdout[PIPE_READ]);
269 - if (pipe_stdout[PIPE_WRITE] >= 0) close(pipe_stdout[PIPE_WRITE]);
270 - freez(instance);
271 - return NULL;
272 -}
273 -
274 -static char* GetErrorString(DWORD errorCode) {
275 - DWORD lastError = GetLastError();
276 -
277 - LPVOID lpMsgBuf;
278 - DWORD bufLen = FormatMessage(
279 - FORMAT_MESSAGE_ALLOCATE_BUFFER |
280 - FORMAT_MESSAGE_FROM_SYSTEM |
281 - FORMAT_MESSAGE_IGNORE_INSERTS,
282 - NULL,
283 - errorCode,
284 - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
285 - (LPTSTR) &lpMsgBuf,
286 - 0, NULL );
287 -
288 - SetLastError(lastError);
289 -
290 - if (bufLen) {
291 - char* errorString = (char*)LocalAlloc(LMEM_FIXED, bufLen + 1);
292 - if (errorString) {
293 - strcpy(errorString, (char*)lpMsgBuf);
294 - }
295 - LocalFree(lpMsgBuf);
296 - return errorString;
297 - }
298 -
299 - return NULL;
300 -}
301 -
302 -static void TerminateChildProcesses(SPAWN_INSTANCE *si) {
303 - HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
304 - if (hSnapshot == INVALID_HANDLE_VALUE)
305 - return;
306 -
307 - PROCESSENTRY32 pe;
308 - pe.dwSize = sizeof(PROCESSENTRY32);
309 -
310 - if (Process32First(hSnapshot, &pe)) {
311 - do {
312 - if (pe.th32ParentProcessID == si->dwProcessId) {
313 - HANDLE hChildProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pe.th32ProcessID);
314 - if (hChildProcess) {
315 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
316 - "SPAWN PARENT: killing subprocess %u of request No %zu, pid %d (winpid %u)",
317 - pe.th32ProcessID, si->request_id, (int)si->child_pid, si->dwProcessId);
318 -
319 - TerminateProcess(hChildProcess, STATUS_CONTROL_C_EXIT);
320 - CloseHandle(hChildProcess);
321 - }
322 - }
323 - } while (Process32Next(hSnapshot, &pe));
324 - }
325 -
326 - CloseHandle(hSnapshot);
327 -}
328 -
329 -int map_status_code_to_signal(DWORD status_code) {
330 - switch (status_code) {
331 - case STATUS_ACCESS_VIOLATION:
332 - return SIGSEGV;
333 - case STATUS_ILLEGAL_INSTRUCTION:
334 - return SIGILL;
335 - case STATUS_FLOAT_DIVIDE_BY_ZERO:
336 - case STATUS_INTEGER_DIVIDE_BY_ZERO:
337 - case STATUS_ARRAY_BOUNDS_EXCEEDED:
338 - case STATUS_FLOAT_OVERFLOW:
339 - case STATUS_FLOAT_UNDERFLOW:
340 - case STATUS_FLOAT_INVALID_OPERATION:
341 - return SIGFPE;
342 - case STATUS_BREAKPOINT:
343 - case STATUS_SINGLE_STEP:
344 - return SIGTRAP;
345 - case STATUS_STACK_OVERFLOW:
346 - case STATUS_INVALID_HANDLE:
347 - case STATUS_INVALID_PARAMETER:
348 - case STATUS_NO_MEMORY:
349 - case STATUS_PRIVILEGED_INSTRUCTION:
350 - case STATUS_DLL_NOT_FOUND:
351 - case STATUS_DLL_INIT_FAILED:
352 - case STATUS_ORDINAL_NOT_FOUND:
353 - case STATUS_ENTRYPOINT_NOT_FOUND:
354 - case STATUS_CONTROL_STACK_VIOLATION:
355 - case STATUS_STACK_BUFFER_OVERRUN:
356 - case STATUS_ASSERTION_FAILURE:
357 - case STATUS_INVALID_CRUNTIME_PARAMETER:
358 - case STATUS_HEAP_CORRUPTION:
359 - return SIGABRT;
360 - case STATUS_CONTROL_C_EXIT:
361 - return SIGTERM; // we use this internally as such
362 - case STATUS_FATAL_APP_EXIT:
363 - return SIGTERM;
364 - default:
365 - return (status_code & 0xFF) << 8;
366 - }
367 -}
368 -
369 -int spawn_server_exec_kill(SPAWN_SERVER *server __maybe_unused, SPAWN_INSTANCE *si) {
370 - if(si->child_pid != -1 && kill(si->child_pid, SIGTERM) != 0)
371 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
372 - "SPAWN PARENT: child of request No %zu, pid %d (winpid %u), failed to be killed",
373 - si->request_id, (int)si->child_pid, si->dwProcessId);
374 -
375 - // this gives some warnings at the spawn-tester, but it is generally better
376 - // to have them, to avoid abnormal shutdown of the plugins
377 - if(si->read_fd != -1) { close(si->read_fd); si->read_fd = -1; }
378 - if(si->write_fd != -1) { close(si->write_fd); si->write_fd = -1; }
379 -
380 - errno_clear();
381 - if(TerminateProcess(si->process_handle, STATUS_CONTROL_C_EXIT) == 0)
382 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
383 - "SPAWN PARENT: child of request No %zu, pid %d (winpid %u), failed to be terminated",
384 - si->request_id, (int)si->child_pid, si->dwProcessId);
385 -
386 - errno_clear();
387 - TerminateChildProcesses(si);
388 -
389 - return spawn_server_exec_wait(server, si);
390 -}
391 -
392 -int spawn_server_exec_wait(SPAWN_SERVER *server __maybe_unused, SPAWN_INSTANCE *si) {
393 - if(si->read_fd != -1) { close(si->read_fd); si->read_fd = -1; }
394 - if(si->write_fd != -1) { close(si->write_fd); si->write_fd = -1; }
395 -
396 - // wait for the process to end
397 - WaitForSingleObject(si->process_handle, INFINITE);
398 -
399 - DWORD exit_code = -1;
400 - GetExitCodeProcess(si->process_handle, &exit_code);
401 - CloseHandle(si->process_handle);
402 -
403 - char *err = GetErrorString(exit_code);
404 -
405 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
406 - "SPAWN PARENT: child of request No %zu, pid %d (winpid %u), exited with code %u (0x%x): %s",
407 - si->request_id, (int)si->child_pid, si->dwProcessId,
408 - (unsigned)exit_code, (unsigned)exit_code, err ? err : "(no reason text)");
409 -
410 - if(err)
411 - LocalFree(err);
412 -
413 - freez(si);
414 - return map_status_code_to_signal(exit_code);
415 -}
416 -
417 -#endif
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "spawn_server_internals.h"
4 +
5 +#if defined(SPAWN_SERVER_VERSION_WINDOWS)
6 +
7 +int spawn_server_instance_read_fd(SPAWN_INSTANCE *si) { return si->read_fd; }
8 +int spawn_server_instance_write_fd(SPAWN_INSTANCE *si) { return si->write_fd; }
9 +void spawn_server_instance_read_fd_unset(SPAWN_INSTANCE *si) { si->read_fd = -1; }
10 +void spawn_server_instance_write_fd_unset(SPAWN_INSTANCE *si) { si->write_fd = -1; }
11 +
12 +pid_t spawn_server_instance_pid(SPAWN_INSTANCE *si) {
13 + if(si->child_pid != -1)
14 + return si->child_pid;
15 +
16 + return (pid_t)si->dwProcessId;
17 +}
18 +
19 +static void update_cygpath_env(void) {
20 + static volatile bool done = false;
21 +
22 + if(done) return;
23 + done = true;
24 +
25 + char win_path[MAX_PATH];
26 +
27 + // Convert Cygwin root path to Windows path
28 + cygwin_conv_path(CCP_POSIX_TO_WIN_A, "/", win_path, sizeof(win_path));
29 +
30 + nd_setenv("NETDATA_CYGWIN_BASE_PATH", win_path, 1);
31 +
32 + nd_log(NDLS_COLLECTORS, NDLP_INFO, "Cygwin/MSYS2 base path set to '%s'", win_path);
33 +}
34 +
35 +SPAWN_SERVER* spawn_server_create(SPAWN_SERVER_OPTIONS options __maybe_unused, const char *name, spawn_request_callback_t cb __maybe_unused, int argc __maybe_unused, const char **argv __maybe_unused) {
36 + update_cygpath_env();
37 +
38 + SPAWN_SERVER* server = callocz(1, sizeof(SPAWN_SERVER));
39 + if(name)
40 + server->name = strdupz(name);
41 + else
42 + server->name = strdupz("unnamed");
43 + return server;
44 +}
45 +
46 +void spawn_server_destroy(SPAWN_SERVER *server) {
47 + if (server) {
48 + freez((void *)server->name);
49 + freez(server);
50 + }
51 +}
52 +
53 +static BUFFER *argv_to_windows(const char **argv) {
54 + BUFFER *wb = buffer_create(0, NULL);
55 +
56 + // argv[0] is the path
57 + char b[strlen(argv[0]) * 2 + 1024];
58 + cygwin_conv_path(CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE, argv[0], b, sizeof(b));
59 +
60 + for(size_t i = 0; argv[i] ;i++) {
61 + const char *s = (i == 0) ? b : argv[i];
62 + size_t len = strlen(s);
63 + buffer_need_bytes(wb, len * 2 + 1);
64 +
65 + bool needs_quotes = false;
66 + for(const char *c = s; !needs_quotes && *c ; c++) {
67 + switch(*c) {
68 + case ' ':
69 + case '\v':
70 + case '\t':
71 + case '\n':
72 + case '"':
73 + needs_quotes = true;
74 + break;
75 +
76 + default:
77 + break;
78 + }
79 + }
80 +
81 + if(buffer_strlen(wb)) {
82 + if (needs_quotes)
83 + buffer_strcat(wb, " \"");
84 + else
85 + buffer_putc(wb, ' ');
86 + }
87 +
88 + for(const char *c = s; *c ; c++) {
89 + switch(*c) {
90 + case '"':
91 + buffer_putc(wb, '\\');
92 + // fall through
93 +
94 + default:
95 + buffer_putc(wb, *c);
96 + break;
97 + }
98 + }
99 +
100 + if(needs_quotes)
101 + buffer_strcat(wb, "\"");
102 + }
103 +
104 + return wb;
105 +}
106 +
107 +int set_fd_blocking(int fd) {
108 + int flags = fcntl(fd, F_GETFL, 0);
109 + if (flags == -1) {
110 + nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN PARENT: fcntl(F_GETFL) failed");
111 + return -1;
112 + }
113 +
114 + flags &= ~O_NONBLOCK;
115 + if (fcntl(fd, F_SETFL, flags) == -1) {
116 + nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN PARENT: fcntl(F_SETFL) failed");
117 + return -1;
118 + }
119 +
120 + return 0;
121 +}
122 +
123 +//static void print_environment_block(char *env_block) {
124 +// if (env_block == NULL) {
125 +// fprintf(stderr, "Environment block is NULL\n");
126 +// return;
127 +// }
128 +//
129 +// char *env = env_block;
130 +// while (*env) {
131 +// fprintf(stderr, "ENVIRONMENT: %s\n", env);
132 +// // Move to the next string in the block
133 +// env += strlen(env) + 1;
134 +// }
135 +//}
136 +
137 +SPAWN_INSTANCE* spawn_server_exec(SPAWN_SERVER *server, int stderr_fd, int custom_fd __maybe_unused, const char **argv, const void *data __maybe_unused, size_t data_size __maybe_unused, SPAWN_INSTANCE_TYPE type) {
138 + static SPINLOCK spinlock = NETDATA_SPINLOCK_INITIALIZER;
139 +
140 + if (type != SPAWN_INSTANCE_TYPE_EXEC)
141 + return NULL;
142 +
143 + int pipe_stdin[2] = { -1, -1 }, pipe_stdout[2] = { -1, -1 };
144 +
145 + errno_clear();
146 +
147 + SPAWN_INSTANCE *instance = callocz(1, sizeof(*instance));
148 + instance->request_id = __atomic_add_fetch(&server->request_id, 1, __ATOMIC_RELAXED);
149 +
150 + CLEAN_BUFFER *wb = argv_to_windows(argv);
151 + char *command = (char *)buffer_tostring(wb);
152 +
153 + if (pipe(pipe_stdin) == -1) {
154 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
155 + "SPAWN PARENT: Cannot create stdin pipe() for request No %zu, command: %s",
156 + instance->request_id, command);
157 + goto cleanup;
158 + }
159 +
160 + if (pipe(pipe_stdout) == -1) {
161 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
162 + "SPAWN PARENT: Cannot create stdout pipe() for request No %zu, command: %s",
163 + instance->request_id, command);
164 + goto cleanup;
165 + }
166 +
167 + // Ensure pipes are in blocking mode
168 + if (set_fd_blocking(pipe_stdin[PIPE_READ]) == -1 || set_fd_blocking(pipe_stdin[PIPE_WRITE]) == -1 ||
169 + set_fd_blocking(pipe_stdout[PIPE_READ]) == -1 || set_fd_blocking(pipe_stdout[PIPE_WRITE]) == -1) {
170 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
171 + "SPAWN PARENT: Failed to set blocking I/O on pipes for request No %zu, command: %s",
172 + instance->request_id, command);
173 + }
174 +
175 + // do not run multiple times this section
176 + // to prevent handles leaking
177 + spinlock_lock(&spinlock);
178 +
179 + // Convert POSIX file descriptors to Windows handles
180 + HANDLE stdin_read_handle = (HANDLE)_get_osfhandle(pipe_stdin[PIPE_READ]);
181 + HANDLE stdout_write_handle = (HANDLE)_get_osfhandle(pipe_stdout[PIPE_WRITE]);
182 + HANDLE stderr_handle = (HANDLE)_get_osfhandle(stderr_fd);
183 +
184 + if (stdin_read_handle == INVALID_HANDLE_VALUE || stdout_write_handle == INVALID_HANDLE_VALUE || stderr_handle == INVALID_HANDLE_VALUE) {
185 + spinlock_unlock(&spinlock);
186 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
187 + "SPAWN PARENT: Invalid handle value(s) for request No %zu, command: %s",
188 + instance->request_id, command);
189 + goto cleanup;
190 + }
191 +
192 + // Set handle inheritance
193 + if (!SetHandleInformation(stdin_read_handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT) ||
194 + !SetHandleInformation(stdout_write_handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT) ||
195 + !SetHandleInformation(stderr_handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) {
196 + spinlock_unlock(&spinlock);
197 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
198 + "SPAWN PARENT: Cannot set handle(s) inheritance for request No %zu, command: %s",
199 + instance->request_id, command);
200 + goto cleanup;
201 + }
202 +
203 + // Set up the STARTUPINFO structure
204 + STARTUPINFO si;
205 + PROCESS_INFORMATION pi;
206 + ZeroMemory(&si, sizeof(si));
207 + si.cb = sizeof(si);
208 + si.dwFlags = STARTF_USESTDHANDLES;
209 + si.hStdInput = stdin_read_handle;
210 + si.hStdOutput = stdout_write_handle;
211 + si.hStdError = stderr_handle;
212 +
213 + // Retrieve the current environment block
214 + char* env_block = GetEnvironmentStrings();
215 +// print_environment_block(env_block);
216 +
217 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
218 + "SPAWN PARENT: Running request No %zu, command: '%s'",
219 + instance->request_id, command);
220 +
221 + int fds[3] = { pipe_stdin[PIPE_READ], pipe_stdout[PIPE_WRITE], stderr_fd };
222 + os_close_all_non_std_open_fds_except(fds, 3, CLOSE_RANGE_CLOEXEC);
223 +
224 + // Spawn the process
225 + errno_clear();
226 + if (!CreateProcess(NULL, command, NULL, NULL, TRUE, 0, env_block, NULL, &si, &pi)) {
227 + spinlock_unlock(&spinlock);
228 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
229 + "SPAWN PARENT: cannot CreateProcess() for request No %zu, command: %s",
230 + instance->request_id, command);
231 + goto cleanup;
232 + }
233 +
234 + FreeEnvironmentStrings(env_block);
235 +
236 + // When we create a process with the CreateProcess function, it returns two handles:
237 + // - one for the process (pi.hProcess) and
238 + // - one for the primary thread of the new process (pi.hThread).
239 + // Both of these handles need to be explicitly closed when they are no longer needed.
240 + CloseHandle(pi.hThread);
241 +
242 + // end of the critical section
243 + spinlock_unlock(&spinlock);
244 +
245 + // Close unused pipe ends
246 + close(pipe_stdin[PIPE_READ]); pipe_stdin[PIPE_READ] = -1;
247 + close(pipe_stdout[PIPE_WRITE]); pipe_stdout[PIPE_WRITE] = -1;
248 +
249 + // Store process information in instance
250 + instance->dwProcessId = pi.dwProcessId;
251 + instance->child_pid = cygwin_winpid_to_pid((pid_t)pi.dwProcessId);
252 + instance->process_handle = pi.hProcess;
253 +
254 + // Convert handles to POSIX file descriptors
255 + instance->write_fd = pipe_stdin[PIPE_WRITE];
256 + instance->read_fd = pipe_stdout[PIPE_READ];
257 +
258 + errno_clear();
259 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
260 + "SPAWN PARENT: created process for request No %zu, pid %d (winpid %d), command: %s",
261 + instance->request_id, (int)instance->child_pid, (int)pi.dwProcessId, command);
262 +
263 + return instance;
264 +
265 + cleanup:
266 + if (pipe_stdin[PIPE_READ] >= 0) close(pipe_stdin[PIPE_READ]);
267 + if (pipe_stdin[PIPE_WRITE] >= 0) close(pipe_stdin[PIPE_WRITE]);
268 + if (pipe_stdout[PIPE_READ] >= 0) close(pipe_stdout[PIPE_READ]);
269 + if (pipe_stdout[PIPE_WRITE] >= 0) close(pipe_stdout[PIPE_WRITE]);
270 + freez(instance);
271 + return NULL;
272 +}
273 +
274 +static char* GetErrorString(DWORD errorCode) {
275 + DWORD lastError = GetLastError();
276 +
277 + LPVOID lpMsgBuf;
278 + DWORD bufLen = FormatMessage(
279 + FORMAT_MESSAGE_ALLOCATE_BUFFER |
280 + FORMAT_MESSAGE_FROM_SYSTEM |
281 + FORMAT_MESSAGE_IGNORE_INSERTS,
282 + NULL,
283 + errorCode,
284 + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
285 + (LPTSTR) &lpMsgBuf,
286 + 0, NULL );
287 +
288 + SetLastError(lastError);
289 +
290 + if (bufLen) {
291 + char* errorString = (char*)LocalAlloc(LMEM_FIXED, bufLen + 1);
292 + if (errorString) {
293 + strcpy(errorString, (char*)lpMsgBuf);
294 + }
295 + LocalFree(lpMsgBuf);
296 + return errorString;
297 + }
298 +
299 + return NULL;
300 +}
301 +
302 +static void TerminateChildProcesses(SPAWN_INSTANCE *si) {
303 + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
304 + if (hSnapshot == INVALID_HANDLE_VALUE)
305 + return;
306 +
307 + PROCESSENTRY32 pe;
308 + pe.dwSize = sizeof(PROCESSENTRY32);
309 +
310 + if (Process32First(hSnapshot, &pe)) {
311 + do {
312 + if (pe.th32ParentProcessID == si->dwProcessId) {
313 + HANDLE hChildProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pe.th32ProcessID);
314 + if (hChildProcess) {
315 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
316 + "SPAWN PARENT: killing subprocess %u of request No %zu, pid %d (winpid %u)",
317 + pe.th32ProcessID, si->request_id, (int)si->child_pid, si->dwProcessId);
318 +
319 + TerminateProcess(hChildProcess, STATUS_CONTROL_C_EXIT);
320 + CloseHandle(hChildProcess);
321 + }
322 + }
323 + } while (Process32Next(hSnapshot, &pe));
324 + }
325 +
326 + CloseHandle(hSnapshot);
327 +}
328 +
329 +int map_status_code_to_signal(DWORD status_code) {
330 + switch (status_code) {
331 + case STATUS_ACCESS_VIOLATION:
332 + return SIGSEGV;
333 + case STATUS_ILLEGAL_INSTRUCTION:
334 + return SIGILL;
335 + case STATUS_FLOAT_DIVIDE_BY_ZERO:
336 + case STATUS_INTEGER_DIVIDE_BY_ZERO:
337 + case STATUS_ARRAY_BOUNDS_EXCEEDED:
338 + case STATUS_FLOAT_OVERFLOW:
339 + case STATUS_FLOAT_UNDERFLOW:
340 + case STATUS_FLOAT_INVALID_OPERATION:
341 + return SIGFPE;
342 + case STATUS_BREAKPOINT:
343 + case STATUS_SINGLE_STEP:
344 + return SIGTRAP;
345 + case STATUS_STACK_OVERFLOW:
346 + case STATUS_INVALID_HANDLE:
347 + case STATUS_INVALID_PARAMETER:
348 + case STATUS_NO_MEMORY:
349 + case STATUS_PRIVILEGED_INSTRUCTION:
350 + case STATUS_DLL_NOT_FOUND:
351 + case STATUS_DLL_INIT_FAILED:
352 + case STATUS_ORDINAL_NOT_FOUND:
353 + case STATUS_ENTRYPOINT_NOT_FOUND:
354 + case STATUS_CONTROL_STACK_VIOLATION:
355 + case STATUS_STACK_BUFFER_OVERRUN:
356 + case STATUS_ASSERTION_FAILURE:
357 + case STATUS_INVALID_CRUNTIME_PARAMETER:
358 + case STATUS_HEAP_CORRUPTION:
359 + return SIGABRT;
360 + case STATUS_CONTROL_C_EXIT:
361 + return SIGTERM; // we use this internally as such
362 + case STATUS_FATAL_APP_EXIT:
363 + return SIGTERM;
364 + default:
365 + return (status_code & 0xFF) << 8;
366 + }
367 +}
368 +
369 +int spawn_server_exec_kill(SPAWN_SERVER *server __maybe_unused, SPAWN_INSTANCE *si) {
370 + if(si->child_pid != -1 && kill(si->child_pid, SIGTERM) != 0)
371 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
372 + "SPAWN PARENT: child of request No %zu, pid %d (winpid %u), failed to be killed",
373 + si->request_id, (int)si->child_pid, si->dwProcessId);
374 +
375 + // this gives some warnings at the spawn-tester, but it is generally better
376 + // to have them, to avoid abnormal shutdown of the plugins
377 + if(si->read_fd != -1) { close(si->read_fd); si->read_fd = -1; }
378 + if(si->write_fd != -1) { close(si->write_fd); si->write_fd = -1; }
379 +
380 + errno_clear();
381 + if(TerminateProcess(si->process_handle, STATUS_CONTROL_C_EXIT) == 0)
382 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
383 + "SPAWN PARENT: child of request No %zu, pid %d (winpid %u), failed to be terminated",
384 + si->request_id, (int)si->child_pid, si->dwProcessId);
385 +
386 + errno_clear();
387 + TerminateChildProcesses(si);
388 +
389 + return spawn_server_exec_wait(server, si);
390 +}
391 +
392 +int spawn_server_exec_wait(SPAWN_SERVER *server __maybe_unused, SPAWN_INSTANCE *si) {
393 + if(si->read_fd != -1) { close(si->read_fd); si->read_fd = -1; }
394 + if(si->write_fd != -1) { close(si->write_fd); si->write_fd = -1; }
395 +
396 + // wait for the process to end
397 + WaitForSingleObject(si->process_handle, INFINITE);
398 +
399 + DWORD exit_code = -1;
400 + GetExitCodeProcess(si->process_handle, &exit_code);
401 + CloseHandle(si->process_handle);
402 +
403 + char *err = GetErrorString(exit_code);
404 +
405 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
406 + "SPAWN PARENT: child of request No %zu, pid %d (winpid %u), exited with code %u (0x%x): %s",
407 + si->request_id, (int)si->child_pid, si->dwProcessId,
408 + (unsigned)exit_code, (unsigned)exit_code, err ? err : "(no reason text)");
409 +
410 + if(err)
411 + LocalFree(err);
412 +
413 + freez(si);
414 + return map_status_code_to_signal(exit_code);
415 +}
416 +
417 +#endif
src/web/server/static/static-threaded.c
-1
@@ -540,7 +540,6 @@ void *socket_listen_main_static_threaded(void *ptr) {
540 char tag[50 + 1];
541 snprintfz(tag, sizeof(tag) - 1, "WEB[%d]", i+1);
542
543 - netdata_log_info("starting worker %d", i+1);
543 static_workers_private_data[i].thread = nd_thread_create(tag, NETDATA_THREAD_OPTION_DEFAULT,
544 socket_listen_main_static_threaded_worker,
545 (void *)&static_workers_private_data[i]);