exchange

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

commit 84d82e110511cc74061e4e210caa3389d0345b39
parent ab74f09dfc9f97d288351a5aa28b37be04f7ed6c
Author: Christian Grothoff <christian@grothoff.org>
Date:   Wed, 24 Jul 2024 17:43:07 +0200

move TALER_JSON_ symbols into libtalerjson where they belong

Diffstat:
Msrc/include/taler_json_lib.h | 170+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Msrc/include/taler_util.h | 50--------------------------------------------------
Msrc/json/.gitignore | 1+
Msrc/json/Makefile.am | 17++++++++++++++++-
Asrc/json/conversion.c | 406+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/json/test_conversion.c | 150+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/util/test_conversion.sh -> src/json/test_conversion.sh | 0
Msrc/util/Makefile.am | 13+------------
Dsrc/util/conversion.c | 405-------------------------------------------------------------------------------
Dsrc/util/test_conversion.c | 149-------------------------------------------------------------------------------
10 files changed, 698 insertions(+), 663 deletions(-)

diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h @@ -54,8 +54,9 @@ * @return json pack specification */ struct GNUNET_JSON_PackSpec -TALER_JSON_pack_time_abs_human (const char *name, - struct GNUNET_TIME_Absolute at); +TALER_JSON_pack_time_abs_human ( + const char *name, + struct GNUNET_TIME_Absolute at); /** @@ -176,8 +177,9 @@ TALER_JSON_pack_exchange_withdraw_values ( * @return json pack specification */ struct GNUNET_JSON_PackSpec -TALER_JSON_pack_amount (const char *name, - const struct TALER_Amount *amount); +TALER_JSON_pack_amount ( + const char *name, + const struct TALER_Amount *amount); /** @@ -478,8 +480,9 @@ struct TALER_JSON_ProtocolVersion * @return corresponding field spec */ struct GNUNET_JSON_Specification -TALER_JSON_spec_version (const char *field, - struct TALER_JSON_ProtocolVersion *ver); +TALER_JSON_spec_version ( + const char *field, + struct TALER_JSON_ProtocolVersion *ver); /** @@ -506,8 +509,9 @@ TALER_JSON_spec_denom_pub_cipher ( * @return corresponding field spec */ struct GNUNET_JSON_Specification -TALER_JSON_spec_denom_sig (const char *field, - struct TALER_DenominationSignature *sig); +TALER_JSON_spec_denom_sig ( + const char *field, + struct TALER_DenominationSignature *sig); /** @@ -547,9 +551,9 @@ TALER_JSON_spec_exchange_withdraw_values ( * @return corresponding field spec */ struct GNUNET_JSON_Specification -TALER_JSON_spec_blinded_planchet (const char *field, - struct TALER_BlindedPlanchet *blinded_planchet - ); +TALER_JSON_spec_blinded_planchet ( + const char *field, + struct TALER_BlindedPlanchet *blinded_planchet); /** @@ -570,9 +574,10 @@ TALER_JSON_spec_blinded_planchet (const char *field, * @param[out] strptr where to store a pointer to the field with the best variant */ struct GNUNET_JSON_Specification -TALER_JSON_spec_i18n_string (const char *name, - const char *language_pattern, - const char **strptr); +TALER_JSON_spec_i18n_string ( + const char *name, + const char *language_pattern, + const char **strptr); /** @@ -587,8 +592,9 @@ TALER_JSON_spec_i18n_string (const char *name, * @param[out] strptr where to store a pointer to the field with the best variant */ struct GNUNET_JSON_Specification -TALER_JSON_spec_i18n_str (const char *name, - const char **strptr); +TALER_JSON_spec_i18n_str ( + const char *name, + const char **strptr); /** @@ -600,8 +606,9 @@ TALER_JSON_spec_i18n_str (const char *name, * @return corresponding field spec */ struct GNUNET_JSON_Specification -TALER_JSON_spec_token_issue_sig (const char *field, - struct TALER_TokenIssueSignatureP *sig); +TALER_JSON_spec_token_issue_sig ( + const char *field, + struct TALER_TokenIssueSignatureP *sig); /** @@ -626,8 +633,9 @@ TALER_JSON_spec_blinded_token_issue_sig ( * @return corresponding field spec */ struct GNUNET_JSON_Specification -TALER_JSON_spec_token_envelope (const char *field, - struct TALER_TokenEnvelopeP *env); +TALER_JSON_spec_token_envelope ( + const char *field, + struct TALER_TokenEnvelopeP *env); /** @@ -646,8 +654,9 @@ TALER_JSON_spec_token_envelope (const char *field, * #GNUNET_SYSERR on internal error */ enum GNUNET_GenericReturnValue -TALER_JSON_contract_hash (const json_t *json, - struct TALER_PrivateContractHashP *hc); +TALER_JSON_contract_hash ( + const json_t *json, + struct TALER_PrivateContractHashP *hc); /** @@ -661,8 +670,9 @@ TALER_JSON_contract_hash (const json_t *json, * @return #GNUNET_OK on success */ enum GNUNET_GenericReturnValue -TALER_JSON_contract_seed_forgettable (const json_t *spec, - json_t *contract); +TALER_JSON_contract_seed_forgettable ( + const json_t *spec, + json_t *contract); /** @@ -673,8 +683,9 @@ TALER_JSON_contract_seed_forgettable (const json_t *spec, * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ enum GNUNET_GenericReturnValue -TALER_JSON_contract_mark_forgettable (json_t *json, - const char *field); +TALER_JSON_contract_mark_forgettable ( + json_t *json, + const char *field); /** @@ -687,8 +698,9 @@ TALER_JSON_contract_mark_forgettable (json_t *json, * #GNUNET_SYSERR on error */ enum GNUNET_GenericReturnValue -TALER_JSON_contract_part_forget (json_t *json, - const char *field); +TALER_JSON_contract_part_forget ( + json_t *json, + const char *field); /** @@ -717,10 +729,11 @@ typedef void * @return #GNUNET_OK on success, #GNUNET_SYSERR if @e path is invalid. */ enum GNUNET_GenericReturnValue -TALER_JSON_expand_path (json_t *json, - const char *path, - TALER_JSON_ExpandPathCallback cb, - void *cb_cls); +TALER_JSON_expand_path ( + json_t *json, + const char *path, + TALER_JSON_ExpandPathCallback cb, + void *cb_cls); /** @@ -770,8 +783,9 @@ TALER_JSON_get_error_code2 (const void *data, * @return #GNUNET_OK on success, #GNUNET_SYSERR if @a wire_s is malformed */ enum GNUNET_GenericReturnValue -TALER_JSON_merchant_wire_signature_hash (const json_t *wire_s, - struct TALER_MerchantWireHashP *hc); +TALER_JSON_merchant_wire_signature_hash ( + const json_t *wire_s, + struct TALER_MerchantWireHashP *hc); /** @@ -800,9 +814,10 @@ TALER_JSON_merchant_wire_signature_hash (const json_t *wire_s, * NOT incremented. */ const json_t * -TALER_JSON_extract_i18n (const json_t *object, - const char *language_pattern, - const char *field); +TALER_JSON_extract_i18n ( + const json_t *object, + const char *language_pattern, + const char *field); /** @@ -812,7 +827,8 @@ TALER_JSON_extract_i18n (const json_t *object, * @return true if @a i18n is well-formed */ bool -TALER_JSON_check_i18n (const json_t *i18n); +TALER_JSON_check_i18n ( + const json_t *i18n); /** @@ -823,7 +839,8 @@ TALER_JSON_check_i18n (const json_t *i18n); * @return NULL on error */ char * -TALER_JSON_wire_to_method (const json_t *wire_s); +TALER_JSON_wire_to_method ( + const json_t *wire_s); /** @@ -834,7 +851,8 @@ TALER_JSON_wire_to_method (const json_t *wire_s); * @return NULL on error */ char * -TALER_JSON_wire_to_payto (const json_t *wire_s); +TALER_JSON_wire_to_payto ( + const json_t *wire_s); /** @@ -844,25 +862,85 @@ TALER_JSON_wire_to_payto (const json_t *wire_s); * @param[out] ech where to write the policy hash */ void -TALER_deposit_policy_hash (const json_t *policy, - struct TALER_ExtensionPolicyHashP *ech); +TALER_deposit_policy_hash ( + const json_t *policy, + struct TALER_ExtensionPolicyHashP *ech); + /** * Hash the @a manifests of extensions, given as JSON * * @param manifests Manifests of the extensions * @param[out] eh where to write the hash - * @return GNUNET_OK on success, GNUNET_SYSERR on failure + * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure */ enum GNUNET_GenericReturnValue -TALER_JSON_extensions_manifests_hash (const json_t *manifests, - struct TALER_ExtensionManifestsHashP *eh); +TALER_JSON_extensions_manifests_hash ( + const json_t *manifests, + struct TALER_ExtensionManifestsHashP *eh); + /** * Canonicalize a JSON input to a string according to RFC 8785. */ char * -TALER_JSON_canonicalize (const json_t *input); +TALER_JSON_canonicalize ( + const json_t *input); + + +/** + * Handle to an external process that will assist + * with some JSON-to-JSON conversion. + */ +struct TALER_JSON_ExternalConversion; + +/** + * Type of a callback that receives a JSON @a result. + * + * @param cls closure + * @param status_type how did the process die + * @param code termination status code from the process + * @param result some JSON result, NULL if we failed to get an JSON output + */ +typedef void +(*TALER_JSON_JsonCallback) ( + void *cls, + enum GNUNET_OS_ProcessStatusType status_type, + unsigned long code, + const json_t *result); + + +/** + * Launch some external helper @a binary to convert some @a input + * and eventually call @a cb with the result. + * + * @param input JSON to serialize and pass to the helper process + * @param cb function to call on the result + * @param cb_cls closure for @a cb + * @param binary name of the binary to execute + * @param ... NULL-terminated list of arguments for the @a binary, + * usually starting with again the name of the binary + * @return handle to cancel the operation (and kill the helper) + */ +struct TALER_JSON_ExternalConversion * +TALER_JSON_external_conversion_start ( + const json_t *input, + TALER_JSON_JsonCallback cb, + void *cb_cls, + const char *binary, + ...); + +/** + * Abort external conversion, killing the process and preventing + * the callback from being called. Must not be called after the + * callback was invoked. + * + * @param[in] ec external conversion handle to cancel + */ +void +TALER_JSON_external_conversion_stop ( + struct TALER_JSON_ExternalConversion *ec); + #endif /* TALER_JSON_LIB_H_ */ diff --git a/src/include/taler_util.h b/src/include/taler_util.h @@ -776,56 +776,6 @@ TALER_get_lowest_age ( #define TALER_adult_age(mask) \ sizeof((mask)->bits) * 8 - __builtin_clz ((mask)->bits) - 1 -/** - * Handle to an external process that will assist - * with some JSON-to-JSON conversion. - */ -struct TALER_JSON_ExternalConversion; - -/** - * Type of a callback that receives a JSON @a result. - * - * @param cls closure - * @param status_type how did the process die - * @param code termination status code from the process - * @param result some JSON result, NULL if we failed to get an JSON output - */ -typedef void -(*TALER_JSON_JsonCallback) (void *cls, - enum GNUNET_OS_ProcessStatusType status_type, - unsigned long code, - const json_t *result); - - -/** - * Launch some external helper @a binary to convert some @a input - * and eventually call @a cb with the result. - * - * @param input JSON to serialize and pass to the helper process - * @param cb function to call on the result - * @param cb_cls closure for @a cb - * @param binary name of the binary to execute - * @param ... NULL-terminated list of arguments for the @a binary, - * usually starting with again the name of the binary - * @return handle to cancel the operation (and kill the helper) - */ -struct TALER_JSON_ExternalConversion * -TALER_JSON_external_conversion_start (const json_t *input, - TALER_JSON_JsonCallback cb, - void *cb_cls, - const char *binary, - ...); - -/** - * Abort external conversion, killing the process and preventing - * the callback from being called. Must not be called after the - * callback was invoked. - * - * @param[in] ec external conversion handle to cancel - */ -void -TALER_JSON_external_conversion_stop ( - struct TALER_JSON_ExternalConversion *ec); #undef __TALER_UTIL_LIB_H_INSIDE__ diff --git a/src/json/.gitignore b/src/json/.gitignore @@ -1 +1,2 @@ test_json_wire +test_conversion diff --git a/src/json/Makefile.am b/src/json/Makefile.am @@ -10,6 +10,7 @@ lib_LTLIBRARIES = \ libtalerjson.la libtalerjson_la_SOURCES = \ + conversion.c \ i18n.c \ json.c \ json_helper.c \ @@ -28,16 +29,30 @@ libtalerjson_la_LIBADD = \ $(XLIB) TESTS = \ + test_conversion \ test_json check_PROGRAMS= \ + test_conversion \ test_json test_json_SOURCES = \ test_json.c test_json_LDADD = \ - $(top_builddir)/src/json/libtalerjson.la \ + libtalerjson.la \ -lgnunetjson \ $(top_builddir)/src/util/libtalerutil.la \ -lgnunetutil \ -ljansson + +test_conversion_SOURCES = \ + test_conversion.c +test_conversion_LDADD = \ + libtalerjson.la \ + $(top_builddir)/src/util/libtalerutil.la \ + -lgnunetjson \ + -lgnunetutil \ + -ljansson + +EXTRA_DIST = \ + test_conversion.sh diff --git a/src/json/conversion.c b/src/json/conversion.c @@ -0,0 +1,406 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file conversion.c + * @brief helper routines to run some external JSON-to-JSON converter + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_util.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_util_lib.h> + + +struct TALER_JSON_ExternalConversion +{ + /** + * Callback to call with the result. + */ + TALER_JSON_JsonCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; + + /** + * Handle to the helper process. + */ + struct GNUNET_OS_Process *helper; + + /** + * Pipe for the stdin of the @e helper. + */ + struct GNUNET_DISK_FileHandle *chld_stdin; + + /** + * Pipe for the stdout of the @e helper. + */ + struct GNUNET_DISK_FileHandle *chld_stdout; + + /** + * Handle to wait on the child to terminate. + */ + struct GNUNET_ChildWaitHandle *cwh; + + /** + * Task to read JSON output from the child. + */ + struct GNUNET_SCHEDULER_Task *read_task; + + /** + * Task to send JSON input to the child. + */ + struct GNUNET_SCHEDULER_Task *write_task; + + /** + * Buffer with data we need to send to the helper. + */ + void *write_buf; + + /** + * Buffer for reading data from the helper. + */ + void *read_buf; + + /** + * Total length of @e write_buf. + */ + size_t write_size; + + /** + * Current write position in @e write_buf. + */ + size_t write_pos; + + /** + * Current size of @a read_buf. + */ + size_t read_size; + + /** + * Current offset in @a read_buf. + */ + size_t read_pos; + +}; + + +/** + * Function called when we can read more data from + * the child process. + * + * @param cls our `struct TALER_JSON_ExternalConversion *` + */ +static void +read_cb (void *cls) +{ + struct TALER_JSON_ExternalConversion *ec = cls; + + ec->read_task = NULL; + while (1) + { + ssize_t ret; + + if (ec->read_size == ec->read_pos) + { + /* Grow input buffer */ + size_t ns; + void *tmp; + + ns = GNUNET_MAX (2 * ec->read_size, + 1024); + if (ns > GNUNET_MAX_MALLOC_CHECKED) + ns = GNUNET_MAX_MALLOC_CHECKED; + if (ec->read_size == ns) + { + /* Helper returned more than 40 MB of data! Stop reading! */ + GNUNET_break (0); + GNUNET_break (GNUNET_OK == + GNUNET_DISK_file_close (ec->chld_stdin)); + return; + } + tmp = GNUNET_malloc_large (ns); + if (NULL == tmp) + { + /* out of memory, also stop reading */ + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "malloc"); + GNUNET_break (GNUNET_OK == + GNUNET_DISK_file_close (ec->chld_stdin)); + return; + } + GNUNET_memcpy (tmp, + ec->read_buf, + ec->read_pos); + GNUNET_free (ec->read_buf); + ec->read_buf = tmp; + ec->read_size = ns; + } + ret = GNUNET_DISK_file_read (ec->chld_stdout, + ec->read_buf + ec->read_pos, + ec->read_size - ec->read_pos); + if (ret < 0) + { + if ( (EAGAIN != errno) && + (EWOULDBLOCK != errno) && + (EINTR != errno) ) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "read"); + return; + } + break; + } + if (0 == ret) + { + /* regular end of stream, good! */ + return; + } + GNUNET_assert (ec->read_size >= ec->read_pos + ret); + ec->read_pos += ret; + } + ec->read_task + = GNUNET_SCHEDULER_add_read_file ( + GNUNET_TIME_UNIT_FOREVER_REL, + ec->chld_stdout, + &read_cb, + ec); +} + + +/** + * Function called when we can write more data to + * the child process. + * + * @param cls our `struct TALER_JSON_ExternalConversion *` + */ +static void +write_cb (void *cls) +{ + struct TALER_JSON_ExternalConversion *ec = cls; + ssize_t ret; + + ec->write_task = NULL; + while (ec->write_size > ec->write_pos) + { + ret = GNUNET_DISK_file_write (ec->chld_stdin, + ec->write_buf + ec->write_pos, + ec->write_size - ec->write_pos); + if (ret < 0) + { + if ( (EAGAIN != errno) && + (EINTR != errno) ) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "write"); + break; + } + if (0 == ret) + { + GNUNET_break (0); + break; + } + GNUNET_assert (ec->write_size >= ec->write_pos + ret); + ec->write_pos += ret; + } + if ( (ec->write_size > ec->write_pos) && + ( (EAGAIN == errno) || + (EWOULDBLOCK == errno) || + (EINTR == errno) ) ) + { + ec->write_task + = GNUNET_SCHEDULER_add_write_file ( + GNUNET_TIME_UNIT_FOREVER_REL, + ec->chld_stdin, + &write_cb, + ec); + } + else + { + GNUNET_break (GNUNET_OK == + GNUNET_DISK_file_close (ec->chld_stdin)); + ec->chld_stdin = NULL; + } +} + + +/** + * Defines a GNUNET_ChildCompletedCallback which is sent back + * upon death or completion of a child process. + * + * @param cls handle for the callback + * @param type type of the process + * @param exit_code status code of the process + * + */ +static void +child_done_cb (void *cls, + enum GNUNET_OS_ProcessStatusType type, + long unsigned int exit_code) +{ + struct TALER_JSON_ExternalConversion *ec = cls; + json_t *j = NULL; + json_error_t err; + + ec->cwh = NULL; + if (NULL != ec->read_task) + { + GNUNET_SCHEDULER_cancel (ec->read_task); + /* We could get the process termination notification before having drained + the read buffer. So drain it now, just in case. */ + read_cb (ec); + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Conversion helper exited with status %d and code %llu after outputting %llu bytes of data\n", + (int) type, + (unsigned long long) exit_code, + (unsigned long long) ec->read_pos); + GNUNET_OS_process_destroy (ec->helper); + ec->helper = NULL; + if (0 != ec->read_pos) + { + j = json_loadb (ec->read_buf, + ec->read_pos, + JSON_REJECT_DUPLICATES, + &err); + if (NULL == j) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to parse JSON from helper at %d: %s\n", + err.position, + err.text); + } + } + ec->cb (ec->cb_cls, + type, + exit_code, + j); + json_decref (j); + TALER_JSON_external_conversion_stop (ec); +} + + +struct TALER_JSON_ExternalConversion * +TALER_JSON_external_conversion_start (const json_t *input, + TALER_JSON_JsonCallback cb, + void *cb_cls, + const char *binary, + ...) +{ + struct TALER_JSON_ExternalConversion *ec; + struct GNUNET_DISK_PipeHandle *pipe_stdin; + struct GNUNET_DISK_PipeHandle *pipe_stdout; + va_list ap; + + ec = GNUNET_new (struct TALER_JSON_ExternalConversion); + ec->cb = cb; + ec->cb_cls = cb_cls; + pipe_stdin = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_READ); + GNUNET_assert (NULL != pipe_stdin); + pipe_stdout = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_WRITE); + GNUNET_assert (NULL != pipe_stdout); + va_start (ap, + binary); + ec->helper = GNUNET_OS_start_process_va (GNUNET_OS_INHERIT_STD_ERR, + pipe_stdin, + pipe_stdout, + NULL, + binary, + ap); + va_end (ap); + if (NULL == ec->helper) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to run conversion helper `%s'\n", + binary); + GNUNET_break (GNUNET_OK == + GNUNET_DISK_pipe_close (pipe_stdin)); + GNUNET_break (GNUNET_OK == + GNUNET_DISK_pipe_close (pipe_stdout)); + GNUNET_free (ec); + return NULL; + } + ec->chld_stdin = + GNUNET_DISK_pipe_detach_end (pipe_stdin, + GNUNET_DISK_PIPE_END_WRITE); + ec->chld_stdout = + GNUNET_DISK_pipe_detach_end (pipe_stdout, + GNUNET_DISK_PIPE_END_READ); + GNUNET_break (GNUNET_OK == + GNUNET_DISK_pipe_close (pipe_stdin)); + GNUNET_break (GNUNET_OK == + GNUNET_DISK_pipe_close (pipe_stdout)); + ec->write_buf = json_dumps (input, JSON_COMPACT); + ec->write_size = strlen (ec->write_buf); + ec->read_task + = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, + ec->chld_stdout, + &read_cb, + ec); + ec->write_task + = GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL, + ec->chld_stdin, + &write_cb, + ec); + ec->cwh = GNUNET_wait_child (ec->helper, + &child_done_cb, + ec); + return ec; +} + + +void +TALER_JSON_external_conversion_stop ( + struct TALER_JSON_ExternalConversion *ec) +{ + if (NULL != ec->cwh) + { + GNUNET_wait_child_cancel (ec->cwh); + ec->cwh = NULL; + } + if (NULL != ec->helper) + { + GNUNET_break (0 == + GNUNET_OS_process_kill (ec->helper, + SIGKILL)); + GNUNET_OS_process_destroy (ec->helper); + } + if (NULL != ec->read_task) + { + GNUNET_SCHEDULER_cancel (ec->read_task); + ec->read_task = NULL; + } + if (NULL != ec->write_task) + { + GNUNET_SCHEDULER_cancel (ec->write_task); + ec->write_task = NULL; + } + if (NULL != ec->chld_stdin) + { + GNUNET_break (GNUNET_OK == + GNUNET_DISK_file_close (ec->chld_stdin)); + ec->chld_stdin = NULL; + } + if (NULL != ec->chld_stdout) + { + GNUNET_break (GNUNET_OK == + GNUNET_DISK_file_close (ec->chld_stdout)); + ec->chld_stdout = NULL; + } + GNUNET_free (ec->read_buf); + free (ec->write_buf); + GNUNET_free (ec); +} diff --git a/src/json/test_conversion.c b/src/json/test_conversion.c @@ -0,0 +1,150 @@ +/* + This file is part of TALER + (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file util/test_conversion.c + * @brief Tests for conversion logic + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_util.h" +#include <gnunet/gnunet_json_lib.h> +#include "taler_json_lib.h" + +/** + * Return value from main(). + */ +static int global_ret; + +/** + * Handle to our helper. + */ +static struct TALER_JSON_ExternalConversion *ec; + + +/** + * Type of a callback that receives a JSON @a result. + * + * @param cls closure + * @param status_type how did the process die + * @apram code termination status code from the process + * @param result some JSON result, NULL if we failed to get an JSON output + */ +static void +conv_cb (void *cls, + enum GNUNET_OS_ProcessStatusType status_type, + unsigned long code, + const json_t *result) +{ + json_t *expect; + + (void) cls; + (void) status_type; + ec = NULL; + global_ret = 3; + if (42 != code) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected return value from helper: %u\n", + (unsigned int) code); + return; + } + expect = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("foo", + "arg") + ); + if (1 == json_equal (expect, + result)) + { + global_ret = 0; + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected JSON result\n"); + json_dumpf (result, + stderr, + JSON_INDENT (2)); + global_ret = 4; + } + json_decref (expect); +} + + +/** + * Function called on shutdown/CTRL-C. + * + * @param cls NULL + */ +static void +do_shutdown (void *cls) +{ + (void) cls; + if (NULL != ec) + { + GNUNET_break (0); + global_ret = 2; + TALER_JSON_external_conversion_stop (ec); + ec = NULL; + } +} + + +/** + * Main test function. + * + * @param cls NULL + */ +static void +run (void *cls) +{ + json_t *input; + + (void) cls; + GNUNET_SCHEDULER_add_shutdown (&do_shutdown, + NULL); + input = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("key", + "foo") + ); + ec = TALER_JSON_external_conversion_start (input, + &conv_cb, + NULL, + "./test_conversion.sh", + "test_conversion.sh", + "arg", + NULL); + json_decref (input); + GNUNET_assert (NULL != ec); +} + + +int +main (int argc, + const char *const argv[]) +{ + (void) argc; + (void) argv; + unsetenv ("XDG_DATA_HOME"); + unsetenv ("XDG_CONFIG_HOME"); + GNUNET_log_setup ("test-conversion", + "WARNING", + NULL); + GNUNET_OS_init (TALER_project_data_default ()); + global_ret = 1; + GNUNET_SCHEDULER_run (&run, + NULL); + return global_ret; +} diff --git a/src/util/test_conversion.sh b/src/json/test_conversion.sh diff --git a/src/util/Makefile.am b/src/util/Makefile.am @@ -21,8 +21,7 @@ EXTRA_DIST = \ taler-config.in \ test_helper_eddsa.conf \ test_helper_rsa.conf \ - test_helper_cs.conf \ - test_conversion.sh + test_helper_cs.conf bin_PROGRAMS = \ taler-exchange-secmod-eddsa \ @@ -82,7 +81,6 @@ libtalerutil_la_SOURCES = \ aml_signatures.c \ auditor_signatures.c \ config.c \ - conversion.c \ crypto.c \ crypto_confirmation.c \ crypto_contract.c \ @@ -131,7 +129,6 @@ AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export PATH= check_PROGRAMS = \ test_age_restriction \ test_amount \ - test_conversion \ test_crypto \ test_helper_eddsa \ test_helper_rsa \ @@ -148,14 +145,6 @@ test_age_restriction_LDADD = \ -lgnunetutil \ libtalerutil.la -test_conversion_SOURCES = \ - test_conversion.c -test_conversion_LDADD = \ - -lgnunetjson \ - -lgnunetutil \ - -ljansson \ - libtalerutil.la - test_amount_SOURCES = \ test_amount.c test_amount_LDADD = \ diff --git a/src/util/conversion.c b/src/util/conversion.c @@ -1,405 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2023 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file conversion.c - * @brief helper routines to run some external JSON-to-JSON converter - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_util.h" -#include <gnunet/gnunet_util_lib.h> - - -struct TALER_JSON_ExternalConversion -{ - /** - * Callback to call with the result. - */ - TALER_JSON_JsonCallback cb; - - /** - * Closure for @e cb. - */ - void *cb_cls; - - /** - * Handle to the helper process. - */ - struct GNUNET_OS_Process *helper; - - /** - * Pipe for the stdin of the @e helper. - */ - struct GNUNET_DISK_FileHandle *chld_stdin; - - /** - * Pipe for the stdout of the @e helper. - */ - struct GNUNET_DISK_FileHandle *chld_stdout; - - /** - * Handle to wait on the child to terminate. - */ - struct GNUNET_ChildWaitHandle *cwh; - - /** - * Task to read JSON output from the child. - */ - struct GNUNET_SCHEDULER_Task *read_task; - - /** - * Task to send JSON input to the child. - */ - struct GNUNET_SCHEDULER_Task *write_task; - - /** - * Buffer with data we need to send to the helper. - */ - void *write_buf; - - /** - * Buffer for reading data from the helper. - */ - void *read_buf; - - /** - * Total length of @e write_buf. - */ - size_t write_size; - - /** - * Current write position in @e write_buf. - */ - size_t write_pos; - - /** - * Current size of @a read_buf. - */ - size_t read_size; - - /** - * Current offset in @a read_buf. - */ - size_t read_pos; - -}; - - -/** - * Function called when we can read more data from - * the child process. - * - * @param cls our `struct TALER_JSON_ExternalConversion *` - */ -static void -read_cb (void *cls) -{ - struct TALER_JSON_ExternalConversion *ec = cls; - - ec->read_task = NULL; - while (1) - { - ssize_t ret; - - if (ec->read_size == ec->read_pos) - { - /* Grow input buffer */ - size_t ns; - void *tmp; - - ns = GNUNET_MAX (2 * ec->read_size, - 1024); - if (ns > GNUNET_MAX_MALLOC_CHECKED) - ns = GNUNET_MAX_MALLOC_CHECKED; - if (ec->read_size == ns) - { - /* Helper returned more than 40 MB of data! Stop reading! */ - GNUNET_break (0); - GNUNET_break (GNUNET_OK == - GNUNET_DISK_file_close (ec->chld_stdin)); - return; - } - tmp = GNUNET_malloc_large (ns); - if (NULL == tmp) - { - /* out of memory, also stop reading */ - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, - "malloc"); - GNUNET_break (GNUNET_OK == - GNUNET_DISK_file_close (ec->chld_stdin)); - return; - } - GNUNET_memcpy (tmp, - ec->read_buf, - ec->read_pos); - GNUNET_free (ec->read_buf); - ec->read_buf = tmp; - ec->read_size = ns; - } - ret = GNUNET_DISK_file_read (ec->chld_stdout, - ec->read_buf + ec->read_pos, - ec->read_size - ec->read_pos); - if (ret < 0) - { - if ( (EAGAIN != errno) && - (EWOULDBLOCK != errno) && - (EINTR != errno) ) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, - "read"); - return; - } - break; - } - if (0 == ret) - { - /* regular end of stream, good! */ - return; - } - GNUNET_assert (ec->read_size >= ec->read_pos + ret); - ec->read_pos += ret; - } - ec->read_task - = GNUNET_SCHEDULER_add_read_file ( - GNUNET_TIME_UNIT_FOREVER_REL, - ec->chld_stdout, - &read_cb, - ec); -} - - -/** - * Function called when we can write more data to - * the child process. - * - * @param cls our `struct TALER_JSON_ExternalConversion *` - */ -static void -write_cb (void *cls) -{ - struct TALER_JSON_ExternalConversion *ec = cls; - ssize_t ret; - - ec->write_task = NULL; - while (ec->write_size > ec->write_pos) - { - ret = GNUNET_DISK_file_write (ec->chld_stdin, - ec->write_buf + ec->write_pos, - ec->write_size - ec->write_pos); - if (ret < 0) - { - if ( (EAGAIN != errno) && - (EINTR != errno) ) - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, - "write"); - break; - } - if (0 == ret) - { - GNUNET_break (0); - break; - } - GNUNET_assert (ec->write_size >= ec->write_pos + ret); - ec->write_pos += ret; - } - if ( (ec->write_size > ec->write_pos) && - ( (EAGAIN == errno) || - (EWOULDBLOCK == errno) || - (EINTR == errno) ) ) - { - ec->write_task - = GNUNET_SCHEDULER_add_write_file ( - GNUNET_TIME_UNIT_FOREVER_REL, - ec->chld_stdin, - &write_cb, - ec); - } - else - { - GNUNET_break (GNUNET_OK == - GNUNET_DISK_file_close (ec->chld_stdin)); - ec->chld_stdin = NULL; - } -} - - -/** - * Defines a GNUNET_ChildCompletedCallback which is sent back - * upon death or completion of a child process. - * - * @param cls handle for the callback - * @param type type of the process - * @param exit_code status code of the process - * - */ -static void -child_done_cb (void *cls, - enum GNUNET_OS_ProcessStatusType type, - long unsigned int exit_code) -{ - struct TALER_JSON_ExternalConversion *ec = cls; - json_t *j = NULL; - json_error_t err; - - ec->cwh = NULL; - if (NULL != ec->read_task) - { - GNUNET_SCHEDULER_cancel (ec->read_task); - /* We could get the process termination notification before having drained - the read buffer. So drain it now, just in case. */ - read_cb (ec); - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Conversion helper exited with status %d and code %llu after outputting %llu bytes of data\n", - (int) type, - (unsigned long long) exit_code, - (unsigned long long) ec->read_pos); - GNUNET_OS_process_destroy (ec->helper); - ec->helper = NULL; - if (0 != ec->read_pos) - { - j = json_loadb (ec->read_buf, - ec->read_pos, - JSON_REJECT_DUPLICATES, - &err); - if (NULL == j) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to parse JSON from helper at %d: %s\n", - err.position, - err.text); - } - } - ec->cb (ec->cb_cls, - type, - exit_code, - j); - json_decref (j); - TALER_JSON_external_conversion_stop (ec); -} - - -struct TALER_JSON_ExternalConversion * -TALER_JSON_external_conversion_start (const json_t *input, - TALER_JSON_JsonCallback cb, - void *cb_cls, - const char *binary, - ...) -{ - struct TALER_JSON_ExternalConversion *ec; - struct GNUNET_DISK_PipeHandle *pipe_stdin; - struct GNUNET_DISK_PipeHandle *pipe_stdout; - va_list ap; - - ec = GNUNET_new (struct TALER_JSON_ExternalConversion); - ec->cb = cb; - ec->cb_cls = cb_cls; - pipe_stdin = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_READ); - GNUNET_assert (NULL != pipe_stdin); - pipe_stdout = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_WRITE); - GNUNET_assert (NULL != pipe_stdout); - va_start (ap, - binary); - ec->helper = GNUNET_OS_start_process_va (GNUNET_OS_INHERIT_STD_ERR, - pipe_stdin, - pipe_stdout, - NULL, - binary, - ap); - va_end (ap); - if (NULL == ec->helper) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to run conversion helper `%s'\n", - binary); - GNUNET_break (GNUNET_OK == - GNUNET_DISK_pipe_close (pipe_stdin)); - GNUNET_break (GNUNET_OK == - GNUNET_DISK_pipe_close (pipe_stdout)); - GNUNET_free (ec); - return NULL; - } - ec->chld_stdin = - GNUNET_DISK_pipe_detach_end (pipe_stdin, - GNUNET_DISK_PIPE_END_WRITE); - ec->chld_stdout = - GNUNET_DISK_pipe_detach_end (pipe_stdout, - GNUNET_DISK_PIPE_END_READ); - GNUNET_break (GNUNET_OK == - GNUNET_DISK_pipe_close (pipe_stdin)); - GNUNET_break (GNUNET_OK == - GNUNET_DISK_pipe_close (pipe_stdout)); - ec->write_buf = json_dumps (input, JSON_COMPACT); - ec->write_size = strlen (ec->write_buf); - ec->read_task - = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, - ec->chld_stdout, - &read_cb, - ec); - ec->write_task - = GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL, - ec->chld_stdin, - &write_cb, - ec); - ec->cwh = GNUNET_wait_child (ec->helper, - &child_done_cb, - ec); - return ec; -} - - -void -TALER_JSON_external_conversion_stop ( - struct TALER_JSON_ExternalConversion *ec) -{ - if (NULL != ec->cwh) - { - GNUNET_wait_child_cancel (ec->cwh); - ec->cwh = NULL; - } - if (NULL != ec->helper) - { - GNUNET_break (0 == - GNUNET_OS_process_kill (ec->helper, - SIGKILL)); - GNUNET_OS_process_destroy (ec->helper); - } - if (NULL != ec->read_task) - { - GNUNET_SCHEDULER_cancel (ec->read_task); - ec->read_task = NULL; - } - if (NULL != ec->write_task) - { - GNUNET_SCHEDULER_cancel (ec->write_task); - ec->write_task = NULL; - } - if (NULL != ec->chld_stdin) - { - GNUNET_break (GNUNET_OK == - GNUNET_DISK_file_close (ec->chld_stdin)); - ec->chld_stdin = NULL; - } - if (NULL != ec->chld_stdout) - { - GNUNET_break (GNUNET_OK == - GNUNET_DISK_file_close (ec->chld_stdout)); - ec->chld_stdout = NULL; - } - GNUNET_free (ec->read_buf); - free (ec->write_buf); - GNUNET_free (ec); -} diff --git a/src/util/test_conversion.c b/src/util/test_conversion.c @@ -1,149 +0,0 @@ -/* - This file is part of TALER - (C) 2023 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file util/test_conversion.c - * @brief Tests for conversion logic - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_util.h" -#include <gnunet/gnunet_json_lib.h> - -/** - * Return value from main(). - */ -static int global_ret; - -/** - * Handle to our helper. - */ -static struct TALER_JSON_ExternalConversion *ec; - - -/** - * Type of a callback that receives a JSON @a result. - * - * @param cls closure - * @param status_type how did the process die - * @apram code termination status code from the process - * @param result some JSON result, NULL if we failed to get an JSON output - */ -static void -conv_cb (void *cls, - enum GNUNET_OS_ProcessStatusType status_type, - unsigned long code, - const json_t *result) -{ - json_t *expect; - - (void) cls; - (void) status_type; - ec = NULL; - global_ret = 3; - if (42 != code) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected return value from helper: %u\n", - (unsigned int) code); - return; - } - expect = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("foo", - "arg") - ); - if (1 == json_equal (expect, - result)) - { - global_ret = 0; - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected JSON result\n"); - json_dumpf (result, - stderr, - JSON_INDENT (2)); - global_ret = 4; - } - json_decref (expect); -} - - -/** - * Function called on shutdown/CTRL-C. - * - * @param cls NULL - */ -static void -do_shutdown (void *cls) -{ - (void) cls; - if (NULL != ec) - { - GNUNET_break (0); - global_ret = 2; - TALER_JSON_external_conversion_stop (ec); - ec = NULL; - } -} - - -/** - * Main test function. - * - * @param cls NULL - */ -static void -run (void *cls) -{ - json_t *input; - - (void) cls; - GNUNET_SCHEDULER_add_shutdown (&do_shutdown, - NULL); - input = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("key", - "foo") - ); - ec = TALER_JSON_external_conversion_start (input, - &conv_cb, - NULL, - "./test_conversion.sh", - "test_conversion.sh", - "arg", - NULL); - json_decref (input); - GNUNET_assert (NULL != ec); -} - - -int -main (int argc, - const char *const argv[]) -{ - (void) argc; - (void) argv; - unsetenv ("XDG_DATA_HOME"); - unsetenv ("XDG_CONFIG_HOME"); - GNUNET_log_setup ("test-conversion", - "WARNING", - NULL); - GNUNET_OS_init (TALER_project_data_default ()); - global_ret = 1; - GNUNET_SCHEDULER_run (&run, - NULL); - return global_ret; -}