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-09-11 18:55:36 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,
     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 */
        

Generated by: LCOV version 2.0-1