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>
29 #include "afb-session.h"
30 #include "afb-context.h"
32 #include "afb-msg-json.h"
36 #include "afb-apiset.h"
42 #define HOOK(x,...) if((svc)->hookflags & afb_hook_flag_svc_##x) afb_hook_svc_##x(__VA_ARGS__)
45 * Structure for requests initiated by the service
54 void (*callback)(void*, int, struct json_object*);
58 struct jobloop *jobloop;
59 struct json_object *result;
63 /* functions for services */
64 static void svc_on_event(void *closure, const char *event, int eventid, struct json_object *object);
65 static void svc_call(void *closure, const char *api, const char *verb, struct json_object *args,
66 void (*callback)(void*, int, struct json_object*), void *cbclosure);
67 static int svc_call_sync(void *closure, const char *api, const char *verb, struct json_object *args,
68 struct json_object **result);
70 /* the interface for services */
71 static const struct afb_service_itf service_itf = {
73 .call_sync = svc_call_sync
76 /* the interface for events */
77 static const struct afb_evt_itf evt_itf = {
78 .broadcast = svc_on_event,
82 /* functions for requests of services */
83 static void svcreq_destroy(struct afb_xreq *xreq);
84 static void svcreq_reply(struct afb_xreq *xreq, int iserror, json_object *obj);
86 /* interface for requests of services */
87 const struct afb_xreq_query_itf afb_svc_xreq_itf = {
88 .unref = svcreq_destroy,
92 /* the common session for services sharing their session */
93 static struct afb_session *common_session;
95 static inline struct afb_service to_afb_service(struct afb_svc *svc)
97 return (struct afb_service){ .itf = &service_itf, .closure = svc };
103 static void svc_free(struct afb_svc *svc)
105 if (svc->listener != NULL)
106 afb_evt_listener_unref(svc->listener);
108 afb_session_unref(svc->session);
109 afb_apiset_unref(svc->apiset);
114 * Allocates a new service
116 static struct afb_svc *afb_svc_alloc(
118 struct afb_apiset *apiset,
124 /* allocates the svc handler */
125 svc = calloc(1, sizeof * svc);
131 /* instanciate the apiset */
133 svc->apiset = afb_apiset_addref(apiset);
135 /* instanciate the session */
137 /* session shared with other svcs */
138 if (common_session == NULL) {
139 common_session = afb_session_create (NULL, 0);
140 if (common_session == NULL)
143 svc->session = afb_session_addref(common_session);
145 /* session dedicated to the svc */
146 svc->session = afb_session_create (NULL, 0);
147 if (svc->session == NULL)
151 svc->hookflags = afb_hook_flags_svc(svc->api);
160 * Creates a new service
162 struct afb_svc *afb_svc_create_v1(
164 struct afb_apiset *apiset,
166 int (*start)(struct afb_service service),
167 void (*on_event)(const char *event, struct json_object *object)
173 /* allocates the svc handler */
174 svc = afb_svc_alloc(api, apiset, share_session);
178 /* initialises the listener if needed */
180 svc->on_event = on_event;
181 svc->listener = afb_evt_listener_create(&evt_itf, svc);
182 if (svc->listener == NULL)
186 /* initialises the svc now */
188 HOOK(start_before, svc);
189 rc = start(to_afb_service(svc));
190 HOOK(start_after, svc, rc);
203 * Creates a new service
205 struct afb_svc *afb_svc_create_v2(
207 struct afb_apiset *apiset,
210 void (*on_event)(const char *event, struct json_object *object),
211 struct afb_binding_data_v2 *data
217 /* allocates the svc handler */
218 svc = afb_svc_alloc(api, apiset, share_session);
221 data->service = to_afb_service(svc);
223 /* initialises the listener if needed */
225 svc->on_event = on_event;
226 svc->listener = afb_evt_listener_create(&evt_itf, svc);
227 if (svc->listener == NULL)
231 /* starts the svc if needed */
233 HOOK(start_before, svc);
235 HOOK(start_after, svc, rc);
247 void afb_svc_update_hook(struct afb_svc *svc)
249 svc->hookflags = afb_hook_flags_svc(svc->api);
253 * Propagates the event to the service
255 static void svc_on_event(void *closure, const char *event, int eventid, struct json_object *object)
257 struct afb_svc *svc = closure;
259 HOOK(on_event_before, svc, event, eventid, object);
260 svc->on_event(event, object);
261 HOOK(on_event_after, svc, event, eventid, object);
262 json_object_put(object);
268 static struct svc_req *svcreq_create(struct afb_svc *svc, const char *api, const char *verb, struct json_object *args)
270 struct svc_req *svcreq;
271 size_t lenapi, lenverb;
274 /* allocates the request */
275 lenapi = 1 + strlen(api);
276 lenverb = 1 + strlen(verb);
277 svcreq = malloc(lenapi + lenverb + sizeof *svcreq);
278 if (svcreq != NULL) {
279 /* initialises the request */
280 afb_xreq_init(&svcreq->xreq, &afb_svc_xreq_itf);
281 afb_context_init(&svcreq->xreq.context, svc->session, NULL);
282 svcreq->xreq.context.validated = 1;
283 copy = (char*)&svcreq[1];
284 memcpy(copy, api, lenapi);
285 svcreq->xreq.api = copy;
286 copy = ©[lenapi];
287 memcpy(copy, verb, lenverb);
288 svcreq->xreq.verb = copy;
289 svcreq->xreq.listener = svc->listener;
290 svcreq->xreq.json = args;
297 * destroys the svc_req
299 static void svcreq_destroy(struct afb_xreq *xreq)
301 struct svc_req *svcreq = CONTAINER_OF_XREQ(struct svc_req, xreq);
303 afb_context_disconnect(&svcreq->xreq.context);
304 json_object_put(svcreq->xreq.json);
305 afb_cred_unref(svcreq->xreq.cred);
309 static void svcreq_sync_leave(struct svc_req *svcreq)
311 struct jobloop *jobloop = svcreq->jobloop;
314 svcreq->jobloop = NULL;
319 static void svcreq_reply(struct afb_xreq *xreq, int iserror, json_object *obj)
321 struct svc_req *svcreq = CONTAINER_OF_XREQ(struct svc_req, xreq);
322 if (svcreq->callback) {
323 struct afb_svc *svc = svcreq->svc;
324 svcreq->callback(svcreq->closure, iserror, obj);
325 HOOK(call_result, svc, iserror, obj);
326 json_object_put(obj);
328 svcreq->iserror = iserror;
329 svcreq->result = obj;
330 svcreq_sync_leave(svcreq);
334 static void svcreq_sync_enter(int signum, void *closure, struct jobloop *jobloop)
336 struct svc_req *svcreq = closure;
339 svcreq->jobloop = jobloop;
340 afb_xreq_process(&svcreq->xreq, svcreq->svc->apiset);
342 svcreq->result = afb_msg_json_internal_error();
344 svcreq_sync_leave(svcreq);
349 * Initiates a call for the service
351 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)
353 struct afb_svc *svc = closure;
354 struct svc_req *svcreq;
355 struct json_object *ierr;
357 HOOK(call, svc, api, verb, args);
359 /* allocates the request */
360 svcreq = svcreq_create(svc, api, verb, args);
361 if (svcreq == NULL) {
362 ERROR("out of memory");
363 json_object_put(args);
364 ierr = afb_msg_json_internal_error();
365 callback(cbclosure, 1, ierr);
366 HOOK(call_result, svc, 1, ierr);
367 json_object_put(ierr);
371 /* initialises the request */
372 svcreq->jobloop = NULL;
373 svcreq->callback = callback;
374 svcreq->closure = cbclosure;
376 /* terminates and frees ressources if needed */
377 afb_xreq_process(&svcreq->xreq, svc->apiset);
380 static int svc_call_sync(void *closure, const char *api, const char *verb, struct json_object *args,
381 struct json_object **result)
383 struct afb_svc *svc = closure;
384 struct svc_req *svcreq;
387 HOOK(callsync, svc, api, verb, args);
389 /* allocates the request */
390 svcreq = svcreq_create(svc, api, verb, args);
391 if (svcreq == NULL) {
392 ERROR("out of memory");
394 json_object_put(args);
395 *result = afb_msg_json_internal_error();
398 /* initialises the request */
399 svcreq->jobloop = NULL;
400 svcreq->callback = NULL;
401 svcreq->result = NULL;
403 afb_xreq_addref(&svcreq->xreq);
404 rc = jobs_enter(NULL, 0, svcreq_sync_enter, svcreq);
405 rc = rc >= 0 && !svcreq->iserror;
406 *result = (rc || svcreq->result) ? svcreq->result : afb_msg_json_internal_error();
407 afb_xreq_unref(&svcreq->xreq);
409 HOOK(callsync_result, svc, rc, *result);