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,
165 : buf,
166 : ret);
167 19 : if ( (0 == strncmp (as->ibuf.mem,
168 : READY_MARKER,
169 19 : strlen (READY_MARKER))) &&
170 19 : (NULL != (testroot = memchr (as->ibuf.mem,
171 : '\n',
172 : as->ibuf.position))) )
173 : {
174 19 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
175 : "Got test root %.*s\n",
176 : (int) (as->ibuf.position - (testroot - as->ibuf.mem)),
177 : testroot);
178 19 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
179 : "Taler system UP\n");
180 19 : as->ready = true;
181 19 : TALER_TESTING_interpreter_next (as->is);
182 19 : return;
183 : }
184 0 : if ( (NULL != memchr (as->ibuf.mem,
185 : '\n',
186 0 : as->ibuf.position)) ||
187 0 : (as->ibuf.position > 4096) )
188 : {
189 0 : TALER_TESTING_interpreter_fail (as->is);
190 : /* Only commands are allowed! */
191 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
192 : "Unexpected stdout of test setup helper: %.*s\n",
193 : (int) ret,
194 : &buf[off]);
195 : }
196 :
197 0 : start_reader (as);
198 : }
199 :
200 :
201 : static void
202 19 : start_reader (struct SystemState *as)
203 : {
204 : const struct GNUNET_DISK_FileHandle *fh;
205 :
206 19 : GNUNET_assert (NULL == as->reader);
207 19 : fh = GNUNET_DISK_pipe_handle (as->pipe_out,
208 : GNUNET_DISK_PIPE_END_READ);
209 19 : as->reader = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
210 : fh,
211 : &read_stdout,
212 : as);
213 19 : }
214 :
215 :
216 : /**
217 : * Run the command. Use the `taler-unified-setup.sh` program.
218 : *
219 : * @param cls closure.
220 : * @param cmd command being run.
221 : * @param is interpreter state.
222 : */
223 : static void
224 19 : system_run (void *cls,
225 : const struct TALER_TESTING_Command *cmd,
226 : struct TALER_TESTING_Interpreter *is)
227 : {
228 19 : struct SystemState *as = cls;
229 :
230 : (void) cmd;
231 19 : as->is = is;
232 19 : as->pipe_in = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_READ);
233 19 : GNUNET_assert (NULL != as->pipe_in);
234 19 : as->pipe_out = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE);
235 19 : GNUNET_assert (NULL != as->pipe_out);
236 19 : as->system_proc = GNUNET_process_create (GNUNET_OS_INHERIT_STD_ERR);
237 19 : GNUNET_assert (GNUNET_OK ==
238 : GNUNET_process_set_options (
239 : as->system_proc,
240 : GNUNET_process_option_inherit_rpipe (as->pipe_in,
241 : STDIN_FILENO),
242 : GNUNET_process_option_inherit_wpipe (as->pipe_out,
243 : STDOUT_FILENO)));
244 19 : if (GNUNET_OK !=
245 19 : GNUNET_process_run_command_argv (as->system_proc,
246 : "taler-unified-setup.sh",
247 19 : (const char **) as->args))
248 : {
249 0 : GNUNET_break (0);
250 0 : GNUNET_process_destroy (as->system_proc);
251 0 : as->system_proc = NULL;
252 0 : TALER_TESTING_interpreter_fail (is);
253 0 : return;
254 : }
255 19 : as->active = true;
256 19 : start_reader (as);
257 19 : as->cwh = GNUNET_wait_child (as->system_proc,
258 : &setup_terminated,
259 : as);
260 : }
261 :
262 :
263 : /**
264 : * Free the state of a "system" CMD, and possibly kill its
265 : * process if it did not terminate correctly.
266 : *
267 : * @param cls closure.
268 : * @param cmd the command being freed.
269 : */
270 : static void
271 19 : system_cleanup (void *cls,
272 : const struct TALER_TESTING_Command *cmd)
273 : {
274 19 : struct SystemState *as = cls;
275 :
276 : (void) cmd;
277 19 : if (NULL != as->cwh)
278 : {
279 19 : GNUNET_wait_child_cancel (as->cwh);
280 19 : as->cwh = NULL;
281 : }
282 19 : if (NULL != as->reader)
283 : {
284 0 : GNUNET_SCHEDULER_cancel (as->reader);
285 0 : as->reader = NULL;
286 : }
287 19 : GNUNET_buffer_clear (&as->ibuf);
288 19 : if (NULL != as->system_proc)
289 : {
290 19 : if (as->active)
291 : {
292 19 : GNUNET_break (GNUNET_OK ==
293 : GNUNET_process_kill (as->system_proc,
294 : SIGTERM));
295 19 : GNUNET_process_wait (as->system_proc,
296 : true,
297 : NULL,
298 : NULL);
299 : }
300 19 : GNUNET_process_destroy (as->system_proc);
301 19 : as->system_proc = NULL;
302 : }
303 19 : if (NULL != as->pipe_in)
304 : {
305 19 : GNUNET_break (GNUNET_OK ==
306 : GNUNET_DISK_pipe_close (as->pipe_in));
307 19 : as->pipe_in = NULL;
308 : }
309 19 : if (NULL != as->pipe_out)
310 : {
311 19 : GNUNET_break (GNUNET_OK ==
312 : GNUNET_DISK_pipe_close (as->pipe_out));
313 19 : as->pipe_out = NULL;
314 : }
315 :
316 105 : for (unsigned int i = 0; NULL != as->args[i]; i++)
317 86 : GNUNET_free (as->args[i]);
318 19 : GNUNET_free (as->args);
319 19 : GNUNET_free (as);
320 19 : }
321 :
322 :
323 : /**
324 : * Offer "system" CMD internal data to other commands.
325 : *
326 : * @param cls closure.
327 : * @param[out] ret result.
328 : * @param trait name of the trait.
329 : * @param index index number of the object to offer.
330 : * @return #GNUNET_OK on success
331 : */
332 : static enum GNUNET_GenericReturnValue
333 20 : system_traits (void *cls,
334 : const void **ret,
335 : const char *trait,
336 : unsigned int index)
337 : {
338 20 : struct SystemState *as = cls;
339 : struct TALER_TESTING_Trait traits[] = {
340 20 : TALER_TESTING_make_trait_process (&as->system_proc),
341 20 : TALER_TESTING_trait_end ()
342 : };
343 :
344 20 : return TALER_TESTING_get_trait (traits,
345 : ret,
346 : trait,
347 : index);
348 : }
349 :
350 :
351 : struct TALER_TESTING_Command
352 19 : TALER_TESTING_cmd_system_start (
353 : const char *label,
354 : const char *config_file,
355 : ...)
356 : {
357 : struct SystemState *as;
358 : va_list ap;
359 : const char *arg;
360 : unsigned int cnt;
361 :
362 19 : as = GNUNET_new (struct SystemState);
363 19 : cnt = 4; /* 0-2 reserved, +1 for NULL termination */
364 19 : va_start (ap,
365 : config_file);
366 48 : while (NULL != (arg = va_arg (ap,
367 : const char *)))
368 : {
369 29 : cnt++;
370 : }
371 19 : va_end (ap);
372 19 : as->args = GNUNET_new_array (cnt,
373 : char *);
374 19 : as->args[0] = GNUNET_strdup ("taler-unified-setup");
375 19 : as->args[1] = GNUNET_strdup ("-c");
376 19 : as->args[2] = GNUNET_strdup (config_file);
377 19 : cnt = 3;
378 19 : va_start (ap,
379 : config_file);
380 48 : while (NULL != (arg = va_arg (ap,
381 : const char *)))
382 : {
383 29 : as->args[cnt++] = GNUNET_strdup (arg);
384 : }
385 19 : va_end (ap);
386 :
387 : {
388 19 : struct TALER_TESTING_Command cmd = {
389 : .cls = as,
390 : .label = label,
391 : .run = &system_run,
392 : .cleanup = &system_cleanup,
393 : .traits = &system_traits
394 : };
395 :
396 19 : return cmd;
397 : }
398 : }
399 :
400 :
401 : /* end of testing_api_cmd_system_start.c */
|