2 Copyright 2015, 2016, 2017 IoT.bzh
4 author: José Bollo <jose.bollo@iot.bzh>
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
10 http://www.apache.org/licenses/LICENSE-2.0
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
25 #include <systemd/sd-bus.h>
26 #include <systemd/sd-event.h>
27 #include <json-c/json.h>
30 #include "utils-jbus.h"
31 #include "utils-json.h"
33 #include "afm-launch-mode.h"
34 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
43 * name of the application
45 static const char appname[] = "afm-user-daemon";
48 * string for printing usage
50 static const char usagestr[] =
51 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
52 "usage: %s [-q] [-v] [-m mode] [-r rootdir]... [-a appdir]...\n"
54 " -a appdir adds an application directory\n"
55 " -r rootdir adds a root directory of applications\n"
56 " -m mode set default launch mode (local or remote)\n"
58 "usage: %s [option(s)]\n"
61 " -d run as a daemon\n"
62 " -u addr address of user D-Bus to use\n"
63 " -s addr address of system D-Bus to use\n"
69 * Option definition for getopt_long
71 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
72 static const char options_s[] = "hdqvr:a:m:";
73 static struct option options_l[] = {
74 { "root", required_argument, NULL, 'r' },
75 { "application", required_argument, NULL, 'a' },
76 { "mode", required_argument, NULL, 'm' },
78 static const char options_s[] = "hdqv";
79 static struct option options_l[] = {
81 { "user-dbus", required_argument, NULL, 'u' },
82 { "system-dbus", required_argument, NULL, 's' },
83 { "daemon", no_argument, NULL, 'd' },
84 { "quiet", no_argument, NULL, 'q' },
85 { "verbose", no_argument, NULL, 'v' },
86 { "help", no_argument, NULL, 'h' },
91 * Connections to D-Bus
92 * This is an array for using the function
93 * jbus_read_write_dispatch_multiple
94 * directly without transformations.
96 static struct jbus *jbuses[2];
97 #define system_bus jbuses[0]
98 #define user_bus jbuses[1]
101 * Handle to the database of applications
103 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
104 static struct afm_db *afdb;
106 static struct afm_udb *afudb;
110 * Returned error strings
112 const char error_nothing[] = "[]";
113 const char error_bad_request[] = "\"bad request\"";
114 const char error_not_found[] = "\"not found\"";
115 const char error_cant_start[] = "\"can't start\"";
116 const char error_system[] = "\"system error\"";
120 * retrieves the 'runid' in 'obj' parameters received with the
121 * request 'smsg' for the 'method'.
123 * Returns 1 in case of success.
124 * Otherwise, if the 'runid' can't be retrived, an error stating
125 * the bad request is replied for 'smsg' and 0 is returned.
127 static int onrunid(struct sd_bus_message *smsg, struct json_object *obj,
128 const char *method, int *runid)
130 if (!j_read_integer(obj, runid)
131 && !j_read_integer_at(obj, "runid", runid)) {
132 INFO("bad request method %s: %s", method,
133 json_object_to_json_string(obj));
134 jbus_reply_error_s(smsg, error_bad_request);
138 INFO("method %s called for %d", method, *runid);
143 * Sends the reply 'resp' to the request 'smsg' if 'resp' is not NULL.
144 * Otherwise, when 'resp' is NULL replies the error string 'errstr'.
146 static void reply(struct sd_bus_message *smsg, struct json_object *resp,
150 jbus_reply_j(smsg, resp);
152 jbus_reply_error_s(smsg, errstr);
156 * Sends the reply "true" to the request 'smsg' if 'status' is zero.
157 * Otherwise, when 'status' is not zero replies the error string 'errstr'.
159 static void reply_status(struct sd_bus_message *smsg, int status, const char *errstr)
162 jbus_reply_error_s(smsg, errstr);
164 jbus_reply_s(smsg, "true");
168 * On query "runnables" from 'smsg' with parameters of 'obj'.
170 * Nothing is expected in 'obj' that can be anything.
172 static void on_runnables(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
174 struct json_object *resp;
175 INFO("method runnables called");
176 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
177 resp = afm_db_application_list(afdb);
179 resp = afm_udb_applications_public(afudb);
181 jbus_reply_j(smsg, resp);
182 json_object_put(resp);
186 * On query "detail" from 'smsg' with parameters of 'obj'.
188 static void on_detail(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
191 struct json_object *resp;
193 /* get the parameters */
194 if (j_read_string(obj, &appid))
195 ; /* appid as a string */
196 else if (j_read_string_at(obj, "id", &appid))
197 ; /* appid as obj.id string */
199 INFO("method detail called but bad request!");
200 jbus_reply_error_s(smsg, error_bad_request);
204 /* wants details for appid */
205 INFO("method detail called for %s", appid);
206 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
207 resp = afm_db_get_application_public(afdb, appid);
209 resp = afm_udb_get_application_public(afudb, appid);
211 reply(smsg, resp, error_not_found);
212 json_object_put(resp);
216 * On query "start" from 'smsg' with parameters of 'obj'.
218 static void on_start(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
220 const char *appid, *modestr;
222 struct json_object *appli, *resp;
225 enum afm_launch_mode mode;
227 /* get the parameters */
228 mode = invalid_launch_mode;
229 if (j_read_string(obj, &appid)) {
230 mode = get_default_launch_mode();
231 } else if (j_read_string_at(obj, "id", &appid)) {
232 if (j_read_string_at(obj, "mode", &modestr)) {
233 mode = launch_mode_of_name(modestr);
235 mode = get_default_launch_mode();
238 if (!is_valid_launch_mode(mode)) {
239 jbus_reply_error_s(smsg, error_bad_request);
243 /* get the application */
244 INFO("method start called for %s mode=%s", appid,
245 name_of_launch_mode(mode));
246 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
247 appli = afm_db_get_application(afdb, appid);
249 appli = afm_udb_get_application_private(afudb, appid);
252 jbus_reply_error_s(smsg, error_not_found);
256 /* launch the application */
258 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
259 runid = afm_run_start(appli, mode, &uri);
261 runid = afm_urun_start(appli);
264 jbus_reply_error_s(smsg, error_cant_start);
270 /* returns only the runid */
271 snprintf(runidstr, sizeof runidstr, "%d", runid);
272 runidstr[sizeof runidstr - 1] = 0;
273 jbus_reply_s(smsg, runidstr);
277 /* returns the runid and its uri */
278 resp = json_object_new_object();
279 if (resp != NULL && j_add_integer(resp, "runid", runid)
280 && j_add_string(resp, "uri", uri))
281 jbus_reply_j(smsg, resp);
283 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
284 afm_run_terminate(runid);
286 afm_urun_terminate(runid);
288 jbus_reply_error_s(smsg, error_system);
290 json_object_put(resp);
295 * On query "once" from 'smsg' with parameters of 'obj'.
297 static void on_once(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
300 struct json_object *appli, *resp;
303 /* get the parameters */
304 if (!j_read_string(obj, &appid) && !j_read_string_at(obj, "id", &appid)) {
305 jbus_reply_error_s(smsg, error_bad_request);
309 /* get the application */
310 INFO("method once called for %s", appid);
311 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
312 appli = afm_db_get_application(afdb, appid);
314 appli = afm_udb_get_application_private(afudb, appid);
317 jbus_reply_error_s(smsg, error_not_found);
321 /* launch the application */
322 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
323 runid = afm_run_once(appli);
325 runid = afm_urun_once(appli);
328 jbus_reply_error_s(smsg, error_cant_start);
332 /* returns the state */
333 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
334 resp = afm_run_state(runid);
336 resp = afm_urun_state(afudb, runid);
338 reply(smsg, resp, error_not_found);
339 json_object_put(resp);
343 * On query "pause" from 'smsg' with parameters of 'obj'.
345 static void on_pause(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
348 if (onrunid(smsg, obj, "pause", &runid)) {
349 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
350 status = afm_run_pause(runid);
352 status = afm_urun_pause(runid);
354 reply_status(smsg, status, error_not_found);
359 * On query "resume" from 'smsg' with parameters of 'obj'.
361 static void on_resume(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
364 if (onrunid(smsg, obj, "resume", &runid)) {
365 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
366 status = afm_run_resume(runid);
368 status = afm_urun_resume(runid);
370 reply_status(smsg, status, error_not_found);
375 * On query "stop" from 'smsg' with parameters of 'obj'.
377 static void on_stop(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
379 NOTICE("call to obsolete 'stop'");
380 on_pause(smsg, obj, unused);
384 * On query "continue" from 'smsg' with parameters of 'obj'.
386 static void on_continue(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
388 NOTICE("call to obsolete 'continue'");
389 on_resume(smsg, obj, unused);
393 * On query "terminate" from 'smsg' with parameters of 'obj'.
395 static void on_terminate(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
398 if (onrunid(smsg, obj, "terminate", &runid)) {
399 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
400 status = afm_run_terminate(runid);
402 status = afm_urun_terminate(runid);
404 reply_status(smsg, status, error_not_found);
409 * On query "runners" from 'smsg' with parameters of 'obj'.
411 static void on_runners(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
413 struct json_object *resp;
414 INFO("method runners called");
415 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
416 resp = afm_run_list();
418 resp = afm_urun_list(afudb);
420 jbus_reply_j(smsg, resp);
421 json_object_put(resp);
425 * On query "state" from 'smsg' with parameters of 'obj'.
427 static void on_state(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
430 struct json_object *resp;
431 if (onrunid(smsg, obj, "state", &runid)) {
432 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
433 resp = afm_run_state(runid);
435 resp = afm_urun_state(afudb, runid);
437 reply(smsg, resp, error_not_found);
438 json_object_put(resp);
443 * Calls the system daemon to achieve application management of
444 * the 'method' gotten from 'smsg' with the parameter's string 'msg'.
446 * The principle is very simple: call the corresponding system method
447 * and reply its response to the caller.
449 * The request and reply is synchronous and is blocking.
450 * It is possible to implment it in an asynchrounous way but it
451 * would brake the common behaviour. It would be a call like
452 * jbus_call_ss(system_bus, method, msg, callback, smsg)
454 static void propagate(struct sd_bus_message *smsg, const char *msg, const char *method)
457 INFO("method %s propagated with %s", method, msg);
458 reply = jbus_call_ss_sync(system_bus, method, msg);
460 jbus_reply_s(smsg, reply);
464 jbus_reply_error_s(smsg, error_system);
467 #if defined(EXPLICIT_CALL)
469 * On query "install" from 'smsg' with parameters of 'msg'.
471 static void on_install(struct sd_bus_message *smsg, const char *msg, void *unused)
473 return propagate(smsg, msg, "install");
477 * On query "uninstall" from 'smsg' with parameters of 'msg'.
479 static void on_uninstall(struct sd_bus_message *smsg, const char *msg, void *unused)
481 return propagate(smsg, msg, "uninstall");
486 * On system signaling that applications list changed
488 static void on_signal_changed(struct json_object *obj, void *unused)
490 /* update the database */
491 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
492 afm_db_update_applications(afdb);
494 afm_udb_update(afudb);
496 /* re-propagate now */
497 jbus_send_signal_j(user_bus, "changed", obj);
501 * Tiny routine to daemonize the program
502 * Return 0 on success or -1 on failure.
504 static int daemonize()
515 * Opens a sd-bus connection and returns it in 'ret'.
516 * The sd-bus connexion is intended to be for user if 'isuser'
517 * is not null. The adress is the default address when 'address'
518 * is NULL or, otherwise, the given address.
519 * It might be necessary to pass the address as an argument because
520 * library systemd uses secure_getenv to retrieves the default
521 * addresses and secure_getenv might return NULL in some cases.
523 static int open_bus(sd_bus **ret, int isuser, const char *address)
529 return (isuser ? sd_bus_default_user : sd_bus_default_system)(ret);
535 rc = sd_bus_set_address(b, address);
539 sd_bus_set_bus_client(b, 1);
541 rc = sd_bus_start(b);
554 * ENTRY POINT OF AFM-USER-DAEMON
556 int main(int ac, char **av)
558 int i, daemon = 0, rc;
559 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
560 enum afm_launch_mode mode;
562 struct sd_event *evloop;
563 struct sd_bus *sysbus, *usrbus;
564 const char *sys_bus_addr, *usr_bus_addr;
568 /* first interpretation of arguments */
571 while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
574 printf(usagestr, appname);
586 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
592 mode = launch_mode_of_name(optarg);
593 if (!is_valid_launch_mode(mode)) {
594 ERROR("invalid mode '%s'", optarg);
597 set_default_launch_mode(mode);
601 usr_bus_addr = optarg;
604 sys_bus_addr = optarg;
607 ERROR("missing argument value");
610 ERROR("unrecognized option");
615 /* init random generator */
616 srandom((unsigned int)time(NULL));
618 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
620 if (afm_run_init()) {
621 ERROR("afm_run_init failed");
626 afdb = afm_db_create();
628 ERROR("afm_db_create failed");
631 if (afm_db_add_root(afdb, FWK_APP_DIR)) {
632 ERROR("can't add root %s", FWK_APP_DIR);
636 /* second interpretation of arguments */
638 while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
641 if (afm_db_add_root(afdb, optarg)) {
642 ERROR("can't add root %s", optarg);
647 if (afm_db_add_application(afdb, optarg)) {
648 ERROR("can't add application %s", optarg);
655 /* update the database */
656 if (afm_db_update_applications(afdb)) {
657 ERROR("afm_update_applications failed");
662 afudb = afm_udb_create(0, 1, "afm-appli-");
664 ERROR("afm_udb_create failed");
669 /* daemonize if requested */
670 if (daemon && daemonize()) {
671 ERROR("daemonization failed");
675 /* get systemd objects */
676 rc = sd_event_new(&evloop);
678 ERROR("can't create event loop");
681 rc = open_bus(&sysbus, 0, sys_bus_addr);
683 ERROR("can't create system bus");
686 rc = sd_bus_attach_event(sysbus, evloop, 0);
688 ERROR("can't attach system bus to event loop");
691 rc = open_bus(&usrbus, 1, usr_bus_addr);
693 ERROR("can't create user bus");
696 rc = sd_bus_attach_event(usrbus, evloop, 0);
698 ERROR("can't attach user bus to event loop");
702 /* connects to the system bus */
703 system_bus = create_jbus(sysbus, AFM_SYSTEM_DBUS_PATH);
705 ERROR("create_jbus failed for system");
709 /* observe signals of system */
710 if(jbus_on_signal_j(system_bus, "changed", on_signal_changed, NULL)) {
711 ERROR("adding signal observer failed");
715 /* connect to the session bus */
716 user_bus = create_jbus(usrbus, AFM_USER_DBUS_PATH);
718 ERROR("create_jbus failed");
723 if (jbus_add_service_j(user_bus, "runnables", on_runnables, NULL)
724 || jbus_add_service_j(user_bus, "detail", on_detail, NULL)
725 || jbus_add_service_j(user_bus, "start", on_start, NULL)
726 || jbus_add_service_j(user_bus, "once", on_once, NULL)
727 || jbus_add_service_j(user_bus, "terminate", on_terminate, NULL)
728 || jbus_add_service_j(user_bus, "pause", on_pause, NULL)
729 || jbus_add_service_j(user_bus, "resume", on_resume, NULL)
730 || jbus_add_service_j(user_bus, "stop", on_stop, NULL)
731 || jbus_add_service_j(user_bus, "continue", on_continue, NULL)
732 || jbus_add_service_j(user_bus, "runners", on_runners, NULL)
733 || jbus_add_service_j(user_bus, "state", on_state, NULL)
734 #if defined(EXPLICIT_CALL)
735 || jbus_add_service_s(user_bus, "install", on_install, NULL)
736 || jbus_add_service_s(user_bus, "uninstall", on_uninstall, NULL)
738 || jbus_add_service_s(user_bus, "install", (void (*)(struct sd_bus_message *, const char *, void *))propagate, "install")
739 || jbus_add_service_s(user_bus, "uninstall", (void (*)(struct sd_bus_message *, const char *, void *))propagate, "uninstall")
742 ERROR("adding services failed");
746 /* start servicing */
747 if (jbus_start_serving(user_bus) < 0) {
748 ERROR("can't start server");
752 /* run until error */
754 sd_event_run(evloop, (uint64_t)-1);