LCOV - code coverage report
Current view: top level - testing - testing_api_cmd_system_start.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 95 114 83.3 %
Date: 2025-06-22 12:09:43 Functions: 6 7 85.7 %

          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         473 : read_stdout (void *cls)
     137             : {
     138         473 :   struct SystemState *as = cls;
     139             :   const struct GNUNET_DISK_FileHandle *fh;
     140             :   char buf[1024 * 10];
     141             :   ssize_t ret;
     142         473 :   size_t off = 0;
     143             : 
     144         473 :   as->reader = NULL;
     145         473 :   strcpy (buf,
     146         473 :           as->ibuf);
     147         473 :   off = strlen (buf);
     148         473 :   fh = GNUNET_DISK_pipe_handle (as->pipe_out,
     149             :                                 GNUNET_DISK_PIPE_END_READ);
     150         473 :   ret = GNUNET_DISK_file_read (fh,
     151         473 :                                &buf[off],
     152             :                                sizeof (buf) - off);
     153         473 :   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         473 :   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         473 :   if ( (1 != ret) ||
     168         110 :        ('.' != buf[off]) )
     169         411 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     170             :                 "TUS: %.*s\n",
     171             :                 (int) ret,
     172             :                 &buf[off]);
     173         473 :   start_reader (as);
     174         473 :   off += ret;
     175         473 :   if (as->ready)
     176             :   {
     177             :     /* already done */
     178           0 :     return;
     179             :   }
     180         473 :   if (NULL !=
     181         473 :       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         454 :     mcpy = GNUNET_MIN (off,
     197             :                        sizeof (as->ibuf) - 1);
     198         454 :     memcpy (as->ibuf,
     199         454 :             &buf[off - mcpy],
     200             :             mcpy);
     201         454 :     as->ibuf[mcpy] = '\0';
     202             :   }
     203             : }
     204             : 
     205             : 
     206             : static void
     207         492 : start_reader (struct SystemState *as)
     208             : {
     209             :   const struct GNUNET_DISK_FileHandle *fh;
     210             : 
     211         492 :   GNUNET_assert (NULL == as->reader);
     212         492 :   fh = GNUNET_DISK_pipe_handle (as->pipe_out,
     213             :                                 GNUNET_DISK_PIPE_END_READ);
     214         492 :   as->reader = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
     215             :                                                fh,
     216             :                                                &read_stdout,
     217             :                                                as);
     218         492 : }
     219             : 
     220             : 
     221             : /**
     222             :  * Run the command.  Use the `taler-exchange-system' 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 1.16