gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-exchange] branch master updated: library function to wrap sanctio


From: gnunet
Subject: [taler-exchange] branch master updated: library function to wrap sanction list evaluation program
Date: Sun, 05 Jan 2025 14:20:36 +0100

This is an automated email from the git hooks/post-receive script.

grothoff pushed a commit to branch master
in repository exchange.

The following commit(s) were added to refs/heads/master by this push:
     new 390f08503 library function to wrap sanction list evaluation program
390f08503 is described below

commit 390f0850310573649b7b33bcb1112b6cbe5ff6ee
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Sun Jan 5 14:20:22 2025 +0100

    library function to wrap sanction list evaluation program
---
 src/include/taler_kyclogic_lib.h  |  68 +++++
 src/kyclogic/Makefile.am          |   3 +-
 src/kyclogic/kyclogic_sanctions.c | 539 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 609 insertions(+), 1 deletion(-)

diff --git a/src/include/taler_kyclogic_lib.h b/src/include/taler_kyclogic_lib.h
index b9421d5b0..ab703fef6 100644
--- a/src/include/taler_kyclogic_lib.h
+++ b/src/include/taler_kyclogic_lib.h
@@ -1131,4 +1131,72 @@ void
 TALER_KYCLOGIC_run_aml_program_cancel (
   struct TALER_KYCLOGIC_AmlProgramRunnerHandle *aprh);
 
+
+/**
+ * Handle to a sanction list evaluation helper process.
+ */
+struct TALER_KYCLOGIC_SanctionRater;
+
+/**
+ * Function called with the result of a sanction evaluation.
+ *
+ * @param cls closure
+ * @param ec error code, #TALER_EC_NONE on success
+ * @param best_match identifies the sanction list entry with the best match
+ * @param rating likelihood of the match, from 0 (none) to 1 (perfect)
+ * @param confidence confidence in the evaluation, from 0 (none) to 1 (perfect)
+ */
+typedef void
+(*TALER_KYCLOGIC_SanctionResultCallback)(
+  void *cls,
+  enum TALER_ErrorCode ec,
+  const char *best_match,
+  double rating,
+  double confidence);
+
+
+/**
+ * Launch sanction rating helper process
+ *
+ * @param binary program name
+ * @param argv argument to give to the process
+ * @return handle for sanction list rating
+ */
+struct TALER_KYCLOGIC_SanctionRater *
+TALER_KYCLOGIC_saction_rater_start (
+  const char *binary,
+  const char **argv);
+
+
+/**
+ * KYC evaluation.
+ */
+struct TALER_KYCLOGIC_EvaluationEntry;
+
+/**
+ * Evaluate KYC attributes against sacntions list using @a sr
+ *
+ * @param[in,out] sr santion list evaluator
+ * @param attributes KYC attributes to evaluate
+ * @param cb function to call with the results
+ * @param cb_cls closure
+ * @return NULL on error
+ */
+struct TALER_KYCLOGIC_EvaluationEntry *
+TALER_KYCLOGIC_saction_rater_eval (
+  struct TALER_KYCLOGIC_SanctionRater *sr,
+  const json_t *attributes,
+  TALER_KYCLOGIC_SanctionResultCallback cb,
+  void *cb_cls);
+
+
+/**
+ * Stop sanction rating helper process.
+ *
+ * @param[in] sr process to stop
+ */
+void
+TALER_KYCLOGIC_saction_rater_stop (
+  struct TALER_KYCLOGIC_SanctionRater *sr);
+
 #endif
diff --git a/src/kyclogic/Makefile.am b/src/kyclogic/Makefile.am
index 0b671d1e3..86a9279b3 100644
--- a/src/kyclogic/Makefile.am
+++ b/src/kyclogic/Makefile.am
@@ -45,7 +45,8 @@ lib_LTLIBRARIES = \
   libtalerkyclogic.la
 
 libtalerkyclogic_la_SOURCES = \
-  kyclogic_api.c
+  kyclogic_api.c \
+  kyclogic_sanctions.c
 libtalerkyclogic_la_LIBADD = \
   $(top_builddir)/src/json/libtalerjson.la \
   $(top_builddir)/src/util/libtalerutil.la \
diff --git a/src/kyclogic/kyclogic_sanctions.c 
b/src/kyclogic/kyclogic_sanctions.c
new file mode 100644
index 000000000..71e07e31a
--- /dev/null
+++ b/src/kyclogic/kyclogic_sanctions.c
@@ -0,0 +1,539 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2025 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU Affero 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 Affero General Public License for more 
details.
+
+  You should have received a copy of the GNU Affero General Public License 
along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file kyclogic_sanctions.c
+ * @brief wrapper around sanction list evaluator
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include "taler_kyclogic_lib.h"
+
+
+/**
+ * Entry in the ordered list of pending evaluations.
+ */
+struct TALER_KYCLOGIC_EvaluationEntry
+{
+  /**
+   * Kept in a DLL.
+   */
+  struct TALER_KYCLOGIC_EvaluationEntry *prev;
+
+  /**
+   * Kept in a DLL.
+   */
+  struct TALER_KYCLOGIC_EvaluationEntry *next;
+
+  /**
+   * Callback to call with the result.
+   */
+  TALER_KYCLOGIC_SanctionResultCallback cb;
+
+  /**
+   * Closure for @e cb.
+   */
+  void *cb_cls;
+
+  /**
+   * Buffer with data we need to send to the helper.
+   */
+  void *write_buf;
+
+  /**
+   * Total length of @e write_buf.
+   */
+  size_t write_size;
+
+  /**
+   * Current write position in @e write_buf.
+   */
+  size_t write_pos;
+
+};
+
+
+/**
+ * Handle to a sanction list evaluation helper process.
+ */
+struct TALER_KYCLOGIC_SanctionRater
+{
+
+  /**
+   * Kept in a DLL.
+   */
+  struct TALER_KYCLOGIC_EvaluationEntry *ee_head;
+
+  /**
+   * Kept in a DLL.
+   */
+  struct TALER_KYCLOGIC_EvaluationEntry *ee_tail;
+
+  /**
+   * 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 for reading data from the helper.
+   */
+  void *read_buf;
+
+  /**
+   * Current size of @a read_buf.
+   */
+  size_t read_size;
+
+  /**
+   * Current offset in @a read_buf.
+   */
+  size_t read_pos;
+
+};
+
+
+/**
+ * We encountered a hard error (or explicit stop) of @a sr.
+ * Shut down processing (but do not yet free @a sr).
+ *
+ * @param[in,out] sr sanction rater to fail
+ */
+static void
+fail_hard (struct TALER_KYCLOGIC_SanctionRater *sr)
+{
+  struct TALER_KYCLOGIC_EvaluationEntry *ee;
+
+  if (NULL != sr->chld_stdin)
+  {
+    GNUNET_break (GNUNET_OK ==
+                  GNUNET_DISK_file_close (sr->chld_stdin));
+    sr->chld_stdin = NULL;
+  }
+  if (NULL != sr->read_task)
+  {
+    GNUNET_SCHEDULER_cancel (sr->read_task);
+    sr->read_task = NULL;
+  }
+  if (NULL != sr->helper)
+  {
+    GNUNET_OS_process_destroy (sr->helper);
+    sr->helper = NULL;
+  }
+  while (NULL != (ee = sr->ee_tail))
+  {
+    GNUNET_CONTAINER_DLL_remove (sr->ee_head,
+                                 sr->ee_tail,
+                                 ee);
+    ee->cb (ee->cb_cls,
+            /* FIXME: better EC? */
+            TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+            NULL,
+            1.0,
+            0.0);
+    free (ee->write_buf);
+    GNUNET_free (ee);
+  }
+}
+
+
+/**
+ * Parse data from input buffer.
+ *
+ * @param[in,out] sr sanction rater to process data from
+ * @return true if everything is fine, false on failure
+ */
+static bool
+process_buffer (struct TALER_KYCLOGIC_SanctionRater *sr)
+{
+  const char *buf = sr->read_buf;
+  size_t buf_len;
+  void *end;
+
+  end = memrchr (sr->read_buf,
+                 '\n',
+                 sr->read_pos);
+  if ( (NULL == end) &&
+       (sr->read_pos < 2048) )
+    return true;
+  buf_len = end - sr->read_buf;
+  while (0 != buf_len)
+  {
+    char *nl;
+    double rating;
+    double confidence;
+    char best_match[1024];
+    size_t line_len;
+
+    nl = memchr (buf,
+                 '\n',
+                 buf_len);
+    GNUNET_assert (NULL != nl);
+    *nl = '\0';
+    line_len = nl - buf;
+    if (3 !=
+        sscanf (buf,
+                "%lf %lf %1023s",
+
+                &rating,
+                &confidence,
+                best_match))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Malformed input line `%s'\n",
+                  buf);
+      GNUNET_break (0);
+      return false;
+    }
+    {
+      struct TALER_KYCLOGIC_EvaluationEntry *ee = sr->ee_tail;
+
+      GNUNET_CONTAINER_DLL_remove (sr->ee_head,
+                                   sr->ee_tail,
+                                   ee);
+      ee->cb (ee->cb_cls,
+              TALER_EC_NONE,
+              best_match,
+              rating,
+              confidence);
+      free (ee->write_buf);
+      GNUNET_free (ee);
+    }
+    buf += line_len;
+    buf_len -= line_len;
+  }
+  buf_len = end - sr->read_buf;
+  memmove (sr->read_buf,
+           end,
+           sr->read_pos - buf_len);
+  sr->read_pos -= buf_len;
+  return true;
+}
+
+
+/**
+ * Function called when we can read more data from
+ * the child process.
+ *
+ * @param cls our `struct TALER_KYCLOGIC_SanctionRater *`
+ */
+static void
+read_cb (void *cls)
+{
+  struct TALER_KYCLOGIC_SanctionRater *sr = cls;
+
+  sr->read_task = NULL;
+  while (1)
+  {
+    ssize_t ret;
+
+    if (sr->read_size == sr->read_pos)
+    {
+      /* Grow input buffer */
+      size_t ns;
+      void *tmp;
+
+      ns = GNUNET_MAX (2 * sr->read_size,
+                       1024);
+      if (ns > GNUNET_MAX_MALLOC_CHECKED)
+        ns = GNUNET_MAX_MALLOC_CHECKED;
+      if (sr->read_size == ns)
+      {
+        /* Helper returned more than 40 MB of data! Stop reading! */
+        GNUNET_break (0);
+        GNUNET_break (GNUNET_OK ==
+                      GNUNET_DISK_file_close (sr->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 (sr->chld_stdin));
+        return;
+      }
+      GNUNET_memcpy (tmp,
+                     sr->read_buf,
+                     sr->read_pos);
+      GNUNET_free (sr->read_buf);
+      sr->read_buf = tmp;
+      sr->read_size = ns;
+    }
+    ret = GNUNET_DISK_file_read (sr->chld_stdout,
+                                 sr->read_buf + sr->read_pos,
+                                 sr->read_size - sr->read_pos);
+    if (ret < 0)
+    {
+      if ( (EAGAIN != errno) &&
+           (EWOULDBLOCK != errno) &&
+           (EINTR != errno) )
+      {
+        GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                             "read");
+        return;
+      }
+      /* Continue later */
+      break;
+    }
+    if (0 == ret)
+    {
+      /* regular end of stream, odd! */
+      GNUNET_break (0);
+      fail_hard (sr);
+      return;
+    }
+    GNUNET_assert (sr->read_size >= sr->read_pos + ret);
+    sr->read_pos += ret;
+    if (! process_buffer (sr))
+      return;
+  }
+  sr->read_task
+    = GNUNET_SCHEDULER_add_read_file (
+        GNUNET_TIME_UNIT_FOREVER_REL,
+        sr->chld_stdout,
+        &read_cb,
+        sr);
+}
+
+
+/**
+ * Function called when we can write more data to
+ * the child process.
+ *
+ * @param cls our `struct SanctionRater *`
+ */
+static void
+write_cb (void *cls)
+{
+  struct TALER_KYCLOGIC_SanctionRater *sr = cls;
+  struct TALER_KYCLOGIC_EvaluationEntry *ee = sr->ee_tail;
+  ssize_t ret;
+
+  sr->write_task = NULL;
+  while (ee->write_size > ee->write_pos)
+  {
+    ret = GNUNET_DISK_file_write (sr->chld_stdin,
+                                  ee->write_buf + ee->write_pos,
+                                  ee->write_size - ee->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 (ee->write_size >= ee->write_pos + ret);
+    ee->write_pos += ret;
+  }
+  if (ee->write_size == ee->write_pos)
+  {
+    free (ee->write_buf);
+    GNUNET_CONTAINER_DLL_remove (sr->ee_head,
+                                 sr->ee_tail,
+                                 ee);
+    GNUNET_free (ee);
+    ee = sr->ee_tail;
+    if (NULL == ee)
+      return;
+  }
+  if ( (ee->write_size > ee->write_pos) &&
+       ( (EAGAIN == errno) ||
+         (EWOULDBLOCK == errno) ||
+         (EINTR == errno) ) )
+  {
+    sr->write_task
+      = GNUNET_SCHEDULER_add_write_file (
+          GNUNET_TIME_UNIT_FOREVER_REL,
+          sr->chld_stdin,
+          &write_cb,
+          sr);
+    return;
+  }
+  /* helper must have died */
+  GNUNET_break (0);
+  fail_hard (sr);
+}
+
+
+/**
+ * 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_KYCLOGIC_SanctionRater *sr = cls;
+
+  sr->cwh = NULL;
+  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) sr->read_pos);
+  fail_hard (sr);
+}
+
+
+struct TALER_KYCLOGIC_SanctionRater *
+TALER_KYCLOGIC_saction_rater_start (const char *binary,
+                                    const char **argv)
+{
+  struct TALER_KYCLOGIC_SanctionRater *sr;
+  struct GNUNET_DISK_PipeHandle *pipe_stdin;
+  struct GNUNET_DISK_PipeHandle *pipe_stdout;
+
+  sr = GNUNET_new (struct TALER_KYCLOGIC_SanctionRater);
+  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);
+  sr->helper = GNUNET_OS_start_process_vap (GNUNET_OS_INHERIT_STD_ERR,
+                                            pipe_stdin,
+                                            pipe_stdout,
+                                            NULL,
+                                            binary,
+                                            (char *const *) argv);
+  if (NULL == sr->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 (sr);
+    return NULL;
+  }
+  sr->chld_stdin =
+    GNUNET_DISK_pipe_detach_end (pipe_stdin,
+                                 GNUNET_DISK_PIPE_END_WRITE);
+  sr->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));
+
+  sr->read_task
+    = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
+                                      sr->chld_stdout,
+                                      &read_cb,
+                                      sr);
+  sr->cwh = GNUNET_wait_child (sr->helper,
+                               &child_done_cb,
+                               sr);
+  return sr;
+}
+
+
+struct TALER_KYCLOGIC_EvaluationEntry *
+TALER_KYCLOGIC_saction_rater_eval (struct TALER_KYCLOGIC_SanctionRater *sr,
+                                   const json_t *attributes,
+                                   TALER_KYCLOGIC_SanctionResultCallback cb,
+                                   void *cb_cls)
+{
+  struct TALER_KYCLOGIC_EvaluationEntry *ee;
+
+  if (NULL == sr->read_task)
+    return NULL;
+  ee = GNUNET_new (struct TALER_KYCLOGIC_EvaluationEntry);
+  ee->cb = cb;
+  ee->cb_cls = cb_cls;
+  GNUNET_CONTAINER_DLL_insert (sr->ee_head,
+                               sr->ee_tail,
+                               ee);
+  ee->write_buf = json_dumps (attributes,
+                              JSON_COMPACT);
+  ee->write_size = strlen (ee->write_buf);
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Passing %llu bytes to JSON conversion tool\n",
+              (unsigned long long) ee->write_size);
+  if (NULL == sr->write_task)
+    sr->write_task
+      = GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL,
+                                         sr->chld_stdin,
+                                         &write_cb,
+                                         sr);
+  return ee;
+}
+
+
+void
+TALER_KYCLOGIC_saction_rater_stop (
+  struct TALER_KYCLOGIC_SanctionRater *sr)
+{
+  fail_hard (sr);
+  if (NULL != sr->cwh)
+  {
+    GNUNET_wait_child_cancel (sr->cwh);
+    sr->cwh = NULL;
+  }
+  if (NULL != sr->write_task)
+  {
+    GNUNET_SCHEDULER_cancel (sr->write_task);
+    sr->write_task = NULL;
+  }
+  if (NULL != sr->chld_stdout)
+  {
+    GNUNET_break (GNUNET_OK ==
+                  GNUNET_DISK_file_close (sr->chld_stdout));
+    sr->chld_stdout = NULL;
+  }
+  GNUNET_free (sr->read_buf);
+  GNUNET_free (sr);
+}

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]