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