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_event event_make_cb(void *closure, const char *name)
139 struct afb_export *export = closure;
140 struct afb_eventid *eventid;
142 /* check daemon state */
143 if (export->state == Api_State_Pre_Init) {
144 ERROR("[API %s] Bad call to 'afb_daemon_event_make(%s)', must not be in PreInit", export->apiname, name);
146 return (struct afb_event){ .itf = NULL, .closure = NULL };
149 /* makes the event name */
150 plen = strlen(export->apiname);
152 event = alloca(nlen + plen + 2);
153 memcpy(event, export->apiname, plen);
155 memcpy(event + plen + 1, name, nlen + 1);
157 /* create the event */
158 eventid = afb_evt_create_event(event);
159 return (struct afb_event){ .itf = eventid ? eventid->itf : NULL, .closure = eventid };
162 static int event_broadcast_cb(void *closure, const char *name, struct json_object *object)
166 struct afb_export *export = closure;
168 /* check daemon state */
169 if (export->state == Api_State_Pre_Init) {
170 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));
175 /* makes the event name */
176 plen = strlen(export->apiname);
178 event = alloca(nlen + plen + 2);
179 memcpy(event, export->apiname, plen);
181 memcpy(event + plen + 1, name, nlen + 1);
183 /* broadcast the event */
184 return afb_evt_broadcast(event, object);
187 static int rootdir_open_locale_cb(void *closure, const char *filename, int flags, const char *locale)
189 return afb_common_rootdir_open_locale(filename, flags, locale);
192 static int queue_job_cb(void *closure, void (*callback)(int signum, void *arg), void *argument, void *group, int timeout)
194 return jobs_queue(group, timeout, callback, argument);
197 static struct afb_req unstore_req_cb(void *closure, struct afb_stored_req *sreq)
199 return afb_xreq_unstore(sreq);
202 static int require_api_cb(void *closure, const char *name, int initialized)
204 struct afb_export *export = closure;
205 if (export->state != Api_State_Init) {
206 ERROR("[API %s] Bad call to 'afb_daemon_require(%s, %d)', must be in Init", export->apiname, name, initialized);
210 return -!(initialized ? afb_apiset_lookup_started : afb_apiset_lookup)(export->apiset, name, 1);
213 static int rename_api_cb(void *closure, const char *name)
215 struct afb_export *export = closure;
216 if (export->state != Api_State_Pre_Init) {
217 ERROR("[API %s] Bad call to 'afb_daemon_rename(%s)', must be in PreInit", export->apiname, name);
221 if (!afb_api_is_valid_name(name)) {
222 ERROR("[API %s] Can't rename to %s: bad API name", export->apiname, name);
226 NOTICE("[API %s] renamed to [API %s]", export->apiname, name);
227 afb_export_rename(export, name);
231 /**********************************************
233 **********************************************/
234 static void hooked_vverbose_cb(void *closure, int level, const char *file, int line, const char *function, const char *fmt, va_list args)
236 struct afb_export *export = closure;
239 vverbose_cb(closure, level, file, line, function, fmt, args);
240 afb_hook_ditf_vverbose(export, level, file, line, function, fmt, ap);
244 static void hooked_old_vverbose_cb(void *closure, int level, const char *file, int line, const char *fmt, va_list args)
246 hooked_vverbose_cb(closure, level, file, line, NULL, fmt, args);
249 static struct afb_event hooked_event_make_cb(void *closure, const char *name)
251 struct afb_export *export = closure;
252 struct afb_event r = event_make_cb(closure, name);
253 afb_hook_ditf_event_make(export, name, r.closure);
257 static int hooked_event_broadcast_cb(void *closure, const char *name, struct json_object *object)
260 struct afb_export *export = closure;
261 json_object_get(object);
262 afb_hook_ditf_event_broadcast_before(export, name, json_object_get(object));
263 r = event_broadcast_cb(closure, name, object);
264 afb_hook_ditf_event_broadcast_after(export, name, object, r);
265 json_object_put(object);
269 static struct sd_event *hooked_get_event_loop(void *closure)
271 struct afb_export *export = closure;
272 struct sd_event *r = afb_common_get_event_loop();
273 return afb_hook_ditf_get_event_loop(export, r);
276 static struct sd_bus *hooked_get_user_bus(void *closure)
278 struct afb_export *export = closure;
279 struct sd_bus *r = afb_common_get_user_bus();
280 return afb_hook_ditf_get_user_bus(export, r);
283 static struct sd_bus *hooked_get_system_bus(void *closure)
285 struct afb_export *export = closure;
286 struct sd_bus *r = afb_common_get_system_bus();
287 return afb_hook_ditf_get_system_bus(export, r);
290 static int hooked_rootdir_get_fd(void *closure)
292 struct afb_export *export = closure;
293 int r = afb_common_rootdir_get_fd();
294 return afb_hook_ditf_rootdir_get_fd(export, r);
297 static int hooked_rootdir_open_locale_cb(void *closure, const char *filename, int flags, const char *locale)
299 struct afb_export *export = closure;
300 int r = rootdir_open_locale_cb(closure, filename, flags, locale);
301 return afb_hook_ditf_rootdir_open_locale(export, filename, flags, locale, r);
304 static int hooked_queue_job_cb(void *closure, void (*callback)(int signum, void *arg), void *argument, void *group, int timeout)
306 struct afb_export *export = closure;
307 int r = queue_job_cb(closure, callback, argument, group, timeout);
308 return afb_hook_ditf_queue_job(export, callback, argument, group, timeout, r);
311 static struct afb_req hooked_unstore_req_cb(void *closure, struct afb_stored_req *sreq)
313 struct afb_export *export = closure;
314 afb_hook_ditf_unstore_req(export, sreq);
315 return unstore_req_cb(closure, sreq);
318 static int hooked_require_api_cb(void *closure, const char *name, int initialized)
321 struct afb_export *export = closure;
322 afb_hook_ditf_require_api(export, name, initialized);
323 result = require_api_cb(closure, name, initialized);
324 return afb_hook_ditf_require_api_result(export, name, initialized, result);
327 static int hooked_rename_api_cb(void *closure, const char *name)
329 struct afb_export *export = closure;
330 const char *oldname = export->apiname;
331 int result = rename_api_cb(closure, name);
332 return afb_hook_ditf_rename_api(export, oldname, name, result);
335 /**********************************************
337 **********************************************/
338 static const struct afb_daemon_itf daemon_itf = {
339 .vverbose_v1 = old_vverbose_cb,
340 .vverbose_v2 = vverbose_cb,
341 .event_make = event_make_cb,
342 .event_broadcast = event_broadcast_cb,
343 .get_event_loop = afb_common_get_event_loop,
344 .get_user_bus = afb_common_get_user_bus,
345 .get_system_bus = afb_common_get_system_bus,
346 .rootdir_get_fd = afb_common_rootdir_get_fd,
347 .rootdir_open_locale = rootdir_open_locale_cb,
348 .queue_job = queue_job_cb,
349 .unstore_req = unstore_req_cb,
350 .require_api = require_api_cb,
351 .rename_api = rename_api_cb
354 static const struct afb_daemon_itf hooked_daemon_itf = {
355 .vverbose_v1 = hooked_old_vverbose_cb,
356 .vverbose_v2 = hooked_vverbose_cb,
357 .event_make = hooked_event_make_cb,
358 .event_broadcast = hooked_event_broadcast_cb,
359 .get_event_loop = hooked_get_event_loop,
360 .get_user_bus = hooked_get_user_bus,
361 .get_system_bus = hooked_get_system_bus,
362 .rootdir_get_fd = hooked_rootdir_get_fd,
363 .rootdir_open_locale = hooked_rootdir_open_locale_cb,
364 .queue_job = hooked_queue_job_cb,
365 .unstore_req = hooked_unstore_req_cb,
366 .require_api = hooked_require_api_cb,
367 .rename_api = hooked_rename_api_cb
371 /*************************************************************************************************************
372 *************************************************************************************************************
373 *************************************************************************************************************
374 *************************************************************************************************************
376 *************************************************************************************************************
377 *************************************************************************************************************
378 *************************************************************************************************************
379 *************************************************************************************************************/
381 /* the common session for services sharing their session */
382 static struct afb_session *common_session;
384 /*************************************************************************************************************
385 *************************************************************************************************************
386 *************************************************************************************************************
387 *************************************************************************************************************
389 *************************************************************************************************************
390 *************************************************************************************************************
391 *************************************************************************************************************
392 *************************************************************************************************************/
395 * Structure for requests initiated by the service
399 struct afb_xreq xreq;
401 struct afb_export *export;
404 void (*callback)(void*, int, struct json_object*);
408 struct jobloop *jobloop;
409 struct json_object *result;
415 * destroys the call_req
417 static void callreq_destroy(struct afb_xreq *xreq)
419 struct call_req *callreq = CONTAINER_OF_XREQ(struct call_req, xreq);
421 afb_context_disconnect(&callreq->xreq.context);
422 json_object_put(callreq->xreq.json);
423 afb_cred_unref(callreq->xreq.cred);
427 static void callreq_reply(struct afb_xreq *xreq, int status, json_object *obj)
429 struct call_req *callreq = CONTAINER_OF_XREQ(struct call_req, xreq);
430 if (callreq->callback)
431 callreq->callback(callreq->closure, status, obj);
432 json_object_put(obj);
435 static void callreq_sync_leave(struct call_req *callreq)
437 struct jobloop *jobloop = callreq->jobloop;
440 callreq->jobloop = NULL;
445 static void callreq_reply_sync(struct afb_xreq *xreq, int status, json_object *obj)
447 struct call_req *callreq = CONTAINER_OF_XREQ(struct call_req, xreq);
448 callreq->status = status;
449 callreq->result = obj;
450 callreq_sync_leave(callreq);
453 static void callreq_sync_enter(int signum, void *closure, struct jobloop *jobloop)
455 struct call_req *callreq = closure;
458 callreq->jobloop = jobloop;
459 afb_xreq_process(&callreq->xreq, callreq->export->apiset);
461 callreq->result = afb_msg_json_internal_error();
462 callreq->status = -1;
463 callreq_sync_leave(callreq);
467 /* interface for requests of services */
468 const struct afb_xreq_query_itf afb_export_xreq_itf = {
469 .unref = callreq_destroy,
470 .reply = callreq_reply
473 /* interface for requests of services */
474 const struct afb_xreq_query_itf afb_export_xreq_sync_itf = {
475 .unref = callreq_destroy,
476 .reply = callreq_reply_sync
482 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)
484 struct call_req *callreq;
485 size_t lenapi, lenverb;
488 /* allocates the request */
489 lenapi = 1 + strlen(api);
490 lenverb = 1 + strlen(verb);
491 callreq = malloc(lenapi + lenverb + sizeof *callreq);
492 if (callreq != NULL) {
493 /* initialises the request */
494 afb_xreq_init(&callreq->xreq, itf);
495 afb_context_init(&callreq->xreq.context, export->session, NULL);
496 callreq->xreq.context.validated = 1;
497 copy = (char*)&callreq[1];
498 memcpy(copy, api, lenapi);
499 callreq->xreq.api = copy;
500 copy = ©[lenapi];
501 memcpy(copy, verb, lenverb);
502 callreq->xreq.verb = copy;
503 callreq->xreq.listener = export->listener;
504 callreq->xreq.json = args;
505 callreq->export = export;
511 * Initiates a call for the service
513 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)
515 struct afb_export *export = closure;
516 struct call_req *callreq;
517 struct json_object *ierr;
519 /* allocates the request */
520 callreq = callreq_create(export, api, verb, args, &afb_export_xreq_itf);
521 if (callreq == NULL) {
522 ERROR("out of memory");
523 json_object_put(args);
524 ierr = afb_msg_json_internal_error();
526 callback(cbclosure, -1, ierr);
527 json_object_put(ierr);
531 /* initialises the request */
532 callreq->jobloop = NULL;
533 callreq->callback = callback;
534 callreq->closure = cbclosure;
537 /* terminates and frees ressources if needed */
538 afb_xreq_process(&callreq->xreq, export->apiset);
541 static int svc_call_sync(void *closure, const char *api, const char *verb, struct json_object *args,
542 struct json_object **result)
544 struct afb_export *export = closure;
545 struct call_req *callreq;
546 struct json_object *resu;
549 /* allocates the request */
550 callreq = callreq_create(export, api, verb, args, &afb_export_xreq_sync_itf);
551 if (callreq == NULL) {
552 ERROR("out of memory");
554 json_object_put(args);
555 resu = afb_msg_json_internal_error();
558 /* initialises the request */
559 callreq->jobloop = NULL;
560 callreq->callback = NULL;
561 callreq->result = NULL;
564 afb_xreq_unhooked_addref(&callreq->xreq); /* avoid early callreq destruction */
565 rc = jobs_enter(NULL, 0, callreq_sync_enter, callreq);
567 rc = callreq->status;
568 resu = (rc >= 0 || callreq->result) ? callreq->result : afb_msg_json_internal_error();
569 afb_xreq_unhooked_unref(&callreq->xreq);
574 json_object_put(resu);
580 struct afb_export *export;
581 void (*callback)(void*, int, struct json_object*);
585 static void svc_hooked_call_result(void *closure, int status, struct json_object *result)
587 struct hooked_call *hc = closure;
588 afb_hook_svc_call_result(hc->export, status, result);
589 hc->callback(hc->cbclosure, status, result);
593 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)
595 struct afb_export *export = closure;
596 struct hooked_call *hc;
598 if (export->hooksvc & afb_hook_flag_svc_call)
599 afb_hook_svc_call(export, api, verb, args);
601 if (export->hooksvc & afb_hook_flag_svc_call_result) {
602 hc = malloc(sizeof *hc);
604 WARNING("allocation failed");
607 hc->callback = callback;
608 hc->cbclosure = cbclosure;
609 callback = svc_hooked_call_result;
613 svc_call(closure, api, verb, args, callback, cbclosure);
616 static int svc_hooked_call_sync(void *closure, const char *api, const char *verb, struct json_object *args,
617 struct json_object **result)
619 struct afb_export *export = closure;
620 struct json_object *resu;
623 if (export->hooksvc & afb_hook_flag_svc_callsync)
624 afb_hook_svc_callsync(export, api, verb, args);
626 rc = svc_call_sync(closure, api, verb, args, &resu);
628 if (export->hooksvc & afb_hook_flag_svc_callsync_result)
629 afb_hook_svc_callsync_result(export, rc, resu);
634 json_object_put(resu);
639 /* the interface for services */
640 static const struct afb_service_itf service_itf = {
642 .call_sync = svc_call_sync
645 /* the interface for services */
646 static const struct afb_service_itf hooked_service_itf = {
647 .call = svc_hooked_call,
648 .call_sync = svc_hooked_call_sync
651 /*************************************************************************************************************
652 *************************************************************************************************************
653 *************************************************************************************************************
654 *************************************************************************************************************
656 *************************************************************************************************************
657 *************************************************************************************************************
658 *************************************************************************************************************
659 *************************************************************************************************************/
662 * Propagates the event to the service
664 static void export_on_event_v12(void *closure, const char *event, int eventid, struct json_object *object)
666 struct afb_export *export = closure;
668 if (export->hooksvc & afb_hook_flag_svc_on_event_before)
669 afb_hook_svc_on_event_before(export, event, eventid, object);
670 export->on_event.v12(event, object);
671 if (export->hooksvc & afb_hook_flag_svc_on_event_after)
672 afb_hook_svc_on_event_after(export, event, eventid, object);
673 json_object_put(object);
676 /* the interface for events */
677 static const struct afb_evt_itf evt_v12_itf = {
678 .broadcast = export_on_event_v12,
679 .push = export_on_event_v12
682 /*************************************************************************************************************
683 *************************************************************************************************************
684 *************************************************************************************************************
685 *************************************************************************************************************
687 *************************************************************************************************************
688 *************************************************************************************************************
689 *************************************************************************************************************
690 *************************************************************************************************************/
692 static struct afb_export *create(struct afb_apiset *apiset, const char *apiname, enum afb_api_version version)
694 struct afb_export *export;
696 /* session shared with other exports */
697 if (common_session == NULL) {
698 common_session = afb_session_create (NULL, 0);
699 if (common_session == NULL)
702 export = calloc(1, sizeof *export);
706 memset(export, 0, sizeof *export);
707 export->apiname = strdup(apiname);
708 export->version = version;
709 export->state = Api_State_Pre_Init;
710 export->session = afb_session_addref(common_session);
711 export->apiset = afb_apiset_addref(apiset);
716 void afb_export_destroy(struct afb_export *export)
719 if (export->listener != NULL)
720 afb_evt_listener_unref(export->listener);
721 afb_session_unref(export->session);
722 afb_apiset_unref(export->apiset);
723 free(export->apiname);
728 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*))
730 struct afb_export *export = create(apiset, apiname, Api_Version_1);
732 export->init.v1 = init;
733 export->on_event.v12 = onevent;
734 export->export.v1.verbosity = verbosity;
735 export->export.v1.mode = AFB_MODE_LOCAL;
736 export->export.v1.daemon.closure = export;
737 afb_export_update_hook(export);
742 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*))
744 struct afb_export *export = create(apiset, apiname, Api_Version_2);
746 export->init.v2 = init;
747 export->on_event.v12 = onevent;
748 export->export.v2 = data;
749 data->verbosity = verbosity;
750 data->daemon.closure = export;
751 data->service.closure = export;
752 afb_export_update_hook(export);
757 void afb_export_rename(struct afb_export *export, const char *apiname)
759 free(export->apiname);
760 export->apiname = strdup(apiname);
761 afb_export_update_hook(export);
764 const char *afb_export_apiname(const struct afb_export *export)
766 return export->apiname;
769 void afb_export_update_hook(struct afb_export *export)
771 export->hookditf = afb_hook_flags_ditf(export->apiname);
772 export->hooksvc = afb_hook_flags_svc(export->apiname);
773 switch (export->version) {
775 export->export.v1.daemon.itf = export->hookditf ? &hooked_daemon_itf : &daemon_itf;
779 export->export.v2->daemon.itf = export->hookditf ? &hooked_daemon_itf : &daemon_itf;
780 export->export.v2->service.itf = export->hooksvc ? &hooked_service_itf : &service_itf;
785 struct afb_binding_interface_v1 *afb_export_get_interface_v1(struct afb_export *export)
787 return export->version == Api_Version_1 ? &export->export.v1 : NULL;
790 int afb_export_unshare_session(struct afb_export *export)
792 if (export->session == common_session) {
793 export->session = afb_session_create (NULL, 0);
795 afb_session_unref(common_session);
797 export->session = common_session;
804 void afb_export_set_apiset(struct afb_export *export, struct afb_apiset *apiset)
806 struct afb_apiset *prvset = export->apiset;
807 export->apiset = afb_apiset_addref(apiset);
808 afb_apiset_unref(prvset);
811 struct afb_apiset *afb_export_get_apiset(struct afb_export *export)
813 return export->apiset;
817 * Creates a new service
819 int afb_export_handle_events_v12(struct afb_export *export, void (*on_event)(const char *event, struct json_object *object))
822 switch (export->version) {
823 case Api_Version_1: case Api_Version_2: break;
825 ERROR("invalid version 12 for API %s", export->apiname);
830 /* set the event handler */
832 if (export->listener) {
833 afb_evt_listener_unref(export->listener);
834 export->listener = NULL;
836 export->on_event.v12 = on_event;
838 export->on_event.v12 = on_event;
839 if (!export->listener) {
840 export->listener = afb_evt_listener_create(&evt_v12_itf, export);
841 if (export->listener == NULL)
849 * Starts a new service (v1)
851 struct afb_binding_v1 *afb_export_register_v1(struct afb_export *export, struct afb_binding_v1 *(*regfun)(const struct afb_binding_interface_v1*))
853 return regfun(&export->export.v1);
856 int afb_export_verbosity_get(const struct afb_export *export)
858 switch (export->version) {
859 case Api_Version_1: return export->export.v1.verbosity;
860 case Api_Version_2: return export->export.v2->verbosity;
865 void afb_export_verbosity_set(struct afb_export *export, int level)
867 switch (export->version) {
868 case Api_Version_1: export->export.v1.verbosity = level; break;
869 case Api_Version_2: export->export.v2->verbosity = level; break;
873 /*************************************************************************************************************
874 *************************************************************************************************************
875 *************************************************************************************************************
876 *************************************************************************************************************
878 *************************************************************************************************************
879 *************************************************************************************************************
880 *************************************************************************************************************
881 *************************************************************************************************************/
883 int afb_export_start(struct afb_export *export, int share_session, int onneed, struct afb_apiset *apiset)
888 if (export->state != Api_State_Pre_Init) {
889 /* not an error when onneed */
893 /* already started: it is an error */
894 ERROR("Service of API %s already started", export->apiname);
898 /* unshare the session if asked */
899 if (!share_session) {
900 rc = afb_export_unshare_session(export);
902 ERROR("Can't unshare the session for %s", export->apiname);
907 /* set event handling */
908 switch (export->version) {
911 rc = afb_export_handle_events_v12(export, export->on_event.v12);
918 ERROR("Can't set event handler for %s", export->apiname);
922 /* Starts the service */
923 if (export->hooksvc & afb_hook_flag_svc_start_before)
924 afb_hook_svc_start_before(export);
925 export->state = Api_State_Init;
926 switch (export->version) {
928 rc = export->init.v1 ? export->init.v1((struct afb_service){ .itf = &hooked_service_itf, .closure = export }) : 0;
931 rc = export->init.v2 ? export->init.v2() : 0;
936 export->state = Api_State_Run;
937 if (export->hooksvc & afb_hook_flag_svc_start_after)
938 afb_hook_svc_start_after(export, rc);
940 /* initialisation error */
941 ERROR("Initialisation of service API %s failed (%d): %m", export->apiname, rc);