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.
24 #include <json-c/json.h>
26 #include <afb/afb-binding-v1.h>
27 #include <afb/afb-binding-v2.h>
30 #include "afb-apiset.h"
31 #include "afb-common.h"
34 #include "afb-export.h"
36 #include "afb-msg-json.h"
37 #include "afb-session.h"
43 /*************************************************************************
44 * internal types and structures
45 ************************************************************************/
67 /* version of the api */
77 /* session for service */
78 struct afb_session *session;
80 /* apiset for service */
81 struct afb_apiset *apiset;
83 /* event listener for service or NULL */
84 struct afb_evt_listener *listener;
88 int (*v1)(struct afb_service);
94 void (*v12)(const char *event, struct json_object *object);
99 struct afb_binding_interface_v1 v1;
100 struct afb_binding_data_v2 *v2;
104 /*************************************************************************************************************
105 *************************************************************************************************************
106 *************************************************************************************************************
107 *************************************************************************************************************
109 *************************************************************************************************************
110 *************************************************************************************************************
111 *************************************************************************************************************
112 *************************************************************************************************************/
114 /**********************************************
116 **********************************************/
117 static void vverbose_cb(void *closure, int level, const char *file, int line, const char *function, const char *fmt, va_list args)
120 struct afb_export *export = closure;
122 if (!fmt || vasprintf(&p, fmt, args) < 0)
123 vverbose(level, file, line, function, fmt, args);
125 verbose(level, file, line, function, "[API %s] %s", export->apiname, p);
130 static void old_vverbose_cb(void *closure, int level, const char *file, int line, const char *fmt, va_list args)
132 vverbose_cb(closure, level, file, line, NULL, fmt, args);
135 static struct afb_eventid *eventid_make_cb(void *closure, const char *name)
139 struct afb_export *export = closure;
141 /* check daemon state */
142 if (export->state == Api_State_Pre_Init) {
143 ERROR("[API %s] Bad call to 'afb_daemon_event_make(%s)', must not be in PreInit", export->apiname, name);
148 /* makes the event name */
149 plen = strlen(export->apiname);
151 event = alloca(nlen + plen + 2);
152 memcpy(event, export->apiname, plen);
154 memcpy(event + plen + 1, name, nlen + 1);
156 /* create the event */
157 return afb_evt_create_event(event);
160 static struct afb_event event_make_cb(void *closure, const char *name)
162 struct afb_eventid *eventid = eventid_make_cb(closure, name);
163 return (struct afb_event){ .itf = eventid ? eventid->itf : NULL, .closure = eventid };
166 static int event_broadcast_cb(void *closure, const char *name, struct json_object *object)
170 struct afb_export *export = closure;
172 /* check daemon state */
173 if (export->state == Api_State_Pre_Init) {
174 ERROR("[API %s] Bad call to 'afb_daemon_event_broadcast(%s, %s)', must not be in PreInit", export->apiname, name, json_object_to_json_string(object));
179 /* makes the event name */
180 plen = strlen(export->apiname);
182 event = alloca(nlen + plen + 2);
183 memcpy(event, export->apiname, plen);
185 memcpy(event + plen + 1, name, nlen + 1);
187 /* broadcast the event */
188 return afb_evt_broadcast(event, object);
191 static int rootdir_open_locale_cb(void *closure, const char *filename, int flags, const char *locale)
193 return afb_common_rootdir_open_locale(filename, flags, locale);
196 static int queue_job_cb(void *closure, void (*callback)(int signum, void *arg), void *argument, void *group, int timeout)
198 return jobs_queue(group, timeout, callback, argument);
201 static struct afb_req unstore_req_cb(void *closure, struct afb_stored_req *sreq)
203 return afb_xreq_unstore(sreq);
206 static int require_api_cb(void *closure, const char *name, int initialized)
208 struct afb_export *export = closure;
209 if (export->state != Api_State_Init) {
210 ERROR("[API %s] Bad call to 'afb_daemon_require(%s, %d)', must be in Init", export->apiname, name, initialized);
214 return -!(initialized ? afb_apiset_lookup_started : afb_apiset_lookup)(export->apiset, name, 1);
217 static int rename_api_cb(void *closure, const char *name)
219 struct afb_export *export = closure;
220 if (export->state != Api_State_Pre_Init) {
221 ERROR("[API %s] Bad call to 'afb_daemon_rename(%s)', must be in PreInit", export->apiname, name);
225 if (!afb_api_is_valid_name(name)) {
226 ERROR("[API %s] Can't rename to %s: bad API name", export->apiname, name);
230 NOTICE("[API %s] renamed to [API %s]", export->apiname, name);
231 afb_export_rename(export, name);
235 /**********************************************
237 **********************************************/
238 static void hooked_vverbose_cb(void *closure, int level, const char *file, int line, const char *function, const char *fmt, va_list args)
240 struct afb_export *export = closure;
243 vverbose_cb(closure, level, file, line, function, fmt, args);
244 afb_hook_ditf_vverbose(export, level, file, line, function, fmt, ap);
248 static void hooked_old_vverbose_cb(void *closure, int level, const char *file, int line, const char *fmt, va_list args)
250 hooked_vverbose_cb(closure, level, file, line, NULL, fmt, args);
253 static struct afb_eventid *hooked_eventid_make_cb(void *closure, const char *name)
255 struct afb_export *export = closure;
256 struct afb_eventid *r = eventid_make_cb(closure, name);
257 afb_hook_ditf_event_make(export, name, r);
261 static struct afb_event hooked_event_make_cb(void *closure, const char *name)
263 struct afb_eventid *eventid = hooked_eventid_make_cb(closure, name);
264 return (struct afb_event){ .itf = eventid ? eventid->itf : NULL, .closure = eventid };
267 static int hooked_event_broadcast_cb(void *closure, const char *name, struct json_object *object)
270 struct afb_export *export = closure;
271 json_object_get(object);
272 afb_hook_ditf_event_broadcast_before(export, name, json_object_get(object));
273 r = event_broadcast_cb(closure, name, object);
274 afb_hook_ditf_event_broadcast_after(export, name, object, r);
275 json_object_put(object);
279 static struct sd_event *hooked_get_event_loop(void *closure)
281 struct afb_export *export = closure;
282 struct sd_event *r = afb_common_get_event_loop();
283 return afb_hook_ditf_get_event_loop(export, r);
286 static struct sd_bus *hooked_get_user_bus(void *closure)
288 struct afb_export *export = closure;
289 struct sd_bus *r = afb_common_get_user_bus();
290 return afb_hook_ditf_get_user_bus(export, r);
293 static struct sd_bus *hooked_get_system_bus(void *closure)
295 struct afb_export *export = closure;
296 struct sd_bus *r = afb_common_get_system_bus();
297 return afb_hook_ditf_get_system_bus(export, r);
300 static int hooked_rootdir_get_fd(void *closure)
302 struct afb_export *export = closure;
303 int r = afb_common_rootdir_get_fd();
304 return afb_hook_ditf_rootdir_get_fd(export, r);
307 static int hooked_rootdir_open_locale_cb(void *closure, const char *filename, int flags, const char *locale)
309 struct afb_export *export = closure;
310 int r = rootdir_open_locale_cb(closure, filename, flags, locale);
311 return afb_hook_ditf_rootdir_open_locale(export, filename, flags, locale, r);
314 static int hooked_queue_job_cb(void *closure, void (*callback)(int signum, void *arg), void *argument, void *group, int timeout)
316 struct afb_export *export = closure;
317 int r = queue_job_cb(closure, callback, argument, group, timeout);
318 return afb_hook_ditf_queue_job(export, callback, argument, group, timeout, r);
321 static struct afb_req hooked_unstore_req_cb(void *closure, struct afb_stored_req *sreq)
323 struct afb_export *export = closure;
324 afb_hook_ditf_unstore_req(export, sreq);
325 return unstore_req_cb(closure, sreq);
328 static int hooked_require_api_cb(void *closure, const char *name, int initialized)
331 struct afb_export *export = closure;
332 afb_hook_ditf_require_api(export, name, initialized);
333 result = require_api_cb(closure, name, initialized);
334 return afb_hook_ditf_require_api_result(export, name, initialized, result);
337 static int hooked_rename_api_cb(void *closure, const char *name)
339 struct afb_export *export = closure;
340 const char *oldname = export->apiname;
341 int result = rename_api_cb(closure, name);
342 return afb_hook_ditf_rename_api(export, oldname, name, result);
345 /**********************************************
347 **********************************************/
348 static const struct afb_daemon_itf daemon_itf = {
349 .vverbose_v1 = old_vverbose_cb,
350 .vverbose_v2 = vverbose_cb,
351 .event_make = event_make_cb,
352 .event_broadcast = event_broadcast_cb,
353 .get_event_loop = afb_common_get_event_loop,
354 .get_user_bus = afb_common_get_user_bus,
355 .get_system_bus = afb_common_get_system_bus,
356 .rootdir_get_fd = afb_common_rootdir_get_fd,
357 .rootdir_open_locale = rootdir_open_locale_cb,
358 .queue_job = queue_job_cb,
359 .unstore_req = unstore_req_cb,
360 .require_api = require_api_cb,
361 .rename_api = rename_api_cb
364 static const struct afb_daemon_itf hooked_daemon_itf = {
365 .vverbose_v1 = hooked_old_vverbose_cb,
366 .vverbose_v2 = hooked_vverbose_cb,
367 .event_make = hooked_event_make_cb,
368 .event_broadcast = hooked_event_broadcast_cb,
369 .get_event_loop = hooked_get_event_loop,
370 .get_user_bus = hooked_get_user_bus,
371 .get_system_bus = hooked_get_system_bus,
372 .rootdir_get_fd = hooked_rootdir_get_fd,
373 .rootdir_open_locale = hooked_rootdir_open_locale_cb,
374 .queue_job = hooked_queue_job_cb,
375 .unstore_req = hooked_unstore_req_cb,
376 .require_api = hooked_require_api_cb,
377 .rename_api = hooked_rename_api_cb
381 /*************************************************************************************************************
382 *************************************************************************************************************
383 *************************************************************************************************************
384 *************************************************************************************************************
386 *************************************************************************************************************
387 *************************************************************************************************************
388 *************************************************************************************************************
389 *************************************************************************************************************/
391 /* the common session for services sharing their session */
392 static struct afb_session *common_session;
394 /*************************************************************************************************************
395 *************************************************************************************************************
396 *************************************************************************************************************
397 *************************************************************************************************************
399 *************************************************************************************************************
400 *************************************************************************************************************
401 *************************************************************************************************************
402 *************************************************************************************************************/
405 * Structure for requests initiated by the service
409 struct afb_xreq xreq;
411 struct afb_export *export;
414 void (*callback)(void*, int, struct json_object*);
418 struct jobloop *jobloop;
419 struct json_object *result;
424 * destroys the call_req
426 static void callreq_destroy(struct afb_xreq *xreq)
428 struct call_req *callreq = CONTAINER_OF_XREQ(struct call_req, xreq);
430 afb_context_disconnect(&callreq->xreq.context);
431 json_object_put(callreq->xreq.json);
432 afb_cred_unref(callreq->xreq.cred);
436 static void callreq_reply(struct afb_xreq *xreq, int status, json_object *obj)
438 struct call_req *callreq = CONTAINER_OF_XREQ(struct call_req, xreq);
439 if (callreq->callback)
440 callreq->callback(callreq->closure, status, obj);
441 json_object_put(obj);
444 static void callreq_sync_leave(struct call_req *callreq)
446 struct jobloop *jobloop = callreq->jobloop;
449 callreq->jobloop = NULL;
454 static void callreq_reply_sync(struct afb_xreq *xreq, int status, json_object *obj)
456 struct call_req *callreq = CONTAINER_OF_XREQ(struct call_req, xreq);
457 callreq->status = status;
458 callreq->result = obj;
459 callreq_sync_leave(callreq);
462 static void callreq_sync_enter(int signum, void *closure, struct jobloop *jobloop)
464 struct call_req *callreq = closure;
467 callreq->jobloop = jobloop;
468 afb_xreq_process(&callreq->xreq, callreq->export->apiset);
470 callreq->result = afb_msg_json_internal_error();
471 callreq->status = -1;
472 callreq_sync_leave(callreq);
476 /* interface for requests of services */
477 const struct afb_xreq_query_itf afb_export_xreq_itf = {
478 .unref = callreq_destroy,
479 .reply = callreq_reply
482 /* interface for requests of services */
483 const struct afb_xreq_query_itf afb_export_xreq_sync_itf = {
484 .unref = callreq_destroy,
485 .reply = callreq_reply_sync
491 static struct call_req *callreq_create(struct afb_export *export, const char *api, const char *verb, struct json_object *args, const struct afb_xreq_query_itf *itf)
493 struct call_req *callreq;
494 size_t lenapi, lenverb;
497 /* allocates the request */
498 lenapi = 1 + strlen(api);
499 lenverb = 1 + strlen(verb);
500 callreq = malloc(lenapi + lenverb + sizeof *callreq);
501 if (callreq != NULL) {
502 /* initialises the request */
503 afb_xreq_init(&callreq->xreq, itf);
504 afb_context_init(&callreq->xreq.context, export->session, NULL);
505 callreq->xreq.context.validated = 1;
506 copy = (char*)&callreq[1];
507 memcpy(copy, api, lenapi);
508 callreq->xreq.api = copy;
509 copy = ©[lenapi];
510 memcpy(copy, verb, lenverb);
511 callreq->xreq.verb = copy;
512 callreq->xreq.listener = export->listener;
513 callreq->xreq.json = args;
514 callreq->export = export;
520 * Initiates a call for the service
522 static void svc_call(void *closure, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*), void *cbclosure)
524 struct afb_export *export = closure;
525 struct call_req *callreq;
526 struct json_object *ierr;
528 /* allocates the request */
529 callreq = callreq_create(export, api, verb, args, &afb_export_xreq_itf);
530 if (callreq == NULL) {
531 ERROR("out of memory");
532 json_object_put(args);
533 ierr = afb_msg_json_internal_error();
535 callback(cbclosure, -1, ierr);
536 json_object_put(ierr);
540 /* initialises the request */
541 callreq->jobloop = NULL;
542 callreq->callback = callback;
543 callreq->closure = cbclosure;
545 /* terminates and frees ressources if needed */
546 afb_xreq_process(&callreq->xreq, export->apiset);
549 static int svc_call_sync(void *closure, const char *api, const char *verb, struct json_object *args,
550 struct json_object **result)
552 struct afb_export *export = closure;
553 struct call_req *callreq;
554 struct json_object *resu;
557 /* allocates the request */
558 callreq = callreq_create(export, api, verb, args, &afb_export_xreq_sync_itf);
559 if (callreq == NULL) {
560 ERROR("out of memory");
562 json_object_put(args);
563 resu = afb_msg_json_internal_error();
566 /* initialises the request */
567 callreq->jobloop = NULL;
568 callreq->callback = NULL;
569 callreq->result = NULL;
571 afb_xreq_unhooked_addref(&callreq->xreq); /* avoid early callreq destruction */
572 rc = jobs_enter(NULL, 0, callreq_sync_enter, callreq);
574 rc = callreq->status;
575 resu = (rc >= 0 || callreq->result) ? callreq->result : afb_msg_json_internal_error();
576 afb_xreq_unhooked_unref(&callreq->xreq);
581 json_object_put(resu);
587 struct afb_export *export;
588 void (*callback)(void*, int, struct json_object*);
592 static void svc_hooked_call_result(void *closure, int status, struct json_object *result)
594 struct hooked_call *hc = closure;
595 afb_hook_svc_call_result(hc->export, status, result);
596 hc->callback(hc->cbclosure, status, result);
600 static void svc_hooked_call(void *closure, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*), void *cbclosure)
602 struct afb_export *export = closure;
603 struct hooked_call *hc;
605 if (export->hooksvc & afb_hook_flag_svc_call)
606 afb_hook_svc_call(export, api, verb, args);
608 if (export->hooksvc & afb_hook_flag_svc_call_result) {
609 hc = malloc(sizeof *hc);
611 WARNING("allocation failed");
614 hc->callback = callback;
615 hc->cbclosure = cbclosure;
616 callback = svc_hooked_call_result;
620 svc_call(closure, api, verb, args, callback, cbclosure);
623 static int svc_hooked_call_sync(void *closure, const char *api, const char *verb, struct json_object *args,
624 struct json_object **result)
626 struct afb_export *export = closure;
627 struct json_object *resu;
630 if (export->hooksvc & afb_hook_flag_svc_callsync)
631 afb_hook_svc_callsync(export, api, verb, args);
633 rc = svc_call_sync(closure, api, verb, args, &resu);
635 if (export->hooksvc & afb_hook_flag_svc_callsync_result)
636 afb_hook_svc_callsync_result(export, rc, resu);
641 json_object_put(resu);
646 /* the interface for services */
647 static const struct afb_service_itf service_itf = {
649 .call_sync = svc_call_sync
652 /* the interface for services */
653 static const struct afb_service_itf hooked_service_itf = {
654 .call = svc_hooked_call,
655 .call_sync = svc_hooked_call_sync
658 /*************************************************************************************************************
659 *************************************************************************************************************
660 *************************************************************************************************************
661 *************************************************************************************************************
663 *************************************************************************************************************
664 *************************************************************************************************************
665 *************************************************************************************************************
666 *************************************************************************************************************/
669 * Propagates the event to the service
671 static void export_on_event_v12(void *closure, const char *event, int eventid, struct json_object *object)
673 struct afb_export *export = closure;
675 if (export->hooksvc & afb_hook_flag_svc_on_event_before)
676 afb_hook_svc_on_event_before(export, event, eventid, object);
677 export->on_event.v12(event, object);
678 if (export->hooksvc & afb_hook_flag_svc_on_event_after)
679 afb_hook_svc_on_event_after(export, event, eventid, object);
680 json_object_put(object);
683 /* the interface for events */
684 static const struct afb_evt_itf evt_v12_itf = {
685 .broadcast = export_on_event_v12,
686 .push = export_on_event_v12
689 /*************************************************************************************************************
690 *************************************************************************************************************
691 *************************************************************************************************************
692 *************************************************************************************************************
694 *************************************************************************************************************
695 *************************************************************************************************************
696 *************************************************************************************************************
697 *************************************************************************************************************/
699 static struct afb_export *create(struct afb_apiset *apiset, const char *apiname, enum afb_api_version version)
701 struct afb_export *export;
703 /* session shared with other exports */
704 if (common_session == NULL) {
705 common_session = afb_session_create (NULL, 0);
706 if (common_session == NULL)
709 export = calloc(1, sizeof *export);
713 memset(export, 0, sizeof *export);
714 export->apiname = strdup(apiname);
715 export->version = version;
716 export->state = Api_State_Pre_Init;
717 export->session = afb_session_addref(common_session);
718 export->apiset = afb_apiset_addref(apiset);
723 void afb_export_destroy(struct afb_export *export)
726 if (export->listener != NULL)
727 afb_evt_listener_unref(export->listener);
728 afb_session_unref(export->session);
729 afb_apiset_unref(export->apiset);
730 free(export->apiname);
735 struct afb_export *afb_export_create_v1(struct afb_apiset *apiset, const char *apiname, int (*init)(struct afb_service), void (*onevent)(const char*, struct json_object*))
737 struct afb_export *export = create(apiset, apiname, Api_Version_1);
739 export->init.v1 = init;
740 export->on_event.v12 = onevent;
741 export->export.v1.verbosity = verbosity;
742 export->export.v1.mode = AFB_MODE_LOCAL;
743 export->export.v1.daemon.closure = export;
744 afb_export_update_hook(export);
749 struct afb_export *afb_export_create_v2(struct afb_apiset *apiset, const char *apiname, struct afb_binding_data_v2 *data, int (*init)(), void (*onevent)(const char*, struct json_object*))
751 struct afb_export *export = create(apiset, apiname, Api_Version_2);
753 export->init.v2 = init;
754 export->on_event.v12 = onevent;
755 export->export.v2 = data;
756 data->verbosity = verbosity;
757 data->daemon.closure = export;
758 data->service.closure = export;
759 afb_export_update_hook(export);
764 void afb_export_rename(struct afb_export *export, const char *apiname)
766 free(export->apiname);
767 export->apiname = strdup(apiname);
768 afb_export_update_hook(export);
771 const char *afb_export_apiname(const struct afb_export *export)
773 return export->apiname;
776 void afb_export_update_hook(struct afb_export *export)
778 export->hookditf = afb_hook_flags_ditf(export->apiname);
779 export->hooksvc = afb_hook_flags_svc(export->apiname);
780 switch (export->version) {
782 export->export.v1.daemon.itf = export->hookditf ? &hooked_daemon_itf : &daemon_itf;
786 export->export.v2->daemon.itf = export->hookditf ? &hooked_daemon_itf : &daemon_itf;
787 export->export.v2->service.itf = export->hooksvc ? &hooked_service_itf : &service_itf;
792 struct afb_binding_interface_v1 *afb_export_get_interface_v1(struct afb_export *export)
794 return export->version == Api_Version_1 ? &export->export.v1 : NULL;
797 int afb_export_unshare_session(struct afb_export *export)
799 if (export->session == common_session) {
800 export->session = afb_session_create (NULL, 0);
802 afb_session_unref(common_session);
804 export->session = common_session;
811 void afb_export_set_apiset(struct afb_export *export, struct afb_apiset *apiset)
813 struct afb_apiset *prvset = export->apiset;
814 export->apiset = afb_apiset_addref(apiset);
815 afb_apiset_unref(prvset);
818 struct afb_apiset *afb_export_get_apiset(struct afb_export *export)
820 return export->apiset;
824 * Creates a new service
826 int afb_export_handle_events_v12(struct afb_export *export, void (*on_event)(const char *event, struct json_object *object))
829 switch (export->version) {
830 case Api_Version_1: case Api_Version_2: break;
832 ERROR("invalid version 12 for API %s", export->apiname);
837 /* set the event handler */
839 if (export->listener) {
840 afb_evt_listener_unref(export->listener);
841 export->listener = NULL;
843 export->on_event.v12 = on_event;
845 export->on_event.v12 = on_event;
846 if (!export->listener) {
847 export->listener = afb_evt_listener_create(&evt_v12_itf, export);
848 if (export->listener == NULL)
856 * Starts a new service (v1)
858 struct afb_binding_v1 *afb_export_register_v1(struct afb_export *export, struct afb_binding_v1 *(*regfun)(const struct afb_binding_interface_v1*))
860 return regfun(&export->export.v1);
863 int afb_export_verbosity_get(const struct afb_export *export)
865 switch (export->version) {
866 case Api_Version_1: return export->export.v1.verbosity;
867 case Api_Version_2: return export->export.v2->verbosity;
872 void afb_export_verbosity_set(struct afb_export *export, int level)
874 switch (export->version) {
875 case Api_Version_1: export->export.v1.verbosity = level; break;
876 case Api_Version_2: export->export.v2->verbosity = level; break;
880 /*************************************************************************************************************
881 *************************************************************************************************************
882 *************************************************************************************************************
883 *************************************************************************************************************
885 *************************************************************************************************************
886 *************************************************************************************************************
887 *************************************************************************************************************
888 *************************************************************************************************************/
890 int afb_export_start(struct afb_export *export, int share_session, int onneed, struct afb_apiset *apiset)
895 if (export->state != Api_State_Pre_Init) {
896 /* not an error when onneed */
900 /* already started: it is an error */
901 ERROR("Service of API %s already started", export->apiname);
905 /* unshare the session if asked */
906 if (!share_session) {
907 rc = afb_export_unshare_session(export);
909 ERROR("Can't unshare the session for %s", export->apiname);
914 /* set event handling */
915 switch (export->version) {
918 rc = afb_export_handle_events_v12(export, export->on_event.v12);
925 ERROR("Can't set event handler for %s", export->apiname);
929 /* Starts the service */
930 if (export->hooksvc & afb_hook_flag_svc_start_before)
931 afb_hook_svc_start_before(export);
932 export->state = Api_State_Init;
933 switch (export->version) {
935 rc = export->init.v1 ? export->init.v1((struct afb_service){ .itf = &hooked_service_itf, .closure = export }) : 0;
938 rc = export->init.v2 ? export->init.v2() : 0;
943 export->state = Api_State_Run;
944 if (export->hooksvc & afb_hook_flag_svc_start_after)
945 afb_hook_svc_start_after(export, rc);
947 /* initialisation error */
948 ERROR("Initialisation of service API %s failed (%d): %m", export->apiname, rc);