master
c 1,437 lines 48.7 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "spawn_server_internals.h"
4
5 #if defined(SPAWN_SERVER_VERSION_NOFORK)
6
7 // the child's output pipe, reading side
8 int spawn_server_instance_read_fd(SPAWN_INSTANCE *si) { return si->read_fd; }
9
10 // the child's input pipe, writing side
11 int spawn_server_instance_write_fd(SPAWN_INSTANCE *si) { return si->write_fd; }
12
13 void spawn_server_instance_read_fd_unset(SPAWN_INSTANCE *si) { si->read_fd = -1; }
14 void spawn_server_instance_write_fd_unset(SPAWN_INSTANCE *si) { si->write_fd = -1; }
15 pid_t spawn_server_instance_pid(SPAWN_INSTANCE *si) { return si->child_pid; }
16
17 pid_t spawn_server_pid(SPAWN_SERVER *server) { return server->server_pid; }
18
19 #ifdef __APPLE__
20 #include <crt_externs.h>
21 #define environ (*_NSGetEnviron())
22 #else
23 extern char **environ;
24 #endif
25
26 static size_t spawn_server_id = 0;
27 static volatile bool spawn_server_exit = false;
28 static volatile bool spawn_server_sigchld = false;
29 static SPAWN_REQUEST *spawn_server_requests = NULL;
30
31 static size_t spawn_server_max_unix_socket_path_length(void) {
32 struct sockaddr_un server_addr = { 0 };
33 return sizeof(server_addr.sun_path) - 1;
34 }
35
36 static bool spawn_server_set_unix_socket_path(struct sockaddr_un *server_addr, const char *path, bool log, const char *action) {
37 const size_t max_path_length = sizeof(server_addr->sun_path) - 1;
38
39 if(strlen(path) > max_path_length) {
40 errno = ENAMETOOLONG;
41 if(log)
42 nd_log(NDLS_COLLECTORS, NDLP_ERR,
43 "%s '%s': exceeds the %zu-byte AF_UNIX limit",
44 action, path, max_path_length);
45 return false;
46 }
47
48 strncpyz(server_addr->sun_path, path, max_path_length);
49 return true;
50 }
51
52 // --------------------------------------------------------------------------------------------------------------------
53
54 static int connect_to_spawn_server(const char *path, bool log) {
55 int sock = -1;
56 struct sockaddr_un server_addr = {
57 .sun_family = AF_UNIX,
58 };
59
60 if(!spawn_server_set_unix_socket_path(&server_addr, path, log,
61 "SPAWN PARENT: Cannot connect() to spawn server on path"))
62 return -1;
63
64 if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
65 if(log)
66 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN PARENT: cannot create socket() to connect to spawn server.");
67 return -1;
68 }
69
70 if (connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
71 if(log)
72 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN PARENT: Cannot connect() to spawn server on path '%s'.", path);
73 close(sock);
74 return -1;
75 }
76
77 return sock;
78 }
79
80 // --------------------------------------------------------------------------------------------------------------------
81 // Encoding and decoding of spawn server request argv type of data
82
83 // Function to encode argv or envp
84 static void* argv_encode(const char **argv, size_t *out_size) {
85 size_t buffer_size = 1024; // Initial buffer size
86 size_t buffer_used = 0;
87 char *buffer = mallocz(buffer_size);
88
89 if(argv) {
90 for (const char **p = argv; *p != NULL; p++) {
91 if (strlen(*p) == 0)
92 continue; // Skip empty strings
93
94 size_t len = strlen(*p) + 1;
95 size_t wanted_size = buffer_used + len + 1;
96
97 if (wanted_size >= buffer_size) {
98 buffer_size *= 2;
99
100 if(buffer_size < wanted_size)
101 buffer_size = wanted_size;
102
103 buffer = reallocz(buffer, buffer_size);
104 }
105
106 memcpy(&buffer[buffer_used], *p, len);
107 buffer_used += len;
108 }
109 }
110
111 buffer[buffer_used++] = '\0'; // Final empty string
112 *out_size = buffer_used;
113
114 return buffer;
115 }
116
117 // Function to decode argv or envp
118 static const char** argv_decode(const char *buffer, size_t size) {
119 size_t count = 0;
120 const char *ptr = buffer;
121 while (ptr < buffer + size) {
122 if(ptr && *ptr) {
123 count++;
124 ptr += strlen(ptr) + 1;
125 }
126 else
127 break;
128 }
129
130 const char **argv = mallocz((count + 1) * sizeof(char *));
131
132 ptr = buffer;
133 for (size_t i = 0; i < count; i++) {
134 argv[i] = ptr;
135 ptr += strlen(ptr) + 1;
136 }
137 argv[count] = NULL; // Null-terminate the array
138
139 return argv;
140 }
141
142 // --------------------------------------------------------------------------------------------------------------------
143 // status reports
144
145 typedef enum {
146 STATUS_REPORT_NONE = 0,
147 STATUS_REPORT_STARTED,
148 STATUS_REPORT_FAILED,
149 STATUS_REPORT_EXITED,
150 STATUS_REPORT_PING,
151 } STATUS_REPORT;
152
153 #define STATUS_REPORT_MAGIC 0xBADA55EE
154
155 struct status_report {
156 uint32_t magic;
157 STATUS_REPORT status;
158 union {
159 struct {
160 pid_t pid;
161 } started;
162
163 struct {
164 int err_no;
165 } failed;
166
167 struct {
168 int waitpid_status;
169 } exited;
170 };
171 };
172
173 static void spawn_server_send_status_ping(int sock) {
174 struct status_report sr = {
175 .magic = STATUS_REPORT_MAGIC,
176 .status = STATUS_REPORT_PING,
177 };
178
179 if(write(sock, &sr, sizeof(sr)) != sizeof(sr))
180 nd_log(NDLS_COLLECTORS, NDLP_ERR,
181 "SPAWN SERVER: Cannot send ping reply.");
182 }
183
184 static void spawn_server_send_status_success(SPAWN_REQUEST *rq) {
185 const struct status_report sr = {
186 .magic = STATUS_REPORT_MAGIC,
187 .status = STATUS_REPORT_STARTED,
188 .started = {
189 .pid = rq->pid,
190 },
191 };
192
193 if(write(rq->sock, &sr, sizeof(sr)) != sizeof(sr))
194 nd_log(NDLS_COLLECTORS, NDLP_ERR,
195 "SPAWN SERVER: Cannot send success status report for pid %d, request %zu: %s",
196 rq->pid, rq->request_id, rq->cmdline);
197 }
198
199 static void spawn_server_send_status_failure(SPAWN_REQUEST *rq) {
200 struct status_report sr = {
201 .magic = STATUS_REPORT_MAGIC,
202 .status = STATUS_REPORT_FAILED,
203 .failed = {
204 .err_no = errno,
205 },
206 };
207
208 if(write(rq->sock, &sr, sizeof(sr)) != sizeof(sr))
209 nd_log(NDLS_COLLECTORS, NDLP_ERR,
210 "SPAWN SERVER: Cannot send failure status report for request %zu: %s",
211 rq->request_id, rq->cmdline);
212 }
213
214 static void spawn_server_send_status_exit(SPAWN_REQUEST *rq, int waitpid_status) {
215 struct status_report sr = {
216 .magic = STATUS_REPORT_MAGIC,
217 .status = STATUS_REPORT_EXITED,
218 .exited = {
219 .waitpid_status = waitpid_status,
220 },
221 };
222
223 if(write(rq->sock, &sr, sizeof(sr)) != sizeof(sr))
224 nd_log(NDLS_COLLECTORS, NDLP_ERR,
225 "SPAWN SERVER: Cannot send exit status (%d) report for pid %d, request %zu: %s",
226 waitpid_status, rq->pid, rq->request_id, rq->cmdline);
227 }
228
229 // --------------------------------------------------------------------------------------------------------------------
230 // execute a received request
231
232 static void request_free(SPAWN_REQUEST *rq) {
233 if(rq->fds[0] != -1) close(rq->fds[0]);
234 if(rq->fds[1] != -1) close(rq->fds[1]);
235 if(rq->fds[2] != -1) close(rq->fds[2]);
236 if(rq->fds[3] != -1) close(rq->fds[3]);
237 if(rq->sock != -1) close(rq->sock);
238 freez((void *)rq->argv);
239 freez((void *)rq->envp);
240 freez((void *)rq->data);
241 freez((void *)rq->cmdline);
242 freez((void *)rq);
243 }
244
245 static bool spawn_external_command(SPAWN_SERVER *server __maybe_unused, SPAWN_REQUEST *rq) {
246 // Close custom_fd - it is not needed for exec mode
247 if(rq->fds[3] != -1) { close(rq->fds[3]); rq->fds[3] = -1; }
248
249 if(!rq->argv) {
250 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN PARENT: there is no argv pointer to exec");
251 return false;
252 }
253
254 if(rq->fds[0] == -1 || rq->fds[1] == -1 || rq->fds[2] == -1) {
255 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN PARENT: stdio fds are missing from the request");
256 return false;
257 }
258
259 CLEAN_BUFFER *wb = argv_to_cmdline_buffer(rq->argv);
260 rq->cmdline = strdupz(buffer_tostring(wb));
261
262 posix_spawn_file_actions_t file_actions;
263 if (posix_spawn_file_actions_init(&file_actions) != 0) {
264 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN PARENT: posix_spawn_file_actions_init() failed: %s", rq->cmdline);
265 return false;
266 }
267
268 posix_spawn_file_actions_adddup2(&file_actions, rq->fds[0], STDIN_FILENO);
269 posix_spawn_file_actions_adddup2(&file_actions, rq->fds[1], STDOUT_FILENO);
270 posix_spawn_file_actions_adddup2(&file_actions, rq->fds[2], STDERR_FILENO);
271 posix_spawn_file_actions_addclose(&file_actions, rq->fds[0]);
272 posix_spawn_file_actions_addclose(&file_actions, rq->fds[1]);
273 posix_spawn_file_actions_addclose(&file_actions, rq->fds[2]);
274
275 posix_spawnattr_t attr;
276 if (posix_spawnattr_init(&attr) != 0) {
277 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN PARENT: posix_spawnattr_init() failed: %s", rq->cmdline);
278 posix_spawn_file_actions_destroy(&file_actions);
279 return false;
280 }
281
282 // Set the flags to reset the signal mask and signal actions
283 sigset_t empty_mask;
284 sigemptyset(&empty_mask);
285 if (posix_spawnattr_setsigmask(&attr, &empty_mask) != 0) {
286 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN PARENT: posix_spawnattr_setsigmask() failed: %s", rq->cmdline);
287 posix_spawn_file_actions_destroy(&file_actions);
288 posix_spawnattr_destroy(&attr);
289 return false;
290 }
291
292 short flags = POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF;
293 if (posix_spawnattr_setflags(&attr, flags) != 0) {
294 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN PARENT: posix_spawnattr_setflags() failed: %s", rq->cmdline);
295 posix_spawn_file_actions_destroy(&file_actions);
296 posix_spawnattr_destroy(&attr);
297 return false;
298 }
299
300 int fds_to_keep[] = {
301 rq->fds[0],
302 rq->fds[1],
303 rq->fds[2],
304 nd_log_systemd_journal_fd(),
305 };
306 os_close_all_non_std_open_fds_except(fds_to_keep, _countof(fds_to_keep), CLOSE_RANGE_CLOEXEC);
307
308 errno_clear();
309 if (posix_spawn(&rq->pid, rq->argv[0], &file_actions, &attr, (char * const *)rq->argv, (char * const *)rq->envp) != 0) {
310 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: posix_spawn() failed: %s", rq->cmdline);
311
312 posix_spawnattr_destroy(&attr);
313 posix_spawn_file_actions_destroy(&file_actions);
314 return false;
315 }
316
317 // Destroy the posix_spawnattr_t and posix_spawn_file_actions_t structures
318 posix_spawnattr_destroy(&attr);
319 posix_spawn_file_actions_destroy(&file_actions);
320
321 // Close the read end of the stdin pipe and the write end of the stdout pipe in the parent process
322 close(rq->fds[0]); rq->fds[0] = -1;
323 close(rq->fds[1]); rq->fds[1] = -1;
324 close(rq->fds[2]); rq->fds[2] = -1;
325
326 nd_log(NDLS_COLLECTORS, NDLP_DEBUG, "SPAWN SERVER: process created with pid %d: %s", rq->pid, rq->cmdline);
327 return true;
328 }
329
330 static bool spawn_server_run_callback(SPAWN_SERVER *server __maybe_unused, SPAWN_REQUEST *rq) {
331 rq->cmdline = strdupz("callback() function");
332
333 if(server->cb == NULL) {
334 errno = ENOSYS;
335 return false;
336 }
337
338 pid_t pid = fork();
339 if (pid < 0) {
340 // fork failed
341
342 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: Failed to fork() child for callback.");
343 return false;
344 }
345 else if (pid == 0) {
346 // the child
347
348 // close the server sockets;
349 close(server->sock); server->sock = -1;
350 if(server->pipe[0] != -1) { close(server->pipe[0]); server->pipe[0] = -1; }
351 if(server->pipe[1] != -1) { close(server->pipe[1]); server->pipe[1] = -1; }
352
353 // set the process name
354 os_setproctitle("spawn-callback", server->argc, server->argv);
355
356 // close all open file descriptors of the parent, but keep ours
357 int fds_to_keep[] = {
358 rq->fds[0],
359 rq->fds[1],
360 rq->fds[2],
361 rq->fds[3],
362 nd_log_systemd_journal_fd(),
363 };
364 os_close_all_non_std_open_fds_except(fds_to_keep, _countof(fds_to_keep), 0);
365 nd_log_reopen_log_files_for_spawn_server("spawn-callback");
366
367 // get the fds from the request
368 int stdin_fd = rq->fds[0];
369 int stdout_fd = rq->fds[1];
370 int stderr_fd = rq->fds[2];
371 int custom_fd = rq->fds[3]; (void)custom_fd;
372
373 // change stdio fds to the ones in the request
374 if (dup2(stdin_fd, STDIN_FILENO) == -1) {
375 nd_log(NDLS_COLLECTORS, NDLP_ERR,
376 "SPAWN SERVER: cannot dup2(%d) stdin of request No %zu: %s",
377 stdin_fd, rq->request_id, rq->cmdline);
378 exit(EXIT_FAILURE);
379 }
380 if (dup2(stdout_fd, STDOUT_FILENO) == -1) {
381 nd_log(NDLS_COLLECTORS, NDLP_ERR,
382 "SPAWN SERVER: cannot dup2(%d) stdin of request No %zu: %s",
383 stdout_fd, rq->request_id, rq->cmdline);
384 exit(EXIT_FAILURE);
385 }
386 if (dup2(stderr_fd, STDERR_FILENO) == -1) {
387 nd_log(NDLS_COLLECTORS, NDLP_ERR,
388 "SPAWN SERVER: cannot dup2(%d) stderr of request No %zu: %s",
389 stderr_fd, rq->request_id, rq->cmdline);
390 exit(EXIT_FAILURE);
391 }
392
393 // close the excess fds
394 close(stdin_fd); stdin_fd = rq->fds[0] = STDIN_FILENO;
395 close(stdout_fd); stdout_fd = rq->fds[1] = STDOUT_FILENO;
396 close(stderr_fd); stderr_fd = rq->fds[2] = STDERR_FILENO;
397
398 // overwrite the process environment
399 environ = (char **)rq->envp;
400
401 // run the callback and return its code
402 exit(server->cb(rq));
403 }
404
405 // the parent
406 rq->pid = pid;
407
408 return true;
409 }
410
411 static void spawn_server_execute_request(SPAWN_SERVER *server, SPAWN_REQUEST *rq) {
412 bool done;
413 switch(rq->type) {
414 case SPAWN_INSTANCE_TYPE_EXEC:
415 done = spawn_external_command(server, rq);
416 break;
417
418 case SPAWN_INSTANCE_TYPE_CALLBACK:
419 done = spawn_server_run_callback(server, rq);
420 break;
421
422 default:
423 errno = EINVAL;
424 done = false;
425 break;
426 }
427
428 if(!done) {
429 spawn_server_send_status_failure(rq);
430 request_free(rq);
431 return;
432 }
433
434 // let the parent know
435 spawn_server_send_status_success(rq);
436
437 // do not keep data we don't need at the parent
438 freez((void *)rq->envp); rq->envp = NULL;
439 freez((void *)rq->argv); rq->argv = NULL;
440 freez((void *)rq->data); rq->data = NULL;
441 rq->data_size = 0;
442
443 // do not keep fds we don't need at the parent
444 if(rq->fds[0] != -1) { close(rq->fds[0]); rq->fds[0] = -1; }
445 if(rq->fds[1] != -1) { close(rq->fds[1]); rq->fds[1] = -1; }
446 if(rq->fds[2] != -1) { close(rq->fds[2]); rq->fds[2] = -1; }
447 if(rq->fds[3] != -1) { close(rq->fds[3]); rq->fds[3] = -1; }
448
449 // keep it in the list
450 DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(spawn_server_requests, rq, prev, next);
451 }
452
453 // --------------------------------------------------------------------------------------------------------------------
454 // Sending and receiving requests
455
456 typedef enum __attribute__((packed)) {
457 SPAWN_SERVER_MSG_INVALID = 0,
458 SPAWN_SERVER_MSG_REQUEST,
459 SPAWN_SERVER_MSG_PING,
460 } SPAWN_SERVER_MSG;
461
462 static bool spawn_server_is_running(const char *path) {
463 struct msghdr msg = {0};
464 struct iovec iov[7];
465 SPAWN_SERVER_MSG msg_type = SPAWN_SERVER_MSG_PING;
466 size_t dummy_size = 0;
467 SPAWN_INSTANCE_TYPE dummy_type = 0;
468 ND_UUID magic = UUID_ZERO;
469 char cmsgbuf[CMSG_SPACE(sizeof(int))] __attribute__((aligned(sizeof(size_t))));
470
471 iov[0].iov_base = &msg_type;
472 iov[0].iov_len = sizeof(msg_type);
473
474 iov[1].iov_base = magic.uuid;
475 iov[1].iov_len = sizeof(magic.uuid);
476
477 iov[2].iov_base = &dummy_size;
478 iov[2].iov_len = sizeof(dummy_size);
479
480 iov[3].iov_base = &dummy_size;
481 iov[3].iov_len = sizeof(dummy_size);
482
483 iov[4].iov_base = &dummy_size;
484 iov[4].iov_len = sizeof(dummy_size);
485
486 iov[5].iov_base = &dummy_size;
487 iov[5].iov_len = sizeof(dummy_size);
488
489 iov[6].iov_base = &dummy_type;
490 iov[6].iov_len = sizeof(dummy_type);
491
492 msg.msg_iov = iov;
493 msg.msg_iovlen = 7;
494 msg.msg_control = cmsgbuf;
495 msg.msg_controllen = sizeof(cmsgbuf);
496
497 int sock = connect_to_spawn_server(path, false);
498 if(sock == -1)
499 return false;
500
501 int rc = sendmsg(sock, &msg, 0);
502 if (rc < 0) {
503 // cannot send the message
504 close(sock);
505 return false;
506 }
507
508 // Receive response
509 struct status_report sr = { 0 };
510 if (read(sock, &sr, sizeof(sr)) != sizeof(sr)) {
511 // cannot receive a ping reply
512 close(sock);
513 return false;
514 }
515
516 close(sock);
517 return sr.status == STATUS_REPORT_PING;
518 }
519
520 static bool spawn_server_send_request(ND_UUID *magic, SPAWN_REQUEST *request) {
521 bool ret = false;
522
523 size_t env_size = 0;
524 size_t argv_size = 0;
525
526 void *encoded_env = argv_encode(request->envp, &env_size);
527 void *encoded_argv = argv_encode(request->argv, &argv_size);
528
529 struct msghdr msg = {0};
530 struct cmsghdr *cmsg;
531 SPAWN_SERVER_MSG msg_type = SPAWN_SERVER_MSG_REQUEST;
532 char cmsgbuf[CMSG_SPACE(sizeof(int) * SPAWN_SERVER_TRANSFER_FDS)] __attribute__((aligned(sizeof(size_t))));
533 struct iovec iov[11];
534
535 // We send 1 request with 10 iovec in it
536 // The request will be received in 2 parts
537 // 1. the first 6 iovec which include the sizes of the memory allocations required
538 // 2. the last 4 iovec which require the memory allocations to be received
539
540 iov[0].iov_base = &msg_type;
541 iov[0].iov_len = sizeof(msg_type);
542
543 iov[1].iov_base = magic->uuid;
544 iov[1].iov_len = sizeof(magic->uuid);
545
546 iov[2].iov_base = &request->request_id;
547 iov[2].iov_len = sizeof(request->request_id);
548
549 iov[3].iov_base = &env_size;
550 iov[3].iov_len = sizeof(env_size);
551
552 iov[4].iov_base = &argv_size;
553 iov[4].iov_len = sizeof(argv_size);
554
555 iov[5].iov_base = &request->data_size;
556 iov[5].iov_len = sizeof(request->data_size);
557
558 iov[6].iov_base = &request->type; // Added this line
559 iov[6].iov_len = sizeof(request->type);
560
561 iov[7].iov_base = encoded_env;
562 iov[7].iov_len = env_size;
563
564 iov[8].iov_base = encoded_argv;
565 iov[8].iov_len = argv_size;
566
567 iov[9].iov_base = (char *)request->data;
568 iov[9].iov_len = request->data_size;
569
570 iov[10].iov_base = NULL;
571 iov[10].iov_len = 0;
572
573 msg.msg_iov = iov;
574 msg.msg_iovlen = 11;
575 msg.msg_control = cmsgbuf;
576 msg.msg_controllen = CMSG_SPACE(sizeof(int) * SPAWN_SERVER_TRANSFER_FDS);
577
578 cmsg = CMSG_FIRSTHDR(&msg);
579 cmsg->cmsg_level = SOL_SOCKET;
580 cmsg->cmsg_type = SCM_RIGHTS;
581 cmsg->cmsg_len = CMSG_LEN(sizeof(int) * SPAWN_SERVER_TRANSFER_FDS);
582
583 memcpy(CMSG_DATA(cmsg), request->fds, sizeof(int) * SPAWN_SERVER_TRANSFER_FDS);
584
585 int rc = sendmsg(request->sock, &msg, 0);
586
587 if (rc < 0) {
588 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN PARENT: Failed to sendmsg() request to spawn server using socket %d.", request->sock);
589 goto cleanup;
590 }
591 else {
592 ret = true;
593 // fprintf(stderr, "PARENT: sent request %zu on socket %d (fds: %d, %d, %d, %d) from tid %d\n",
594 // request->request_id, request->socket, request->fds[0], request->fds[1], request->fds[2], request->fds[3], os_gettid());
595 }
596
597 cleanup:
598 freez(encoded_env);
599 freez(encoded_argv);
600 return ret;
601 }
602
603 static void spawn_server_receive_request(int sock, SPAWN_SERVER *server) {
604 struct msghdr msg = {0};
605 struct iovec iov[7];
606 SPAWN_SERVER_MSG msg_type = SPAWN_SERVER_MSG_INVALID;
607 size_t request_id;
608 size_t env_size;
609 size_t argv_size;
610 size_t data_size;
611 ND_UUID magic = UUID_ZERO;
612 SPAWN_INSTANCE_TYPE type;
613 char cmsgbuf[CMSG_SPACE(sizeof(int) * SPAWN_SERVER_TRANSFER_FDS)] __attribute__((aligned(sizeof(size_t))));
614 char *envp_encoded = NULL, *argv_encoded = NULL, *data = NULL;
615 int stdin_fd = -1, stdout_fd = -1, stderr_fd = -1, custom_fd = -1;
616
617 // First recvmsg() to read sizes and control message
618 iov[0].iov_base = &msg_type;
619 iov[0].iov_len = sizeof(msg_type);
620
621 iov[1].iov_base = magic.uuid;
622 iov[1].iov_len = sizeof(magic.uuid);
623
624 iov[2].iov_base = &request_id;
625 iov[2].iov_len = sizeof(request_id);
626
627 iov[3].iov_base = &env_size;
628 iov[3].iov_len = sizeof(env_size);
629
630 iov[4].iov_base = &argv_size;
631 iov[4].iov_len = sizeof(argv_size);
632
633 iov[5].iov_base = &data_size;
634 iov[5].iov_len = sizeof(data_size);
635
636 iov[6].iov_base = &type;
637 iov[6].iov_len = sizeof(type);
638
639 msg.msg_iov = iov;
640 msg.msg_iovlen = 7;
641 msg.msg_control = cmsgbuf;
642 msg.msg_controllen = sizeof(cmsgbuf);
643
644 if (recvmsg(sock, &msg, 0) < 0) {
645 nd_log(NDLS_COLLECTORS, NDLP_ERR,
646 "SPAWN SERVER: failed to recvmsg() the first part of the request.");
647 close(sock);
648 return;
649 }
650
651 if(msg_type == SPAWN_SERVER_MSG_PING) {
652 spawn_server_send_status_ping(sock);
653 close(sock);
654 return;
655 }
656
657 if(!UUIDeq(magic, server->magic)) {
658 nd_log(NDLS_COLLECTORS, NDLP_ERR,
659 "SPAWN SERVER: Invalid authorization key for request %zu. "
660 "Rejecting request.",
661 request_id);
662 close(sock);
663 return;
664 }
665
666 if(type == SPAWN_INSTANCE_TYPE_EXEC && !(server->options & SPAWN_SERVER_OPTION_EXEC)) {
667 nd_log(NDLS_COLLECTORS, NDLP_ERR,
668 "SPAWN SERVER: Request %zu wants to exec, but exec is not allowed for this spawn server. "
669 "Rejecting request.",
670 request_id);
671 close(sock);
672 return;
673 }
674
675 if(type == SPAWN_INSTANCE_TYPE_CALLBACK && !(server->options & SPAWN_SERVER_OPTION_CALLBACK)) {
676 nd_log(NDLS_COLLECTORS, NDLP_ERR,
677 "SPAWN SERVER: Request %zu wants to run a callback, but callbacks are not allowed for this spawn server. "
678 "Rejecting request.",
679 request_id);
680 close(sock);
681 return;
682 }
683
684 // Extract file descriptors from control message
685 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
686 if (cmsg == NULL || cmsg->cmsg_len != CMSG_LEN(sizeof(int) * SPAWN_SERVER_TRANSFER_FDS)) {
687 nd_log(NDLS_COLLECTORS, NDLP_ERR,
688 "SPAWN SERVER: Received invalid control message (expected %zu bytes, received %zu bytes)",
689 (size_t)(CMSG_LEN(sizeof(int) * SPAWN_SERVER_TRANSFER_FDS)), (size_t)(cmsg?cmsg->cmsg_len:0));
690 close(sock);
691 return;
692 }
693
694 if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
695 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: Received unexpected control message type.");
696 close(sock);
697 return;
698 }
699
700 int *fds = (int *)CMSG_DATA(cmsg);
701 stdin_fd = fds[0];
702 stdout_fd = fds[1];
703 stderr_fd = fds[2];
704 custom_fd = fds[3];
705
706 if (stdin_fd < 0 || stdout_fd < 0 || stderr_fd < 0) {
707 nd_log(NDLS_COLLECTORS, NDLP_ERR,
708 "SPAWN SERVER: invalid file descriptors received, stdin = %d, stdout = %d, stderr = %d",
709 stdin_fd, stdout_fd, stderr_fd);
710 goto cleanup;
711 }
712
713 // Second recvmsg() to read buffer contents
714 iov[0].iov_base = envp_encoded = mallocz(env_size);
715 iov[0].iov_len = env_size;
716 iov[1].iov_base = argv_encoded = mallocz(argv_size);
717 iov[1].iov_len = argv_size;
718 iov[2].iov_base = data = mallocz(data_size);
719 iov[2].iov_len = data_size;
720
721 msg.msg_iov = iov;
722 msg.msg_iovlen = 3;
723 msg.msg_control = NULL;
724 msg.msg_controllen = 0;
725
726 ssize_t total_bytes_received = recvmsg(sock, &msg, 0);
727 if (total_bytes_received < 0) {
728 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: failed to recvmsg() the second part of the request.");
729 goto cleanup;
730 }
731
732 // fprintf(stderr, "SPAWN SERVER: received request %zu (fds: %d, %d, %d, %d)\n", request_id,
733 // stdin_fd, stdout_fd, stderr_fd, custom_fd);
734
735 SPAWN_REQUEST *rq = mallocz(sizeof(*rq));
736 *rq = (SPAWN_REQUEST){
737 .pid = 0,
738 .request_id = request_id,
739 .sock = sock,
740 .fds = {
741 [0] = stdin_fd,
742 [1] = stdout_fd,
743 [2] = stderr_fd,
744 [3] = custom_fd,
745 },
746 .envp = argv_decode(envp_encoded, env_size),
747 .argv = argv_decode(argv_encoded, argv_size),
748 .data = data,
749 .data_size = data_size,
750 .type = type
751 };
752
753 // all allocations given to the request are now handled by this
754 spawn_server_execute_request(server, rq);
755
756 // since we make rq->argv and rq->environment NULL when we keep it,
757 // we don't need these anymore.
758 freez(envp_encoded);
759 freez(argv_encoded);
760 return;
761
762 cleanup:
763 close(sock);
764 if(stdin_fd != -1) close(stdin_fd);
765 if(stdout_fd != -1) close(stdout_fd);
766 if(stderr_fd != -1) close(stderr_fd);
767 if(custom_fd != -1) close(custom_fd);
768 freez(envp_encoded);
769 freez(argv_encoded);
770 freez(data);
771 }
772
773 // --------------------------------------------------------------------------------------------------------------------
774 // the spawn server main event loop
775
776 static void spawn_server_sigchld_handler(int signo __maybe_unused) {
777 spawn_server_sigchld = true;
778 }
779
780 static void spawn_server_sigterm_handler(int signo __maybe_unused) {
781 spawn_server_exit = true;
782 }
783
784 static SPAWN_REQUEST *find_request_by_pid(pid_t pid) {
785 for(SPAWN_REQUEST *rq = spawn_server_requests; rq ;rq = rq->next)
786 if(rq->pid == pid)
787 return rq;
788
789 return NULL;
790 }
791
792 static void spawn_server_process_sigchld(void) {
793 // nd_log(NDLS_COLLECTORS, NDLP_INFO, "SPAWN SERVER: checking for exited children");
794
795 spawn_server_sigchld = false;
796
797 int status;
798 pid_t pid;
799
800 // Loop to check for exited child processes
801 while ((pid = waitpid((pid_t)(-1), &status, WNOHANG)) != 0) {
802 if(pid == -1)
803 break;
804
805 errno_clear();
806
807 SPAWN_REQUEST *rq = find_request_by_pid(pid);
808 size_t request_id = rq ? rq->request_id : 0;
809 bool send_report_remove_request = false;
810
811 if(WIFEXITED(status)) {
812 if(WEXITSTATUS(status))
813 nd_log(NDLS_COLLECTORS, NDLP_WARNING,
814 "SPAWN SERVER: child with pid %d (request %zu) exited with exit code %d: %s",
815 pid, request_id, WEXITSTATUS(status), rq ? rq->cmdline : "[request not found]");
816 send_report_remove_request = true;
817 }
818 else if(WIFSIGNALED(status)) {
819 if(WCOREDUMP(status))
820 nd_log(NDLS_COLLECTORS, NDLP_WARNING,
821 "SPAWN SERVER: child with pid %d (request %zu) coredump'd due to signal %d: %s",
822 pid, request_id, WTERMSIG(status), rq ? rq->cmdline : "[request not found]");
823 else
824 nd_log(NDLS_COLLECTORS, NDLP_WARNING,
825 "SPAWN SERVER: child with pid %d (request %zu) killed by signal %d: %s",
826 pid, request_id, WTERMSIG(status), rq ? rq->cmdline : "[request not found]");
827 send_report_remove_request = true;
828 }
829 else if(WIFSTOPPED(status)) {
830 nd_log(NDLS_COLLECTORS, NDLP_WARNING,
831 "SPAWN SERVER: child with pid %d (request %zu) stopped due to signal %d: %s",
832 pid, request_id, WSTOPSIG(status), rq ? rq->cmdline : "[request not found]");
833 send_report_remove_request = false;
834 }
835 else if(WIFCONTINUED(status)) {
836 nd_log(NDLS_COLLECTORS, NDLP_WARNING,
837 "SPAWN SERVER: child with pid %d (request %zu) continued due to signal %d: %s",
838 pid, request_id, SIGCONT, rq ? rq->cmdline : "[request not found]");
839 send_report_remove_request = false;
840 }
841 else {
842 nd_log(NDLS_COLLECTORS, NDLP_WARNING,
843 "SPAWN SERVER: child with pid %d (request %zu) reports unhandled status: %s",
844 pid, request_id, rq ? rq->cmdline : "[request not found]");
845 send_report_remove_request = false;
846 }
847
848 if(send_report_remove_request && rq) {
849 spawn_server_send_status_exit(rq, status);
850 DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(spawn_server_requests, rq, prev, next);
851 request_free(rq);
852 }
853 }
854 }
855
856 static int spawn_server_event_loop(SPAWN_SERVER *server) {
857 int pipe_fd = server->pipe[1];
858 close(server->pipe[0]); server->pipe[0] = -1;
859
860 signals_block_all();
861 int wanted_signals[] = {SIGTERM, SIGCHLD};
862 signals_unblock(wanted_signals, _countof(wanted_signals));
863
864 // Set up the signal handler for SIGCHLD and SIGTERM
865 struct sigaction sa;
866 sa.sa_handler = spawn_server_sigchld_handler;
867 sigemptyset(&sa.sa_mask);
868 sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
869 if (sigaction(SIGCHLD, &sa, NULL) == -1) {
870 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: sigaction() failed for SIGCHLD");
871 return 1;
872 }
873
874 sa.sa_handler = spawn_server_sigterm_handler;
875 if (sigaction(SIGTERM, &sa, NULL) == -1) {
876 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: sigaction() failed for SIGTERM");
877 return 1;
878 }
879
880 struct status_report sr = {
881 .status = STATUS_REPORT_STARTED,
882 .started = {
883 .pid = getpid(),
884 },
885 };
886 if (write(pipe_fd, &sr, sizeof(sr)) != sizeof(sr)) {
887 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: failed to write initial status report.");
888 return 1;
889 }
890
891 struct pollfd fds[2];
892 fds[0].fd = server->sock;
893 fds[0].events = POLLIN;
894 fds[1].fd = pipe_fd;
895 fds[1].events = POLLHUP | POLLERR;
896
897 while(!spawn_server_exit) {
898 int ret = poll(fds, 2, 500);
899 if (spawn_server_sigchld || ret == 0) {
900 spawn_server_process_sigchld();
901 errno_clear();
902
903 if(ret == -1 || ret == 0)
904 continue;
905 }
906
907 if (ret == -1) {
908 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: poll() failed");
909 break;
910 }
911
912 if (fds[1].revents & (POLLHUP|POLLERR)) {
913 // Pipe has been closed (parent has exited)
914 nd_log(NDLS_COLLECTORS, NDLP_DEBUG, "SPAWN SERVER: Parent process closed socket (exited?)");
915 break;
916 }
917
918 if (fds[0].revents & POLLIN) {
919 int sock = accept(server->sock, NULL, NULL);
920 if (sock == -1) {
921 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: accept() failed");
922 continue;
923 }
924
925 // do not fork this socket
926 sock_setcloexec(sock, true);
927
928 // receive the request and process it
929 spawn_server_receive_request(sock, server);
930 }
931 }
932
933 // Cleanup before exiting
934 unlink(server->path);
935
936 // stop all children
937 if(spawn_server_requests) {
938 // nd_log(NDLS_COLLECTORS, NDLP_INFO, "SPAWN SERVER: killing all children...");
939 size_t killed = 0;
940 for(SPAWN_REQUEST *rq = spawn_server_requests; rq ; rq = rq->next) {
941 kill(rq->pid, SIGTERM);
942 killed++;
943 }
944 while(spawn_server_requests) {
945 spawn_server_process_sigchld();
946 tinysleep();
947 }
948 // nd_log(NDLS_COLLECTORS, NDLP_INFO, "SPAWN SERVER: all %zu children finished", killed);
949 }
950
951 return 0;
952 }
953
954 // --------------------------------------------------------------------------------------------------------------------
955 // management of the spawn server
956
957 void spawn_server_destroy(SPAWN_SERVER *server) {
958 if(server->pipe[0] != -1) close(server->pipe[0]);
959 if(server->pipe[1] != -1) close(server->pipe[1]);
960 if(server->sock != -1) close(server->sock);
961
962 if(server->server_pid) {
963 kill(server->server_pid, SIGTERM);
964 waitpid(server->server_pid, NULL, 0);
965 }
966
967 if(server->path) {
968 unlink(server->path);
969 freez(server->path);
970 }
971
972 freez((void *)server->name);
973 freez(server);
974 }
975
976 static bool spawn_server_create_listening_socket(SPAWN_SERVER *server) {
977 if(spawn_server_is_running(server->path)) {
978 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: Server is already listening on path '%s'", server->path);
979 return false;
980 }
981
982 struct sockaddr_un server_addr = {
983 .sun_family = AF_UNIX,
984 };
985
986 if(!spawn_server_set_unix_socket_path(&server_addr, server->path, true,
987 "SPAWN SERVER: Cannot listen on path"))
988 return false;
989
990 if ((server->sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
991 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: Failed to create socket()");
992 return false;
993 }
994
995 unlink(server->path);
996 errno = 0;
997
998 if (bind(server->sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
999 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: Failed to bind()");
1000 return false;
1001 }
1002
1003 if (listen(server->sock, 5) == -1) {
1004 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: Failed to listen()");
1005 return false;
1006 }
1007
1008 if(chmod(server->path, 0770) != 0)
1009 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: failed to chmod '%s' to 0770", server->path);
1010
1011 return true;
1012 }
1013
1014 static void replace_stdio_with_dev_null() {
1015 // we cannot log in this function - the logger is not yet initialized after fork()
1016
1017 int dev_null_fd = open("/dev/null", O_RDWR);
1018 if (dev_null_fd == -1) {
1019 // nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: Failed to open /dev/null: %s", strerror(errno));
1020 return;
1021 }
1022
1023 // Redirect stdin (fd 0)
1024 if (dup2(dev_null_fd, STDIN_FILENO) == -1) {
1025 // nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: Failed to redirect stdin to /dev/null: %s", strerror(errno));
1026 close(dev_null_fd);
1027 return;
1028 }
1029
1030 // Redirect stdout (fd 1)
1031 if (dup2(dev_null_fd, STDOUT_FILENO) == -1) {
1032 // nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: Failed to redirect stdout to /dev/null: %s", strerror(errno));
1033 close(dev_null_fd);
1034 return;
1035 }
1036
1037 // Close the original /dev/null file descriptor
1038 close(dev_null_fd);
1039 }
1040
1041 SPAWN_SERVER* spawn_server_create(SPAWN_SERVER_OPTIONS options, const char *name, spawn_request_callback_t child_callback, int argc, const char **argv) {
1042 SPAWN_SERVER *server = callocz(1, sizeof(SPAWN_SERVER));
1043 server->pipe[0] = -1;
1044 server->pipe[1] = -1;
1045 server->sock = -1;
1046 server->cb = child_callback;
1047 server->argc = argc;
1048 server->argv = argv;
1049 server->options = options;
1050 server->id = __atomic_add_fetch(&spawn_server_id, 1, __ATOMIC_RELAXED);
1051 os_uuid_generate_random(server->magic.uuid);
1052
1053 const char *runtime_directory = getenv("NETDATA_RUN_DIR");
1054 if(!runtime_directory || !*runtime_directory)
1055 runtime_directory = os_run_dir(true);
1056
1057 if (runtime_directory) {
1058 struct stat statbuf;
1059
1060 if(!*runtime_directory)
1061 // it is empty
1062 runtime_directory = NULL;
1063
1064 else if (stat(runtime_directory, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
1065 // it exists and it is a directory
1066
1067 if (access(runtime_directory, W_OK) != 0) {
1068 // it is not writable by us
1069 nd_log(NDLS_COLLECTORS, NDLP_ERR, "Runtime directory '%s' is not writable, falling back to '/tmp'", runtime_directory);
1070 runtime_directory = NULL;
1071 }
1072 }
1073 else {
1074 // it does not exist
1075 nd_log(NDLS_COLLECTORS, NDLP_ERR, "Runtime directory '%s' does not exist, falling back to '/tmp'", runtime_directory);
1076 runtime_directory = NULL;
1077 }
1078 }
1079 if(!runtime_directory)
1080 runtime_directory = "/tmp";
1081
1082 char path[1024];
1083 int path_length;
1084 const size_t max_path_length = spawn_server_max_unix_socket_path_length();
1085 if(name && *name) {
1086 server->name = strdupz(name);
1087 path_length = snprintf(path, sizeof(path), "%s/netdata-spawn-%s.sock", runtime_directory, name);
1088 }
1089 else {
1090 server->name = strdupz("unnamed");
1091 path_length = snprintf(path, sizeof(path), "%s/netdata-spawn-%d-%zu.sock", runtime_directory, getpid(), server->id);
1092 }
1093
1094 if(path_length < 0) {
1095 nd_log(NDLS_COLLECTORS, NDLP_ERR,
1096 "SPAWN SERVER: failed to generate socket path for '%s'",
1097 server->name);
1098 goto cleanup;
1099 }
1100
1101 if((size_t)path_length >= sizeof(path)) {
1102 errno = ENAMETOOLONG;
1103 nd_log(NDLS_COLLECTORS, NDLP_ERR,
1104 "SPAWN SERVER: socket path for '%s' in runtime directory '%s' was truncated (needed %d chars plus NUL, buffer is %zu bytes)",
1105 server->name, runtime_directory, path_length, sizeof(path));
1106 goto cleanup;
1107 }
1108
1109 if((size_t)path_length > max_path_length) {
1110 errno = ENAMETOOLONG;
1111 nd_log(NDLS_COLLECTORS, NDLP_ERR,
1112 "SPAWN SERVER: socket path for '%s' in runtime directory '%s' exceeds the %zu-byte AF_UNIX limit",
1113 server->name, runtime_directory, max_path_length);
1114 goto cleanup;
1115 }
1116
1117 server->path = strdupz(path);
1118
1119 if (!spawn_server_create_listening_socket(server))
1120 goto cleanup;
1121
1122 if (pipe(server->pipe) == -1) {
1123 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: Cannot create status pipe()");
1124 goto cleanup;
1125 }
1126
1127 pid_t pid = fork();
1128 if (pid == 0) {
1129 // the child - the spawn server
1130
1131 char buf[16];
1132 snprintfz(buf, sizeof(buf), "spawn-%s", server->name);
1133 os_setproctitle(buf, server->argc, server->argv);
1134
1135 replace_stdio_with_dev_null();
1136
1137 if(nd_log_collectors_fd() != STDERR_FILENO)
1138 dup2(nd_log_collectors_fd(), STDERR_FILENO);
1139
1140 int fds_to_keep[] = {
1141 server->sock,
1142 server->pipe[1],
1143 nd_log_systemd_journal_fd(),
1144 };
1145 os_close_all_non_std_open_fds_except(fds_to_keep, _countof(fds_to_keep), 0);
1146 nd_log_reopen_log_files_for_spawn_server(buf);
1147 _exit(spawn_server_event_loop(server));
1148 }
1149 else if (pid > 0) {
1150 // the parent
1151 server->server_pid = pid;
1152 close(server->sock); server->sock = -1;
1153 close(server->pipe[1]); server->pipe[1] = -1;
1154
1155 struct status_report sr = { 0 };
1156 if (read(server->pipe[0], &sr, sizeof(sr)) != sizeof(sr)) {
1157 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: cannot read() initial status report from spawn server");
1158 goto cleanup;
1159 }
1160
1161 if(sr.status != STATUS_REPORT_STARTED) {
1162 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: server did not respond with success.");
1163 goto cleanup;
1164 }
1165
1166 if(sr.started.pid != server->server_pid) {
1167 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: server sent pid %d but we have created %d.", sr.started.pid, server->server_pid);
1168 goto cleanup;
1169 }
1170
1171 nd_log(NDLS_COLLECTORS, NDLP_DEBUG, "SPAWN SERVER: server created on pid %d", server->server_pid);
1172
1173 return server;
1174 }
1175
1176 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: Cannot fork()");
1177
1178 cleanup:
1179 spawn_server_destroy(server);
1180 return NULL;
1181 }
1182
1183 // --------------------------------------------------------------------------------------------------------------------
1184 // creating spawn server instances
1185
1186 void spawn_server_exec_destroy(SPAWN_INSTANCE *instance) {
1187 if(instance->child_pid) kill(instance->child_pid, SIGTERM);
1188 if(instance->write_fd != -1) close(instance->write_fd);
1189 if(instance->read_fd != -1) close(instance->read_fd);
1190 if(instance->sock != -1) close(instance->sock);
1191 freez(instance);
1192 }
1193
1194 static void log_invalid_magic(SPAWN_INSTANCE *instance, struct status_report *sr) {
1195 unsigned char buf[sizeof(*sr) + 1];
1196 memcpy(buf, sr, sizeof(*sr));
1197 buf[sizeof(buf) - 1] = '\0';
1198
1199 for(size_t i = 0; i < sizeof(buf) - 1; i++) {
1200 if (iscntrl(buf[i]) || !isprint(buf[i]))
1201 buf[i] = '_';
1202 }
1203
1204 nd_log(NDLS_COLLECTORS, NDLP_ERR,
1205 "SPAWN PARENT: invalid final status report for child %d, request %zu (invalid magic %#x in response, reads like '%s')",
1206 instance->child_pid, instance->request_id, sr->magic, buf);
1207 }
1208
1209 SPAWN_TIMEDWAIT_RESULT spawn_server_exec_timedwait(SPAWN_SERVER *server, SPAWN_INSTANCE *instance, int timeout_ms, int *status) {
1210 if(!instance) { if(status) *status = -1; return SPAWN_TIMEDWAIT_EXITED; }
1211
1212 // close the child pipes, to make it exit (same as spawn_server_exec_wait)
1213 if(instance->write_fd != -1) { close(instance->write_fd); instance->write_fd = -1; }
1214 if(instance->read_fd != -1) { close(instance->read_fd); instance->read_fd = -1; }
1215
1216 // a non-positive timeout means "wait forever" to wait_on_socket_or_cancel_with_timeout();
1217 // this primitive must always be bounded, so clamp to a minimal positive slice.
1218 if(timeout_ms <= 0) timeout_ms = 1;
1219
1220 // the spawn server sends the final status report on instance->sock when the child exits
1221 short revents = 0;
1222 NETDATA_SSL ssl = { 0 };
1223 int rc = wait_on_socket_or_cancel_with_timeout(&ssl, instance->sock, timeout_ms, POLLIN, &revents);
1224 if(rc == -1 /* thread cancelled */ || rc == 1 /* timeout */)
1225 // the child is still running; the caller decides whether to keep waiting or kill it
1226 return SPAWN_TIMEDWAIT_RUNNING;
1227
1228 if(rc == 2 /* error on the socket */) {
1229 // the status channel to the spawn server is broken (the spawn server itself died). We
1230 // cannot confirm the child exited, so we must NOT resolve as EXITED and free the instance
1231 // (that could leak a still-alive child). But this is terminal, not a transient "still
1232 // running" state, so we must NOT report RUNNING either (a caller looping on RUNNING with a
1233 // 0/"wait forever" timeout would spin forever). Report ERROR: the caller keeps the instance
1234 // and reclaims it by killing it.
1235 nd_log(NDLS_COLLECTORS, NDLP_ERR,
1236 "SPAWN PARENT: status socket error for request No %zu, pid %d",
1237 instance->request_id, instance->child_pid);
1238 return SPAWN_TIMEDWAIT_ERROR;
1239 }
1240
1241 // rc == 0: the status report is ready to read; the blocking wait returns immediately now.
1242 int st = spawn_server_exec_wait(server, instance);
1243 if(status) *status = st;
1244 return SPAWN_TIMEDWAIT_EXITED;
1245 }
1246
1247 int spawn_server_exec_wait(SPAWN_SERVER *server __maybe_unused, SPAWN_INSTANCE *instance) {
1248 int rc = -1;
1249
1250 // close the child pipes, to make it exit
1251 if(instance->write_fd != -1) { close(instance->write_fd); instance->write_fd = -1; }
1252 if(instance->read_fd != -1) { close(instance->read_fd); instance->read_fd = -1; }
1253
1254 // get the result
1255 struct status_report sr = { 0 };
1256 if(read(instance->sock, &sr, sizeof(sr)) != sizeof(sr))
1257 nd_log(NDLS_COLLECTORS, NDLP_ERR,
1258 "SPAWN PARENT: failed to read final status report for child %d, request %zu",
1259 instance->child_pid, instance->request_id);
1260
1261 else if(sr.magic != STATUS_REPORT_MAGIC)
1262 log_invalid_magic(instance, &sr);
1263 else {
1264 switch (sr.status) {
1265 case STATUS_REPORT_EXITED:
1266 rc = sr.exited.waitpid_status;
1267 break;
1268
1269 case STATUS_REPORT_STARTED:
1270 case STATUS_REPORT_FAILED:
1271 default:
1272 errno = 0;
1273 nd_log(
1274 NDLS_COLLECTORS, NDLP_ERR,
1275 "SPAWN PARENT: invalid status report to exec spawn request %zu for pid %d (status = %u)",
1276 instance->request_id, instance->child_pid, sr.status);
1277 break;
1278 }
1279 }
1280
1281 instance->child_pid = 0;
1282 spawn_server_exec_destroy(instance);
1283 return rc;
1284 }
1285
1286 int spawn_server_exec_kill(SPAWN_SERVER *server, SPAWN_INSTANCE *instance, int timeout_ms) {
1287 if(instance->write_fd != -1) { close(instance->write_fd); instance->write_fd = -1; }
1288 if(instance->read_fd != -1) { close(instance->read_fd); instance->read_fd = -1; }
1289
1290 if(timeout_ms > 0) {
1291 short revents;
1292 NETDATA_SSL ssl = { 0 };
1293 wait_on_socket_or_cancel_with_timeout(&ssl, instance->sock, timeout_ms, POLLIN, &revents);
1294 }
1295
1296 // kill the child, if it is still running
1297 if(instance->child_pid) {
1298 kill(instance->child_pid, SIGTERM);
1299
1300 // wait a bounded grace for the child to exit after SIGTERM. NOTE: timeout_ms is already
1301 // consumed above as the pre-kill grace (voluntary exit before SIGTERM); the post-SIGTERM
1302 // grace uses the fixed default so the caller's grace is not applied twice.
1303 // No PID-reuse race on the RUNNING path: the spawn server reaps the child and only then
1304 // sends the status report that makes timedwait return EXITED, so a RUNNING result means
1305 // the child has not been reaped yet and its PID is still held.
1306 int status;
1307 if(spawn_server_exec_timedwait(server, instance, SPAWN_KILL_DEFAULT_GRACE_MS, &status) == SPAWN_TIMEDWAIT_EXITED)
1308 return status;
1309
1310 // still not gone: force-kill, then wait another bounded grace. We must NOT fall through to
1311 // an unbounded blocking wait here - a child we cannot signal (e.g. SIGKILL returns EPERM)
1312 // would otherwise hang the caller (and shutdown) forever, the very thing this path prevents.
1313 if(kill(instance->child_pid, SIGKILL) != 0)
1314 nd_log(NDLS_COLLECTORS, NDLP_ERR,
1315 "SPAWN PARENT: SIGKILL of pid %d failed for request No %zu", instance->child_pid, instance->request_id);
1316
1317 if(spawn_server_exec_timedwait(server, instance, SPAWN_KILL_DEFAULT_GRACE_MS, &status) == SPAWN_TIMEDWAIT_EXITED)
1318 return status;
1319
1320 // could not confirm the child exited within the bounded waits; reclaim the instance so we
1321 // neither leak it nor block. The spawn server reaps the child if/when it actually dies.
1322 nd_log(NDLS_COLLECTORS, NDLP_ERR,
1323 "SPAWN PARENT: giving up waiting for pid %d after SIGKILL (request No %zu) - reclaiming",
1324 instance->child_pid, instance->request_id);
1325 instance->child_pid = 0; // already signalled; skip the SIGTERM in destroy
1326 spawn_server_exec_destroy(instance);
1327 return -1;
1328 }
1329
1330 return spawn_server_exec_wait(server, instance);
1331 }
1332
1333 SPAWN_INSTANCE* spawn_server_exec(SPAWN_SERVER *server, int stderr_fd, int custom_fd, const char **argv, const void *data, size_t data_size, SPAWN_INSTANCE_TYPE type) {
1334 if(!server) return NULL;
1335
1336 int pipe_stdin[2] = { -1, -1 }, pipe_stdout[2] = { -1, -1 };
1337
1338 SPAWN_INSTANCE *instance = callocz(1, sizeof(SPAWN_INSTANCE));
1339 instance->read_fd = -1;
1340 instance->write_fd = -1;
1341
1342 instance->sock = connect_to_spawn_server(server->path, true);
1343 if(instance->sock == -1)
1344 goto cleanup;
1345
1346 if (pipe(pipe_stdin) == -1) {
1347 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN PARENT: Cannot create stdin pipe()");
1348 goto cleanup;
1349 }
1350
1351 if (pipe(pipe_stdout) == -1) {
1352 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN PARENT: Cannot create stdout pipe()");
1353 goto cleanup;
1354 }
1355
1356 SPAWN_REQUEST request = {
1357 .request_id = __atomic_add_fetch(&server->request_id, 1, __ATOMIC_RELAXED),
1358 .sock = instance->sock,
1359 .fds = {
1360 [0] = pipe_stdin[0],
1361 [1] = pipe_stdout[1],
1362 [2] = stderr_fd,
1363 [3] = custom_fd,
1364 },
1365 .envp = (const char **)environ,
1366 .argv = argv,
1367 .data = data,
1368 .data_size = data_size,
1369 .type = type
1370 };
1371
1372 if(!spawn_server_send_request(&server->magic, &request))
1373 goto cleanup;
1374
1375 close(pipe_stdin[0]); pipe_stdin[0] = -1;
1376 instance->write_fd = pipe_stdin[1]; pipe_stdin[1] = -1;
1377
1378 close(pipe_stdout[1]); pipe_stdout[1] = -1;
1379 instance->read_fd = pipe_stdout[0]; pipe_stdout[0] = -1;
1380
1381 // copy the request id to the instance
1382 instance->request_id = request.request_id;
1383
1384 struct status_report sr = { 0 };
1385 if(read(instance->sock, &sr, sizeof(sr)) != sizeof(sr)) {
1386 nd_log(NDLS_COLLECTORS, NDLP_ERR,
1387 "SPAWN PARENT: Failed to exec spawn request %zu (cannot get initial status report)",
1388 request.request_id);
1389 goto cleanup;
1390 }
1391
1392 if(sr.magic != STATUS_REPORT_MAGIC) {
1393 nd_log(NDLS_COLLECTORS, NDLP_ERR,
1394 "SPAWN PARENT: Failed to exec spawn request %zu (invalid magic %#x in response)",
1395 request.request_id, sr.magic);
1396 goto cleanup;
1397 }
1398
1399 switch(sr.status) {
1400 case STATUS_REPORT_STARTED:
1401 instance->child_pid = sr.started.pid;
1402 return instance;
1403
1404 case STATUS_REPORT_FAILED:
1405 errno = sr.failed.err_no;
1406 nd_log(NDLS_COLLECTORS, NDLP_ERR,
1407 "SPAWN PARENT: Failed to exec spawn request %zu (server reports failure, errno is updated)",
1408 request.request_id);
1409 errno = 0;
1410 break;
1411
1412 case STATUS_REPORT_EXITED:
1413 errno = ENOEXEC;
1414 nd_log(NDLS_COLLECTORS, NDLP_ERR,
1415 "SPAWN PARENT: Failed to exec spawn request %zu (server reports exit, errno is updated)",
1416 request.request_id);
1417 errno = 0;
1418 break;
1419
1420 default:
1421 errno = 0;
1422 nd_log(NDLS_COLLECTORS, NDLP_ERR,
1423 "SPAWN PARENT: Invalid status report to exec spawn request %zu (received invalid data)",
1424 request.request_id);
1425 break;
1426 }
1427
1428 cleanup:
1429 if (pipe_stdin[0] >= 0) close(pipe_stdin[0]);
1430 if (pipe_stdin[1] >= 0) close(pipe_stdin[1]);
1431 if (pipe_stdout[0] >= 0) close(pipe_stdout[0]);
1432 if (pipe_stdout[1] >= 0) close(pipe_stdout[1]);
1433 spawn_server_exec_destroy(instance);
1434 return NULL;
1435 }
1436
1437 #endif