fix binding version to 1
[src/app-framework-main.git] / src / afm-main-binding.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
18 #define _GNU_SOURCE         /* See feature_test_macros(7) */
19 #include <stdio.h>
20 #include <string.h>
21 #include <assert.h>
22 #include <json-c/json.h>
23
24 #define AFB_BINDING_VERSION 1
25 #include <afb/afb-binding.h>
26
27 #include "utils-jbus.h"
28
29 static const char _added_[]     = "added";
30 static const char _auto_[]      = "auto";
31 static const char _continue_[]  = "continue";
32 static const char _changed_[]   = "changed";
33 static const char _detail_[]    = "detail";
34 static const char _id_[]        = "id";
35 static const char _install_[]   = "install";
36 static const char _local_[]     = "local";
37 static const char _mode_[]      = "mode";
38 static const char _once_[]      = "once";
39 static const char _pause_[]     = "pause";
40 static const char _remote_[]    = "remote";
41 static const char _resume_[]    = "resume";
42 static const char _runid_[]     = "runid";
43 static const char _runnables_[] = "runnables";
44 static const char _runners_[]   = "runners";
45 static const char _start_[]     = "start";
46 static const char _state_[]     = "state";
47 static const char _stop_[]      = "stop";
48 static const char _terminate_[] = "terminate";
49 static const char _uninstall_[] = "uninstall";
50
51 static const struct afb_binding_interface *binder;
52
53 static struct jbus *jbus;
54
55 /*
56  * Structure for asynchronous call handling
57  */
58 struct memo
59 {
60         struct afb_req request; /* the recorded request */
61         const char *method;     /* the called method */
62 };
63
64 /*
65  * Creates the memo for the 'request' and the 'method'.
66  * Returns the memo in case of success.
67  * In case of error, send a failure answer and returns NULL.
68  */
69 static struct memo *memo_create(struct afb_req request, const char *method)
70 {
71         struct memo *memo = malloc(sizeof *memo);
72         if (memo == NULL)
73                 afb_req_fail(request, "failed", "out of memory");
74         else {
75                 memo->request = request;
76                 memo->method = method;
77                 afb_req_addref(request);
78         }
79         return memo;
80 }
81
82 /*
83  * Sends the asynchronous failed reply to the request recorded by 'memo'
84  * Then fress the resources.
85  */
86 static void memo_fail(struct memo *memo, const char *info)
87 {
88         afb_req_fail(memo->request, "failed", info);
89         afb_req_unref(memo->request);
90         free(memo);
91 }
92
93 /*
94  * Sends the asynchronous success reply to the request recorded by 'memo'
95  * Then fress the resources.
96  */
97 static void memo_success(struct memo *memo, struct json_object *obj, const char *info)
98 {
99         afb_req_success(memo->request, obj, info);
100         afb_req_unref(memo->request);
101         free(memo);
102 }
103
104 /*
105  * Broadcast the event "application-list-changed".
106  * This event is sent was the event "changed" is received from dbus.
107  */
108 static void application_list_changed(const char *data, void *closure)
109 {
110         afb_daemon_broadcast_event(binder->daemon, "application-list-changed", NULL);
111 }
112
113 /*
114  * Builds if possible the json object having one field of name 'tag'
115  * whose value is 'obj' like here: { tag: obj } and returns it.
116  * In case of error or when 'tag'==NULL or 'obj'==NULL, 'obj' is returned.
117  * The reference count of 'obj' is not incremented.
118  */
119 static struct json_object *embed(const char *tag, struct json_object *obj)
120 {
121         struct json_object *result;
122
123         if (obj == NULL || tag == NULL)
124                 result = obj;
125         else {
126                 result = json_object_new_object();
127                 if (result == NULL) {
128                         /* can't embed */
129                         result = obj;
130                 }
131                 else {
132                         /* TODO why is json-c not returning a status? */
133                         json_object_object_add(result, tag, obj);
134                 }
135         }
136         return result;
137 }
138
139 /*
140  * Callback for replies made by 'embed_call_void'.
141  */
142 static void embed_call_void_callback(int iserror, struct json_object *obj, struct memo *memo)
143 {
144         DEBUG(binder, "(afm-main-binding) %s(true) -> %s\n", memo->method,
145                         obj ? json_object_to_json_string(obj) : "NULL");
146
147         if (iserror) {
148                 memo_fail(memo, obj ? json_object_get_string(obj) : "framework daemon failure");
149         } else {
150                 memo_success(memo, embed(memo->method, json_object_get(obj)), NULL);
151         }
152 }
153
154 /*
155  * Calls with DBus the 'method' of the user daemon without arguments.
156  */
157 static void embed_call_void(struct afb_req request, const char *method)
158 {
159         struct memo *memo;
160
161         /* store the request */
162         memo = memo_create(request, method);
163         if (memo == NULL)
164                 return;
165
166         if (jbus_call_sj(jbus, method, "true", (void*)embed_call_void_callback, memo) < 0)
167                 memo_fail(memo, "dbus failure");
168 }
169
170 /*
171  * Callback for replies made by 'call_appid' and 'call_runid'.
172  */
173 static void call_xxxid_callback(int iserror, struct json_object *obj, struct memo *memo)
174 {
175         DEBUG(binder, "(afm-main-binding) %s -> %s\n", memo->method, 
176                         obj ? json_object_to_json_string(obj) : "NULL");
177
178         if (iserror) {
179                 memo_fail(memo, obj ? json_object_get_string(obj) : "framework daemon failure");
180         } else {
181                 memo_success(memo, json_object_get(obj), NULL);
182         }
183 }
184
185 /*
186  * Calls with DBus the 'method' of the user daemon with the argument "id".
187  */
188 static void call_appid(struct afb_req request, const char *method)
189 {
190         struct memo *memo;
191         char *sid;
192         const char *id;
193
194         id = afb_req_value(request, _id_);
195         if (id == NULL) {
196                 afb_req_fail(request, "bad-request", "missing 'id'");
197                 return;
198         }
199
200         memo = memo_create(request, method);
201         if (memo == NULL)
202                 return;
203
204         if (asprintf(&sid, "\"%s\"", id) <= 0) {
205                 memo_fail(memo, "out of memory");
206                 return;
207         }
208
209         if (jbus_call_sj(jbus, method, sid, (void*)call_xxxid_callback, memo) < 0)
210                 memo_fail(memo, "dbus failure");
211
212         free(sid);
213 }
214
215 static void call_runid(struct afb_req request, const char *method)
216 {
217         struct memo *memo;
218         const char *id;
219
220         id = afb_req_value(request, _runid_);
221         if (id == NULL) {
222                 afb_req_fail(request, "bad-request", "missing 'runid'");
223                 return;
224         }
225
226         memo = memo_create(request, method);
227         if (memo == NULL)
228                 return;
229
230         if (jbus_call_sj(jbus, method, id, (void*)call_xxxid_callback, memo) < 0)
231                 memo_fail(memo, "dbus failure");
232 }
233
234 /************************** entries ******************************/
235
236 static void runnables(struct afb_req request)
237 {
238         embed_call_void(request, _runnables_);
239 }
240
241 static void detail(struct afb_req request)
242 {
243         call_appid(request, _detail_);
244 }
245
246 static void start_callback(int iserror, struct json_object *obj, struct memo *memo)
247 {
248         DEBUG(binder, "(afm-main-binding) %s -> %s\n", memo->method, 
249                         obj ? json_object_to_json_string(obj) : "NULL");
250
251         if (iserror) {
252                 memo_fail(memo, obj ? json_object_get_string(obj) : "framework daemon failure");
253         } else {
254                 obj = json_object_get(obj);
255                 if (json_object_get_type(obj) == json_type_int)
256                         obj = embed(_runid_, obj);
257                 memo_success(memo, obj, NULL);
258         }
259 }
260
261 static void start(struct afb_req request)
262 {
263         struct memo *memo;
264         const char *id, *mode;
265         char *query;
266         int rc;
267
268         /* get the id */
269         id = afb_req_value(request, _id_);
270         if (id == NULL) {
271                 afb_req_fail(request, "bad-request", "missing 'id'");
272                 return;
273         }
274
275         /* get the mode */
276         mode = afb_req_value(request, _mode_);
277         if (mode == NULL || !strcmp(mode, _auto_)) {
278                 mode = binder->mode == AFB_MODE_REMOTE ? _remote_ : _local_;
279         }
280
281         /* prepares asynchronous request */
282         memo = memo_create(request, _start_);
283         if (memo == NULL)
284                 return;
285
286         /* create the query */
287         rc = asprintf(&query, "{\"id\":\"%s\",\"mode\":\"%s\"}", id, mode);
288         if (rc < 0) {
289                 memo_fail(memo, "out of memory");
290                 return;
291         }
292
293         /* calls the service asynchronously */
294         if (jbus_call_sj(jbus, _start_, query, (void*)start_callback, memo) < 0)
295                 memo_fail(memo, "dbus failure");
296         free(query);
297 }
298
299 static void once(struct afb_req request)
300 {
301         call_appid(request, _once_);
302 }
303
304 static void terminate(struct afb_req request)
305 {
306         call_runid(request, _terminate_);
307 }
308
309 static void pause(struct afb_req request)
310 {
311         call_runid(request, _pause_);
312 }
313
314 static void resume(struct afb_req request)
315 {
316         call_runid(request, _resume_);
317 }
318
319 static void runners(struct afb_req request)
320 {
321         embed_call_void(request, _runners_);
322 }
323
324 static void state(struct afb_req request)
325 {
326         call_runid(request, _state_);
327 }
328
329 static void install_callback(int iserror, struct json_object *obj, struct memo *memo)
330 {
331         struct json_object *added;
332
333         if (iserror) {
334                 memo_fail(memo, obj ? json_object_get_string(obj) : "framework daemon failure");
335         } else {
336                 if (json_object_object_get_ex(obj, _added_, &added))
337                         obj = added;
338                 obj = json_object_get(obj);
339                 obj = embed(_id_, obj);
340                 memo_success(memo, obj, NULL);
341         }
342 }
343 static void install(struct afb_req request)
344 {
345         struct memo *memo;
346         char *query;
347         const char *filename;
348         struct afb_arg arg;
349
350         /* get the argument */
351         arg = afb_req_get(request, "widget");
352         filename = arg.path;
353         if (filename == NULL) {
354                 afb_req_fail(request, "bad-request", "missing 'widget' file");
355                 return;
356         }
357
358         /* prepares asynchronous request */
359         memo = memo_create(request, _install_);
360         if (memo == NULL)
361                 return;
362
363         /* makes the query */
364         if (0 >= asprintf(&query, "\"%s\"", filename)) {
365                 afb_req_fail(request, "server-error", "out of memory");
366                 return;
367         }
368
369         /* calls the service asynchronously */
370         if (jbus_call_sj(jbus, _install_, query, (void*)install_callback, memo) < 0)
371                 memo_fail(memo, "dbus failure");
372         free(query);
373 }
374
375 static void uninstall(struct afb_req request)
376 {
377         call_appid(request, _uninstall_);
378 }
379
380 static const struct afb_verb_desc_v1 verbs[] =
381 {
382         {_runnables_, AFB_SESSION_CHECK, runnables,  "Get list of runnable applications"},
383         {_detail_   , AFB_SESSION_CHECK, detail, "Get the details for one application"},
384         {_start_    , AFB_SESSION_CHECK, start, "Start an application"},
385         {_once_     , AFB_SESSION_CHECK, once, "Start once an application"},
386         {_terminate_, AFB_SESSION_CHECK, terminate, "Terminate a running application"},
387         {_pause_    , AFB_SESSION_CHECK, pause, "Pause a running application"},
388         {_resume_   , AFB_SESSION_CHECK, resume, "Resume a paused application"},
389         {_stop_     , AFB_SESSION_CHECK, pause, "Obsolete since 2016/11/08, use 'pause' instead"},
390         {_continue_ , AFB_SESSION_CHECK, resume, "Obsolete since 2016/11/08, use 'resume' instead"},
391         {_runners_  , AFB_SESSION_CHECK, runners,  "Get the list of running applications"},
392         {_state_    , AFB_SESSION_CHECK, state, "Get the state of a running application"},
393         {_install_  , AFB_SESSION_CHECK, install,  "Install an application using a widget file"},
394         {_uninstall_, AFB_SESSION_CHECK, uninstall, "Uninstall an application"},
395         { NULL, 0, NULL, NULL }
396 };
397
398 static const struct afb_binding plug_desc = {
399         .type = AFB_BINDING_VERSION_1,
400         .v1 = {
401                 .info = "Application Framework Master Service",
402                 .prefix = "afm-main",
403                 .verbs = verbs
404         }
405 };
406
407 const struct afb_binding *afbBindingV1Register(const struct afb_binding_interface *itf)
408 {
409         int rc;
410         struct sd_bus *sbus;
411
412         /* records the interface */
413         assert (binder == NULL);
414         binder = itf;
415
416         /* creates the jbus for accessing afm-user-daemon */
417         sbus = afb_daemon_get_user_bus(itf->daemon);
418         if (sbus == NULL)
419                 return NULL;
420         jbus = create_jbus(sbus, "/org/AGL/afm/user");
421         if (jbus == NULL)
422                 return NULL;
423
424         /* records the signal handler */
425         rc = jbus_on_signal_s(jbus, _changed_, application_list_changed, NULL);
426         if (rc < 0) {
427                 jbus_unref(jbus);
428                 return NULL;
429         }
430
431         return &plug_desc;
432 }
433