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"
32 #include "utils-systemd.h"
34 #include "afm-launch-mode.h"
35 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
44 * name of the application
46 static const char appname[] = "afm-user-daemon";
49 * string for printing usage
51 static const char usagestr[] =
52 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
53 "usage: %s [-q] [-v] [-m mode] [-r rootdir]... [-a appdir]...\n"
55 " -a appdir adds an application directory\n"
56 " -r rootdir adds a root directory of applications\n"
57 " -m mode set default launch mode (local or remote)\n"
59 "usage: %s [option(s)]\n"
62 " -d run as a daemon\n"
63 " -u addr address of user D-Bus to use\n"
64 " -s addr address of system D-Bus to use\n"
70 * Option definition for getopt_long
72 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
73 static const char options_s[] = "hdqvr:a:m:";
74 static struct option options_l[] = {
75 { "root", required_argument, NULL, 'r' },
76 { "application", required_argument, NULL, 'a' },
77 { "mode", required_argument, NULL, 'm' },
79 static const char options_s[] = "hdqv";
80 static struct option options_l[] = {
82 { "user-dbus", required_argument, NULL, 'u' },
83 { "system-dbus", required_argument, NULL, 's' },
84 { "daemon", no_argument, NULL, 'd' },
85 { "quiet", no_argument, NULL, 'q' },
86 { "verbose", no_argument, NULL, 'v' },
87 { "help", no_argument, NULL, 'h' },
92 * Connections to D-Bus
93 * This is an array for using the function
94 * jbus_read_write_dispatch_multiple
95 * directly without transformations.
97 static struct jbus *jbuses[2];
98 #define system_bus jbuses[0]
99 #define user_bus jbuses[1]
102 * Handle to the database of applications
104 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
105 static struct afm_db *afdb;
107 static struct afm_udb *afudb;
111 * Returned error strings
113 const char error_nothing[] = "[]";
114 const char error_bad_request[] = "\"bad request\"";
115 const char error_not_found[] = "\"not found\"";
116 const char error_cant_start[] = "\"can't start\"";
117 const char error_system[] = "\"system error\"";
121 * retrieves the 'runid' in 'obj' parameters received with the
122 * request 'smsg' for the 'method'.
124 * Returns 1 in case of success.
125 * Otherwise, if the 'runid' can't be retrived, an error stating
126 * the bad request is replied for 'smsg' and 0 is returned.
128 static int onrunid(struct sd_bus_message *smsg, struct json_object *obj,
129 const char *method, int *runid)
131 if (!j_read_integer(obj, runid)
132 && !j_read_integer_at(obj, "runid", runid)) {
133 INFO("bad request method %s: %s", method,
134 json_object_to_json_string(obj));
135 jbus_reply_error_s(smsg, error_bad_request);
139 INFO("method %s called for %d", method, *runid);
144 * Sends the reply 'resp' to the request 'smsg' if 'resp' is not NULL.
145 * Otherwise, when 'resp' is NULL replies the error string 'errstr'.
147 static void reply(struct sd_bus_message *smsg, struct json_object *resp,
151 jbus_reply_j(smsg, resp);
153 jbus_reply_error_s(smsg, errstr);
157 * Sends the reply "true" to the request 'smsg' if 'status' is zero.
158 * Otherwise, when 'status' is not zero replies the error string 'errstr'.
160 static void reply_status(struct sd_bus_message *smsg, int status, const char *errstr)
163 jbus_reply_error_s(smsg, errstr);
165 jbus_reply_s(smsg, "true");
169 * On query "runnables" from 'smsg' with parameters of 'obj'.
171 * Nothing is expected in 'obj' that can be anything.
173 static void on_runnables(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
175 struct json_object *resp;
176 INFO("method runnables called");
177 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
178 resp = afm_db_application_list(afdb);
180 resp = afm_udb_applications_public(afudb);
182 jbus_reply_j(smsg, resp);
183 json_object_put(resp);
187 * On query "detail" from 'smsg' with parameters of 'obj'.
189 static void on_detail(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
192 struct json_object *resp;
194 /* get the parameters */
195 if (j_read_string(obj, &appid))
196 ; /* appid as a string */
197 else if (j_read_string_at(obj, "id", &appid))
198 ; /* appid as obj.id string */
200 INFO("method detail called but bad request!");
201 jbus_reply_error_s(smsg, error_bad_request);
205 /* wants details for appid */
206 INFO("method detail called for %s", appid);
207 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
208 resp = afm_db_get_application_public(afdb, appid);
210 resp = afm_udb_get_application_public(afudb, appid);
212 reply(smsg, resp, error_not_found);
213 json_object_put(resp);
217 * On query "start" from 'smsg' with parameters of 'obj'.
219 static void on_start(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
221 const char *appid, *modestr;
223 struct json_object *appli, *resp;
226 enum afm_launch_mode mode;
228 /* get the parameters */
229 mode = invalid_launch_mode;
230 if (j_read_string(obj, &appid)) {
231 mode = get_default_launch_mode();
232 } else if (j_read_string_at(obj, "id", &appid)) {
233 if (j_read_string_at(obj, "mode", &modestr)) {
234 mode = launch_mode_of_name(modestr);
236 mode = get_default_launch_mode();
239 if (!is_valid_launch_mode(mode)) {
240 jbus_reply_error_s(smsg, error_bad_request);
244 /* get the application */
245 INFO("method start called for %s mode=%s", appid,
246 name_of_launch_mode(mode));
247 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
248 appli = afm_db_get_application(afdb, appid);
250 appli = afm_udb_get_application_private(afudb, appid);
253 jbus_reply_error_s(smsg, error_not_found);
257 /* launch the application */
259 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
260 runid = afm_run_start(appli, mode, &uri);
262 runid = afm_urun_start(appli);
265 jbus_reply_error_s(smsg, error_cant_start);
271 /* returns only the runid */
272 snprintf(runidstr, sizeof runidstr, "%d", runid);
273 runidstr[sizeof runidstr - 1] = 0;
274 jbus_reply_s(smsg, runidstr);
278 /* returns the runid and its uri */
279 resp = json_object_new_object();
280 if (resp != NULL && j_add_integer(resp, "runid", runid)
281 && j_add_string(resp, "uri", uri))
282 jbus_reply_j(smsg, resp);
284 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
285 afm_run_terminate(runid);
287 afm_urun_terminate(runid);
289 jbus_reply_error_s(smsg, error_system);
291 json_object_put(resp);
296 * On query "once" from 'smsg' with parameters of 'obj'.
298 static void on_once(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
301 struct json_object *appli, *resp;
304 /* get the parameters */
305 if (!j_read_string(obj, &appid) && !j_read_string_at(obj, "id", &appid)) {
306 jbus_reply_error_s(smsg, error_bad_request);
310 /* get the application */
311 INFO("method once called for %s", appid);
312 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
313 appli = afm_db_get_application(afdb, appid);
315 appli = afm_udb_get_application_private(afudb, appid);
318 jbus_reply_error_s(smsg, error_not_found);
322 /* launch the application */
323 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
324 runid = afm_run_once(appli);
326 runid = afm_urun_once(appli);
329 jbus_reply_error_s(smsg, error_cant_start);
333 /* returns the state */
334 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
335 resp = afm_run_state(runid);
337 resp = afm_urun_state(afudb, runid);
339 reply(smsg, resp, error_not_found);
340 json_object_put(resp);
344 * On query "pause" from 'smsg' with parameters of 'obj'.
346 static void on_pause(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
349 if (onrunid(smsg, obj, "pause", &runid)) {
350 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
351 status = afm_run_pause(runid);
353 status = afm_urun_pause(runid);
355 reply_status(smsg, status, error_not_found);
360 * On query "resume" from 'smsg' with parameters of 'obj'.
362 static void on_resume(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
365 if (onrunid(smsg, obj, "resume", &runid)) {
366 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
367 status = afm_run_resume(runid);
369 status = afm_urun_resume(runid);
371 reply_status(smsg, status, error_not_found);
376 * On query "stop" from 'smsg' with parameters of 'obj'.
378 static void on_stop(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
380 NOTICE("call to obsolete 'stop'");
381 on_pause(smsg, obj, unused);
385 * On query "continue" from 'smsg' with parameters of 'obj'.
387 static void on_continue(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
389 NOTICE("call to obsolete 'continue'");
390 on_resume(smsg, obj, unused);
394 * On query "terminate" from 'smsg' with parameters of 'obj'.
396 static void on_terminate(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
399 if (onrunid(smsg, obj, "terminate", &runid)) {
400 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
401 status = afm_run_terminate(runid);
403 status = afm_urun_terminate(runid);
405 reply_status(smsg, status, error_not_found);
410 * On query "runners" from 'smsg' with parameters of 'obj'.
412 static void on_runners(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
414 struct json_object *resp;
415 INFO("method runners called");
416 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
417 resp = afm_run_list();
419 resp = afm_urun_list(afudb);
421 jbus_reply_j(smsg, resp);
422 json_object_put(resp);
426 * On query "state" from 'smsg' with parameters of 'obj'.
428 static void on_state(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
431 struct json_object *resp;
432 if (onrunid(smsg, obj, "state", &runid)) {
433 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
434 resp = afm_run_state(runid);
436 resp = afm_urun_state(afudb, runid);
438 reply(smsg, resp, error_not_found);
439 json_object_put(resp);
444 * Calls the system daemon to achieve application management of
445 * the 'method' gotten from 'smsg' with the parameter's string 'msg'.
447 * The principle is very simple: call the corresponding system method
448 * and reply its response to the caller.
450 * The request and reply is synchronous and is blocking.
451 * It is possible to implment it in an asynchrounous way but it
452 * would brake the common behaviour. It would be a call like
453 * jbus_call_ss(system_bus, method, msg, callback, smsg)
455 static void propagate(struct sd_bus_message *smsg, const char *msg, const char *method)
458 INFO("method %s propagated with %s", method, msg);
459 reply = jbus_call_ss_sync(system_bus, method, msg);
461 jbus_reply_s(smsg, reply);
465 jbus_reply_error_s(smsg, error_system);
468 #if defined(EXPLICIT_CALL)
470 * On query "install" from 'smsg' with parameters of 'msg'.
472 static void on_install(struct sd_bus_message *smsg, const char *msg, void *unused)
474 return propagate(smsg, msg, "install");
478 * On query "uninstall" from 'smsg' with parameters of 'msg'.
480 static void on_uninstall(struct sd_bus_message *smsg, const char *msg, void *unused)
482 return propagate(smsg, msg, "uninstall");
487 * On system signaling that applications list changed
489 static void on_signal_changed(struct json_object *obj, void *unused)
491 /* enforce daemon reload */
492 systemd_daemon_reload(1);
494 /* update the database */
495 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
496 afm_db_update_applications(afdb);
498 afm_udb_update(afudb);
500 /* re-propagate now */
501 jbus_send_signal_j(user_bus, "changed", obj);
505 * Tiny routine to daemonize the program
506 * Return 0 on success or -1 on failure.
508 static int daemonize()
519 * Opens a sd-bus connection and returns it in 'ret'.
520 * The sd-bus connexion is intended to be for user if 'isuser'
521 * is not null. The adress is the default address when 'address'
522 * is NULL or, otherwise, the given address.
523 * It might be necessary to pass the address as an argument because
524 * library systemd uses secure_getenv to retrieves the default
525 * addresses and secure_getenv might return NULL in some cases.
527 static int open_bus(sd_bus **ret, int isuser, const char *address)
533 return (isuser ? sd_bus_default_user : sd_bus_default_system)(ret);
539 rc = sd_bus_set_address(b, address);
543 sd_bus_set_bus_client(b, 1);
545 rc = sd_bus_start(b);
558 * ENTRY POINT OF AFM-USER-DAEMON
560 int main(int ac, char **av)
562 int i, daemon = 0, rc;
563 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
564 enum afm_launch_mode mode;
566 struct sd_event *evloop;
567 struct sd_bus *sysbus, *usrbus;
568 const char *sys_bus_addr, *usr_bus_addr;
572 /* first interpretation of arguments */
575 while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
578 printf(usagestr, appname);
590 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
596 mode = launch_mode_of_name(optarg);
597 if (!is_valid_launch_mode(mode)) {
598 ERROR("invalid mode '%s'", optarg);
601 set_default_launch_mode(mode);
605 usr_bus_addr = optarg;
608 sys_bus_addr = optarg;
611 ERROR("missing argument value");
614 ERROR("unrecognized option");
619 /* init random generator */
620 srandom((unsigned int)time(NULL));
622 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
624 if (afm_run_init()) {
625 ERROR("afm_run_init failed");
630 afdb = afm_db_create();
632 ERROR("afm_db_create failed");
635 if (afm_db_add_root(afdb, FWK_APP_DIR)) {
636 ERROR("can't add root %s", FWK_APP_DIR);
640 /* second interpretation of arguments */
642 while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
645 if (afm_db_add_root(afdb, optarg)) {
646 ERROR("can't add root %s", optarg);
651 if (afm_db_add_application(afdb, optarg)) {
652 ERROR("can't add application %s", optarg);
659 /* update the database */
660 if (afm_db_update_applications(afdb)) {
661 ERROR("afm_update_applications failed");
666 afudb = afm_udb_create(0, 1, "afm-appli-");
668 ERROR("afm_udb_create failed");
673 /* daemonize if requested */
674 if (daemon && daemonize()) {
675 ERROR("daemonization failed");
679 /* get systemd objects */
680 rc = sd_event_new(&evloop);
682 ERROR("can't create event loop");
685 rc = open_bus(&sysbus, 0, sys_bus_addr);
687 ERROR("can't create system bus");
690 rc = sd_bus_attach_event(sysbus, evloop, 0);
692 ERROR("can't attach system bus to event loop");
695 rc = open_bus(&usrbus, 1, usr_bus_addr);
697 ERROR("can't create user bus");
700 rc = sd_bus_attach_event(usrbus, evloop, 0);
702 ERROR("can't attach user bus to event loop");
706 /* connects to the system bus */
707 system_bus = create_jbus(sysbus, AFM_SYSTEM_DBUS_PATH);
709 ERROR("create_jbus failed for system");
713 /* observe signals of system */
714 if(jbus_on_signal_j(system_bus, "changed", on_signal_changed, NULL)) {
715 ERROR("adding signal observer failed");
719 /* connect to the session bus */
720 user_bus = create_jbus(usrbus, AFM_USER_DBUS_PATH);
722 ERROR("create_jbus failed");
727 if (jbus_add_service_j(user_bus, "runnables", on_runnables, NULL)
728 || jbus_add_service_j(user_bus, "detail", on_detail, NULL)
729 || jbus_add_service_j(user_bus, "start", on_start, NULL)
730 || jbus_add_service_j(user_bus, "once", on_once, NULL)
731 || jbus_add_service_j(user_bus, "terminate", on_terminate, NULL)
732 || jbus_add_service_j(user_bus, "pause", on_pause, NULL)
733 || jbus_add_service_j(user_bus, "resume", on_resume, NULL)
734 || jbus_add_service_j(user_bus, "stop", on_stop, NULL)
735 || jbus_add_service_j(user_bus, "continue", on_continue, NULL)
736 || jbus_add_service_j(user_bus, "runners", on_runners, NULL)
737 || jbus_add_service_j(user_bus, "state", on_state, NULL)
738 #if defined(EXPLICIT_CALL)
739 || jbus_add_service_s(user_bus, "install", on_install, NULL)
740 || jbus_add_service_s(user_bus, "uninstall", on_uninstall, NULL)
742 || jbus_add_service_s(user_bus, "install", (void (*)(struct sd_bus_message *, const char *, void *))propagate, "install")
743 || jbus_add_service_s(user_bus, "uninstall", (void (*)(struct sd_bus_message *, const char *, void *))propagate, "uninstall")
746 ERROR("adding services failed");
750 /* start servicing */
751 if (jbus_start_serving(user_bus) < 0) {
752 ERROR("can't start server");
756 /* run until error */
758 sd_event_run(evloop, (uint64_t)-1);