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 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
492 /* update the database */
493 afm_db_update_applications(afdb);
495 /* enforce daemon reload */
496 systemd_daemon_reload(1);
497 systemd_unit_restart_name(1, "sockets.target");
499 /* update the database */
500 afm_udb_update(afudb);
502 /* re-propagate now */
503 jbus_send_signal_j(user_bus, "changed", obj);
507 * Tiny routine to daemonize the program
508 * Return 0 on success or -1 on failure.
510 static int daemonize()
521 * Opens a sd-bus connection and returns it in 'ret'.
522 * The sd-bus connexion is intended to be for user if 'isuser'
523 * is not null. The adress is the default address when 'address'
524 * is NULL or, otherwise, the given address.
525 * It might be necessary to pass the address as an argument because
526 * library systemd uses secure_getenv to retrieves the default
527 * addresses and secure_getenv might return NULL in some cases.
529 static int open_bus(sd_bus **ret, int isuser, const char *address)
535 return (isuser ? sd_bus_default_user : sd_bus_default_system)(ret);
541 rc = sd_bus_set_address(b, address);
545 sd_bus_set_bus_client(b, 1);
547 rc = sd_bus_start(b);
560 * ENTRY POINT OF AFM-USER-DAEMON
562 int main(int ac, char **av)
564 int i, daemon = 0, rc;
565 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
566 enum afm_launch_mode mode;
568 struct sd_event *evloop;
569 struct sd_bus *sysbus, *usrbus;
570 const char *sys_bus_addr, *usr_bus_addr;
574 /* first interpretation of arguments */
577 while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
580 printf(usagestr, appname);
592 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
598 mode = launch_mode_of_name(optarg);
599 if (!is_valid_launch_mode(mode)) {
600 ERROR("invalid mode '%s'", optarg);
603 set_default_launch_mode(mode);
607 usr_bus_addr = optarg;
610 sys_bus_addr = optarg;
613 ERROR("missing argument value");
616 ERROR("unrecognized option");
621 /* init random generator */
622 srandom((unsigned int)time(NULL));
624 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
626 if (afm_run_init()) {
627 ERROR("afm_run_init failed");
632 afdb = afm_db_create();
634 ERROR("afm_db_create failed");
637 if (afm_db_add_root(afdb, FWK_APP_DIR)) {
638 ERROR("can't add root %s", FWK_APP_DIR);
642 /* second interpretation of arguments */
644 while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
647 if (afm_db_add_root(afdb, optarg)) {
648 ERROR("can't add root %s", optarg);
653 if (afm_db_add_application(afdb, optarg)) {
654 ERROR("can't add application %s", optarg);
661 /* update the database */
662 if (afm_db_update_applications(afdb)) {
663 ERROR("afm_update_applications failed");
668 afudb = afm_udb_create(0, 1, "afm-appli-");
670 ERROR("afm_udb_create failed");
675 /* daemonize if requested */
676 if (daemon && daemonize()) {
677 ERROR("daemonization failed");
681 /* get systemd objects */
682 rc = sd_event_new(&evloop);
684 ERROR("can't create event loop");
687 rc = open_bus(&sysbus, 0, sys_bus_addr);
689 ERROR("can't create system bus");
692 rc = sd_bus_attach_event(sysbus, evloop, 0);
694 ERROR("can't attach system bus to event loop");
697 rc = open_bus(&usrbus, 1, usr_bus_addr);
699 ERROR("can't create user bus");
702 rc = sd_bus_attach_event(usrbus, evloop, 0);
704 ERROR("can't attach user bus to event loop");
708 /* connects to the system bus */
709 system_bus = create_jbus(sysbus, AFM_SYSTEM_DBUS_PATH);
711 ERROR("create_jbus failed for system");
715 /* observe signals of system */
716 if(jbus_on_signal_j(system_bus, "changed", on_signal_changed, NULL)) {
717 ERROR("adding signal observer failed");
721 /* connect to the session bus */
722 user_bus = create_jbus(usrbus, AFM_USER_DBUS_PATH);
724 ERROR("create_jbus failed");
729 if (jbus_add_service_j(user_bus, "runnables", on_runnables, NULL)
730 || jbus_add_service_j(user_bus, "detail", on_detail, NULL)
731 || jbus_add_service_j(user_bus, "start", on_start, NULL)
732 || jbus_add_service_j(user_bus, "once", on_once, NULL)
733 || jbus_add_service_j(user_bus, "terminate", on_terminate, NULL)
734 || jbus_add_service_j(user_bus, "pause", on_pause, NULL)
735 || jbus_add_service_j(user_bus, "resume", on_resume, NULL)
736 || jbus_add_service_j(user_bus, "stop", on_stop, NULL)
737 || jbus_add_service_j(user_bus, "continue", on_continue, NULL)
738 || jbus_add_service_j(user_bus, "runners", on_runners, NULL)
739 || jbus_add_service_j(user_bus, "state", on_state, NULL)
740 #if defined(EXPLICIT_CALL)
741 || jbus_add_service_s(user_bus, "install", on_install, NULL)
742 || jbus_add_service_s(user_bus, "uninstall", on_uninstall, NULL)
744 || jbus_add_service_s(user_bus, "install", (void (*)(struct sd_bus_message *, const char *, void *))propagate, "install")
745 || jbus_add_service_s(user_bus, "uninstall", (void (*)(struct sd_bus_message *, const char *, void *))propagate, "uninstall")
748 ERROR("adding services failed");
752 /* start servicing */
753 if (jbus_start_serving(user_bus) < 0) {
754 ERROR("can't start server");
758 /* run until error */
760 sd_event_run(evloop, (uint64_t)-1);