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