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 version
51 static const char versionstr[] =
53 " %s version="AFM_VERSION"\n"
55 " Copyright (C) 2015, 2016, 2017 \"IoT.bzh\"\n"
56 " AFB comes with ABSOLUTELY NO WARRANTY.\n"
61 * string for printing usage
63 static const char usagestr[] =
64 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
65 "usage: %s [-q] [-v] [-m mode] [-r rootdir]... [-a appdir]...\n"
67 " -a appdir adds an application directory\n"
68 " -r rootdir adds a root directory of applications\n"
69 " -m mode set default launch mode (local or remote)\n"
71 "usage: %s [option(s)]\n"
74 " -d run as a daemon\n"
75 " -u addr address of user D-Bus to use\n"
76 " -s addr address of system D-Bus to use\n"
83 * Option definition for getopt_long
85 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
86 static const char options_s[] = "hdqvVr:a:m:";
87 static struct option options_l[] = {
88 { "root", required_argument, NULL, 'r' },
89 { "application", required_argument, NULL, 'a' },
90 { "mode", required_argument, NULL, 'm' },
92 static const char options_s[] = "hdqvV";
93 static struct option options_l[] = {
95 { "user-dbus", required_argument, NULL, 'u' },
96 { "system-dbus", required_argument, NULL, 's' },
97 { "daemon", no_argument, NULL, 'd' },
98 { "quiet", no_argument, NULL, 'q' },
99 { "verbose", no_argument, NULL, 'v' },
100 { "help", no_argument, NULL, 'h' },
101 { "version", no_argument, NULL, 'V' },
106 * Connections to D-Bus
107 * This is an array for using the function
108 * jbus_read_write_dispatch_multiple
109 * directly without transformations.
111 static struct jbus *jbuses[2];
112 #define system_bus jbuses[0]
113 #define user_bus jbuses[1]
116 * Handle to the database of applications
118 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
119 static struct afm_db *afdb;
121 static struct afm_udb *afudb;
125 * Returned error strings
127 const char error_nothing[] = "[]";
128 const char error_bad_request[] = "\"bad request\"";
129 const char error_not_found[] = "\"not found\"";
130 const char error_cant_start[] = "\"can't start\"";
131 const char error_system[] = "\"system error\"";
135 * retrieves the 'runid' in 'obj' parameters received with the
136 * request 'smsg' for the 'method'.
138 * Returns 1 in case of success.
139 * Otherwise, if the 'runid' can't be retrived, an error stating
140 * the bad request is replied for 'smsg' and 0 is returned.
142 static int onrunid(struct sd_bus_message *smsg, struct json_object *obj,
143 const char *method, int *runid)
145 if (!j_read_integer(obj, runid)
146 && !j_read_integer_at(obj, "runid", runid)) {
147 INFO("bad request method %s: %s", method,
148 json_object_to_json_string(obj));
149 jbus_reply_error_s(smsg, error_bad_request);
153 INFO("method %s called for %d", method, *runid);
158 * Sends the reply 'resp' to the request 'smsg' if 'resp' is not NULL.
159 * Otherwise, when 'resp' is NULL replies the error string 'errstr'.
161 static void reply(struct sd_bus_message *smsg, struct json_object *resp,
165 jbus_reply_j(smsg, resp);
167 jbus_reply_error_s(smsg, errstr);
171 * Sends the reply "true" to the request 'smsg' if 'status' is zero.
172 * Otherwise, when 'status' is not zero replies the error string 'errstr'.
174 static void reply_status(struct sd_bus_message *smsg, int status, const char *errstr)
177 jbus_reply_error_s(smsg, errstr);
179 jbus_reply_s(smsg, "true");
183 * On query "runnables" from 'smsg' with parameters of 'obj'.
185 * Nothing is expected in 'obj' that can be anything.
187 static void on_runnables(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
189 struct json_object *resp;
190 INFO("method runnables called");
191 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
192 resp = afm_db_application_list(afdb);
194 resp = afm_udb_applications_public(afudb);
196 jbus_reply_j(smsg, resp);
197 json_object_put(resp);
201 * On query "detail" from 'smsg' with parameters of 'obj'.
203 static void on_detail(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
206 struct json_object *resp;
208 /* get the parameters */
209 if (j_read_string(obj, &appid))
210 ; /* appid as a string */
211 else if (j_read_string_at(obj, "id", &appid))
212 ; /* appid as obj.id string */
214 INFO("method detail called but bad request!");
215 jbus_reply_error_s(smsg, error_bad_request);
219 /* wants details for appid */
220 INFO("method detail called for %s", appid);
221 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
222 resp = afm_db_get_application_public(afdb, appid);
224 resp = afm_udb_get_application_public(afudb, appid);
226 reply(smsg, resp, error_not_found);
227 json_object_put(resp);
231 * On query "start" from 'smsg' with parameters of 'obj'.
233 static void on_start(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
235 const char *appid, *modestr;
237 struct json_object *appli, *resp;
240 enum afm_launch_mode mode;
242 /* get the parameters */
243 mode = invalid_launch_mode;
244 if (j_read_string(obj, &appid)) {
245 mode = get_default_launch_mode();
246 } else if (j_read_string_at(obj, "id", &appid)) {
247 if (j_read_string_at(obj, "mode", &modestr)) {
248 mode = launch_mode_of_name(modestr);
250 mode = get_default_launch_mode();
253 if (!is_valid_launch_mode(mode)) {
254 jbus_reply_error_s(smsg, error_bad_request);
258 /* get the application */
259 INFO("method start called for %s mode=%s", appid,
260 name_of_launch_mode(mode));
261 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
262 appli = afm_db_get_application(afdb, appid);
264 appli = afm_udb_get_application_private(afudb, appid);
267 jbus_reply_error_s(smsg, error_not_found);
271 /* launch the application */
273 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
274 runid = afm_run_start(appli, mode, &uri);
276 runid = afm_urun_start(appli);
279 jbus_reply_error_s(smsg, error_cant_start);
285 /* returns only the runid */
286 snprintf(runidstr, sizeof runidstr, "%d", runid);
287 runidstr[sizeof runidstr - 1] = 0;
288 jbus_reply_s(smsg, runidstr);
292 /* returns the runid and its uri */
293 resp = json_object_new_object();
294 if (resp != NULL && j_add_integer(resp, "runid", runid)
295 && j_add_string(resp, "uri", uri))
296 jbus_reply_j(smsg, resp);
298 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
299 afm_run_terminate(runid);
301 afm_urun_terminate(runid);
303 jbus_reply_error_s(smsg, error_system);
305 json_object_put(resp);
310 * On query "once" from 'smsg' with parameters of 'obj'.
312 static void on_once(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
315 struct json_object *appli, *resp;
318 /* get the parameters */
319 if (!j_read_string(obj, &appid) && !j_read_string_at(obj, "id", &appid)) {
320 jbus_reply_error_s(smsg, error_bad_request);
324 /* get the application */
325 INFO("method once called for %s", appid);
326 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
327 appli = afm_db_get_application(afdb, appid);
329 appli = afm_udb_get_application_private(afudb, appid);
332 jbus_reply_error_s(smsg, error_not_found);
336 /* launch the application */
337 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
338 runid = afm_run_once(appli);
340 runid = afm_urun_once(appli);
343 jbus_reply_error_s(smsg, error_cant_start);
347 /* returns the state */
348 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
349 resp = afm_run_state(runid);
351 resp = afm_urun_state(afudb, runid);
353 reply(smsg, resp, error_not_found);
354 json_object_put(resp);
358 * On query "pause" from 'smsg' with parameters of 'obj'.
360 static void on_pause(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
363 if (onrunid(smsg, obj, "pause", &runid)) {
364 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
365 status = afm_run_pause(runid);
367 status = afm_urun_pause(runid);
369 reply_status(smsg, status, error_not_found);
374 * On query "resume" from 'smsg' with parameters of 'obj'.
376 static void on_resume(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
379 if (onrunid(smsg, obj, "resume", &runid)) {
380 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
381 status = afm_run_resume(runid);
383 status = afm_urun_resume(runid);
385 reply_status(smsg, status, error_not_found);
390 * On query "stop" from 'smsg' with parameters of 'obj'.
392 static void on_stop(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
394 NOTICE("call to obsolete 'stop'");
395 on_pause(smsg, obj, unused);
399 * On query "continue" from 'smsg' with parameters of 'obj'.
401 static void on_continue(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
403 NOTICE("call to obsolete 'continue'");
404 on_resume(smsg, obj, unused);
408 * On query "terminate" from 'smsg' with parameters of 'obj'.
410 static void on_terminate(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
413 if (onrunid(smsg, obj, "terminate", &runid)) {
414 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
415 status = afm_run_terminate(runid);
417 status = afm_urun_terminate(runid);
419 reply_status(smsg, status, error_not_found);
424 * On query "runners" from 'smsg' with parameters of 'obj'.
426 static void on_runners(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
428 struct json_object *resp;
429 INFO("method runners called");
430 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
431 resp = afm_run_list();
433 resp = afm_urun_list(afudb);
435 jbus_reply_j(smsg, resp);
436 json_object_put(resp);
440 * On query "state" from 'smsg' with parameters of 'obj'.
442 static void on_state(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
445 struct json_object *resp;
446 if (onrunid(smsg, obj, "state", &runid)) {
447 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
448 resp = afm_run_state(runid);
450 resp = afm_urun_state(afudb, runid);
452 reply(smsg, resp, error_not_found);
453 json_object_put(resp);
458 * Calls the system daemon to achieve application management of
459 * the 'method' gotten from 'smsg' with the parameter's string 'msg'.
461 * The principle is very simple: call the corresponding system method
462 * and reply its response to the caller.
464 * The request and reply is synchronous and is blocking.
465 * It is possible to implment it in an asynchrounous way but it
466 * would brake the common behaviour. It would be a call like
467 * jbus_call_ss(system_bus, method, msg, callback, smsg)
469 static void propagate(struct sd_bus_message *smsg, const char *msg, const char *method)
472 INFO("method %s propagated with %s", method, msg);
473 reply = jbus_call_ss_sync(system_bus, method, msg);
475 jbus_reply_s(smsg, reply);
479 jbus_reply_error_s(smsg, error_system);
482 #if defined(EXPLICIT_CALL)
484 * On query "install" from 'smsg' with parameters of 'msg'.
486 static void on_install(struct sd_bus_message *smsg, const char *msg, void *unused)
488 return propagate(smsg, msg, "install");
492 * On query "uninstall" from 'smsg' with parameters of 'msg'.
494 static void on_uninstall(struct sd_bus_message *smsg, const char *msg, void *unused)
496 return propagate(smsg, msg, "uninstall");
501 * On system signaling that applications list changed
503 static void on_signal_changed(struct json_object *obj, void *unused)
505 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
506 /* update the database */
507 afm_db_update_applications(afdb);
509 /* enforce daemon reload */
510 systemd_daemon_reload(1);
511 systemd_unit_restart_name(1, "sockets.target");
513 /* update the database */
514 afm_udb_update(afudb);
516 /* re-propagate now */
517 jbus_send_signal_j(user_bus, "changed", obj);
521 * Tiny routine to daemonize the program
522 * Return 0 on success or -1 on failure.
524 static int daemonize()
535 * Opens a sd-bus connection and returns it in 'ret'.
536 * The sd-bus connexion is intended to be for user if 'isuser'
537 * is not null. The adress is the default address when 'address'
538 * is NULL or, otherwise, the given address.
539 * It might be necessary to pass the address as an argument because
540 * library systemd uses secure_getenv to retrieves the default
541 * addresses and secure_getenv might return NULL in some cases.
543 static int open_bus(sd_bus **ret, int isuser, const char *address)
549 return (isuser ? sd_bus_default_user : sd_bus_default_system)(ret);
555 rc = sd_bus_set_address(b, address);
559 sd_bus_set_bus_client(b, 1);
561 rc = sd_bus_start(b);
574 * ENTRY POINT OF AFM-USER-DAEMON
576 int main(int ac, char **av)
578 int i, daemon = 0, rc;
579 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
580 enum afm_launch_mode mode;
582 struct sd_event *evloop;
583 struct sd_bus *sysbus, *usrbus;
584 const char *sys_bus_addr, *usr_bus_addr;
588 /* first interpretation of arguments */
591 while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
594 printf(usagestr, appname);
597 printf(versionstr, appname);
609 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
615 mode = launch_mode_of_name(optarg);
616 if (!is_valid_launch_mode(mode)) {
617 ERROR("invalid mode '%s'", optarg);
620 set_default_launch_mode(mode);
624 usr_bus_addr = optarg;
627 sys_bus_addr = optarg;
630 ERROR("missing argument value");
633 ERROR("unrecognized option");
638 /* init random generator */
639 srandom((unsigned int)time(NULL));
641 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
643 if (afm_run_init()) {
644 ERROR("afm_run_init failed");
649 afdb = afm_db_create();
651 ERROR("afm_db_create failed");
654 if (afm_db_add_root(afdb, FWK_APP_DIR)) {
655 ERROR("can't add root %s", FWK_APP_DIR);
659 /* second interpretation of arguments */
661 while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
664 if (afm_db_add_root(afdb, optarg)) {
665 ERROR("can't add root %s", optarg);
670 if (afm_db_add_application(afdb, optarg)) {
671 ERROR("can't add application %s", optarg);
678 /* update the database */
679 if (afm_db_update_applications(afdb)) {
680 ERROR("afm_update_applications failed");
685 afudb = afm_udb_create(0, 1, "afm-appli-");
687 ERROR("afm_udb_create failed");
692 /* daemonize if requested */
693 if (daemon && daemonize()) {
694 ERROR("daemonization failed");
698 /* get systemd objects */
699 rc = sd_event_new(&evloop);
701 ERROR("can't create event loop");
704 rc = open_bus(&sysbus, 0, sys_bus_addr);
706 ERROR("can't create system bus");
709 rc = sd_bus_attach_event(sysbus, evloop, 0);
711 ERROR("can't attach system bus to event loop");
714 rc = open_bus(&usrbus, 1, usr_bus_addr);
716 ERROR("can't create user bus");
719 rc = sd_bus_attach_event(usrbus, evloop, 0);
721 ERROR("can't attach user bus to event loop");
725 /* connects to the system bus */
726 system_bus = create_jbus(sysbus, AFM_SYSTEM_DBUS_PATH);
728 ERROR("create_jbus failed for system");
732 /* observe signals of system */
733 if(jbus_on_signal_j(system_bus, "changed", on_signal_changed, NULL)) {
734 ERROR("adding signal observer failed");
738 /* connect to the session bus */
739 user_bus = create_jbus(usrbus, AFM_USER_DBUS_PATH);
741 ERROR("create_jbus failed");
746 if (jbus_add_service_j(user_bus, "runnables", on_runnables, NULL)
747 || jbus_add_service_j(user_bus, "detail", on_detail, NULL)
748 || jbus_add_service_j(user_bus, "start", on_start, NULL)
749 || jbus_add_service_j(user_bus, "once", on_once, NULL)
750 || jbus_add_service_j(user_bus, "terminate", on_terminate, NULL)
751 || jbus_add_service_j(user_bus, "pause", on_pause, NULL)
752 || jbus_add_service_j(user_bus, "resume", on_resume, NULL)
753 || jbus_add_service_j(user_bus, "stop", on_stop, NULL)
754 || jbus_add_service_j(user_bus, "continue", on_continue, NULL)
755 || jbus_add_service_j(user_bus, "runners", on_runners, NULL)
756 || jbus_add_service_j(user_bus, "state", on_state, NULL)
757 #if defined(EXPLICIT_CALL)
758 || jbus_add_service_s(user_bus, "install", on_install, NULL)
759 || jbus_add_service_s(user_bus, "uninstall", on_uninstall, NULL)
761 || jbus_add_service_s(user_bus, "install", (void (*)(struct sd_bus_message *, const char *, void *))propagate, "install")
762 || jbus_add_service_s(user_bus, "uninstall", (void (*)(struct sd_bus_message *, const char *, void *))propagate, "uninstall")
765 ERROR("adding services failed");
769 /* start servicing */
770 if (jbus_start_serving(user_bus) < 0) {
771 ERROR("can't start server");
775 /* run until error */
777 sd_event_run(evloop, (uint64_t)-1);