summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--meson.build27
-rw-r--r--qtart.c4
-rw-r--r--quickjs/quickjs-libc.c139
-rw-r--r--quickjs/quickjs-libc.h4
-rw-r--r--quickjs/repl.js1
-rw-r--r--taler_wallet_core_lib.c120
-rw-r--r--taler_wallet_core_lib.h8
-rw-r--r--tart_module.c1
-rw-r--r--wallet-client-example.c42
10 files changed, 334 insertions, 14 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..719d735
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+tags
+.vscode
diff --git a/meson.build b/meson.build
index 92be89a..afd5772 100644
--- a/meson.build
+++ b/meson.build
@@ -65,9 +65,10 @@ repl_c = custom_target('repl',
repl = static_library('repl', repl_c)
+tart = static_library('tart', 'tart_module.c')
+
qtart_exe = executable('qtart', [
'qtart.c',
- 'tart_module.c',
],
link_with: [
libbf,
@@ -77,6 +78,7 @@ qtart_exe = executable('qtart', [
quickjs_libc,
quickjs,
repl,
+ tart,
],
dependencies: [
m_dep,
@@ -85,3 +87,26 @@ qtart_exe = executable('qtart', [
mbedx509_dep,
curl_dep,
sodium_dep])
+
+talerwalletcore_lib = shared_library('talerwalletcore', 'taler_wallet_core_lib.c',
+ link_with : [
+ libbf,
+ libregexp,
+ libunicode ,
+ cutils,
+ quickjs_libc,
+ quickjs,
+ tart,
+ ],
+ dependencies: [
+ m_dep,
+ mbedcrypto_dep,
+ mbedtls_dep,
+ mbedx509_dep,
+ curl_dep,
+ sodium_dep
+ ])
+
+wallet_client_example_exe = executable('wallet-client-example',
+ 'wallet-client-example.c',
+ link_with : [talerwalletcore_lib]) \ No newline at end of file
diff --git a/qtart.c b/qtart.c
index 2e71767..3645758 100644
--- a/qtart.c
+++ b/qtart.c
@@ -39,10 +39,10 @@
#include <malloc.h>
#endif
-#include "tart_module.h"
-
#include "quickjs/cutils.h"
+#include "tart_module.h"
+
extern const uint8_t qjsc_repl[];
extern const uint32_t qjsc_repl_size;
diff --git a/quickjs/quickjs-libc.c b/quickjs/quickjs-libc.c
index b4799b7..160308e 100644
--- a/quickjs/quickjs-libc.c
+++ b/quickjs/quickjs-libc.c
@@ -120,6 +120,18 @@ typedef struct {
typedef struct {
struct list_head link;
+ char *msg_data;
+} JSHostMessage;
+
+typedef struct {
+ pthread_mutex_t mutex;
+ struct list_head msg_queue; /* list of JSHostMessage.link */
+ int read_fd;
+ int write_fd;
+} JSHostMessagePipe;
+
+typedef struct {
+ struct list_head link;
JSWorkerMessagePipe *recv_pipe;
JSValue on_message_func;
} JSWorkerMessageHandler;
@@ -130,8 +142,18 @@ typedef struct JSThreadState {
struct list_head os_timers; /* list of JSOSTimer.link */
struct list_head port_list; /* list of JSWorkerMessageHandler.link */
int eval_script_recurse; /* only used in the main thread */
- /* not used in the main thread */
+
JSWorkerMessagePipe *recv_pipe, *send_pipe;
+
+ // send/receive message to/from the host in the main thread
+ JSHostMessagePipe *host_pipe;
+
+ JSValue on_host_message_func;
+
+ JSHostMessageHandlerFn host_message_handler_f;
+ void *host_message_handler_cls;
+
+ int is_worker_thread;
} JSThreadState;
static uint64_t os_pending_signals;
@@ -768,16 +790,16 @@ static JSValue js_evalScript(JSContext *ctx, JSValueConst this_val,
str = JS_ToCStringLen(ctx, &len, argv[0]);
if (!str)
return JS_EXCEPTION;
- if (!ts->recv_pipe && ++ts->eval_script_recurse == 1) {
+ if (!ts->is_worker_thread && ++ts->eval_script_recurse == 1) {
/* install the interrupt handler */
JS_SetInterruptHandler(JS_GetRuntime(ctx), interrupt_handler, NULL);
}
- flags = JS_EVAL_TYPE_GLOBAL;
+ flags = JS_EVAL_TYPE_GLOBAL;
if (backtrace_barrier)
flags |= JS_EVAL_FLAG_BACKTRACE_BARRIER;
ret = JS_Eval(ctx, str, len, "<evalScript>", flags);
JS_FreeCString(ctx, str);
- if (!ts->recv_pipe && --ts->eval_script_recurse == 0) {
+ if (!ts->is_worker_thread && --ts->eval_script_recurse == 0) {
/* remove the interrupt handler */
JS_SetInterruptHandler(JS_GetRuntime(ctx), NULL, NULL);
os_pending_signals &= ~((uint64_t)1 << SIGINT);
@@ -1825,7 +1847,7 @@ static JSValue js_os_rename(JSContext *ctx, JSValueConst this_val,
static BOOL is_main_thread(JSRuntime *rt)
{
JSThreadState *ts = JS_GetRuntimeOpaque(rt);
- return !ts->recv_pipe;
+ return !ts->is_worker_thread;
}
static JSOSRWHandler *find_rh(JSThreadState *ts, int fd)
@@ -2489,7 +2511,7 @@ static int js_os_poll(JSContext *ctx)
struct timeval tv, *tvp;
/* only check signals in the main thread */
- if (!ts->recv_pipe &&
+ if (!ts->is_worker_thread &&
unlikely(os_pending_signals != 0)) {
JSOSSignalHandler *sh;
uint64_t mask;
@@ -2558,6 +2580,9 @@ static int js_os_poll(JSContext *ctx)
}
}
+ fd_max = max_int(fd_max, ts->host_pipe->read_fd);
+ FD_SET(ts->host_pipe->read_fd, &rfds);
+
ret = select(fd_max + 1, &rfds, &wfds, NULL, tvp);
if (ret > 0) {
list_for_each(el, &ts->os_rw_handlers) {
@@ -3536,6 +3561,7 @@ static void *worker_func(void *opaque)
ts = JS_GetRuntimeOpaque(rt);
ts->recv_pipe = args->recv_pipe;
ts->send_pipe = args->send_pipe;
+ ts->is_worker_thread = TRUE;
/* function pointer to avoid linking the whole JS_NewContext() if
not needed */
@@ -3747,7 +3773,6 @@ static JSValue js_worker_postMessage(JSContext *ctx, JSValueConst this_val,
js_free(ctx, data);
js_free(ctx, sab_tab);
return JS_EXCEPTION;
-
}
static JSValue js_worker_set_onmessage(JSContext *ctx, JSValueConst this_val,
@@ -3806,6 +3831,103 @@ static const JSCFunctionListEntry js_worker_proto_funcs[] = {
#endif /* USE_WORKER */
+int
+js_os_post_message_from_host(JSContext *ctx, const char *msg_str)
+{
+ JSThreadState *ts = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
+ JSHostMessage *msg;
+ JSHostMessagePipe *hp;
+ uint8_t *data;
+
+ msg = malloc(sizeof (*msg));
+ if (!msg) {
+ goto fail;
+ }
+ msg->msg_data = strdup(msg_str);
+ if (!msg->msg_data) {
+ goto fail;
+ }
+
+ hp = ts->host_pipe;
+ pthread_mutex_lock(&hp->mutex);
+ /* indicate that data is present */
+ if (list_empty(&hp->msg_queue)) {
+ uint8_t ch = '\0';
+ int ret;
+ for(;;) {
+ ret = write(hp->write_fd, &ch, 1);
+ if (ret == 1)
+ break;
+ if (ret < 0 && (errno != EAGAIN || errno != EINTR))
+ break;
+ }
+ }
+ list_add_tail(&msg->link, &hp->msg_queue);
+ pthread_mutex_unlock(&hp->mutex);
+ return 0;
+ fail:
+ if (msg) {
+ free(msg->msg_data);
+ free(msg);
+ }
+ js_free(ctx, data);
+ return -1;
+}
+
+void js_os_set_host_message_handler(JSContext *ctx, JSHostMessageHandlerFn f, void *cls)
+{
+ JSThreadState *ts = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
+ ts->host_message_handler_f = f;
+ ts->host_message_handler_cls = f;
+}
+
+static JSValue js_os_postHostMessage(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSThreadState *ts = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
+ const char *s;
+
+ s = JS_ToCString(ctx, argv[0]);
+
+ if (!s) {
+ return JS_EXCEPTION;
+ }
+
+ if (ts->host_message_handler_f) {
+ ts->host_message_handler_f(ts->host_message_handler_cls, s);
+ }
+}
+
+static JSValue js_os_get_onhostmessage(JSContext *ctx, JSValueConst this_val)
+{
+ JSThreadState *ts = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
+ if (!ts) {
+ return JS_EXCEPTION;
+ }
+ if (JS_IsFunction(ctx, ts->on_host_message_func)) {
+ return JS_DupValue(ctx, ts->on_host_message_func);
+ } else {
+ return JS_NULL;
+ }
+}
+
+static JSValue js_os_set_onhostmessage(JSContext *ctx, JSValueConst this_val,
+ JSValueConst func)
+{
+ JSRuntime *rt = JS_GetRuntime(ctx);
+ JSThreadState *ts = JS_GetRuntimeOpaque(rt);
+
+ if (JS_IsNull(func)) {
+ ts->on_host_message_func = JS_NULL;
+ } else {
+ if (!JS_IsFunction(ctx, func))
+ return JS_ThrowTypeError(ctx, "not a function");
+ JS_FreeValue(ctx, ts->on_host_message_func);
+ ts->on_host_message_func = JS_DupValue(ctx, func);
+ }
+ return JS_UNDEFINED;
+}
+
void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt))
{
#ifdef USE_WORKER
@@ -3906,6 +4028,8 @@ static const JSCFunctionListEntry js_os_funcs[] = {
JS_CFUNC_DEF("dup", 1, js_os_dup ),
JS_CFUNC_DEF("dup2", 2, js_os_dup2 ),
#endif
+ JS_CFUNC_DEF("postHostMessage", 1, js_os_postHostMessage ),
+ JS_CGETSET_DEF("onhostmessage", js_os_get_onhostmessage, js_os_set_onhostmessage ),
};
static int js_os_init(JSContext *ctx, JSModuleDef *m)
@@ -4024,6 +4148,7 @@ void js_std_init_handlers(JSRuntime *rt)
ts = malloc(sizeof(*ts));
if (!ts) {
+oom_fail:
fprintf(stderr, "Could not allocate memory for the worker");
exit(1);
}
diff --git a/quickjs/quickjs-libc.h b/quickjs/quickjs-libc.h
index fbbe5b0..4a7697e 100644
--- a/quickjs/quickjs-libc.h
+++ b/quickjs/quickjs-libc.h
@@ -33,6 +33,8 @@
extern "C" {
#endif
+typedef void (*JSHostMessageHandlerFn)(void *cls, const char *msg);
+
JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name);
JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name);
void js_std_add_helpers(JSContext *ctx, int argc, char **argv);
@@ -51,6 +53,8 @@ void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise,
JSValueConst reason,
JS_BOOL is_handled, void *opaque);
void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt));
+void js_os_set_host_message_handler(JSContext *ctx, JSHostMessageHandlerFn f, void *cls);
+int js_os_post_message_from_host(JSContext *ctx, const char *msg_str);
#ifdef __cplusplus
} /* extern "C" { */
diff --git a/quickjs/repl.js b/quickjs/repl.js
index 67a121e..2d2d19a 100644
--- a/quickjs/repl.js
+++ b/quickjs/repl.js
@@ -575,6 +575,7 @@ import * as os from "os";
std.exit(0);
} else {
std.puts("\n(Press Ctrl-C again to quit)\n");
+ reset();
readline_print_prompt();
}
}
diff --git a/taler_wallet_core_lib.c b/taler_wallet_core_lib.c
new file mode 100644
index 0000000..cd1f0e3
--- /dev/null
+++ b/taler_wallet_core_lib.c
@@ -0,0 +1,120 @@
+/*
+ This file is part of GNU Taler
+ Copyright (C) 2014-2022 Taler Systems SA
+
+ GNU 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.
+
+ GNU 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
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "taler_wallet_core_lib.h"
+
+#include "quickjs/quickjs.h"
+#include "quickjs/quickjs-libc.h"
+#include "tart_module.h"
+#include <pthread.h>
+#include "quickjs/list.h"
+#include <unistd.h>
+
+struct HostMessage {
+ struct list_head link;
+ char *data;
+};
+
+struct TALER_WALLET_Handle {
+ JSRuntime *rt;
+ JSContext *ctx;
+
+ TALER_WALLET_MessageHandlerFn *handler_f;
+ void *handler_cls;
+
+ int hostmsg_send_pipe;
+ int hostmsg_recv_pipe;
+ pthread_mutex_t hostmsg_mutex;
+ struct list_head hostmg_queue; /* list of HostMessage.link */
+};
+
+/* also used to initialize the worker context */
+static JSContext *JS_NewCustomContext(JSRuntime *rt)
+{
+ JSContext *ctx;
+ ctx = JS_NewContext(rt);
+ if (!ctx)
+ return NULL;
+ /* system modules */
+ js_init_module_std(ctx, "std");
+ js_init_module_os(ctx, "os");
+ /* taler runtime */
+ tart_init_module_talercrypto(ctx, "tart");
+ return ctx;
+}
+
+void
+TALER_WALLET_set_handler(struct TALER_WALLET_Handle *h,
+ TALER_WALLET_MessageHandlerFn handler_f,
+ void *handler_p)
+{
+
+}
+
+int
+TALER_WALLET_send_message (struct TALER_WALLET_Handle *h,
+ const char *msg)
+{
+ return js_os_post_message_from_host(h->ctx, msg);
+}
+
+struct TALER_WALLET_Handle *
+TALER_WALLET_create(void)
+{
+ struct TALER_WALLET_Handle *wh;
+ JSRuntime *rt;
+ JSContext *ctx;
+
+ rt = JS_NewRuntime();
+ js_std_init_handlers(rt);
+ ctx = JS_NewCustomContext(rt);
+
+ if (!ctx) {
+ fprintf(stderr, "qjs: cannot allocate JS context\n");
+ return NULL;
+ }
+
+ JS_SetHostPromiseRejectionTracker(rt, js_std_promise_rejection_tracker,
+ NULL);
+ wh = malloc(sizeof (*wh));
+ wh->ctx = ctx;
+ wh->rt = rt;
+ wh->handler_cls = NULL;
+ wh->handler_f = NULL;
+ if (0 != pthread_mutex_init(&wh->hostmsg_mutex, NULL)) {
+ return NULL;
+ }
+
+
+
+ return wh;
+}
+
+static void *
+run(void *cls)
+{
+ struct TALER_WALLET_Handle *wh = cls;
+
+ js_std_loop(wh->ctx);
+}
+
+void
+TALER_WALLET_run (struct TALER_WALLET_Handle *wh)
+{
+ pthread_t wallet_thread;
+
+ pthread_create(&wallet_thread, NULL, run, wh);
+}
diff --git a/taler_wallet_core_lib.h b/taler_wallet_core_lib.h
index 1e42cfb..78d6ad2 100644
--- a/taler_wallet_core_lib.h
+++ b/taler_wallet_core_lib.h
@@ -36,7 +36,7 @@ struct TALER_WALLET_Handle;
* @param handler_p opaque closure for the message handler
* @param message message from wallet-core as a JSON string
*/
-typedef void (*TALER_WALLET_MessageHandlerFn)(void *handler_p, char *message);
+typedef void (*TALER_WALLET_MessageHandlerFn)(void *handler_p, const char *message);
/**
* Create a new wallet-core handle.
@@ -52,7 +52,7 @@ TALER_WALLET_create(void);
*/
void
TALER_WALLET_set_handler(struct TALER_WALLET_Handle *h,
- TALER_WALLET_MessageHandlerFn *handler_f,
+ TALER_WALLET_MessageHandlerFn handler_f,
void *handler_p);
@@ -70,7 +70,7 @@ TALER_WALLET_set_jsfile(struct TALER_WALLET_Handle *h,
* Responses will be sent asynchronously to the message handler
* set with #TALER_WALLET_set_handler.
*/
-void
+int
TALER_WALLET_send_message (struct TALER_WALLET_Handle *h,
const char *msg);
@@ -80,7 +80,7 @@ TALER_WALLET_send_message (struct TALER_WALLET_Handle *h,
* This function creates a new thread and returns immediately.
*/
void
-TALER_WALLET_run (struct TALER_WLLET_Handle *h);
+TALER_WALLET_run (struct TALER_WALLET_Handle *h);
/**
* Destroy the wallet handle and free resources associated with it.
diff --git a/tart_module.c b/tart_module.c
index 859f0aa..e244e6e 100644
--- a/tart_module.c
+++ b/tart_module.c
@@ -1302,6 +1302,7 @@ static JSValue js_talercrypto_hash_state_finish(JSContext *ctx, JSValue this_val
return make_js_ta_copy(ctx, hashval, crypto_hash_sha512_BYTES);
}
+
static const JSCFunctionListEntry tart_talercrypto_funcs[] = {
JS_CFUNC_DEF("structuredClone", 1, js_structured_clone),
JS_CFUNC_DEF("encodeUtf8", 1, js_encode_utf8),
diff --git a/wallet-client-example.c b/wallet-client-example.c
new file mode 100644
index 0000000..396a56a
--- /dev/null
+++ b/wallet-client-example.c
@@ -0,0 +1,42 @@
+/*
+ This file is part of GNU Taler
+ Copyright (C) 2014-2022 Taler Systems SA
+
+ GNU 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.
+
+ GNU 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
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+
+/**
+ * Sample client for a TALER_WALLET_Handle.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include "taler_wallet_core_lib.h"
+
+
+void
+my_handler(void *cls, const char *message)
+{
+ printf("got message: %s\n", message);
+}
+
+
+int main(int argc, char **argv)
+{
+ struct TALER_WALLET_Handle *wh = TALER_WALLET_create();
+
+ TALER_WALLET_set_handler(wh, &my_handler, NULL);
+
+ TALER_WALLET_run(wh);
+
+ TALER_WALLET_send_message(wh, "{}");
+}