5aad281db8075f166f52a718e2d178d7d492bf28
[src/app-framework-main.git] / src / afm-binding.c
1 /*
2  * Copyright (C) 2015-2018 "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 _lang_[]      = "lang";
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  * Retrieve the required language from 'req'.
95  */
96 static const char *get_lang(struct afb_req req)
97 {
98         const char *lang;
99
100         /* get the optional language */
101         lang = afb_req_value(req, _lang_);
102
103         /* TODO use the req to get the lang of the session (if any) */
104
105         return lang;
106 }
107
108
109 /*
110  * retrieves the 'appid' in parameters received with the
111  * request 'req' for the 'method'.
112  *
113  * Returns 1 in case of success.
114  * Otherwise, if the 'appid' can't be retrieved, an error stating
115  * the bad request is replied for 'req' and 0 is returned.
116  */
117 static int onappid(struct afb_req req, const char *method, const char **appid)
118 {
119         struct json_object *json;
120
121         /* get the paramaters of the request */
122         json = afb_req_json(req);
123
124         /* get the appid if any */
125         if (!wrap_json_unpack(json, "s", appid)
126          || !wrap_json_unpack(json, "{si}", _id_, appid)) {
127                 /* found */
128                 INFO("method %s called for %s", method, *appid);
129                 return 1;
130         }
131
132         /* nothing appropriate */
133         INFO("bad request method %s: %s", method,
134                                         json_object_to_json_string(json));
135         bad_request(req);
136         return 0;
137 }
138
139 /*
140  * retrieves the 'runid' in parameters received with the
141  * request 'req' for the 'method'.
142  *
143  * Returns 1 in case of success.
144  * Otherwise, if the 'runid' can't be retrieved, an error stating
145  * the bad request is replied for 'req' and 0 is returned.
146  */
147 static int onrunid(struct afb_req req, const char *method, int *runid)
148 {
149         struct json_object *json;
150         const char *appid;
151
152         /* get the paramaters of the request */
153         json = afb_req_json(req);
154
155         /* get the runid if any */
156         if (!wrap_json_unpack(json, "i", runid)
157          || !wrap_json_unpack(json, "{si}", _runid_, runid)) {
158                 INFO("method %s called for %d", method, *runid);
159                 return 1;
160         }
161
162         /* get the appid if any */
163         if (!onappid(req, method, &appid))
164                 return 0;
165
166         /* search the runid of the appid */
167         *runid = afm_urun_search_runid(afudb, appid, afb_req_get_uid(req));
168         if (*runid < 0) {
169                 /* nothing appropriate */
170                 INFO("method %s can't get runid for %s: %m", method,
171                                                         appid);
172                 not_found(req);
173                 return 0;
174         }
175
176         /* found */
177         INFO("method %s called for %s -> %d", method, appid, *runid);
178         return 1;
179 }
180
181 /*
182  * Sends the reply 'resp' to the request 'req' if 'resp' is not NULLzero.
183  * Otherwise, when 'resp' is NULL replies the error string 'errstr'.
184  */
185 static void reply(struct afb_req req, struct json_object *resp, const char *errstr)
186 {
187         if (!resp)
188                 afb_req_fail(req, errstr, NULL);
189         else
190                 afb_req_success(req, resp, NULL);
191 }
192
193 /*
194  * Sends the reply "true" to the request 'req' if 'status' is zero.
195  * Otherwise, when 'status' is not zero replies the error string 'errstr'.
196  */
197 static void reply_status(struct afb_req req, int status, const char *errstr)
198 {
199         reply(req, status ? NULL : json_object_get(json_true), errstr);
200 }
201
202 /*
203  * On query "runnables"
204  */
205 static void runnables(struct afb_req req)
206 {
207         const char *lang;
208         struct json_object *resp;
209
210         /* get the language */
211         lang = get_lang(req);
212
213         /* get the details */
214         resp = afm_udb_applications_public(afudb, afb_req_get_uid(req), lang);
215         afb_req_success(req, resp, NULL);
216 }
217
218 /*
219  * On query "detail"
220  */
221 static void detail(struct afb_req req)
222 {
223         const char *lang;
224         const char *appid;
225         struct json_object *resp;
226
227         /* scan the request */
228         if (!onappid(req, _detail_, &appid))
229                 return;
230
231         /* get the language */
232         lang = get_lang(req);
233
234         /* wants details for appid */
235         resp = afm_udb_get_application_public(afudb, appid, afb_req_get_uid(req), lang);
236         if (resp)
237                 afb_req_success(req, resp, NULL);
238         else
239                 not_found(req);
240 }
241
242 /*
243  * On query "start"
244  */
245 static void start(struct afb_req req)
246 {
247         const char *appid;
248         struct json_object *appli, *resp;
249         int runid;
250
251         /* scan the request */
252         if (!onappid(req, _start_, &appid))
253                 return;
254
255         /* get the application */
256         appli = afm_udb_get_application_private(afudb, appid, afb_req_get_uid(req));
257         if (appli == NULL) {
258                 not_found(req);
259                 return;
260         }
261
262         /* launch the application */
263         runid = afm_urun_start(appli, afb_req_get_uid(req));
264         if (runid <= 0) {
265                 cant_start(req);
266                 return;
267         }
268
269         /* returns */
270         resp = NULL;
271 #if 0
272         wrap_json_pack(&resp, "{si}", _runid_, runid);
273 #else
274         wrap_json_pack(&resp, "i", runid);
275 #endif
276         afb_req_success(req, resp, NULL);
277 }
278
279 /*
280  * On query "once"
281  */
282 static void once(struct afb_req req)
283 {
284         const char *appid;
285         struct json_object *appli, *resp;
286         int runid;
287
288         /* scan the request */
289         if (!onappid(req, _once_, &appid))
290                 return;
291
292         /* get the application */
293         appli = afm_udb_get_application_private(afudb, appid, afb_req_get_uid(req));
294         if (appli == NULL) {
295                 not_found(req);
296                 return;
297         }
298
299         /* launch the application */
300         runid = afm_urun_once(appli, afb_req_get_uid(req));
301         if (runid <= 0) {
302                 cant_start(req);
303                 return;
304         }
305
306         /* returns the state */
307         resp = afm_urun_state(afudb, runid, afb_req_get_uid(req));
308         afb_req_success(req, resp, NULL);
309 }
310
311 /*
312  * On query "pause"
313  */
314 static void pause(struct afb_req req)
315 {
316         int runid, status;
317         if (onrunid(req, "pause", &runid)) {
318                 status = afm_urun_pause(runid, afb_req_get_uid(req));
319                 reply_status(req, status, _not_found_);
320         }
321 }
322
323 /*
324  * On query "resume" from 'smsg' with parameters of 'obj'.
325  */
326 static void resume(struct afb_req req)
327 {
328         int runid, status;
329         if (onrunid(req, "resume", &runid)) {
330                 status = afm_urun_resume(runid, afb_req_get_uid(req));
331                 reply_status(req, status, _not_found_);
332         }
333 }
334
335 /*
336  * On query "terminate"
337  */
338 static void terminate(struct afb_req req)
339 {
340         int runid, status;
341         if (onrunid(req, "terminate", &runid)) {
342                 status = afm_urun_terminate(runid, afb_req_get_uid(req));
343                 reply_status(req, status, _not_found_);
344         }
345 }
346
347 /*
348  * On query "runners"
349  */
350 static void runners(struct afb_req req)
351 {
352         struct json_object *resp;
353         resp = afm_urun_list(afudb, afb_req_get_uid(req));
354         afb_req_success(req, resp, NULL);
355 }
356
357 /*
358  * On query "state"
359  */
360 static void state(struct afb_req req)
361 {
362         int runid;
363         struct json_object *resp;
364         if (onrunid(req, "state", &runid)) {
365                 resp = afm_urun_state(afudb, runid, afb_req_get_uid(req));
366                 reply(req, resp, _not_found_);
367         }
368 }
369
370 /*
371  * On querying installation of widget(s)
372  */
373 static void install(struct afb_req req)
374 {
375         const char *wgtfile;
376         const char *root;
377         int force;
378         int reload;
379         struct wgt_info *ifo;
380         struct json_object *json;
381         struct json_object *resp;
382
383         /* default settings */
384         root = rootdir;
385         force = 0;
386         reload = 1;
387
388         /* scan the request */
389         json = afb_req_json(req);
390         if (wrap_json_unpack(json, "s", &wgtfile)
391                 && wrap_json_unpack(json, "{ss s?s s?b s?b}",
392                                 "wgt", &wgtfile,
393                                 "root", &root,
394                                 "force", &force,
395                                 "reload", &reload)) {
396                 return bad_request(req);
397         }
398
399         /* install the widget */
400         ifo = install_widget(wgtfile, root, force);
401         if (ifo == NULL)
402                 afb_req_fail_f(req, "failed", "installation failed: %m");
403         else {
404                 afm_udb_update(afudb);
405                 /* reload if needed */
406                 if (reload)
407                         do_reloads();
408
409                 /* build the response */
410                 wrap_json_pack(&resp, "{ss}", _added_, wgt_info_desc(ifo)->idaver);
411                 afb_req_success(req, resp, NULL);
412                 application_list_changed(_install_, wgt_info_desc(ifo)->idaver);
413
414                 /* clean-up */
415                 wgt_info_unref(ifo);
416         }
417 }
418
419 static void uninstall(struct afb_req req)
420 {
421         const char *idaver;
422         const char *root;
423         struct json_object *json;
424         int rc;
425
426         /* default settings */
427         root = rootdir;
428
429         /* scan the request */
430         json = afb_req_json(req);
431         if (wrap_json_unpack(json, "s", &idaver)
432                 && wrap_json_unpack(json, "{ss s?s}",
433                                 _id_, &idaver,
434                                 "root", &root)) {
435                 return bad_request(req);
436         }
437
438         /* install the widget */
439         rc = uninstall_widget(idaver, root);
440         if (rc)
441                 afb_req_fail_f(req, "failed", "uninstallation failed: %m");
442         else {
443                 afm_udb_update(afudb);
444                 afb_req_success(req, NULL, NULL);
445                 application_list_changed(_uninstall_, idaver);
446         }
447 }
448
449 static int init()
450 {
451         /* init database */
452         afudb = afm_udb_create(1, 0, "afm-appli-");
453         if (!afudb) {
454                 ERROR("afm_udb_create failed");
455                 return -1;
456         }
457
458         /* create TRUE */
459         json_true = json_object_new_boolean(1);
460
461         /* create the event */
462         applist_changed_event = afb_daemon_make_event(_a_l_c_);
463         return -!afb_event_is_valid(applist_changed_event);
464 }
465
466 static const struct afb_auth
467         auth_install = {
468                 .type = afb_auth_Permission,
469                 .text = "urn:AGL:permission:afm:system:widget:install"
470         },
471         auth_uninstall = {
472                 .type = afb_auth_Permission,
473                 .text = "urn:AGL:permission:afm:system:widget:uninstall"
474         }
475 ;
476
477 static const struct afb_verb_v2 verbs[] =
478 {
479         {_runnables_, runnables, NULL, "Get list of runnable applications",          AFB_SESSION_CHECK_V2 },
480         {_detail_   , detail,    NULL, "Get the details for one application",        AFB_SESSION_CHECK_V2 },
481         {_start_    , start,     NULL, "Start an application",                       AFB_SESSION_CHECK_V2 },
482         {_once_     , once,      NULL, "Start once an application",                  AFB_SESSION_CHECK_V2 },
483         {_terminate_, terminate, NULL, "Terminate a running application",            AFB_SESSION_CHECK_V2 },
484         {_pause_    , pause,     NULL, "Pause a running application",                AFB_SESSION_CHECK_V2 },
485         {_resume_   , resume,    NULL, "Resume a paused application",                AFB_SESSION_CHECK_V2 },
486         {_runners_  , runners,   NULL, "Get the list of running applications",       AFB_SESSION_CHECK_V2 },
487         {_state_    , state,     NULL, "Get the state of a running application",     AFB_SESSION_CHECK_V2 },
488         {_install_  , install,   NULL, "Install an application using a widget file", AFB_SESSION_CHECK_V2 },
489         {_uninstall_, uninstall, NULL, "Uninstall an application",                   AFB_SESSION_CHECK_V2 },
490         { NULL, NULL, NULL, NULL, 0 }
491 };
492
493 const struct afb_binding_v2 afbBindingV2 = {
494         .api = "afm-main",
495         .specification = NULL,
496         .info = "Application Framework Master Service",
497         .verbs = verbs,
498         .preinit = NULL,
499         .init = init,
500         .onevent = NULL,
501         .noconcurrency = 1 /* relies on binder for serialisation of requests */
502 };
503