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