Bindings V2: migration of HelloWorld binder
[src/app-framework-binder.git] / bindings / samples / HelloWorld.c
1 /*
2  * Copyright (C) 2015, 2016, 2017 "IoT.bzh"
3  * Author "Fulup Ar Foll"
4  *
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
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  */
17 #define _GNU_SOURCE
18 #include <stdio.h>
19 #include <string.h>
20 #include <pthread.h>
21
22 #include <json-c/json.h>
23
24 #define AFB_BINDING_VERSION 2
25 #include <afb/afb-binding.h>
26
27 const struct afb_binding_interface *interface;
28 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
29
30 struct event
31 {
32         struct event *next;
33         struct afb_event event;
34         char tag[1];
35 };
36
37 static struct event *events = 0;
38
39 /* searchs the event of tag */
40 static struct event *event_get(const char *tag)
41 {
42         struct event *e = events;
43         while(e && strcmp(e->tag, tag))
44                 e = e->next;
45         return e;
46 }
47
48 /* deletes the event of tag */
49 static int event_del(const char *tag)
50 {
51         struct event *e, **p;
52
53         /* check exists */
54         e = event_get(tag);
55         if (!e) return -1;
56
57         /* unlink */
58         p = &events;
59         while(*p != e) p = &(*p)->next;
60         *p = e->next;
61
62         /* destroys */
63         afb_event_drop(e->event);
64         free(e);
65         return 0;
66 }
67
68 /* creates the event of tag */
69 static int event_add(const char *tag, const char *name)
70 {
71         struct event *e;
72
73         /* check valid tag */
74         e = event_get(tag);
75         if (e) return -1;
76
77         /* creation */
78         e = malloc(strlen(tag) + sizeof *e);
79         if (!e) return -1;
80         strcpy(e->tag, tag);
81
82         /* make the event */
83         e->event = afb_daemon_make_event(name);
84         if (!e->event.closure) { free(e); return -1; }
85
86         /* link */
87         e->next = events;
88         events = e;
89         return 0;
90 }
91
92 static int event_subscribe(struct afb_req request, const char *tag)
93 {
94         struct event *e;
95         e = event_get(tag);
96         return e ? afb_req_subscribe(request, e->event) : -1;
97 }
98
99 static int event_unsubscribe(struct afb_req request, const char *tag)
100 {
101         struct event *e;
102         e = event_get(tag);
103         return e ? afb_req_unsubscribe(request, e->event) : -1;
104 }
105
106 static int event_push(struct json_object *args, const char *tag)
107 {
108         struct event *e;
109         e = event_get(tag);
110         return e ? afb_event_push(e->event, json_object_get(args)) : -1;
111 }
112
113 // Sample Generic Ping Debug API
114 static void ping(struct afb_req request, json_object *jresp, const char *tag)
115 {
116         static int pingcount = 0;
117         json_object *query = afb_req_json(request);
118         afb_req_success_f(request, jresp, "Ping Binder Daemon tag=%s count=%d query=%s", tag, ++pingcount, json_object_to_json_string(query));
119 }
120
121 static void pingSample (struct afb_req request)
122 {
123         ping(request, json_object_new_string ("Some String"), "pingSample");
124 }
125
126 static void pingFail (struct afb_req request)
127 {
128         afb_req_fail(request, "failed", "Ping Binder Daemon fails");
129 }
130
131 static void pingNull (struct afb_req request)
132 {
133         ping(request, NULL, "pingNull");
134 }
135
136 static void pingBug (struct afb_req request)
137 {
138         ping((struct afb_req){NULL,NULL}, NULL, "pingBug");
139 }
140
141 static void pingEvent(struct afb_req request)
142 {
143         json_object *query = afb_req_json(request);
144         afb_daemon_broadcast_event("event", json_object_get(query));
145         ping(request, json_object_get(query), "event");
146 }
147
148
149 // For samples https://linuxprograms.wordpress.com/2010/05/20/json-c-libjson-tutorial/
150 static void pingJson (struct afb_req request) {
151     json_object *jresp, *embed;
152
153     jresp = json_object_new_object();
154     json_object_object_add(jresp, "myString", json_object_new_string ("Some String"));
155     json_object_object_add(jresp, "myInt", json_object_new_int (1234));
156
157     embed  = json_object_new_object();
158     json_object_object_add(embed, "subObjString", json_object_new_string ("Some String"));
159     json_object_object_add(embed, "subObjInt", json_object_new_int (5678));
160
161     json_object_object_add(jresp,"eobj", embed);
162
163     ping(request, jresp, "pingJson");
164 }
165
166 static void subcallcb (void *prequest, int iserror, json_object *object)
167 {
168         struct afb_req request = afb_req_unstore(prequest);
169         if (iserror)
170                 afb_req_fail(request, "failed", json_object_to_json_string(object));
171         else
172                 afb_req_success(request, json_object_get(object), NULL);
173         afb_req_unref(request);
174 }
175
176 static void subcall (struct afb_req request)
177 {
178         const char *api = afb_req_value(request, "api");
179         const char *verb = afb_req_value(request, "verb");
180         const char *args = afb_req_value(request, "args");
181         json_object *object = api && verb && args ? json_tokener_parse(args) : NULL;
182
183         if (object == NULL)
184                 afb_req_fail(request, "failed", "bad arguments");
185         else {
186                 afb_req_subcall(request, api, verb, object, subcallcb, afb_req_store(request));
187                 json_object_put(object);
188         }
189 }
190
191 static void subcallsync (struct afb_req request)
192 {
193         int rc;
194         const char *api = afb_req_value(request, "api");
195         const char *verb = afb_req_value(request, "verb");
196         const char *args = afb_req_value(request, "args");
197         json_object *result, *object = api && verb && args ? json_tokener_parse(args) : NULL;
198
199         if (object == NULL)
200                 afb_req_fail(request, "failed", "bad arguments");
201         else {
202                 rc = afb_req_subcall_sync(request, api, verb, object, &result);
203                 if (rc) {
204                         afb_req_success(request, result, NULL);
205                 } else {
206                         afb_req_fail(request, "failed", json_object_to_json_string(result));
207                         json_object_put(result);
208                 }
209                 json_object_put(object);
210         }
211 }
212
213 static void eventadd (struct afb_req request)
214 {
215         const char *tag = afb_req_value(request, "tag");
216         const char *name = afb_req_value(request, "name");
217
218         pthread_mutex_lock(&mutex);
219         if (tag == NULL || name == NULL)
220                 afb_req_fail(request, "failed", "bad arguments");
221         else if (0 != event_add(tag, name))
222                 afb_req_fail(request, "failed", "creation error");
223         else
224                 afb_req_success(request, NULL, NULL);
225         pthread_mutex_unlock(&mutex);
226 }
227
228 static void eventdel (struct afb_req request)
229 {
230         const char *tag = afb_req_value(request, "tag");
231
232         pthread_mutex_lock(&mutex);
233         if (tag == NULL)
234                 afb_req_fail(request, "failed", "bad arguments");
235         else if (0 != event_del(tag))
236                 afb_req_fail(request, "failed", "deletion error");
237         else
238                 afb_req_success(request, NULL, NULL);
239         pthread_mutex_unlock(&mutex);
240 }
241
242 static void eventsub (struct afb_req request)
243 {
244         const char *tag = afb_req_value(request, "tag");
245
246         pthread_mutex_lock(&mutex);
247         if (tag == NULL)
248                 afb_req_fail(request, "failed", "bad arguments");
249         else if (0 != event_subscribe(request, tag))
250                 afb_req_fail(request, "failed", "subscription error");
251         else
252                 afb_req_success(request, NULL, NULL);
253         pthread_mutex_unlock(&mutex);
254 }
255
256 static void eventunsub (struct afb_req request)
257 {
258         const char *tag = afb_req_value(request, "tag");
259
260         pthread_mutex_lock(&mutex);
261         if (tag == NULL)
262                 afb_req_fail(request, "failed", "bad arguments");
263         else if (0 != event_unsubscribe(request, tag))
264                 afb_req_fail(request, "failed", "unsubscription error");
265         else
266                 afb_req_success(request, NULL, NULL);
267         pthread_mutex_unlock(&mutex);
268 }
269
270 static void eventpush (struct afb_req request)
271 {
272         const char *tag = afb_req_value(request, "tag");
273         const char *data = afb_req_value(request, "data");
274         json_object *object = data ? json_tokener_parse(data) : NULL;
275
276         pthread_mutex_lock(&mutex);
277         if (tag == NULL)
278                 afb_req_fail(request, "failed", "bad arguments");
279         else if (0 > event_push(object, tag))
280                 afb_req_fail(request, "failed", "push error");
281         else
282                 afb_req_success(request, NULL, NULL);
283         pthread_mutex_unlock(&mutex);
284         json_object_put(object);
285 }
286
287 static void exitnow (struct afb_req request)
288 {
289         exit(0);
290 }
291
292 static int preinit()
293 {
294         NOTICE("hello binding comes to live");
295         return 0;
296 }
297
298 static int init()
299 {
300         NOTICE("hello binding starting");
301         return 0;
302 }
303
304 // NOTE: this sample does not use session to keep test a basic as possible
305 //       in real application most APIs should be protected with AFB_SESSION_CHECK
306 static const struct afb_verb_v2 verbs[]= {
307   { "ping"     ,    pingSample , NULL, AFB_SESSION_NONE },
308   { "pingfail" ,    pingFail   , NULL, AFB_SESSION_NONE },
309   { "pingnull" ,    pingNull   , NULL, AFB_SESSION_NONE },
310   { "pingbug"  ,    pingBug    , NULL, AFB_SESSION_NONE },
311   { "pingJson" ,    pingJson   , NULL, AFB_SESSION_NONE },
312   { "pingevent",    pingEvent  , NULL, AFB_SESSION_NONE },
313   { "subcall",      subcall    , NULL, AFB_SESSION_NONE },
314   { "subcallsync",  subcallsync, NULL, AFB_SESSION_NONE },
315   { "eventadd",     eventadd   , NULL, AFB_SESSION_NONE },
316   { "eventdel",     eventdel   , NULL, AFB_SESSION_NONE },
317   { "eventsub",     eventsub   , NULL, AFB_SESSION_NONE },
318   { "eventunsub",   eventunsub , NULL, AFB_SESSION_NONE },
319   { "eventpush",    eventpush  , NULL, AFB_SESSION_NONE },
320   { "exit",         exitnow    , NULL, AFB_SESSION_NONE },
321   { NULL}
322 };
323
324 const struct afb_binding_v2 afbBindingV2 = {
325         .api = "hello",
326         .specification = NULL,
327         .verbs = verbs,
328         .preinit = preinit,
329         .init = init
330 };
331