LCOV - code coverage report
Current view: top level - testing - testing_api_cmd_system_start.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 75.4 % 114 86
Test Date: 2026-05-12 15:34:29 Functions: 85.7 % 7 6

            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 */
        

Generated by: LCOV version 2.0-1