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:
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;
-}