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]);