X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fafb-api-js.c;fp=src%2Fafb-api-js.c;h=67532aa0f93f97bd1e6a9120613f376e46b8e7d2;hb=7aad83842cdab620e91fe73ac4731d75eb9c283a;hp=0000000000000000000000000000000000000000;hpb=9a623c3aa32ec0fbf9682f37a990abd00f38da60;p=src%2Fapp-framework-binder.git diff --git a/src/afb-api-js.c b/src/afb-api-js.c new file mode 100644 index 00000000..67532aa0 --- /dev/null +++ b/src/afb-api-js.c @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2016, 2017 "IoT.bzh" + * Author José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "duktape.h" + +#include "afb-common.h" +#include "afb-api.h" +#include "afb-apiset.h" +#include "afb-api-js.h" +#include "afb-xreq.h" +#include "jobs.h" +#include "verbose.h" + +/********************************************************************/ +struct jsapi +{ + int logmask; + duk_context *context; + char api[1]; +}; + +/********************************************************************/ +static void jsapi_call(void *closure, struct afb_xreq *xreq); +static int jsapi_service_start(void *closure); +static int jsapi_get_logmask(void *closure); +static void jsapi_set_logmask(void *closure, int level); +static struct json_object *jsapi_describe(void *closure); + +static struct afb_api_itf jsapi_itf = +{ + .call = jsapi_call, + .service_start = jsapi_service_start, + .set_logmask = jsapi_set_logmask, + .get_logmask = jsapi_get_logmask, + .describe = jsapi_describe +}; + +/********************************************************************/ +static duk_ret_t do_success(duk_context *ctx); +static duk_ret_t do_fail(duk_context *ctx); +static duk_ret_t do_subcall_sync(duk_context *ctx); +static duk_ret_t do_error(duk_context *ctx); +static duk_ret_t do_require(duk_context *ctx); + +static const duk_function_list_entry funcs[] = +{ + { "afb_req_success", do_success, 3 }, + { "afb_req_fail", do_fail, 3 }, + { "afb_req_subcall_sync", do_subcall_sync, 4 }, + { "afb_error", do_error, 1 }, + { "require", do_require, 1 }, + { NULL, NULL, 0 } +}; + +/********************************************************************/ + +static void on_heap_fatal(void *udata, const char *msg) +{ + ERROR("Got fatal from duktape: %s", msg); + abort(); +} + +static int jsapi_load(duk_context *ctx, const char *path) +{ + static const char prefix[] = "function(exports){"; + static const char suffix[] = "}"; + + int fd, rc; + struct stat st; + char *buffer; + ssize_t s; + + fd = afb_common_rootdir_open_locale(path, O_RDONLY, NULL); + if (fd < 0) { + fd = open(path, O_RDONLY); + if (fd < 0) { + ERROR("Can't open %s: %m", path); + duk_push_error_object(ctx, DUK_ERR_ERROR, "Can't open file %s: %m", path); + goto error; + } + } + + rc = fstat(fd, &st); + if (rc < 0) { + goto error2; + } + + buffer = alloca(st.st_size + sizeof prefix + sizeof suffix); + s = read(fd, &buffer[sizeof prefix - 1], st.st_size); + if (s < 0) + goto error2; + if (s != st.st_size) + goto error2; + + memcpy(buffer, prefix, sizeof prefix - 1); + memcpy(&buffer[sizeof prefix - 1 + st.st_size], suffix, sizeof suffix); + close(fd); + + duk_push_object(ctx); /* exports */ + duk_push_string(ctx, path); /* exports path */ + rc = duk_pcompile_string_filename(ctx, DUK_COMPILE_FUNCTION|DUK_COMPILE_STRICT, buffer); /* exports func */ + if (rc) { + duk_dup_top(ctx); /* exports error error */ + ERROR("compiling of %s failed: %s", path, duk_safe_to_string(ctx, -1)); /* exports error error */ + duk_pop(ctx); /* exports error */ + duk_replace(ctx, -2); /* error */ + goto error; + } + duk_dup(ctx, -2); /* exports func exports */ + rc = duk_pcall(ctx, 1); /* exports ret */ + if (rc) { + duk_dup_top(ctx); /* exports error error */ + if (!duk_is_error(ctx, -1)) { + duk_push_error_object(ctx, DUK_ERR_ERROR, "%s", duk_safe_to_string(ctx, -1)); /* exports error error error */ + duk_replace(ctx, -3); /* exports error error */ + } + ERROR("initialisation of %s failed: %s", path, duk_safe_to_string(ctx, -1)); /* exports error */ + duk_pop(ctx); /* exports error */ + duk_replace(ctx, -2); /* error */ + goto error; + } + duk_pop(ctx); /* exports */ + return 1; +error2: + ERROR("can't process file %s: %m", path); + duk_push_error_object(ctx, DUK_ERR_ERROR, "Can't process file %s: %m", path); + close(fd); +error: + return duk_throw(ctx); +} + +static duk_ret_t do_require(duk_context *ctx) +{ + int rc; + const char *path; + + path = duk_require_string(ctx, -1); /* path */ + duk_push_global_stash(ctx); /* path gstash */ + duk_dup(ctx, -2); /* path gstash path */ + rc = duk_get_prop(ctx, -2); /* path gstash ? */ + if (!rc) { + /* path gstash error (ELSE: path gstash exports) */ + duk_pop(ctx); /* path gstash */ + rc = jsapi_load(ctx, path); /* path gstash ? */ + if (rc > 0) { + /* path gstash exports (ELSE: path gstash error) */ + duk_dup_top(ctx); /* path gstash exports exports */ + duk_swap(ctx, -2, -4); /* exports gstash path exports */ + duk_put_prop(ctx, -3); /* exports gstash */ + duk_pop(ctx); /* exports */ + } + } + return 1; +} + +static void jsapi_destroy(struct jsapi *jsapi) +{ + duk_destroy_heap(jsapi->context); + free(jsapi); +} + +static struct jsapi *jsapi_create(const char *path) +{ + struct jsapi *jsapi; + duk_context *ctx; + const char *api, *ext; + + /* allocate and initialise names */ + api = strrchr(path, '/'); + api = api ? api + 1 : path; + ext = strrchr(api, '.') ?: &api[strlen(api)]; + jsapi = malloc(sizeof *jsapi + 1 + (ext - api)); + if (!jsapi) + goto error; + memcpy(jsapi->api, api, ext - api); + jsapi->api[ext - api] = 0; + jsapi->logmask = logmask; + + /* create the duktape context */ + ctx = duk_create_heap(NULL, NULL, NULL, NULL, on_heap_fatal); + if (!ctx) + goto error; + + jsapi->context = ctx; + + /* populate global with functions */ + duk_push_global_object(ctx); + duk_put_function_list(ctx, -1, funcs); + duk_pop(ctx); + + /* call the require path */ + ctx = jsapi->context; + duk_get_global_string(ctx, "require"); + duk_push_string(ctx, path); + duk_pcall(ctx, 1); + if (duk_is_error(ctx, -1)) { + const char *message, *file, *stack; + int line; + duk_get_prop_string(ctx, -1, "message"); + message = duk_get_string(ctx, -1); + duk_get_prop_string(ctx, -2, "fileName"); + file = duk_get_string(ctx, -1); + duk_get_prop_string(ctx, -3, "lineNumber"); + line = (int)duk_get_int(ctx, -1); + duk_get_prop_string(ctx, -4, "stack"); + stack = duk_get_string(ctx, -1); + ERROR("Initialisation of API %s from jsapi %s failed file %s (file %s, line %d) stack:\n%s\n", jsapi->api, path, message, file, line, stack); + jsapi_destroy(jsapi); + return NULL; + } + duk_put_global_string(ctx, "exports"); + return jsapi; + +error: + ERROR("out of memory"); + free(jsapi); + errno = ENOMEM; + return NULL; +} + +int afb_api_js_add(const char *path, struct afb_apiset *declare_set, struct afb_apiset* call_set) +{ + int rc; + struct jsapi *jsapi; + struct afb_api_item api; + + jsapi = jsapi_create(path); + if (!jsapi) + goto error; + + api.closure = jsapi; + api.itf = &jsapi_itf; + api.group = jsapi; + rc = afb_apiset_add(declare_set, jsapi->api, api); + if (!rc) + return 0; + + duk_destroy_heap(jsapi->context); + free(jsapi); +error: + return -1; +} + +/********************************************************************/ + +static duk_ret_t do_success(duk_context *ctx) +{ + struct afb_xreq *xreq; + const char *json, *info; + + xreq = duk_get_pointer(ctx, -3); + duk_json_encode(ctx, -2); + json = duk_get_string(ctx, -2); + info = duk_get_string(ctx, -1); + + afb_xreq_reply(xreq, json ? json_tokener_parse(json) : NULL, NULL, info); + return 0; +} + +static duk_ret_t do_fail(duk_context *ctx) +{ + struct afb_xreq *xreq; + const char *status, *info; + + xreq = duk_get_pointer(ctx, -3); + status = duk_get_string(ctx, -2); + info = duk_get_string(ctx, -1); + + afb_xreq_reply(xreq, NULL, status ?: "error", info); + return 0; +} + +static duk_ret_t do_subcall_sync(duk_context *ctx) +{ + int rc; + struct afb_xreq *xreq; + const char *api, *verb, *json; + struct json_object *resu; + + xreq = duk_get_pointer(ctx, -4); + api = duk_get_string(ctx, -3); + verb = duk_get_string(ctx, -2); + duk_json_decode(ctx, -1); + json = duk_get_string(ctx, -1); + + resu = NULL; + rc = afb_xreq_legacy_subcall_sync(xreq, api, verb, json ? json_tokener_parse(json) : NULL, &resu); + if (rc) + duk_push_null(ctx); + else { + duk_push_string(ctx, json_object_to_json_string(resu)); + duk_json_decode(ctx, -1); + } + json_object_put(resu); + return 1; +} + +static duk_ret_t do_error(duk_context *ctx) +{ + const char *message; + + message = duk_get_string(ctx, -1); + + ERROR("%s", message ? : "null"); + return 0; +} + +/********************************************************************/ + +static void jsapi_call(void *closure, struct afb_xreq *xreq) +{ + duk_idx_t top; + duk_context *ctx; + json_object *args; + const char *json; + struct jsapi *jsapi = closure; + + ctx = jsapi->context; + top = duk_get_top(ctx); + duk_get_global_string(ctx, "exports"); + if (!duk_is_object(ctx, -1)) { + afb_xreq_reply(xreq, NULL, "internal-error", "no exports!?"); + goto end; + } + duk_get_prop_string(ctx, -1, xreq->request.called_verb); + if (!duk_is_function(ctx, -1)) { + afb_xreq_reply_unknown_verb(xreq); + goto end; + } + duk_push_pointer(ctx, xreq); + args = afb_xreq_json(xreq); + json = json_object_to_json_string(args); + duk_push_string(ctx, json); + duk_json_decode(ctx, -1); + duk_pcall(ctx, 2); +end: + duk_pop_n(ctx, duk_get_top(ctx) - top); +} + +static int jsapi_service_start(void *closure) +{ + struct jsapi *jsapi = closure; + return 0; +} + +static int jsapi_get_logmask(void *closure) +{ + struct jsapi *jsapi = closure; + return jsapi->logmask; +} + +static void jsapi_set_logmask(void *closure, int level) +{ + struct jsapi *jsapi = closure; + jsapi->logmask = level; +} + +static struct json_object *jsapi_describe(void *closure) +{ + struct jsapi *jsapi = closure; + return NULL; +} +