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 (9271B)


      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 setup_terminated (void *cls,
    102                   enum GNUNET_OS_ProcessStatusType type,
    103                   long unsigned int exit_code)
    104 {
    105   struct SystemState *as = cls;
    106 
    107   as->cwh = NULL;
    108   as->active = false;
    109   if (NULL != as->reader)
    110   {
    111     GNUNET_SCHEDULER_cancel (as->reader);
    112     as->reader = NULL;
    113   }
    114   if (! as->ready)
    115   {
    116     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    117                 "Launching Taler system failed: %d/%llu\n",
    118                 (int) type,
    119                 (unsigned long long) exit_code);
    120     TALER_TESTING_interpreter_fail (as->is);
    121     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 read_stdout (void *cls)
    137 {
    138   struct SystemState *as = cls;
    139   const struct GNUNET_DISK_FileHandle *fh;
    140   char buf[1024 * 10];
    141   ssize_t ret;
    142   size_t off = 0;
    143 
    144   as->reader = NULL;
    145   strcpy (buf,
    146           as->ibuf);
    147   off = strlen (buf);
    148   fh = GNUNET_DISK_pipe_handle (as->pipe_out,
    149                                 GNUNET_DISK_PIPE_END_READ);
    150   ret = GNUNET_DISK_file_read (fh,
    151                                &buf[off],
    152                                sizeof (buf) - off);
    153   if (-1 == ret)
    154   {
    155     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    156                          "read");
    157     TALER_TESTING_interpreter_fail (as->is);
    158     return;
    159   }
    160   if (0 == ret)
    161   {
    162     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    163                 "Child closed stdout\n");
    164     return;
    165   }
    166   /* forward log, except single '.' outputs */
    167   if ( (1 != ret) ||
    168        ('.' != buf[off]) )
    169     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    170                 "TUS: %.*s\n",
    171                 (int) ret,
    172                 &buf[off]);
    173   start_reader (as);
    174   off += ret;
    175   if (as->ready)
    176   {
    177     /* already done */
    178     return;
    179   }
    180   if (NULL !=
    181       memmem (buf,
    182               off,
    183               "\n<<READY>>\n",
    184               strlen ("\n<<READY>>\n")))
    185   {
    186     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    187                 "Taler system UP\n");
    188     as->ready = true;
    189     TALER_TESTING_interpreter_next (as->is);
    190     return;
    191   }
    192 
    193   {
    194     size_t mcpy;
    195 
    196     mcpy = GNUNET_MIN (off,
    197                        sizeof (as->ibuf) - 1);
    198     memcpy (as->ibuf,
    199             &buf[off - mcpy],
    200             mcpy);
    201     as->ibuf[mcpy] = '\0';
    202   }
    203 }
    204 
    205 
    206 static void
    207 start_reader (struct SystemState *as)
    208 {
    209   const struct GNUNET_DISK_FileHandle *fh;
    210 
    211   GNUNET_assert (NULL == as->reader);
    212   fh = GNUNET_DISK_pipe_handle (as->pipe_out,
    213                                 GNUNET_DISK_PIPE_END_READ);
    214   as->reader = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
    215                                                fh,
    216                                                &read_stdout,
    217                                                as);
    218 }
    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 system_run (void *cls,
    230             const struct TALER_TESTING_Command *cmd,
    231             struct TALER_TESTING_Interpreter *is)
    232 {
    233   struct SystemState *as = cls;
    234 
    235   (void) cmd;
    236   as->is = is;
    237   as->pipe_in = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_READ);
    238   GNUNET_assert (NULL != as->pipe_in);
    239   as->pipe_out = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE);
    240   GNUNET_assert (NULL != as->pipe_out);
    241   as->system_proc
    242     = 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         as->args);
    247   if (NULL == as->system_proc)
    248   {
    249     GNUNET_break (0);
    250     TALER_TESTING_interpreter_fail (is);
    251     return;
    252   }
    253   as->active = true;
    254   start_reader (as);
    255   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 system_cleanup (void *cls,
    270                 const struct TALER_TESTING_Command *cmd)
    271 {
    272   struct SystemState *as = cls;
    273 
    274   (void) cmd;
    275   if (NULL != as->cwh)
    276   {
    277     GNUNET_wait_child_cancel (as->cwh);
    278     as->cwh = NULL;
    279   }
    280   if (NULL != as->reader)
    281   {
    282     GNUNET_SCHEDULER_cancel (as->reader);
    283     as->reader = NULL;
    284   }
    285   if (NULL != as->system_proc)
    286   {
    287     if (as->active)
    288     {
    289       GNUNET_break (0 ==
    290                     GNUNET_OS_process_kill (as->system_proc,
    291                                             SIGTERM));
    292       GNUNET_OS_process_wait (as->system_proc);
    293     }
    294     GNUNET_OS_process_destroy (as->system_proc);
    295     as->system_proc = NULL;
    296   }
    297   if (NULL != as->pipe_in)
    298   {
    299     GNUNET_break (GNUNET_OK ==
    300                   GNUNET_DISK_pipe_close (as->pipe_in));
    301     as->pipe_in = NULL;
    302   }
    303   if (NULL != as->pipe_out)
    304   {
    305     GNUNET_break (GNUNET_OK ==
    306                   GNUNET_DISK_pipe_close (as->pipe_out));
    307     as->pipe_out = NULL;
    308   }
    309 
    310   for (unsigned int i = 0; NULL != as->args[i]; i++)
    311     GNUNET_free (as->args[i]);
    312   GNUNET_free (as->args);
    313   GNUNET_free (as);
    314 }
    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 system_traits (void *cls,
    328                const void **ret,
    329                const char *trait,
    330                unsigned int index)
    331 {
    332   struct SystemState *as = cls;
    333   struct TALER_TESTING_Trait traits[] = {
    334     TALER_TESTING_make_trait_process (&as->system_proc),
    335     TALER_TESTING_trait_end ()
    336   };
    337 
    338   return TALER_TESTING_get_trait (traits,
    339                                   ret,
    340                                   trait,
    341                                   index);
    342 }
    343 
    344 
    345 struct TALER_TESTING_Command
    346 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   as = GNUNET_new (struct SystemState);
    357   cnt = 4; /* 0-2 reserved, +1 for NULL termination */
    358   va_start (ap,
    359             config_file);
    360   while (NULL != (arg = va_arg (ap,
    361                                 const char *)))
    362   {
    363     cnt++;
    364   }
    365   va_end (ap);
    366   as->args = GNUNET_new_array (cnt,
    367                                char *);
    368   as->args[0] = GNUNET_strdup ("taler-unified-setup");
    369   as->args[1] = GNUNET_strdup ("-c");
    370   as->args[2] = GNUNET_strdup (config_file);
    371   cnt = 3;
    372   va_start (ap,
    373             config_file);
    374   while (NULL != (arg = va_arg (ap,
    375                                 const char *)))
    376   {
    377     as->args[cnt++] = GNUNET_strdup (arg);
    378   }
    379   va_end (ap);
    380 
    381   {
    382     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     return cmd;
    391   }
    392 }
    393 
    394 
    395 /* end of testing_api_cmd_system_start.c */