onappid: retrieves the 'appid' with id field
[src/app-framework-main.git] / src / afm-binding.c
1 /*
2  * Copyright (C) 2015-2018 "IoT.bzh"
3  * Author José Bollo <jose.bollo@iot.bzh>
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 <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <assert.h>
23 #include <signal.h>
24 #include <errno.h>
25
26 #include <json-c/json.h>
27
28 #define AFB_BINDING_VERSION 3
29 #include <afb/afb-binding.h>
30
31 #include "verbose.h"
32 #include "utils-systemd.h"
33 #include "afm-udb.h"
34 #include "afm-urun.h"
35 #include "wgt-info.h"
36 #include "wgtpkg-install.h"
37 #include "wgtpkg-uninstall.h"
38 #include "wrap-json.h"
39
40 /*
41  * constant strings
42  */
43 static const char _added_[]     = "added";
44 static const char _a_l_c_[]     = "application-list-changed";
45 static const char _bad_request_[] = "bad-request";
46 static const char _cannot_start_[] = "cannot-start";
47 static const char _detail_[]    = "detail";
48 static const char _id_[]        = "id";
49 static const char _install_[]   = "install";
50 static const char _lang_[]      = "lang";
51 static const char _not_found_[] = "not-found";
52 static const char _not_running_[] = "not-running";
53 static const char _once_[]      = "once";
54 static const char _pause_[]     = "pause";
55 static const char _resume_[]    = "resume";
56 static const char _runid_[]     = "runid";
57 static const char _runnables_[] = "runnables";
58 static const char _runners_[]   = "runners";
59 static const char _start_[]     = "start";
60 static const char _state_[]     = "state";
61 static const char _terminate_[] = "terminate";
62 static const char _uninstall_[] = "uninstall";
63 static const char _update_[]    = "update";
64
65 /*
66  * the permissions
67  */
68 static const struct afb_auth
69         auth_perm_widget = {
70                 .type = afb_auth_Permission,
71                 .text = "urn:AGL:permission:afm:system:widget"
72         },
73         auth_perm_widget_install = {
74                 .type = afb_auth_Permission,
75                 .text = "urn:AGL:permission:afm:system:widget:install"
76         },
77         auth_perm_widget_uninstall = {
78                 .type = afb_auth_Permission,
79                 .text = "urn:AGL:permission:afm:system:widget:uninstall"
80         },
81         auth_perm_widget_preinstall = {
82                 .type = afb_auth_Permission,
83                 .text = "urn:AGL:permission:afm:system:widget:preinstall"
84         },
85         auth_perm_widget_detail = {
86                 .type = afb_auth_Permission,
87                 .text = "urn:AGL:permission:afm:system:widget:detail"
88         },
89         auth_perm_widget_start = {
90                 .type = afb_auth_Permission,
91                 .text = "urn:AGL:permission:afm:system:widget:start"
92         },
93         auth_perm_widget_view_all = {
94                 .type = afb_auth_Permission,
95                 .text = "urn:AGL:permission:afm:system:widget:view-all"
96         },
97         auth_perm_runner = {
98                 .type = afb_auth_Permission,
99                 .text = "urn:AGL:permission:afm:system:runner"
100         },
101         auth_perm_runner_state = {
102                 .type = afb_auth_Permission,
103                 .text = "urn:AGL:permission:afm:system:runner:state"
104         },
105         auth_perm_runner_kill = {
106                 .type = afb_auth_Permission,
107                 .text = "urn:AGL:permission:afm:system:runner:kill"
108         },
109
110         auth_install = {
111                 .type = afb_auth_Or,
112                 .first = &auth_perm_widget,
113                 .next = &auth_perm_widget_install
114         },
115         auth_uninstall = {
116                 .type = afb_auth_Or,
117                 .first = &auth_perm_widget,
118                 .next = &auth_perm_widget_uninstall
119         },
120         auth_preinstall = {
121                 .type = afb_auth_Or,
122                 .first = &auth_perm_widget,
123                 .next = &auth_perm_widget_preinstall
124         },
125         auth_detail = {
126                 .type = afb_auth_Or,
127                 .first = &auth_perm_widget,
128                 .next = &auth_perm_widget_detail
129         },
130         auth_start = {
131                 .type = afb_auth_Or,
132                 .first = &auth_perm_widget,
133                 .next = &auth_perm_widget_start
134         },
135         auth_view_all = {
136                 .type = afb_auth_Or,
137                 .first = &auth_perm_widget,
138                 .next = &auth_perm_widget_view_all
139         },
140         auth_state = {
141                 .type = afb_auth_Or,
142                 .first = &auth_perm_runner,
143                 .next = &auth_perm_runner_state
144         },
145         auth_kill = {
146                 .type = afb_auth_Or,
147                 .first = &auth_perm_runner,
148                 .next = &auth_perm_runner_kill
149         }
150 ;
151
152 /*
153  * default root
154  */
155 static const char *rootdir = FWK_APP_DIR;
156
157 /*
158  * the internal application database
159  */
160 static struct afm_udb *afudb;
161
162 /*
163  * the event signaling that application list changed
164  */
165 static afb_event_t applist_changed_event;
166
167 /*
168  * the preallocated true json_object
169  */
170 static struct json_object *json_true;
171
172 /* enforce daemon reload */
173 static void do_reloads()
174 {
175         systemd_daemon_reload(0);
176         systemd_unit_restart_name(0, "sockets.target");
177 }
178
179 /* common bad request reply */
180 static void bad_request(afb_req_t req)
181 {
182         afb_req_fail(req, _bad_request_, NULL);
183 }
184
185 /* common not found reply */
186 static void not_found(afb_req_t req)
187 {
188         afb_req_fail(req, _not_found_, NULL);
189 }
190
191 /* common not running reply */
192 static void not_running(afb_req_t req)
193 {
194         afb_req_fail(req, _not_running_, NULL);
195 }
196
197 /* common can't start reply */
198 static void cant_start(afb_req_t req)
199 {
200         afb_req_fail(req, _cannot_start_, NULL);
201 }
202
203 /*
204  * Broadcast the event "application-list-changed".
205  * This event is sent was the event "changed" is received from dbus.
206  */
207 static void application_list_changed(const char *operation, const char *data)
208 {
209         struct json_object *e = NULL;
210         wrap_json_pack(&e, "{ss ss}", "operation", operation, "data", data);
211         afb_event_broadcast(applist_changed_event, e);
212 }
213
214 /*
215  * Retrieve the required language from 'req'.
216  */
217 static const char *get_lang(afb_req_t req)
218 {
219         const char *lang;
220
221         /* get the optional language */
222         lang = afb_req_value(req, _lang_);
223
224         /* TODO use the req to get the lang of the session (if any) */
225
226         return lang;
227 }
228
229
230 /*
231  * retrieves the 'appid' in parameters received with the
232  * request 'req' for the 'method'.
233  *
234  * Returns 1 in case of success.
235  * Otherwise, if the 'appid' can't be retrieved, an error stating
236  * the bad request is replied for 'req' and 0 is returned.
237  */
238 static int onappid(afb_req_t req, const char *method, const char **appid)
239 {
240         struct json_object *json;
241
242         /* get the paramaters of the request */
243         json = afb_req_json(req);
244
245         /* get the appid if any */
246         if (!wrap_json_unpack(json, "s", appid)
247          || !wrap_json_unpack(json, "{ss}", _id_, appid)) {
248                 /* found */
249                 INFO("method %s called for %s", method, *appid);
250                 return 1;
251         }
252
253         /* nothing appropriate */
254         INFO("bad request method %s: %s", method,
255                                         json_object_to_json_string(json));
256         bad_request(req);
257         return 0;
258 }
259
260 /*
261  * retrieves the 'runid' in parameters received with the
262  * request 'req' for the 'method'.
263  *
264  * Returns 1 in case of success.
265  * Otherwise, if the 'runid' can't be retrieved, an error stating
266  * the bad request is replied for 'req' and 0 is returned.
267  */
268 static int onrunid(afb_req_t req, const char *method, int *runid)
269 {
270         struct json_object *json;
271         const char *appid;
272
273         /* get the paramaters of the request */
274         json = afb_req_json(req);
275
276         /* get the runid if any */
277         if (!wrap_json_unpack(json, "i", runid)
278          || !wrap_json_unpack(json, "{si}", _runid_, runid)) {
279                 INFO("method %s called for %d", method, *runid);
280                 return 1;
281         }
282
283         /* get the appid if any */
284         if (!onappid(req, method, &appid))
285                 return 0;
286
287         /* search the runid of the appid */
288         *runid = afm_urun_search_runid(afudb, appid, afb_req_get_uid(req));
289         if (*runid < 0) {
290                 /* nothing appropriate */
291                 INFO("method %s can't get runid for %s: %m", method,
292                                                         appid);
293                 if (errno == ESRCH)
294                         not_running(req);
295                 else
296                         not_found(req);
297                 return 0;
298         }
299
300         /* found */
301         INFO("method %s called for %s -> %d", method, appid, *runid);
302         return 1;
303 }
304
305 /*
306  * Sends the reply 'resp' to the request 'req' if 'resp' is not NULLzero.
307  * Otherwise, when 'resp' is NULL replies the error string 'errstr'.
308  */
309 static void reply(afb_req_t req, struct json_object *resp)
310 {
311         if (resp)
312                 afb_req_reply(req, resp, NULL, NULL);
313         else
314                 afb_req_reply(req, NULL, "failed", strerror(errno));
315 }
316
317 /*
318  * Sends the reply "true" to the request 'req' if 'status' is zero.
319  * Otherwise, when 'status' is not zero replies the error string 'errstr'.
320  */
321 static void reply_status(afb_req_t req, int status)
322 {
323         reply(req, status ? NULL : json_object_get(json_true));
324 }
325
326 /*
327  * On query "runnables"
328  */
329 static void runnables(afb_req_t req)
330 {
331         const char *lang;
332         struct json_object *resp;
333
334         /* get the language */
335         lang = get_lang(req);
336
337         /* get the details */
338         resp = afm_udb_applications_public(afudb, afb_req_get_uid(req), lang);
339         afb_req_success(req, resp, NULL);
340 }
341
342 /*
343  * On query "detail"
344  */
345 static void detail(afb_req_t req)
346 {
347         const char *lang;
348         const char *appid;
349         struct json_object *resp;
350
351         /* scan the request */
352         if (!onappid(req, _detail_, &appid))
353                 return;
354
355         /* get the language */
356         lang = get_lang(req);
357
358         /* wants details for appid */
359         resp = afm_udb_get_application_public(afudb, appid, afb_req_get_uid(req), lang);
360         if (resp)
361                 afb_req_success(req, resp, NULL);
362         else
363                 not_found(req);
364 }
365
366 /*
367  * On query "start"
368  */
369 static void start(afb_req_t req)
370 {
371         const char *appid;
372         struct json_object *appli, *resp;
373         int runid;
374
375         /* scan the request */
376         if (!onappid(req, _start_, &appid))
377                 return;
378
379         /* get the application */
380         appli = afm_udb_get_application_private(afudb, appid, afb_req_get_uid(req));
381         if (appli == NULL) {
382                 not_found(req);
383                 return;
384         }
385
386         /* launch the application */
387         runid = afm_urun_start(appli, afb_req_get_uid(req));
388         if (runid <= 0) {
389                 cant_start(req);
390                 return;
391         }
392
393         /* returns */
394         resp = NULL;
395 #if 0
396         wrap_json_pack(&resp, "{si}", _runid_, runid);
397 #else
398         wrap_json_pack(&resp, "i", runid);
399 #endif
400         afb_req_success(req, resp, NULL);
401 }
402
403 /*
404  * On query "once"
405  */
406 static void once(afb_req_t req)
407 {
408         const char *appid;
409         struct json_object *appli, *resp;
410         int runid;
411
412         /* scan the request */
413         if (!onappid(req, _once_, &appid))
414                 return;
415
416         /* get the application */
417         appli = afm_udb_get_application_private(afudb, appid, afb_req_get_uid(req));
418         if (appli == NULL) {
419                 not_found(req);
420                 return;
421         }
422
423         /* launch the application */
424         runid = afm_urun_once(appli, afb_req_get_uid(req));
425         if (runid <= 0) {
426                 cant_start(req);
427                 return;
428         }
429
430         /* returns the state */
431         resp = afm_urun_state(afudb, runid, afb_req_get_uid(req));
432         afb_req_success(req, resp, NULL);
433 }
434
435 /*
436  * On query "pause"
437  */
438 static void pause(afb_req_t req)
439 {
440         int runid, status;
441         if (onrunid(req, "pause", &runid)) {
442                 status = afm_urun_pause(runid, afb_req_get_uid(req));
443                 reply_status(req, status);
444         }
445 }
446
447 /*
448  * On query "resume" from 'smsg' with parameters of 'obj'.
449  */
450 static void resume(afb_req_t req)
451 {
452         int runid, status;
453         if (onrunid(req, "resume", &runid)) {
454                 status = afm_urun_resume(runid, afb_req_get_uid(req));
455                 reply_status(req, status);
456         }
457 }
458
459 /*
460  * On query "terminate"
461  */
462 static void terminate(afb_req_t req)
463 {
464         int runid, status;
465         if (onrunid(req, "terminate", &runid)) {
466                 status = afm_urun_terminate(runid, afb_req_get_uid(req));
467                 reply_status(req, status);
468         }
469 }
470
471 /*
472  * On query "runners"
473  */
474 static void runners(afb_req_t req)
475 {
476         struct json_object *resp;
477         resp = afm_urun_list(afudb, afb_req_get_uid(req));
478         afb_req_success(req, resp, NULL);
479 }
480
481 /*
482  * On query "state"
483  */
484 static void state(afb_req_t req)
485 {
486         int runid;
487         struct json_object *resp;
488         if (onrunid(req, "state", &runid)) {
489                 resp = afm_urun_state(afudb, runid, afb_req_get_uid(req));
490                 reply(req, resp);
491         }
492 }
493
494 /*
495  * On querying installation of widget(s)
496  */
497 static void install(afb_req_t req)
498 {
499         const char *wgtfile;
500         const char *root;
501         int force;
502         int reload;
503         struct wgt_info *ifo;
504         struct json_object *json;
505         struct json_object *resp;
506
507         /* default settings */
508         root = rootdir;
509         force = 0;
510         reload = 1;
511
512         /* scan the request */
513         json = afb_req_json(req);
514         if (wrap_json_unpack(json, "s", &wgtfile)
515                 && wrap_json_unpack(json, "{ss s?s s?b s?b}",
516                                 "wgt", &wgtfile,
517                                 "root", &root,
518                                 "force", &force,
519                                 "reload", &reload)) {
520                 return bad_request(req);
521         }
522
523         /* install the widget */
524         ifo = install_widget(wgtfile, root, force);
525         if (ifo == NULL)
526                 afb_req_fail_f(req, "failed", "installation failed: %m");
527         else {
528                 afm_udb_update(afudb);
529                 /* reload if needed */
530                 if (reload)
531                         do_reloads();
532
533                 /* build the response */
534                 wrap_json_pack(&resp, "{ss}", _added_, wgt_info_desc(ifo)->idaver);
535                 afb_req_success(req, resp, NULL);
536                 application_list_changed(_install_, wgt_info_desc(ifo)->idaver);
537
538                 /* clean-up */
539                 wgt_info_unref(ifo);
540         }
541 }
542
543 /*
544  * On querying uninstallation of widget(s)
545  */
546 static void uninstall(afb_req_t req)
547 {
548         const char *idaver;
549         const char *root;
550         struct json_object *json;
551         int rc;
552
553         /* default settings */
554         root = rootdir;
555
556         /* scan the request */
557         json = afb_req_json(req);
558         if (wrap_json_unpack(json, "s", &idaver)
559                 && wrap_json_unpack(json, "{ss s?s}",
560                                 _id_, &idaver,
561                                 "root", &root)) {
562                 return bad_request(req);
563         }
564
565         /* install the widget */
566         rc = uninstall_widget(idaver, root);
567         if (rc)
568                 afb_req_fail_f(req, "failed", "uninstallation failed: %m");
569         else {
570                 afm_udb_update(afudb);
571                 afb_req_success(req, NULL, NULL);
572                 application_list_changed(_uninstall_, idaver);
573         }
574 }
575
576 static void onsighup(int signal)
577 {
578         afm_udb_update(afudb);
579         application_list_changed(_update_, _update_);
580 }
581
582 static int init(afb_api_t api)
583 {
584         /* create TRUE */
585         json_true = json_object_new_boolean(1);
586
587         /* init database */
588         afudb = afm_udb_create(1, 0, "afm-appli-");
589         if (!afudb) {
590                 ERROR("afm_udb_create failed");
591                 return -1;
592         }
593
594         signal(SIGHUP, onsighup);
595
596         /* create the event */
597         applist_changed_event = afb_api_make_event(api, _a_l_c_);
598         return -!afb_event_is_valid(applist_changed_event);
599 }
600
601 static const afb_verb_t verbs[] =
602 {
603         {.verb=_runnables_, .callback=runnables, .auth=&auth_detail,    .info="Get list of runnable applications",          .session=AFB_SESSION_CHECK },
604         {.verb=_detail_   , .callback=detail,    .auth=&auth_detail,    .info="Get the details for one application",        .session=AFB_SESSION_CHECK },
605         {.verb=_start_    , .callback=start,     .auth=&auth_start,     .info="Start an application",                       .session=AFB_SESSION_CHECK },
606         {.verb=_once_     , .callback=once,      .auth=&auth_start,     .info="Start once an application",                  .session=AFB_SESSION_CHECK },
607         {.verb=_terminate_, .callback=terminate, .auth=&auth_kill,      .info="Terminate a running application",            .session=AFB_SESSION_CHECK },
608         {.verb=_pause_    , .callback=pause,     .auth=&auth_kill,      .info="Pause a running application",                .session=AFB_SESSION_CHECK },
609         {.verb=_resume_   , .callback=resume,    .auth=&auth_kill,      .info="Resume a paused application",                .session=AFB_SESSION_CHECK },
610         {.verb=_runners_  , .callback=runners,   .auth=&auth_state,     .info="Get the list of running applications",       .session=AFB_SESSION_CHECK },
611         {.verb=_state_    , .callback=state,     .auth=&auth_state,     .info="Get the state of a running application",     .session=AFB_SESSION_CHECK },
612         {.verb=_install_  , .callback=install,   .auth=&auth_install,   .info="Install an application using a widget file", .session=AFB_SESSION_CHECK },
613         {.verb=_uninstall_, .callback=uninstall, .auth=&auth_uninstall, .info="Uninstall an application",                   .session=AFB_SESSION_CHECK },
614         {.verb=NULL }
615 };
616
617 const afb_binding_t afbBindingExport = {
618         .api = "afm-main",
619         .specification = NULL,
620         .info = "Application Framework Master Service",
621         .verbs = verbs,
622         .preinit = NULL,
623         .init = init,
624         .onevent = NULL,
625         .noconcurrency = 1 /* relies on binder for serialization of requests */
626 };
627