LCOV - code coverage report
Current view: top level - testing - testing_api_cmd_system_start.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 83.3 % 114 95
Test Date: 2026-01-18 12:54:31 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 "taler/platform.h"
      25              : #include "taler/taler_json_lib.h"
      26              : #include <gnunet/gnunet_curl_lib.h>
      27              : #include "taler/taler_signatures.h"
      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_OS_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              :    * Current input buffer, 0-terminated.  Contains the last 15 bytes of input
      76              :    * so we can search them again for the "<<READY>>" tag.
      77              :    */
      78              :   char ibuf[16];
      79              : 
      80              :   /**
      81              :    * Did we find the ready tag?
      82              :    */
      83              :   bool ready;
      84              : 
      85              :   /**
      86              :    * Is the child process still running?
      87              :    */
      88              :   bool active;
      89              : };
      90              : 
      91              : 
      92              : /**
      93              :  * Defines a GNUNET_ChildCompletedCallback which is sent back
      94              :  * upon death or completion of a child process.
      95              :  *
      96              :  * @param cls our `struct SystemState *`
      97              :  * @param type type of the process
      98              :  * @param exit_code status code of the process
      99              :  */
     100              : static void
     101            0 : setup_terminated (void *cls,
     102              :                   enum GNUNET_OS_ProcessStatusType type,
     103              :                   long unsigned int exit_code)
     104              : {
     105            0 :   struct SystemState *as = cls;
     106              : 
     107            0 :   as->cwh = NULL;
     108            0 :   as->active = false;
     109            0 :   if (NULL != as->reader)
     110              :   {
     111            0 :     GNUNET_SCHEDULER_cancel (as->reader);
     112            0 :     as->reader = NULL;
     113              :   }
     114            0 :   if (! as->ready)
     115              :   {
     116            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     117              :                 "Launching Taler system failed: %d/%llu\n",
     118              :                 (int) type,
     119              :                 (unsigned long long) exit_code);
     120            0 :     TALER_TESTING_interpreter_fail (as->is);
     121            0 :     return;
     122              :   }
     123              : }
     124              : 
     125              : 
     126              : /**
     127              :  * Start helper to read from stdout of child.
     128              :  *
     129              :  * @param as our system state
     130              :  */
     131              : static void
     132              : start_reader (struct SystemState *as);
     133              : 
     134              : 
     135              : static void
     136          439 : read_stdout (void *cls)
     137              : {
     138          439 :   struct SystemState *as = cls;
     139              :   const struct GNUNET_DISK_FileHandle *fh;
     140              :   char buf[1024 * 10];
     141              :   ssize_t ret;
     142          439 :   size_t off = 0;
     143              : 
     144          439 :   as->reader = NULL;
     145          439 :   strcpy (buf,
     146          439 :           as->ibuf);
     147          439 :   off = strlen (buf);
     148          439 :   fh = GNUNET_DISK_pipe_handle (as->pipe_out,
     149              :                                 GNUNET_DISK_PIPE_END_READ);
     150          439 :   ret = GNUNET_DISK_file_read (fh,
     151          439 :                                &buf[off],
     152              :                                sizeof (buf) - off);
     153          439 :   if (-1 == ret)
     154              :   {
     155            0 :     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     156              :                          "read");
     157            0 :     TALER_TESTING_interpreter_fail (as->is);
     158           19 :     return;
     159              :   }
     160          439 :   if (0 == ret)
     161              :   {
     162            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     163              :                 "Child closed stdout\n");
     164            0 :     return;
     165              :   }
     166              :   /* forward log, except single '.' outputs */
     167          439 :   if ( (1 != ret) ||
     168           83 :        ('.' != buf[off]) )
     169          381 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     170              :                 "TUS: %.*s\n",
     171              :                 (int) ret,
     172              :                 &buf[off]);
     173          439 :   start_reader (as);
     174          439 :   off += ret;
     175          439 :   if (as->ready)
     176              :   {
     177              :     /* already done */
     178            0 :     return;
     179              :   }
     180          439 :   if (NULL !=
     181          439 :       memmem (buf,
     182              :               off,
     183              :               "\n<<READY>>\n",
     184              :               strlen ("\n<<READY>>\n")))
     185              :   {
     186           19 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     187              :                 "Taler system UP\n");
     188           19 :     as->ready = true;
     189           19 :     TALER_TESTING_interpreter_next (as->is);
     190           19 :     return;
     191              :   }
     192              : 
     193              :   {
     194              :     size_t mcpy;
     195              : 
     196          420 :     mcpy = GNUNET_MIN (off,
     197              :                        sizeof (as->ibuf) - 1);
     198          420 :     memcpy (as->ibuf,
     199          420 :             &buf[off - mcpy],
     200              :             mcpy);
     201          420 :     as->ibuf[mcpy] = '\0';
     202              :   }
     203              : }
     204              : 
     205              : 
     206              : static void
     207          458 : start_reader (struct SystemState *as)
     208              : {
     209              :   const struct GNUNET_DISK_FileHandle *fh;
     210              : 
     211          458 :   GNUNET_assert (NULL == as->reader);
     212          458 :   fh = GNUNET_DISK_pipe_handle (as->pipe_out,
     213              :                                 GNUNET_DISK_PIPE_END_READ);
     214          458 :   as->reader = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
     215              :                                                fh,
     216              :                                                &read_stdout,
     217              :                                                as);
     218          458 : }
     219              : 
     220              : 
     221              : /**
     222              :  * Run the command.  Use the `taler-unified-setup.sh` program.
     223              :  *
     224              :  * @param cls closure.
     225              :  * @param cmd command being run.
     226              :  * @param is interpreter state.
     227              :  */
     228              : static void
     229           19 : system_run (void *cls,
     230              :             const struct TALER_TESTING_Command *cmd,
     231              :             struct TALER_TESTING_Interpreter *is)
     232              : {
     233           19 :   struct SystemState *as = cls;
     234              : 
     235              :   (void) cmd;
     236           19 :   as->is = is;
     237           19 :   as->pipe_in = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_READ);
     238           19 :   GNUNET_assert (NULL != as->pipe_in);
     239           19 :   as->pipe_out = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE);
     240           19 :   GNUNET_assert (NULL != as->pipe_out);
     241              :   as->system_proc
     242           38 :     = GNUNET_OS_start_process_vap (
     243              :         GNUNET_OS_INHERIT_STD_ERR,
     244              :         as->pipe_in, as->pipe_out, NULL,
     245              :         "taler-unified-setup.sh",
     246           19 :         as->args);
     247           19 :   if (NULL == as->system_proc)
     248              :   {
     249            0 :     GNUNET_break (0);
     250            0 :     TALER_TESTING_interpreter_fail (is);
     251            0 :     return;
     252              :   }
     253           19 :   as->active = true;
     254           19 :   start_reader (as);
     255           19 :   as->cwh = GNUNET_wait_child (as->system_proc,
     256              :                                &setup_terminated,
     257              :                                as);
     258              : }
     259              : 
     260              : 
     261              : /**
     262              :  * Free the state of a "system" CMD, and possibly kill its
     263              :  * process if it did not terminate correctly.
     264              :  *
     265              :  * @param cls closure.
     266              :  * @param cmd the command being freed.
     267              :  */
     268              : static void
     269           19 : system_cleanup (void *cls,
     270              :                 const struct TALER_TESTING_Command *cmd)
     271              : {
     272           19 :   struct SystemState *as = cls;
     273              : 
     274              :   (void) cmd;
     275           19 :   if (NULL != as->cwh)
     276              :   {
     277           19 :     GNUNET_wait_child_cancel (as->cwh);
     278           19 :     as->cwh = NULL;
     279              :   }
     280           19 :   if (NULL != as->reader)
     281              :   {
     282           19 :     GNUNET_SCHEDULER_cancel (as->reader);
     283           19 :     as->reader = NULL;
     284              :   }
     285           19 :   if (NULL != as->system_proc)
     286              :   {
     287           19 :     if (as->active)
     288              :     {
     289           19 :       GNUNET_break (0 ==
     290              :                     GNUNET_OS_process_kill (as->system_proc,
     291              :                                             SIGTERM));
     292           19 :       GNUNET_OS_process_wait (as->system_proc);
     293              :     }
     294           19 :     GNUNET_OS_process_destroy (as->system_proc);
     295           19 :     as->system_proc = NULL;
     296              :   }
     297           19 :   if (NULL != as->pipe_in)
     298              :   {
     299           19 :     GNUNET_break (GNUNET_OK ==
     300              :                   GNUNET_DISK_pipe_close (as->pipe_in));
     301           19 :     as->pipe_in = NULL;
     302              :   }
     303           19 :   if (NULL != as->pipe_out)
     304              :   {
     305           19 :     GNUNET_break (GNUNET_OK ==
     306              :                   GNUNET_DISK_pipe_close (as->pipe_out));
     307           19 :     as->pipe_out = NULL;
     308              :   }
     309              : 
     310          105 :   for (unsigned int i = 0; NULL != as->args[i]; i++)
     311           86 :     GNUNET_free (as->args[i]);
     312           19 :   GNUNET_free (as->args);
     313           19 :   GNUNET_free (as);
     314           19 : }
     315              : 
     316              : 
     317              : /**
     318              :  * Offer "system" CMD internal data to other commands.
     319              :  *
     320              :  * @param cls closure.
     321              :  * @param[out] ret result.
     322              :  * @param trait name of the trait.
     323              :  * @param index index number of the object to offer.
     324              :  * @return #GNUNET_OK on success
     325              :  */
     326              : static enum GNUNET_GenericReturnValue
     327           20 : system_traits (void *cls,
     328              :                const void **ret,
     329              :                const char *trait,
     330              :                unsigned int index)
     331              : {
     332           20 :   struct SystemState *as = cls;
     333              :   struct TALER_TESTING_Trait traits[] = {
     334           20 :     TALER_TESTING_make_trait_process (&as->system_proc),
     335           20 :     TALER_TESTING_trait_end ()
     336              :   };
     337              : 
     338           20 :   return TALER_TESTING_get_trait (traits,
     339              :                                   ret,
     340              :                                   trait,
     341              :                                   index);
     342              : }
     343              : 
     344              : 
     345              : struct TALER_TESTING_Command
     346           19 : TALER_TESTING_cmd_system_start (
     347              :   const char *label,
     348              :   const char *config_file,
     349              :   ...)
     350              : {
     351              :   struct SystemState *as;
     352              :   va_list ap;
     353              :   const char *arg;
     354              :   unsigned int cnt;
     355              : 
     356           19 :   as = GNUNET_new (struct SystemState);
     357           19 :   cnt = 4; /* 0-2 reserved, +1 for NULL termination */
     358           19 :   va_start (ap,
     359              :             config_file);
     360           48 :   while (NULL != (arg = va_arg (ap,
     361              :                                 const char *)))
     362              :   {
     363           29 :     cnt++;
     364              :   }
     365           19 :   va_end (ap);
     366           19 :   as->args = GNUNET_new_array (cnt,
     367              :                                char *);
     368           19 :   as->args[0] = GNUNET_strdup ("taler-unified-setup");
     369           19 :   as->args[1] = GNUNET_strdup ("-c");
     370           19 :   as->args[2] = GNUNET_strdup (config_file);
     371           19 :   cnt = 3;
     372           19 :   va_start (ap,
     373              :             config_file);
     374           48 :   while (NULL != (arg = va_arg (ap,
     375              :                                 const char *)))
     376              :   {
     377           29 :     as->args[cnt++] = GNUNET_strdup (arg);
     378              :   }
     379           19 :   va_end (ap);
     380              : 
     381              :   {
     382           19 :     struct TALER_TESTING_Command cmd = {
     383              :       .cls = as,
     384              :       .label = label,
     385              :       .run = &system_run,
     386              :       .cleanup = &system_cleanup,
     387              :       .traits = &system_traits
     388              :     };
     389              : 
     390           19 :     return cmd;
     391              :   }
     392              : }
     393              : 
     394              : 
     395              : /* end of testing_api_cmd_system_start.c */
        

Generated by: LCOV version 2.0-1