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