commit 8474c9f0dba54d73d9fc83cc4804170a10d19547
parent dfbd78642a7d5392d3c881ab7406fb8ffdb1c9a5
Author: Tellenbach Reto <tellr1@bfh.ch>
Date: Mon, 8 Jun 2026 20:58:37 +0200
[dbg] Display: use processes
Diffstat:
| A | src/show-qr.c | | | 305 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | src/taler-digitizer.c | | | 196 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------ |
2 files changed, 441 insertions(+), 60 deletions(-)
diff --git a/src/show-qr.c b/src/show-qr.c
@@ -0,0 +1,305 @@
+/*
+ This file is part of TALER cash2ecash
+ Copyright (C) 2026 GNUnet e.V.
+
+ This program 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 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ 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 this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @brief coppied/edited from taler-mdb
+ */
+
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <gnunet/gnunet_util_lib.h>
+#include <linux/fb.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <qrencode.h>
+
+#define BACKLIGHT_INVERTER false
+
+/**
+ * Standard backlight on value
+ */
+static char backlight_on;
+
+/**
+ * Standard backlight off value
+ */
+static char backlight_off;
+
+/**
+ * Handle for the Framebuffer device
+ */
+struct Display
+{
+ /**
+ * File descriptor for the screen
+ */
+ int devicefd;
+
+ /**
+ * File descriptor to set backlight information
+ */
+ int backlightfd;
+
+ /**
+ * The display memory to set the pixel information
+ */
+ uint16_t *memory;
+
+ /**
+ * Original screen information
+ */
+ struct fb_var_screeninfo orig_vinfo;
+
+ /**
+ * Variable screen information (color depth ...)
+ */
+ struct fb_var_screeninfo var_info;
+
+ /**
+ * Fixed screen informtaion
+ */
+ struct fb_fix_screeninfo fix_info;
+};
+
+void show_qr_init(struct Display *dp ,char *fb_device_file, char *backlight_device_file)
+{
+
+/* open the framebuffer device */
+ dp->devicefd = open (fb_device_file,
+ O_RDWR);
+ if (-1 != dp->devicefd)
+ {
+ /* read information about the screen */
+ ioctl (dp->devicefd,
+ FBIOGET_VSCREENINFO,
+ &dp->var_info);
+
+ /* store current screeninfo for reset */
+ dp->orig_vinfo = dp->var_info;
+
+ if (16 != dp->var_info.bits_per_pixel)
+ {
+ /* Change variable info to 16 bit per pixel */
+ dp->var_info.bits_per_pixel = 16;
+ if (0 > ioctl (dp->devicefd,
+ FBIOPUT_VSCREENINFO,
+ &dp->var_info))
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,"ioctl(FBIOPUT_VSCREENINFO)");
+ return;
+ }
+ }
+
+ /* Get fixed screen information */
+ if (0 > ioctl (dp->devicefd,
+ FBIOGET_FSCREENINFO,
+ &dp->fix_info))
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+ "ioctl(FBIOGET_FSCREENINFO)");
+ return;
+ }
+
+ /* get pointer onto frame buffer */
+ dp->memory = mmap (NULL,
+ dp->fix_info.smem_len,
+ PROT_READ | PROT_WRITE, MAP_SHARED,
+ dp->devicefd,
+ 0);
+ if (MAP_FAILED == dp->memory)
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+ "mmap");
+ return;
+ }
+
+ /* set the screen to white */
+ memset (dp->memory,
+ 0xFF,
+ dp->var_info.xres * dp->var_info.yres
+ * sizeof (uint16_t));
+
+ /* open backlight file to turn display backlight on and off */
+ dp->backlightfd = open (
+ backlight_device_file, O_WRONLY);
+ if (0 > dp->backlightfd)
+ {
+ GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
+ "open",
+ backlight_device_file);
+ }
+ else
+ {
+ if (BACKLIGHT_INVERTER)
+ {
+ backlight_on = '0';
+ backlight_off = '1';
+ }
+ else
+ {
+ backlight_off = '0';
+ backlight_on = '1';
+ }
+ /* turn off the backlight */
+ (void) ! write (dp->backlightfd,
+ &backlight_off,
+ 1);
+ }
+ }
+ else
+ {
+ GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
+ "open",
+ fb_device_file);
+ }
+};
+
+/**
+ * @brief Create the QR code to pay and display it on screen
+ *
+ * @param uri what text to show in the QR code
+ */
+static void
+show_qrcode (const char *uri)
+{
+ QRinput *qri;
+ QRcode *qrc;
+ unsigned int size;
+ char *upper;
+ char *base;
+ char *ubase;
+ size_t xOff;
+ size_t yOff;
+ const char *dddash;
+ unsigned int nwidth;
+
+ stop_advertising ();
+ hide_error ();
+ if (0 > qrDisplay.devicefd)
+ return; /* no display, no dice */
+ /* find the fourth '/' in the payto://pay/hostname/-uri */
+ dddash = strchr (uri, '/');
+ if (NULL != dddash)
+ dddash = strchr (dddash + 1, '/');
+ if (NULL != dddash)
+ dddash = strchr (dddash + 1, '/');
+ if (NULL != dddash)
+ dddash = strchr (dddash + 1, '/');
+ if (NULL == dddash)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "taler://pay/-URI malformed: `%s'\n",
+ uri);
+ return;
+ }
+
+ qri = QRinput_new2 (0, QR_ECLEVEL_L);
+ if (NULL == qri)
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+ "QRinput_new2");
+ return;
+ }
+ /* convert all characters until the fourth '/' to upper
+ case. The rest _should_ be upper case in a NICE setup,
+ but we can't warrant it and must not touch those. */
+ base = GNUNET_strndup (uri,
+ dddash - uri);
+
+ ubase = GNUNET_STRINGS_utf8_toupper (base);
+ GNUNET_free (base);
+ GNUNET_asprintf (&upper,
+ "%s%s",
+ ubase,
+ dddash);
+ GNUNET_free (ubase);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Showing QR code for `%s'\n",
+ upper);
+ /* first try encoding as uppercase-only alpha-numerical
+ QR code (much smaller encoding); if that fails, also
+ try using binary encoding (in case nick contains
+ special characters). */
+ if ( (0 !=
+ QRinput_append (qri,
+ QR_MODE_AN,
+ strlen (upper),
+ (unsigned char *) upper)) &&
+ (0 !=
+ QRinput_append (qri,
+ QR_MODE_8,
+ strlen (upper),
+ (unsigned char *) upper)))
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+ "QRinput_append");
+ GNUNET_free (upper);
+ return;
+ }
+ GNUNET_free (upper);
+ qrc = QRcode_encodeInput (qri);
+ if (NULL == qrc)
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+ "QRcode_encodeInput");
+ QRinput_free (qri);
+ return;
+ }
+
+ /* set QR-code border */
+ memset (qrDisplay.memory,
+ 0xFF,
+ qrDisplay.var_info.xres * qrDisplay.var_info.yres
+ * sizeof (uint16_t));
+ size = GNUNET_MIN (qrDisplay.var_info.xres,
+ qrDisplay.var_info.yres);
+
+ nwidth = qrc->width + 8; /* +8 for 4 pixel border */
+ xOff = 4 * size / nwidth;
+ yOff = 4 * size / nwidth;
+
+ /* calculate offset to show the code centered */
+ if (qrDisplay.var_info.xres < qrDisplay.var_info.yres)
+ yOff += (qrDisplay.var_info.yres - qrDisplay.var_info.xres) / 2;
+ else
+ xOff += (qrDisplay.var_info.xres - qrDisplay.var_info.yres) / 2;
+ for (unsigned int x = 0; x < qrDisplay.var_info.xres - 2 * xOff; x++)
+ for (unsigned int y = 0; y < qrDisplay.var_info.yres - 2 * yOff; y++)
+ {
+ unsigned int xoff = x * nwidth / size;
+ unsigned int yoff = y * nwidth / size;
+ unsigned int off = xoff + yoff * qrc->width;
+ if ( (xoff >= (unsigned) qrc->width) ||
+ (yoff >= (unsigned) qrc->width) )
+ continue;
+ /* set the pixels in the display memory */
+ qrDisplay.memory[(y + yOff) * qrDisplay.var_info.xres + (x + xOff)] =
+ (0 == (qrc->data[off] & 1)) ? 0xFFFF : 0x0000;
+ }
+
+ QRcode_free (qrc);
+ QRinput_free (qri);
+
+ /* Turn on backlight if supported */
+ if (0 < qrDisplay.backlightfd)
+ (void) ! write (qrDisplay.backlightfd,
+ &backlight_on,
+ 1);
+}
diff --git a/src/taler-digitizer.c b/src/taler-digitizer.c
@@ -25,6 +25,7 @@
#include <stdio.h>
#include <stdlib.h>
+#include <signal.h>
#include <gnunet/gnunet_util_lib.h>
#include "taler_digitizer_util.h"
#include "taler/taler_digitizer_service.h"
@@ -184,6 +185,11 @@ static struct TALER_BANK_GetConfigHandle *get_config_handle;
static struct TALER_BANK_GetAccountsHandle *get_accounts_handle;
/**
+ * Handle for get_accounts request
+ */
+static struct TALER_BANK_GetWithdrawalHandle *get_withdrawal_handle;
+
+/**
* Handle for post_accounts_withdrawal request
*/
static struct TALER_BANK_PostCreateWithdrawalHandle *post_accounts_withdrawal_handle;
@@ -203,6 +209,11 @@ static struct gpiod_line_settings *gpio_settings;
*/
static struct gpiod_line_request *rl_ca_enable;
+/**
+ * show-qr process
+ */
+static struct GNUNET_Process *qr_child;
+
enum
DIGITIZER_states
@@ -319,55 +330,123 @@ struct DIGITIZER_DisplayContext *display_ctx;
static void state_controller_task(void *cls);
-enum GNUNET_GenericReturnValue
-run_qr_show(int showtime)
+void
+stop_qr_show(void);
+
+
+/**
+ * Start process using the @a command command-line.
+ *
+ * @param command command to run
+ * @param ... extra arguments to pass
+ * @return process handle, NULL on failure
+ */
+static struct GNUNET_Process *
+start_command (const char *command,
+ ...)
{
- //char buffer[FRAMEBUFFER_SIZE];
- char *command;
- char *url;
- char *path;
-
- const char *prefix = getenv("TALER_DIGITIZER_PREFIX");
- if (NULL != prefix)
- GNUNET_asprintf (&path, "%s%s", prefix,PATH_QR_SHOW);
- else
- path = GNUNET_strdup ("/usr/local/bin/taler-digitizer-qr-show");
- url = TALER_url_join(state_ctx->wi.taler_withdraw_uri,
- "",
- "external-confirmation",
- "1",
- NULL);
- GNUNET_asprintf(&command,
- "%s -c taler-digitizer.conf -d \"%d s\" %s\n",
- path,
- showtime,
- url);
- TALER_LOG_DEBUG("Command for QRshow: %s\n",command);
- fp = popen(command, "r");
- if (fp == NULL) {
- GNUNET_free(url);
- GNUNET_free(command);
- GNUNET_free(path);
- TALER_LOG_ERROR("Failed to open framebuffer\n");
- return GNUNET_SYSERR;
- }
+ char **argv = NULL;
+ unsigned int argc = 0;
+ char *cpy = GNUNET_strdup (command);
+ struct GNUNET_Process *ret;
+ va_list ap;
+ const char *arg;
+
+ for (const char *tok = strtok (cpy, " ");
+ NULL != tok;
+ tok = strtok (NULL, " "))
+ {
+ GNUNET_array_append (argv,
+ argc,
+ GNUNET_strdup (tok));
+ }
+ va_start (ap,
+ command);
+ while (NULL != (arg = va_arg (ap,
+ const char *)))
+ {
+ GNUNET_array_append (argv,
+ argc,
+ GNUNET_strdup (arg));
+ }
+ va_end (ap);
+ GNUNET_array_append (argv,
+ argc,
+ NULL);
+ ret = GNUNET_process_create (GNUNET_OS_INHERIT_STD_ERR);
+ if (GNUNET_OK !=
+ GNUNET_process_run_command_argv (ret,
+ argv[0],
+ (const char **) argv))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to launch %s\n",
+ argv[0]);
+ GNUNET_process_destroy (ret);
+ ret = NULL;
+ }
+ for (unsigned int i = 0; i<argc; i++)
+ GNUNET_free (argv[i]);
+ GNUNET_array_grow (argv,
+ argc,
+ 0);
+ GNUNET_free (cpy);
+ return ret;
+}
- GNUNET_free(url);
- GNUNET_free(command);
- GNUNET_free(path);
- return GNUNET_OK;
+enum GNUNET_GenericReturnValue
+start_qr_show(int showtime)
+{
+ stop_qr_show ();
+ //char buffer[FRAMEBUFFER_SIZE];
+ char *command;
+ char *url;
+ char *path;
+
+ const char *prefix = getenv("TALER_DIGITIZER_PREFIX");
+ if (NULL != prefix)
+ GNUNET_asprintf (&path, "%s%s", prefix,PATH_QR_SHOW);
+ else
+ path = GNUNET_strdup ("/usr/local/bin/taler-digitizer-qr-show");
+ url = TALER_url_join(state_ctx->wi.taler_withdraw_uri,
+ "",
+ "external-confirmation",
+ "1",
+ NULL);
+ GNUNET_asprintf(&command,
+ "%s -c taler-digitizer.conf -d \"%d s\" %s\n",
+ path,
+ showtime,
+ url);
+ GNUNET_free(url);
+ TALER_LOG_DEBUG("Command for QRshow: %s\n",command);
+ qr_child = start_command (command,
+ NULL);
+
+ GNUNET_free(command);
+ GNUNET_free(path);
+ return GNUNET_OK;
}
void
stop_qr_show(void)
{
- if (NULL != fp)
- {
- pclose(fp);
- fp = NULL;
- }
+ if(qr_child == NULL)
+ return;
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_process_kill (qr_child,
+ SIGTERM));
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_process_wait (qr_child,
+ true,
+ NULL,
+ NULL));
+ GNUNET_process_destroy (qr_child);
+ qr_child = NULL;
}
+
+
/**
* Joinn currency and V.F format value string to TALER amount".
*
@@ -467,15 +546,13 @@ on_get_accounts_done(void *cls,
"taler-digitizer",
"BANK_ACCOUNT",
"bank account is not in active state");
- stop_qr_show();
state = DIGITIZER_STATE_TERMINAL_ERROR;
GNUNET_SCHEDULER_add_now(state_controller_task,NULL);
return;
}
- account_max_withdrawal =
- ((-1 == TALER_amount_cmp(&vr->details.ok.acc.balance.amount,
- &vr->details.ok.acc.debit_threshold))?
- vr->details.ok.acc.balance.amount : vr->details.ok.acc.debit_threshold);
+ TALER_amount_min (&account_max_withdrawal,
+ &vr->details.ok.acc.balance.amount,
+ &vr->details.ok.acc.debit_threshold);
if(-1 == TALER_amount_cmp (&account_max_withdrawal,
&state_ctx->max_insertion_amount))
{
@@ -484,11 +561,8 @@ on_get_accounts_done(void *cls,
GNUNET_SCHEDULER_add_now(state_controller_task,NULL);
return;
}
- state_ctx->withdrawal_limit =
- ((-1 == TALER_amount_cmp(&account_max_withdrawal,
- &state_ctx->bank_max_wire_transfer_amount))?
- account_max_withdrawal : state_ctx->bank_max_wire_transfer_amount);
- TALER_amount_min(&state_ctx->withdrawal_limit,&state_ctx->withdrawal_limit,&cfg_user_withdrawallimit),
+ TALER_amount_min(&state_ctx->withdrawal_limit,&account_max_withdrawal,&state_ctx->bank_max_wire_transfer_amount);
+ TALER_amount_min(&state_ctx->withdrawal_limit,&state_ctx->withdrawal_limit,&cfg_user_withdrawallimit);
@@ -538,6 +612,7 @@ on_get_withdrawal_status_done(void *cls,
if(0 == strcasecmp("selected", vr->details.ok.acc.status))
{
GNUNET_SCHEDULER_cancel(timeout_handle);
+ stop_qr_show();
state = DIGITIZER_STATE_COUNT_MONEY;
// give context for non terminal errors!
GNUNET_SCHEDULER_add_now(state_controller_task,NULL);
@@ -561,6 +636,7 @@ get_withdrawal_task(void *cls)
struct GNUNET_SCHEDULER_Task *timeout_handle;
timeout_handle = cls;
+ get_withdrawal_handle =
TALER_BANK_get_withdrawal(curl_ctx,
cfg_bank_base_url,
state_ctx->wi.withdrawal_id,
@@ -596,10 +672,13 @@ static void
shutdown_task (void *cls)
{
(void)cls;
- GNUNET_free(state_ctx);
- GNUNET_free(display_ctx);
gpiod_line_request_release(rl_ca_enable);
+ if (NULL != get_withdrawal_handle)
+ {
+ TALER_BANK_get_withdrawal_cancel (get_withdrawal_handle);
+ get_withdrawal_handle = NULL;
+ }
if (NULL != get_config_handle)
{
TALER_BANK_get_config_cancel (get_config_handle);
@@ -622,6 +701,8 @@ shutdown_task (void *cls)
}
DIGITIZER_bank_auth_free (cfg_bank_authentication);
GNUNET_free(cfg_bank_authentication);
+ GNUNET_free(state_ctx);
+ GNUNET_free(display_ctx);
}
/**
@@ -1126,13 +1207,8 @@ static void ScanQR_state_task(void *cls)
delay = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS,SCAN_QR_TIMEOUT_SECONDS);
poll = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS,200);
timeout_handle = GNUNET_SCHEDULER_add_delayed(delay,timeout_task,NULL);
- if(GNUNET_OK != run_qr_show(SCAN_QR_TIMEOUT_SECONDS))
- {
- TALER_LOG_ERROR("run_qr_show failed\n");
- state = DIGITIZER_STATE_CANCEL_WITHDRAWAL;
- // give context for non terminal errors!
- GNUNET_SCHEDULER_add_now(state_controller_task,NULL);
- }
+ start_qr_show(SCAN_QR_TIMEOUT_SECONDS);
+
//char *buf;
//GNUNET_asprintf(&buf,"%d",SCAN_QR_TIMEOUT_SECONDS*1000);
@@ -1198,7 +1274,7 @@ static void AcceptCash_state_task(void *cls)
{
(void)cls;
TALER_LOG_DEBUG("AcceptingCash state");
- state = DIGITIZER_STATE_ACCEPT_CASH;
+ state = DIGITIZER_STATE_CASHING_UP;
GNUNET_SCHEDULER_add_now(state_controller_task,NULL);
}