master
c 228 lines 6.45 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "spawn_popen.h"
4
5 struct popen_instance {
6 SPAWN_INSTANCE *si;
7 FILE *child_stdin_fp;
8 FILE *child_stdout_fp;
9 };
10
11 SPAWN_SERVER *netdata_main_spawn_server = NULL;
12 static SPINLOCK netdata_main_spawn_server_spinlock = SPINLOCK_INITIALIZER;
13
14 bool netdata_main_spawn_server_init(const char *name, int argc, const char **argv) {
15 if(netdata_main_spawn_server == NULL) {
16 spinlock_lock(&netdata_main_spawn_server_spinlock);
17 if(netdata_main_spawn_server == NULL)
18 netdata_main_spawn_server = spawn_server_create(SPAWN_SERVER_OPTION_EXEC, name, NULL, argc, argv);
19 spinlock_unlock(&netdata_main_spawn_server_spinlock);
20 }
21
22 return netdata_main_spawn_server != NULL;
23 }
24
25 void netdata_main_spawn_server_cleanup(void) {
26 if(netdata_main_spawn_server) {
27 spinlock_lock(&netdata_main_spawn_server_spinlock);
28 if(netdata_main_spawn_server) {
29 spawn_server_destroy(netdata_main_spawn_server);
30 netdata_main_spawn_server = NULL;
31 }
32 spinlock_unlock(&netdata_main_spawn_server_spinlock);
33 }
34 }
35
36 FILE *spawn_popen_stdin(POPEN_INSTANCE *pi) {
37 if(!pi->child_stdin_fp)
38 pi->child_stdin_fp = fdopen(spawn_server_instance_write_fd(pi->si), "w");
39
40 if(!pi->child_stdin_fp)
41 nd_log(NDLS_COLLECTORS, NDLP_ERR,
42 "Cannot open FILE on child's stdin on fd %d.",
43 spawn_server_instance_write_fd(pi->si));
44
45 return pi->child_stdin_fp;
46 }
47
48 FILE *spawn_popen_stdout(POPEN_INSTANCE *pi) {
49 if(!pi->child_stdout_fp)
50 pi->child_stdout_fp = fdopen(spawn_server_instance_read_fd(pi->si), "r");
51
52 if(!pi->child_stdout_fp)
53 nd_log(NDLS_COLLECTORS, NDLP_ERR,
54 "Cannot open FILE on child's stdout on fd %d.",
55 spawn_server_instance_read_fd(pi->si));
56
57 return pi->child_stdout_fp;
58 }
59
60 POPEN_INSTANCE *spawn_popen_run_argv(const char **argv) {
61 netdata_main_spawn_server_init(NULL, 0, NULL);
62
63 SPAWN_INSTANCE *si = spawn_server_exec(netdata_main_spawn_server, nd_log_collectors_fd(),
64 0, argv, NULL, 0, SPAWN_INSTANCE_TYPE_EXEC);
65
66 if(si == NULL) return NULL;
67
68 POPEN_INSTANCE *pi = callocz(1, sizeof(*pi));
69 pi->si = si;
70 return pi;
71 }
72
73 POPEN_INSTANCE *spawn_popen_run_variadic(const char *cmd, ...) {
74 va_list args;
75 va_list args_copy;
76 int argc = 0;
77
78 // Start processing variadic arguments
79 va_start(args, cmd);
80
81 // Make a copy of args to count the number of arguments
82 va_copy(args_copy, args);
83 while (va_arg(args_copy, char *) != NULL) argc++;
84 va_end(args_copy);
85
86 // Allocate memory for argv array (+2 for cmd and NULL terminator)
87 const char **argv = callocz(argc + 2, sizeof(*argv));
88
89 // Populate the argv array
90 argv[0] = cmd;
91
92 for (int i = 1; i <= argc; i++)
93 argv[i] = va_arg(args, const char *);
94
95 argv[argc + 1] = NULL; // NULL-terminate the array
96
97 // End processing variadic arguments
98 va_end(args);
99
100 POPEN_INSTANCE *pi = spawn_popen_run_argv(argv);
101 freez(argv);
102
103 return pi;
104 }
105
106 POPEN_INSTANCE *spawn_popen_run(const char *cmd) {
107 if(!cmd || !*cmd) return NULL;
108
109 //#if defined(OS_WINDOWS)
110 // if(strncmp(cmd, "exec ", 5) == 0) {
111 // size_t len = strlen(cmd);
112 // char cmd_copy[strlen(cmd) + 1];
113 // memcpy(cmd_copy, cmd, len + 1);
114 // char *words[100];
115 // size_t num_words = quoted_strings_splitter(cmd_copy, words, 100, isspace_map_pluginsd);
116 // char *exec = get_word(words, num_words, 0);
117 // char *prog = get_word(words, num_words, 1);
118 // if (strcmp(exec, "exec") == 0 &&
119 // prog &&
120 // strendswith(prog, ".plugin") &&
121 // !strendswith(prog, "charts.d.plugin") &&
122 // !strendswith(prog, "ioping.plugin")) {
123 // const char *argv[num_words - 1 + 1]; // remove exec, add terminator
124 //
125 // size_t dst = 0;
126 // for (size_t i = 1; i < num_words; i++)
127 // argv[dst++] = get_word(words, num_words, i);
128 //
129 // argv[dst] = NULL;
130 // return spawn_popen_run_argv(argv);
131 // }
132 // }
133 //#endif
134
135 const char *argv[] = {
136 "/bin/sh",
137 "-c",
138 cmd,
139 NULL
140 };
141 return spawn_popen_run_argv(argv);
142 }
143
144 static int spawn_popen_status_rc(int status) {
145 if(WIFEXITED(status))
146 return WEXITSTATUS(status);
147
148 if(WIFSIGNALED(status)) {
149 int sig = WTERMSIG(status);
150 switch(sig) {
151 case SIGTERM:
152 case SIGPIPE:
153 return 0;
154
155 default:
156 return -1;
157 }
158 }
159
160 return -1;
161 }
162
163 static void spawn_popen_close_files(POPEN_INSTANCE *pi) {
164 if(pi->child_stdin_fp) {
165 fclose(pi->child_stdin_fp);
166 pi->child_stdin_fp = NULL;
167 spawn_server_instance_write_fd_unset(pi->si);
168 }
169
170 if(pi->child_stdout_fp) {
171 fclose(pi->child_stdout_fp);
172 pi->child_stdout_fp = NULL;
173 spawn_server_instance_read_fd_unset(pi->si);
174 }
175 }
176
177 int spawn_popen_wait(POPEN_INSTANCE *pi) {
178 if(!pi) return -1;
179
180 spawn_popen_close_files(pi);
181 int status = spawn_server_exec_wait(netdata_main_spawn_server, pi->si);
182 freez(pi);
183 return spawn_popen_status_rc(status);
184 }
185
186 SPAWN_TIMEDWAIT_RESULT spawn_popen_timedwait(POPEN_INSTANCE *pi, int timeout_ms, int *code) {
187 if(!pi) {
188 if(code) *code = -1;
189 return SPAWN_TIMEDWAIT_EXITED;
190 }
191
192 spawn_popen_close_files(pi);
193
194 int status = 0;
195 SPAWN_TIMEDWAIT_RESULT rc = spawn_server_exec_timedwait(netdata_main_spawn_server, pi->si, timeout_ms, &status);
196 if(rc != SPAWN_TIMEDWAIT_EXITED)
197 // RUNNING or ERROR: pi->si is still valid, so pi stays alive for the caller
198 return rc;
199
200 // EXITED: spawn_server_exec_timedwait() has freed pi->si; free the wrapper too
201 freez(pi);
202 if(code) *code = spawn_popen_status_rc(status);
203 return SPAWN_TIMEDWAIT_EXITED;
204 }
205
206 int spawn_popen_kill(POPEN_INSTANCE *pi, int timeout_ms) {
207 if(!pi) return -1;
208
209 spawn_popen_close_files(pi);
210 int status = spawn_server_exec_kill(netdata_main_spawn_server, pi->si, timeout_ms);
211 freez(pi);
212 return spawn_popen_status_rc(status);
213 }
214
215 pid_t spawn_popen_pid(POPEN_INSTANCE *pi) {
216 if(!pi) return -1;
217 return spawn_server_instance_pid(pi->si);
218 }
219
220 int spawn_popen_read_fd(POPEN_INSTANCE *pi) {
221 if(!pi) return -1;
222 return spawn_server_instance_read_fd(pi->si);
223 }
224
225 int spawn_popen_write_fd(POPEN_INSTANCE *pi) {
226 if(!pi) return -1;
227 return spawn_server_instance_write_fd(pi->si);
228 }