exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

testing_api_cmd_system_start.c (10209B)


      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 setup_terminated (void *cls,
    101                   enum GNUNET_OS_ProcessStatusType type,
    102                   long unsigned int exit_code)
    103 {
    104   struct SystemState *as = cls;
    105 
    106   as->cwh = NULL;
    107   as->active = false;
    108   if (NULL != as->reader)
    109   {
    110     GNUNET_SCHEDULER_cancel (as->reader);
    111     as->reader = NULL;
    112   }
    113   if (! as->ready)
    114   {
    115     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    116                 "Launching Taler system failed: %d/%llu\n",
    117                 (int) type,
    118                 (unsigned long long) exit_code);
    119     TALER_TESTING_interpreter_fail (as->is);
    120     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 read_stdout (void *cls)
    137 {
    138   struct SystemState *as = cls;
    139   const struct GNUNET_DISK_FileHandle *fh;
    140   char buf[1024];
    141   ssize_t ret;
    142   size_t off = 0;
    143   char *testroot = NULL;
    144 
    145   as->reader = NULL;
    146   fh = GNUNET_DISK_pipe_handle (as->pipe_out,
    147                                 GNUNET_DISK_PIPE_END_READ);
    148   ret = GNUNET_DISK_file_read (fh,
    149                                buf,
    150                                sizeof (buf) - 1);
    151   if (-1 == ret)
    152   {
    153     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    154                          "read");
    155     TALER_TESTING_interpreter_fail (as->is);
    156     return;
    157   }
    158   if (0 == ret)
    159   {
    160     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    161                 "Child closed stdout\n");
    162     return;
    163   }
    164   GNUNET_buffer_write (&as->ibuf,
    165                        buf,
    166                        ret);
    167   if ( (0 == strncmp (as->ibuf.mem,
    168                       READY_MARKER,
    169                       strlen (READY_MARKER))) &&
    170        (NULL != (testroot = memchr (as->ibuf.mem,
    171                                     '\n',
    172                                     as->ibuf.position))) )
    173   {
    174     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    175                 "Got test root %.*s\n",
    176                 (int) (as->ibuf.position - (testroot - as->ibuf.mem)),
    177                 testroot);
    178     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    179                 "Taler system UP\n");
    180     as->ready = true;
    181     TALER_TESTING_interpreter_next (as->is);
    182     return;
    183   }
    184   if ( (NULL != memchr (as->ibuf.mem,
    185                         '\n',
    186                         as->ibuf.position)) ||
    187        (as->ibuf.position > 4096) )
    188   {
    189     TALER_TESTING_interpreter_fail (as->is);
    190     /* Only commands are allowed! */
    191     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    192                 "Unexpected stdout of test setup helper: %.*s\n",
    193                 (int) ret,
    194                 &buf[off]);
    195   }
    196 
    197   start_reader (as);
    198 }
    199 
    200 
    201 static void
    202 start_reader (struct SystemState *as)
    203 {
    204   const struct GNUNET_DISK_FileHandle *fh;
    205 
    206   GNUNET_assert (NULL == as->reader);
    207   fh = GNUNET_DISK_pipe_handle (as->pipe_out,
    208                                 GNUNET_DISK_PIPE_END_READ);
    209   as->reader = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
    210                                                fh,
    211                                                &read_stdout,
    212                                                as);
    213 }
    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 system_run (void *cls,
    225             const struct TALER_TESTING_Command *cmd,
    226             struct TALER_TESTING_Interpreter *is)
    227 {
    228   struct SystemState *as = cls;
    229 
    230   (void) cmd;
    231   as->is = is;
    232   as->pipe_in = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_READ);
    233   GNUNET_assert (NULL != as->pipe_in);
    234   as->pipe_out = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE);
    235   GNUNET_assert (NULL != as->pipe_out);
    236   as->system_proc = GNUNET_process_create (GNUNET_OS_INHERIT_STD_ERR);
    237   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   if (GNUNET_OK !=
    245       GNUNET_process_run_command_argv (as->system_proc,
    246                                        "taler-unified-setup.sh",
    247                                        (const char **) as->args))
    248   {
    249     GNUNET_break (0);
    250     GNUNET_process_destroy (as->system_proc);
    251     as->system_proc = NULL;
    252     TALER_TESTING_interpreter_fail (is);
    253     return;
    254   }
    255   as->active = true;
    256   start_reader (as);
    257   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 system_cleanup (void *cls,
    272                 const struct TALER_TESTING_Command *cmd)
    273 {
    274   struct SystemState *as = cls;
    275 
    276   (void) cmd;
    277   if (NULL != as->cwh)
    278   {
    279     GNUNET_wait_child_cancel (as->cwh);
    280     as->cwh = NULL;
    281   }
    282   if (NULL != as->reader)
    283   {
    284     GNUNET_SCHEDULER_cancel (as->reader);
    285     as->reader = NULL;
    286   }
    287   GNUNET_buffer_clear (&as->ibuf);
    288   if (NULL != as->system_proc)
    289   {
    290     if (as->active)
    291     {
    292       GNUNET_break (GNUNET_OK ==
    293                     GNUNET_process_kill (as->system_proc,
    294                                          SIGTERM));
    295       GNUNET_process_wait (as->system_proc,
    296                            true,
    297                            NULL,
    298                            NULL);
    299     }
    300     GNUNET_process_destroy (as->system_proc);
    301     as->system_proc = NULL;
    302   }
    303   if (NULL != as->pipe_in)
    304   {
    305     GNUNET_break (GNUNET_OK ==
    306                   GNUNET_DISK_pipe_close (as->pipe_in));
    307     as->pipe_in = NULL;
    308   }
    309   if (NULL != as->pipe_out)
    310   {
    311     GNUNET_break (GNUNET_OK ==
    312                   GNUNET_DISK_pipe_close (as->pipe_out));
    313     as->pipe_out = NULL;
    314   }
    315 
    316   for (unsigned int i = 0; NULL != as->args[i]; i++)
    317     GNUNET_free (as->args[i]);
    318   GNUNET_free (as->args);
    319   GNUNET_free (as);
    320 }
    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 system_traits (void *cls,
    334                const void **ret,
    335                const char *trait,
    336                unsigned int index)
    337 {
    338   struct SystemState *as = cls;
    339   struct TALER_TESTING_Trait traits[] = {
    340     TALER_TESTING_make_trait_process (&as->system_proc),
    341     TALER_TESTING_trait_end ()
    342   };
    343 
    344   return TALER_TESTING_get_trait (traits,
    345                                   ret,
    346                                   trait,
    347                                   index);
    348 }
    349 
    350 
    351 struct TALER_TESTING_Command
    352 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   as = GNUNET_new (struct SystemState);
    363   cnt = 4; /* 0-2 reserved, +1 for NULL termination */
    364   va_start (ap,
    365             config_file);
    366   while (NULL != (arg = va_arg (ap,
    367                                 const char *)))
    368   {
    369     cnt++;
    370   }
    371   va_end (ap);
    372   as->args = GNUNET_new_array (cnt,
    373                                char *);
    374   as->args[0] = GNUNET_strdup ("taler-unified-setup");
    375   as->args[1] = GNUNET_strdup ("-c");
    376   as->args[2] = GNUNET_strdup (config_file);
    377   cnt = 3;
    378   va_start (ap,
    379             config_file);
    380   while (NULL != (arg = va_arg (ap,
    381                                 const char *)))
    382   {
    383     as->args[cnt++] = GNUNET_strdup (arg);
    384   }
    385   va_end (ap);
    386 
    387   {
    388     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     return cmd;
    397   }
    398 }
    399 
    400 
    401 /* end of testing_api_cmd_system_start.c */