2 * Copyright (C) 2016, 2017 "IoT.bzh"
3 * Author José Bollo <jose.bollo@iot.bzh>
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
26 #include <sys/types.h>
28 #include <json-c/json.h>
31 #include "afb-common.h"
33 #include "afb-apiset.h"
34 #include "afb-api-js.h"
39 /********************************************************************/
47 /********************************************************************/
48 static void jsapi_call(void *closure, struct afb_xreq *xreq);
49 static int jsapi_service_start(void *closure);
50 static int jsapi_get_logmask(void *closure);
51 static void jsapi_set_logmask(void *closure, int level);
52 static struct json_object *jsapi_describe(void *closure);
54 static struct afb_api_itf jsapi_itf =
57 .service_start = jsapi_service_start,
58 .set_logmask = jsapi_set_logmask,
59 .get_logmask = jsapi_get_logmask,
60 .describe = jsapi_describe
63 /********************************************************************/
64 static duk_ret_t do_success(duk_context *ctx);
65 static duk_ret_t do_fail(duk_context *ctx);
66 static duk_ret_t do_subcall_sync(duk_context *ctx);
67 static duk_ret_t do_error(duk_context *ctx);
68 static duk_ret_t do_require(duk_context *ctx);
70 static const duk_function_list_entry funcs[] =
72 { "afb_req_success", do_success, 3 },
73 { "afb_req_fail", do_fail, 3 },
74 { "afb_req_subcall_sync", do_subcall_sync, 4 },
75 { "afb_error", do_error, 1 },
76 { "require", do_require, 1 },
80 /********************************************************************/
82 static void on_heap_fatal(void *udata, const char *msg)
84 ERROR("Got fatal from duktape: %s", msg);
88 static int jsapi_load(duk_context *ctx, const char *path)
90 static const char prefix[] = "function(exports){";
91 static const char suffix[] = "}";
98 fd = afb_common_rootdir_open_locale(path, O_RDONLY, NULL);
100 fd = open(path, O_RDONLY);
102 ERROR("Can't open %s: %m", path);
103 duk_push_error_object(ctx, DUK_ERR_ERROR, "Can't open file %s: %m", path);
113 buffer = alloca(st.st_size + sizeof prefix + sizeof suffix);
114 s = read(fd, &buffer[sizeof prefix - 1], st.st_size);
120 memcpy(buffer, prefix, sizeof prefix - 1);
121 memcpy(&buffer[sizeof prefix - 1 + st.st_size], suffix, sizeof suffix);
124 duk_push_object(ctx); /* exports */
125 duk_push_string(ctx, path); /* exports path */
126 rc = duk_pcompile_string_filename(ctx, DUK_COMPILE_FUNCTION|DUK_COMPILE_STRICT, buffer); /* exports func */
128 duk_dup_top(ctx); /* exports error error */
129 ERROR("compiling of %s failed: %s", path, duk_safe_to_string(ctx, -1)); /* exports error error */
130 duk_pop(ctx); /* exports error */
131 duk_replace(ctx, -2); /* error */
134 duk_dup(ctx, -2); /* exports func exports */
135 rc = duk_pcall(ctx, 1); /* exports ret */
137 duk_dup_top(ctx); /* exports error error */
138 if (!duk_is_error(ctx, -1)) {
139 duk_push_error_object(ctx, DUK_ERR_ERROR, "%s", duk_safe_to_string(ctx, -1)); /* exports error error error */
140 duk_replace(ctx, -3); /* exports error error */
142 ERROR("initialisation of %s failed: %s", path, duk_safe_to_string(ctx, -1)); /* exports error */
143 duk_pop(ctx); /* exports error */
144 duk_replace(ctx, -2); /* error */
147 duk_pop(ctx); /* exports */
150 ERROR("can't process file %s: %m", path);
151 duk_push_error_object(ctx, DUK_ERR_ERROR, "Can't process file %s: %m", path);
154 return duk_throw(ctx);
157 static duk_ret_t do_require(duk_context *ctx)
162 path = duk_require_string(ctx, -1); /* path */
163 duk_push_global_stash(ctx); /* path gstash */
164 duk_dup(ctx, -2); /* path gstash path */
165 rc = duk_get_prop(ctx, -2); /* path gstash ? */
167 /* path gstash error (ELSE: path gstash exports) */
168 duk_pop(ctx); /* path gstash */
169 rc = jsapi_load(ctx, path); /* path gstash ? */
171 /* path gstash exports (ELSE: path gstash error) */
172 duk_dup_top(ctx); /* path gstash exports exports */
173 duk_swap(ctx, -2, -4); /* exports gstash path exports */
174 duk_put_prop(ctx, -3); /* exports gstash */
175 duk_pop(ctx); /* exports */
181 static void jsapi_destroy(struct jsapi *jsapi)
183 duk_destroy_heap(jsapi->context);
187 static struct jsapi *jsapi_create(const char *path)
191 const char *api, *ext;
193 /* allocate and initialise names */
194 api = strrchr(path, '/');
195 api = api ? api + 1 : path;
196 ext = strrchr(api, '.') ?: &api[strlen(api)];
197 jsapi = malloc(sizeof *jsapi + 1 + (ext - api));
200 memcpy(jsapi->api, api, ext - api);
201 jsapi->api[ext - api] = 0;
202 jsapi->logmask = logmask;
204 /* create the duktape context */
205 ctx = duk_create_heap(NULL, NULL, NULL, NULL, on_heap_fatal);
209 jsapi->context = ctx;
211 /* populate global with functions */
212 duk_push_global_object(ctx);
213 duk_put_function_list(ctx, -1, funcs);
216 /* call the require path */
217 ctx = jsapi->context;
218 duk_get_global_string(ctx, "require");
219 duk_push_string(ctx, path);
221 if (duk_is_error(ctx, -1)) {
222 const char *message, *file, *stack;
224 duk_get_prop_string(ctx, -1, "message");
225 message = duk_get_string(ctx, -1);
226 duk_get_prop_string(ctx, -2, "fileName");
227 file = duk_get_string(ctx, -1);
228 duk_get_prop_string(ctx, -3, "lineNumber");
229 line = (int)duk_get_int(ctx, -1);
230 duk_get_prop_string(ctx, -4, "stack");
231 stack = duk_get_string(ctx, -1);
232 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);
233 jsapi_destroy(jsapi);
236 duk_put_global_string(ctx, "exports");
240 ERROR("out of memory");
246 int afb_api_js_add(const char *path, struct afb_apiset *declare_set, struct afb_apiset* call_set)
250 struct afb_api_item api;
252 jsapi = jsapi_create(path);
257 api.itf = &jsapi_itf;
259 rc = afb_apiset_add(declare_set, jsapi->api, api);
263 duk_destroy_heap(jsapi->context);
269 /********************************************************************/
271 static duk_ret_t do_success(duk_context *ctx)
273 struct afb_xreq *xreq;
274 const char *json, *info;
276 xreq = duk_get_pointer(ctx, -3);
277 duk_json_encode(ctx, -2);
278 json = duk_get_string(ctx, -2);
279 info = duk_get_string(ctx, -1);
281 afb_xreq_reply(xreq, json ? json_tokener_parse(json) : NULL, NULL, info);
285 static duk_ret_t do_fail(duk_context *ctx)
287 struct afb_xreq *xreq;
288 const char *status, *info;
290 xreq = duk_get_pointer(ctx, -3);
291 status = duk_get_string(ctx, -2);
292 info = duk_get_string(ctx, -1);
294 afb_xreq_reply(xreq, NULL, status ?: "error", info);
298 static duk_ret_t do_subcall_sync(duk_context *ctx)
301 struct afb_xreq *xreq;
302 const char *api, *verb, *json;
303 struct json_object *resu;
305 xreq = duk_get_pointer(ctx, -4);
306 api = duk_get_string(ctx, -3);
307 verb = duk_get_string(ctx, -2);
308 duk_json_decode(ctx, -1);
309 json = duk_get_string(ctx, -1);
312 rc = afb_xreq_legacy_subcall_sync(xreq, api, verb, json ? json_tokener_parse(json) : NULL, &resu);
316 duk_push_string(ctx, json_object_to_json_string(resu));
317 duk_json_decode(ctx, -1);
319 json_object_put(resu);
323 static duk_ret_t do_error(duk_context *ctx)
327 message = duk_get_string(ctx, -1);
329 ERROR("%s", message ? : "null");
333 /********************************************************************/
335 static void jsapi_call(void *closure, struct afb_xreq *xreq)
341 struct jsapi *jsapi = closure;
343 ctx = jsapi->context;
344 top = duk_get_top(ctx);
345 duk_get_global_string(ctx, "exports");
346 if (!duk_is_object(ctx, -1)) {
347 afb_xreq_reply(xreq, NULL, "internal-error", "no exports!?");
350 duk_get_prop_string(ctx, -1, xreq->request.called_verb);
351 if (!duk_is_function(ctx, -1)) {
352 afb_xreq_reply_unknown_verb(xreq);
355 duk_push_pointer(ctx, xreq);
356 args = afb_xreq_json(xreq);
357 json = json_object_to_json_string(args);
358 duk_push_string(ctx, json);
359 duk_json_decode(ctx, -1);
362 duk_pop_n(ctx, duk_get_top(ctx) - top);
365 static int jsapi_service_start(void *closure)
367 struct jsapi *jsapi = closure;
371 static int jsapi_get_logmask(void *closure)
373 struct jsapi *jsapi = closure;
374 return jsapi->logmask;
377 static void jsapi_set_logmask(void *closure, int level)
379 struct jsapi *jsapi = closure;
380 jsapi->logmask = level;
383 static struct json_object *jsapi_describe(void *closure)
385 struct jsapi *jsapi = closure;