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