51316e750c20fbbfaaad332c8406de01667b61a4
[src/app-framework-main.git] / src / afm-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
23 #include <json-c/json.h>
24
25 #define AFB_BINDING_VERSION 2
26 #include <afb/afb-binding.h>
27
28 #include "verbose.h"
29 #include "utils-systemd.h"
30 #include "afm-udb.h"
31 #include "afm-urun.h"
32 #include "wgt-info.h"
33 #include "wgtpkg-install.h"
34 #include "wgtpkg-uninstall.h"
35 #include "wrap-json.h"
36
37 static const char _added_[]     = "added";
38 static const char _a_l_c_[]     = "application-list-changed";
39 static const char _detail_[]    = "detail";
40 static const char _id_[]        = "id";
41 static const char _install_[]   = "install";
42 static const char _not_found_[] = "not-found";
43 static const char _once_[]      = "once";
44 static const char _pause_[]     = "pause";
45 static const char _resume_[]    = "resume";
46 static const char _runid_[]     = "runid";
47 static const char _runnables_[] = "runnables";
48 static const char _runners_[]   = "runners";
49 static const char _start_[]     = "start";
50 static const char _state_[]     = "state";
51 static const char _terminate_[] = "terminate";
52 static const char _uninstall_[] = "uninstall";
53
54 static const char *rootdir = FWK_APP_DIR;
55 static struct afb_event applist_changed_event;
56 static struct afm_udb *afudb;
57 static struct json_object *json_true;
58
59 static void do_reloads()
60 {
61         /* enforce daemon reload */
62         systemd_daemon_reload(0);
63         systemd_unit_restart_name(0, "sockets.target");
64 }
65
66 static void bad_request(struct afb_req req)
67 {
68         afb_req_fail(req, "bad-request", NULL);
69 }
70
71 static void not_found(struct afb_req req)
72 {
73         afb_req_fail(req, _not_found_, NULL);
74 }
75
76 static void cant_start(struct afb_req req)
77 {
78         afb_req_fail(req, "cannot-start", NULL);
79 }
80
81 /*
82  * Broadcast the event "application-list-changed".
83  * This event is sent was the event "changed" is received from dbus.
84  */
85 static void application_list_changed(const char *operation, const char *data)
86 {
87         struct json_object *e = NULL;
88         wrap_json_pack(&e, "{ss ss}", "operation", operation, "data", data);
89         afb_event_broadcast(applist_changed_event, e);
90 }
91
92 /*
93  * retrieves the 'appid' in parameters received with the
94  * request 'req' for the 'method'.
95  *
96  * Returns 1 in case of success.
97  * Otherwise, if the 'appid' can't be retrieved, an error stating
98  * the bad request is replied for 'req' and 0 is returned.
99  */
100 static int onappid(struct afb_req req, const char *method, const char **appid)
101 {
102         struct json_object *json;
103
104         /* get the paramaters of the request */
105         json = afb_req_json(req);
106
107         /* get the appid if any */
108         if (!wrap_json_unpack(json, "s", appid)
109          || !wrap_json_unpack(json, "{si}", _id_, appid)) {
110                 /* found */
111                 INFO("method %s called for %s", method, *appid);
112                 return 1;
113         }
114
115         /* nothing appropriate */
116         INFO("bad request method %s: %s", method,
117                                         json_object_to_json_string(json));
118         bad_request(req);
119         return 0;
120 }
121
122 /*
123  * retrieves the 'runid' in parameters received with the
124  * request 'req' for the 'method'.
125  *
126  * Returns 1 in case of success.
127  * Otherwise, if the 'runid' can't be retrieved, an error stating
128  * the bad request is replied for 'req' and 0 is returned.
129  */
130 static int onrunid(struct afb_req req, const char *method, int *runid)
131 {
132         struct json_object *json;
133
134         json = afb_req_json(req);
135         if (wrap_json_unpack(json, "i", runid)
136                 && wrap_json_unpack(json, "{si}", _runid_, runid)) {
137                 INFO("bad request method %s: %s", method,
138                                         json_object_to_json_string(json));
139                 bad_request(req);
140                 return 0;
141         }
142
143         INFO("method %s called for %d", method, *runid);
144         return 1;
145 }
146
147 /*
148  * Sends the reply 'resp' to the request 'req' if 'resp' is not NULLzero.
149  * Otherwise, when 'resp' is NULL replies the error string 'errstr'.
150  */
151 static void reply(struct afb_req req, struct json_object *resp, const char *errstr)
152 {
153         if (!resp)
154                 afb_req_fail(req, errstr, NULL);
155         else
156                 afb_req_success(req, resp, NULL);
157 }
158
159 /*
160  * Sends the reply "true" to the request 'req' if 'status' is zero.
161  * Otherwise, when 'status' is not zero replies the error string 'errstr'.
162  */
163 static void reply_status(struct afb_req req, int status, const char *errstr)
164 {
165         reply(req, status ? NULL : json_object_get(json_true), errstr);
166 }
167
168 /*
169  * On query "runnables"
170  */
171 static void runnables(struct afb_req req)
172 {
173         struct json_object *resp;
174         INFO("method runnables called");
175         resp = afm_udb_applications_public(afudb, afb_req_get_uid(req));
176         afb_req_success(req, resp, NULL);
177 }
178
179 /*
180  * On query "detail"
181  */
182 static void detail(struct afb_req req)
183 {
184         const char *appid;
185         struct json_object *resp;
186
187         /* scan the request */
188         if (!onappid(req, _detail_, &appid))
189                 return;
190
191         /* wants details for appid */
192         resp = afm_udb_get_application_public(afudb, appid, afb_req_get_uid(req));
193         if (resp)
194                 afb_req_success(req, resp, NULL);
195         else
196                 not_found(req);
197 }
198
199 /*
200  * On query "start"
201  */
202 static void start(struct afb_req req)
203 {
204         const char *appid;
205         struct json_object *appli, *resp;
206         int runid;
207
208         /* scan the request */
209         if (!onappid(req, _start_, &appid))
210                 return;
211
212         /* get the application */
213         appli = afm_udb_get_application_private(afudb, appid, afb_req_get_uid(req));
214         if (appli == NULL) {
215                 not_found(req);
216                 return;
217         }
218
219         /* launch the application */
220         runid = afm_urun_start(appli, afb_req_get_uid(req));
221         if (runid <= 0) {
222                 cant_start(req);
223                 return;
224         }
225
226         /* returns */
227         resp = NULL;
228 #if 0
229         wrap_json_pack(&resp, "{si}", _runid_, runid);
230 #else
231         wrap_json_pack(&resp, "i", runid);
232 #endif
233         afb_req_success(req, resp, NULL);
234 }
235
236 /*
237  * On query "once"
238  */
239 static void once(struct afb_req req)
240 {
241         const char *appid;
242         struct json_object *appli, *resp;
243         int runid;
244
245         /* scan the request */
246         if (!onappid(req, _once_, &appid))
247                 return;
248
249         /* get the application */
250         appli = afm_udb_get_application_private(afudb, appid, afb_req_get_uid(req));
251         if (appli == NULL) {
252                 not_found(req);
253                 return;
254         }
255
256         /* launch the application */
257         runid = afm_urun_once(appli, afb_req_get_uid(req));
258         if (runid <= 0) {
259                 cant_start(req);
260                 return;
261         }
262
263         /* returns the state */
264         resp = afm_urun_state(afudb, runid, afb_req_get_uid(req));
265         afb_req_success(req, resp, NULL);
266 }
267
268 /*
269  * On query "pause"
270  */
271 static void pause(struct afb_req req)
272 {
273         int runid, status;
274         if (onrunid(req, "pause", &runid)) {
275                 status = afm_urun_pause(runid, afb_req_get_uid(req));
276                 reply_status(req, status, _not_found_);
277         }
278 }
279
280 /*
281  * On query "resume" from 'smsg' with parameters of 'obj'.
282  */
283 static void resume(struct afb_req req)
284 {
285         int runid, status;
286         if (onrunid(req, "resume", &runid)) {
287                 status = afm_urun_resume(runid, afb_req_get_uid(req));
288                 reply_status(req, status, _not_found_);
289         }
290 }
291
292 /*
293  * On query "terminate"
294  */
295 static void terminate(struct afb_req req)
296 {
297         int runid, status;
298         if (onrunid(req, "terminate", &runid)) {
299                 status = afm_urun_terminate(runid, afb_req_get_uid(req));
300                 reply_status(req, status, _not_found_);
301         }
302 }
303
304 /*
305  * On query "runners"
306  */
307 static void runners(struct afb_req req)
308 {
309         struct json_object *resp;
310         INFO("method runners called");
311         resp = afm_urun_list(afudb, afb_req_get_uid(req));
312         afb_req_success(req, resp, NULL);
313 }
314
315 /*
316  * On query "state"
317  */
318 static void state(struct afb_req req)
319 {
320         int runid;
321         struct json_object *resp;
322         if (onrunid(req, "state", &runid)) {
323                 resp = afm_urun_state(afudb, runid, afb_req_get_uid(req));
324                 reply(req, resp, _not_found_);
325         }
326 }
327
328 static void install(struct afb_req req)
329 {
330         const char *wgtfile;
331         const char *root;
332         int force;
333         int reload;
334         struct wgt_info *ifo;
335         struct json_object *json;
336         struct json_object *resp;
337
338         /* default settings */
339         root = rootdir;
340         force = 0;
341         reload = 1;
342
343         /* scan the request */
344         json = afb_req_json(req);
345         if (wrap_json_unpack(json, "s", &wgtfile)
346                 && wrap_json_unpack(json, "{ss s?s s?b s?b}",
347                                 "wgt", &wgtfile,
348                                 "root", &root,
349                                 "force", &force,
350                                 "reload", &reload)) {
351                 return bad_request(req);
352         }
353
354         /* install the widget */
355         ifo = install_widget(wgtfile, root, force);
356         if (ifo == NULL)
357                 afb_req_fail_f(req, "failed", "installation failed: %m");
358         else {
359                 afm_udb_update(afudb);
360                 /* reload if needed */
361                 if (reload)
362                         do_reloads();
363
364                 /* build the response */
365                 wrap_json_pack(&resp, "{ss}", _added_, wgt_info_desc(ifo)->idaver);
366                 afb_req_success(req, resp, NULL);
367                 application_list_changed(_install_, wgt_info_desc(ifo)->idaver);
368
369                 /* clean-up */
370                 wgt_info_unref(ifo);
371         }
372 }
373
374 static void uninstall(struct afb_req req)
375 {
376         const char *idaver;
377         const char *root;
378         struct json_object *json;
379         int rc;
380
381         /* default settings */
382         root = rootdir;
383
384         /* scan the request */
385         json = afb_req_json(req);
386         if (wrap_json_unpack(json, "s", &idaver)
387                 && wrap_json_unpack(json, "{ss s?s}",
388                                 _id_, &idaver,
389                                 "root", &root)) {
390                 return bad_request(req);
391         }
392
393         /* install the widget */
394         rc = uninstall_widget(idaver, root);
395         if (rc)
396                 afb_req_fail_f(req, "failed", "uninstallation failed: %m");
397         else {
398                 afm_udb_update(afudb);
399                 afb_req_success(req, NULL, NULL);
400                 application_list_changed(_uninstall_, idaver);
401         }
402 }
403
404 static int init()
405 {
406         /* init database */
407         afudb = afm_udb_create(1, 0, "afm-appli-");
408         if (!afudb) {
409                 ERROR("afm_udb_create failed");
410                 return -1;
411         }
412
413         /* create TRUE */
414         json_true = json_object_new_boolean(1);
415
416         /* create the event */
417         applist_changed_event = afb_daemon_make_event(_a_l_c_);
418         return -!afb_event_is_valid(applist_changed_event);
419 }
420
421 static const struct afb_auth
422         auth_install = {
423                 .type = afb_auth_Permission,
424                 .text = "urn:AGL:permission:afm:system:widget:install"
425         },
426         auth_uninstall = {
427                 .type = afb_auth_Permission,
428                 .text = "urn:AGL:permission:afm:system:widget:uninstall"
429         }
430 ;
431
432 static const struct afb_verb_v2 verbs[] =
433 {
434         {_runnables_, runnables, NULL, "Get list of runnable applications",          AFB_SESSION_CHECK_V2 },
435         {_detail_   , detail,    NULL, "Get the details for one application",        AFB_SESSION_CHECK_V2 },
436         {_start_    , start,     NULL, "Start an application",                       AFB_SESSION_CHECK_V2 },
437         {_once_     , once,      NULL, "Start once an application",                  AFB_SESSION_CHECK_V2 },
438         {_terminate_, terminate, NULL, "Terminate a running application",            AFB_SESSION_CHECK_V2 },
439         {_pause_    , pause,     NULL, "Pause a running application",                AFB_SESSION_CHECK_V2 },
440         {_resume_   , resume,    NULL, "Resume a paused application",                AFB_SESSION_CHECK_V2 },
441         {_runners_  , runners,   NULL, "Get the list of running applications",       AFB_SESSION_CHECK_V2 },
442         {_state_    , state,     NULL, "Get the state of a running application",     AFB_SESSION_CHECK_V2 },
443         {_install_  , install,   NULL, "Install an application using a widget file", AFB_SESSION_CHECK_V2 },
444         {_uninstall_, uninstall, NULL, "Uninstall an application",                   AFB_SESSION_CHECK_V2 },
445         { NULL, NULL, NULL, NULL, 0 }
446 };
447
448 const struct afb_binding_v2 afbBindingV2 = {
449         .api = "afm-main",
450         .specification = NULL,
451         .info = "Application Framework Master Service",
452         .verbs = verbs,
453         .preinit = NULL,
454         .init = init,
455         .onevent = NULL,
456         .noconcurrency = 1 /* relies on binder for serialisation of requests */
457 };
458