afm-binding: provide afm as a unique binding
[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, "s", runid)
107                 || wrap_json_unpack(json, "{ss}", "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);
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);
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);
195         if (appli == NULL) {
196                 not_found(req);
197                 return;
198         }
199
200         /* launch the application */
201         runid = afm_urun_start(appli);
202         if (runid <= 0) {
203                 cant_start(req);
204                 return;
205         }
206
207         /* returns */
208         resp = NULL;
209         wrap_json_pack(&resp, "{si}", _runid_, runid);
210         afb_req_success(req, resp, NULL);
211 }
212
213 /*
214  * On query "once"
215  */
216 static void once(struct afb_req req)
217 {
218         const char *appid;
219         struct json_object *appli, *resp, *json;
220         int runid;
221
222         /* scan the request */
223         json = afb_req_json(req);
224         if (wrap_json_unpack(json, "s", &appid)
225                 || wrap_json_unpack(json, "{ss}", _id_, &appid)) {
226                 bad_request(req);
227                 return;
228         }
229
230         /* get the application */
231         INFO("method once called for %s", appid);
232         appli = afm_udb_get_application_private(afudb, appid);
233         if (appli == NULL) {
234                 not_found(req);
235                 return;
236         }
237
238         /* launch the application */
239         runid = afm_urun_once(appli);
240         if (runid <= 0) {
241                 cant_start(req);
242                 return;
243         }
244
245         /* returns the state */
246         resp = afm_urun_state(afudb, runid);
247         afb_req_success(req, resp, NULL);
248 }
249
250 /*
251  * On query "pause"
252  */
253 static void pause(struct afb_req req)
254 {
255         int runid, status;
256         if (onrunid(req, "pause", &runid)) {
257                 status = afm_urun_pause(runid);
258                 reply_status(req, status, _not_found_);
259         }
260 }
261
262 /*
263  * On query "resume" from 'smsg' with parameters of 'obj'.
264  */
265 static void resume(struct afb_req req)
266 {
267         int runid, status;
268         if (onrunid(req, "resume", &runid)) {
269                 status = afm_urun_resume(runid);
270                 reply_status(req, status, _not_found_);
271         }
272 }
273
274 /*
275  * On query "terminate"
276  */
277 static void terminate(struct afb_req req)
278 {
279         int runid, status;
280         if (onrunid(req, "terminate", &runid)) {
281                 status = afm_urun_terminate(runid);
282                 reply_status(req, status, _not_found_);
283         }
284 }
285
286 /*
287  * On query "runners"
288  */
289 static void runners(struct afb_req req)
290 {
291         struct json_object *resp;
292         INFO("method runners called");
293         resp = afm_urun_list(afudb);
294         afb_req_success(req, resp, NULL);
295 }
296
297 /*
298  * On query "state"
299  */
300 static void state(struct afb_req req)
301 {
302         int runid;
303         struct json_object *resp;
304         if (onrunid(req, "state", &runid)) {
305                 resp = afm_urun_state(afudb, runid);
306                 reply(req, resp, _not_found_);
307         }
308 }
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355 static void install(struct afb_req req)
356 {
357         const char *wgtfile;
358         const char *root;
359         int force;
360         int reload;
361         struct wgt_info *ifo;
362         struct json_object *json;
363         struct json_object *resp;
364
365         /* default settings */
366         root = rootdir;
367         force = 0;
368         reload = 1;
369
370         /* scan the request */
371         json = afb_req_json(req);
372         if (wrap_json_unpack(json, "s", &wgtfile)
373                 || wrap_json_unpack(json, "{ss s?s s?b s?b}",
374                                 "widget", &wgtfile,
375                                 "root", &root,
376                                 "force", &force,
377                                 "reload", &reload)) {
378                 return bad_request(req);
379         }
380
381         /* install the widget */
382         ifo = install_widget(wgtfile, root, force);
383         if (ifo == NULL)
384                 afb_req_fail_f(req, "failed", "installation failed: %m");
385         else {
386                 /* reload if needed */
387                 if (reload)
388                         do_reloads();
389
390                 /* build the response */
391                 wrap_json_pack(&resp, "{ss}", _added_, wgt_info_desc(ifo)->idaver);
392                 afb_req_success(req, resp, NULL);
393                 application_list_changed(_install_, wgt_info_desc(ifo)->idaver);
394
395                 /* clean-up */
396                 wgt_info_unref(ifo);
397         }
398 }
399
400 static void uninstall(struct afb_req req)
401 {
402         const char *idaver;
403         const char *root;
404         struct json_object *json;
405         int rc;
406
407         /* default settings */
408         root = rootdir;
409
410         /* scan the request */
411         json = afb_req_json(req);
412         if (wrap_json_unpack(json, "s", &idaver)
413                 || wrap_json_unpack(json, "{ss s?s}",
414                                 _id_, &idaver,
415                                 "root", &root)) {
416                 return bad_request(req);
417         }
418
419         /* install the widget */
420         rc = uninstall_widget(idaver, root);
421         if (rc)
422                 afb_req_fail_f(req, "failed", "uninstallation failed: %m");
423         else {
424                 afb_req_success(req, NULL, NULL);
425                 application_list_changed(_uninstall_, idaver);
426         }
427 }
428
429 static int init()
430 {
431         /* init database */
432         afudb = afm_udb_create(1, 1, "afm-appli-");
433         if (!afudb) {
434                 ERROR("afm_udb_create failed");
435                 return -1;
436         }
437
438         /* set the systemd's buses */
439         systemd_set_bus(0, afb_daemon_get_system_bus());
440         systemd_set_bus(1, afb_daemon_get_user_bus());
441
442         /* create TRUE */
443         json_true = json_object_new_boolean(1);
444
445         /* create the event */
446         applist_changed_event = afb_daemon_make_event(_a_l_c_);
447         return -!afb_event_is_valid(applist_changed_event);
448 }
449
450 static const struct afb_auth
451         auth_install = {
452                 .type = afb_auth_Permission,
453                 .text = "urn:AGL:permission:afm:system:widget:install"
454         },
455         auth_uninstall = {
456                 .type = afb_auth_Permission,
457                 .text = "urn:AGL:permission:afm:system:widget:uninstall"
458         }
459 ;
460
461 static const struct afb_verb_v2 verbs[] =
462 {
463         {_runnables_, runnables, NULL, "Get list of runnable applications",          AFB_SESSION_CHECK_V2 },
464         {_detail_   , detail,    NULL, "Get the details for one application",        AFB_SESSION_CHECK_V2 },
465         {_start_    , start,     NULL, "Start an application",                       AFB_SESSION_CHECK_V2 },
466         {_once_     , once,      NULL, "Start once an application",                  AFB_SESSION_CHECK_V2 },
467         {_terminate_, terminate, NULL, "Terminate a running application",            AFB_SESSION_CHECK_V2 },
468         {_pause_    , pause,     NULL, "Pause a running application",                AFB_SESSION_CHECK_V2 },
469         {_resume_   , resume,    NULL, "Resume a paused application",                AFB_SESSION_CHECK_V2 },
470         {_runners_  , runners,   NULL, "Get the list of running applications",       AFB_SESSION_CHECK_V2 },
471         {_state_    , state,     NULL, "Get the state of a running application",     AFB_SESSION_CHECK_V2 },
472         {_install_  , install,   NULL, "Install an application using a widget file", AFB_SESSION_CHECK_V2 },
473         {_uninstall_, uninstall, NULL, "Uninstall an application",                   AFB_SESSION_CHECK_V2 },
474         { NULL, NULL, NULL, NULL, 0 }
475 };
476
477 const struct afb_binding_v2 afbBindingV2 = {
478         .api = "afm-main",
479         .specification = NULL,
480         .info = "Application Framework Master Service",
481         .verbs = verbs,
482         .preinit = NULL,
483         .init = init,
484         .onevent = NULL,
485         .noconcurrency = 1 /* relies on binder for serialisation of requests */
486 };
487