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