cash2ecash

cash2ecash: cash acceptor that issues digital cash (experimental)
Log | Files | Refs | README | LICENSE

commit 8474c9f0dba54d73d9fc83cc4804170a10d19547
parent dfbd78642a7d5392d3c881ab7406fb8ffdb1c9a5
Author: Tellenbach Reto <tellr1@bfh.ch>
Date:   Mon,  8 Jun 2026 20:58:37 +0200

[dbg] Display: use processes

Diffstat:
Asrc/show-qr.c | 305++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/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); }