Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2023 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it
6 : under the terms of the GNU General Public License as published
7 : by the Free Software Foundation; either version 3, or (at your
8 : option) any later version.
9 :
10 : TALER is distributed in the hope that it will be useful, but
11 : WITHOUT ANY WARRANTY; without even the implied warranty of
12 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : GNU General Public License for more details.
14 :
15 : You should have received a copy of the GNU General Public
16 : License along with TALER; see the file COPYING. If not,
17 : see <http://www.gnu.org/licenses/>
18 : */
19 : /**
20 : * @file testing/testing_api_cmd_system_start.c
21 : * @brief run taler-benchmark-setup.sh command
22 : * @author Christian Grothoff
23 : */
24 : #include "platform.h" /* UNNECESSARY? */
25 : #include "taler/taler_json_lib.h"
26 : #include <gnunet/gnunet_curl_lib.h>
27 : #include "taler/taler_signatures.h" /* UNNECESSARY? */
28 : #include "taler/taler_testing_lib.h"
29 :
30 :
31 : /**
32 : * State for a "system" CMD.
33 : */
34 : struct SystemState
35 : {
36 :
37 : /**
38 : * System process.
39 : */
40 : struct GNUNET_Process *system_proc;
41 :
42 : /**
43 : * Input pipe to @e system_proc, used to keep the
44 : * process alive until we are done.
45 : */
46 : struct GNUNET_DISK_PipeHandle *pipe_in;
47 :
48 : /**
49 : * Output pipe to @e system_proc, used to find out
50 : * when the services are ready.
51 : */
52 : struct GNUNET_DISK_PipeHandle *pipe_out;
53 :
54 : /**
55 : * Task reading from @e pipe_in.
56 : */
57 : struct GNUNET_SCHEDULER_Task *reader;
58 :
59 : /**
60 : * Waiting for child to die.
61 : */
62 : struct GNUNET_ChildWaitHandle *cwh;
63 :
64 : /**
65 : * Our interpreter state.
66 : */
67 : struct TALER_TESTING_Interpreter *is;
68 :
69 : /**
70 : * NULL-terminated array of command-line arguments.
71 : */
72 : char **args;
73 :
74 : /**
75 : * Input buffer for the stdin of the test setup helper.
76 : */
77 : struct GNUNET_Buffer ibuf;
78 :
79 : /**
80 : * Did we find the ready tag?
81 : */
82 : bool ready;
83 :
84 : /**
85 : * Is the child process still running?
86 : */
87 : bool active;
88 : };
89 :
90 :
91 : /**
92 : * Defines a GNUNET_ChildCompletedCallback which is sent back
93 : * upon death or completion of a child process.
94 : *
95 : * @param cls our `struct SystemState *`
96 : * @param type type of the process
97 : * @param exit_code status code of the process
98 : */
99 : static void
100 0 : setup_terminated (void *cls,
101 : enum GNUNET_OS_ProcessStatusType type,
102 : long unsigned int exit_code)
103 : {
104 0 : struct SystemState *as = cls;
105 :
106 0 : as->cwh = NULL;
107 0 : as->active = false;
108 0 : if (NULL != as->reader)
109 : {
110 0 : GNUNET_SCHEDULER_cancel (as->reader);
111 0 : as->reader = NULL;
112 : }
113 0 : if (! as->ready)
114 : {
115 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
116 : "Launching Taler system failed: %d/%llu\n",
117 : (int) type,
118 : (unsigned long long) exit_code);
119 0 : TALER_TESTING_interpreter_fail (as->is);
120 0 : return;
121 : }
122 : }
123 :
124 :
125 : /**
126 : * Start helper to read from stdout of child.
127 : *
128 : * @param as our system state
129 : */
130 : static void
131 : start_reader (struct SystemState *as);
132 :
133 : #define READY_MARKER "READY:"
134 :
135 : static void
136 19 : read_stdout (void *cls)
137 : {
138 19 : struct SystemState *as = cls;
139 : const struct GNUNET_DISK_FileHandle *fh;
140 : char buf[1024];
141 : ssize_t ret;
142 19 : size_t off = 0;
143 19 : char *testroot = NULL;
144 :
145 19 : as->reader = NULL;
146 19 : fh = GNUNET_DISK_pipe_handle (as->pipe_out,
147 : GNUNET_DISK_PIPE_END_READ);
148 19 : ret = GNUNET_DISK_file_read (fh,
149 : buf,
150 : sizeof (buf) - 1);
151 19 : if (-1 == ret)
152 : {
153 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
154 : "read");
155 0 : TALER_TESTING_interpreter_fail (as->is);
156 19 : return;
157 : }
158 19 : if (0 == ret)
159 : {
160 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
161 : "Child closed stdout\n");
162 0 : return;
163 : }
164 19 : GNUNET_buffer_write (&as->ibuf, buf, ret);
165 19 : if ( (0 == strncmp (as->ibuf.mem,
166 : READY_MARKER,
167 19 : strlen (READY_MARKER))) &&
168 19 : (NULL != (testroot = strchr (as->ibuf.mem,
169 : '\n'))) )
170 : {
171 19 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
172 : "Got test root %s\n",
173 : testroot);
174 19 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
175 : "Taler system UP\n");
176 19 : as->ready = true;
177 19 : TALER_TESTING_interpreter_next (as->is);
178 19 : return;
179 : }
180 0 : if (NULL != strchr (as->ibuf.mem,
181 0 : '\n') ||
182 0 : as->ibuf.position > 4096)
183 : {
184 0 : TALER_TESTING_interpreter_fail (as->is);
185 : /* Only commands are allowed! */
186 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
187 : "Unexpected stdout of test setup helper: %.*s\n",
188 : (int) ret,
189 : &buf[off]);
190 : }
191 :
192 0 : start_reader (as);
193 : }
194 :
195 :
196 : static void
197 19 : start_reader (struct SystemState *as)
198 : {
199 : const struct GNUNET_DISK_FileHandle *fh;
200 :
201 19 : GNUNET_assert (NULL == as->reader);
202 19 : fh = GNUNET_DISK_pipe_handle (as->pipe_out,
203 : GNUNET_DISK_PIPE_END_READ);
204 19 : as->reader = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
205 : fh,
206 : &read_stdout,
207 : as);
208 19 : }
209 :
210 :
211 : /**
212 : * Run the command. Use the `taler-unified-setup.sh` program.
213 : *
214 : * @param cls closure.
215 : * @param cmd command being run.
216 : * @param is interpreter state.
217 : */
218 : static void
219 19 : system_run (void *cls,
220 : const struct TALER_TESTING_Command *cmd,
221 : struct TALER_TESTING_Interpreter *is)
222 : {
223 19 : struct SystemState *as = cls;
224 :
225 : (void) cmd;
226 19 : as->is = is;
227 19 : as->pipe_in = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_READ);
228 19 : GNUNET_assert (NULL != as->pipe_in);
229 19 : as->pipe_out = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE);
230 19 : GNUNET_assert (NULL != as->pipe_out);
231 19 : as->system_proc = GNUNET_process_create (GNUNET_OS_INHERIT_STD_ERR);
232 19 : GNUNET_assert (GNUNET_OK ==
233 : GNUNET_process_set_options (
234 : as->system_proc,
235 : GNUNET_process_option_inherit_rpipe (as->pipe_in,
236 : STDIN_FILENO),
237 : GNUNET_process_option_inherit_wpipe (as->pipe_out,
238 : STDOUT_FILENO)));
239 19 : if (GNUNET_OK !=
240 19 : GNUNET_process_run_command_argv (as->system_proc,
241 : "taler-unified-setup.sh",
242 19 : (const char **) as->args))
243 : {
244 0 : GNUNET_break (0);
245 0 : GNUNET_process_destroy (as->system_proc);
246 0 : as->system_proc = NULL;
247 0 : TALER_TESTING_interpreter_fail (is);
248 0 : return;
249 : }
250 19 : as->active = true;
251 19 : start_reader (as);
252 19 : as->cwh = GNUNET_wait_child (as->system_proc,
253 : &setup_terminated,
254 : as);
255 : }
256 :
257 :
258 : /**
259 : * Free the state of a "system" CMD, and possibly kill its
260 : * process if it did not terminate correctly.
261 : *
262 : * @param cls closure.
263 : * @param cmd the command being freed.
264 : */
265 : static void
266 19 : system_cleanup (void *cls,
267 : const struct TALER_TESTING_Command *cmd)
268 : {
269 19 : struct SystemState *as = cls;
270 :
271 : (void) cmd;
272 19 : if (NULL != as->cwh)
273 : {
274 19 : GNUNET_wait_child_cancel (as->cwh);
275 19 : as->cwh = NULL;
276 : }
277 19 : if (NULL != as->reader)
278 : {
279 0 : GNUNET_SCHEDULER_cancel (as->reader);
280 0 : as->reader = NULL;
281 : }
282 19 : GNUNET_buffer_clear (&as->ibuf);
283 19 : if (NULL != as->system_proc)
284 : {
285 19 : if (as->active)
286 : {
287 19 : GNUNET_break (GNUNET_OK ==
288 : GNUNET_process_kill (as->system_proc,
289 : SIGTERM));
290 19 : GNUNET_process_wait (as->system_proc,
291 : true,
292 : NULL,
293 : NULL);
294 : }
295 19 : GNUNET_process_destroy (as->system_proc);
296 19 : as->system_proc = NULL;
297 : }
298 19 : if (NULL != as->pipe_in)
299 : {
300 19 : GNUNET_break (GNUNET_OK ==
301 : GNUNET_DISK_pipe_close (as->pipe_in));
302 19 : as->pipe_in = NULL;
303 : }
304 19 : if (NULL != as->pipe_out)
305 : {
306 19 : GNUNET_break (GNUNET_OK ==
307 : GNUNET_DISK_pipe_close (as->pipe_out));
308 19 : as->pipe_out = NULL;
309 : }
310 :
311 105 : for (unsigned int i = 0; NULL != as->args[i]; i++)
312 86 : GNUNET_free (as->args[i]);
313 19 : GNUNET_free (as->args);
314 19 : GNUNET_free (as);
315 19 : }
316 :
317 :
318 : /**
319 : * Offer "system" CMD internal data to other commands.
320 : *
321 : * @param cls closure.
322 : * @param[out] ret result.
323 : * @param trait name of the trait.
324 : * @param index index number of the object to offer.
325 : * @return #GNUNET_OK on success
326 : */
327 : static enum GNUNET_GenericReturnValue
328 20 : system_traits (void *cls,
329 : const void **ret,
330 : const char *trait,
331 : unsigned int index)
332 : {
333 20 : struct SystemState *as = cls;
334 : struct TALER_TESTING_Trait traits[] = {
335 20 : TALER_TESTING_make_trait_process (&as->system_proc),
336 20 : TALER_TESTING_trait_end ()
337 : };
338 :
339 20 : return TALER_TESTING_get_trait (traits,
340 : ret,
341 : trait,
342 : index);
343 : }
344 :
345 :
346 : struct TALER_TESTING_Command
347 19 : TALER_TESTING_cmd_system_start (
348 : const char *label,
349 : const char *config_file,
350 : ...)
351 : {
352 : struct SystemState *as;
353 : va_list ap;
354 : const char *arg;
355 : unsigned int cnt;
356 :
357 19 : as = GNUNET_new (struct SystemState);
358 19 : cnt = 4; /* 0-2 reserved, +1 for NULL termination */
359 19 : va_start (ap,
360 : config_file);
361 48 : while (NULL != (arg = va_arg (ap,
362 : const char *)))
363 : {
364 29 : cnt++;
365 : }
366 19 : va_end (ap);
367 19 : as->args = GNUNET_new_array (cnt,
368 : char *);
369 19 : as->args[0] = GNUNET_strdup ("taler-unified-setup");
370 19 : as->args[1] = GNUNET_strdup ("-c");
371 19 : as->args[2] = GNUNET_strdup (config_file);
372 19 : cnt = 3;
373 19 : va_start (ap,
374 : config_file);
375 48 : while (NULL != (arg = va_arg (ap,
376 : const char *)))
377 : {
378 29 : as->args[cnt++] = GNUNET_strdup (arg);
379 : }
380 19 : va_end (ap);
381 :
382 : {
383 19 : struct TALER_TESTING_Command cmd = {
384 : .cls = as,
385 : .label = label,
386 : .run = &system_run,
387 : .cleanup = &system_cleanup,
388 : .traits = &system_traits
389 : };
390 :
391 19 : return cmd;
392 : }
393 : }
394 :
395 :
396 : /* end of testing_api_cmd_system_start.c */
|