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"
34 #include "afm-launch-mode.h"
38 * name of the application
40 static const char appname[] = "afm-user-daemon";
43 * string for printing usage
45 static const char usagestr[] =
46 "usage: %s [-q] [-v] [-m mode] [-r rootdir]... [-a appdir]...\n"
48 " -a appdir adds an application directory\n"
49 " -r rootdir adds a root directory of applications\n"
50 " -m mode set default launch mode (local or remote)\n"
51 " -d run as a daemon\n"
52 " -u addr address of user D-Bus to use\n"
53 " -s addr address of system D-Bus to use\n"
59 * Option definition for getopt_long
61 static const char options_s[] = "hdqvr:a:m:";
62 static struct option options_l[] = {
63 { "root", required_argument, NULL, 'r' },
64 { "application", required_argument, NULL, 'a' },
65 { "mode", required_argument, NULL, 'm' },
66 { "user-dbus", required_argument, NULL, 'u' },
67 { "system-dbus", required_argument, NULL, 's' },
68 { "daemon", no_argument, NULL, 'd' },
69 { "quiet", no_argument, NULL, 'q' },
70 { "verbose", no_argument, NULL, 'v' },
71 { "help", no_argument, NULL, 'h' },
76 * Connections to D-Bus
77 * This is an array for using the function
78 * jbus_read_write_dispatch_multiple
79 * directly without transformations.
81 static struct jbus *jbuses[2];
82 #define system_bus jbuses[0]
83 #define user_bus jbuses[1]
86 * Handle to the database of applications
88 static struct afm_db *afdb;
91 * Returned error strings
93 const char error_nothing[] = "[]";
94 const char error_bad_request[] = "\"bad request\"";
95 const char error_not_found[] = "\"not found\"";
96 const char error_cant_start[] = "\"can't start\"";
97 const char error_system[] = "\"system error\"";
101 * retrieves the 'runid' in 'obj' parameters received with the
102 * request 'smsg' for the 'method'.
104 * Returns 1 in case of success.
105 * Otherwise, if the 'runid' can't be retrived, an error stating
106 * the bad request is replied for 'smsg' and 0 is returned.
108 static int onrunid(struct sd_bus_message *smsg, struct json_object *obj,
109 const char *method, int *runid)
111 if (!j_read_integer(obj, runid)
112 && !j_read_integer_at(obj, "runid", runid)) {
113 INFO("bad request method %s: %s", method,
114 json_object_to_json_string(obj));
115 jbus_reply_error_s(smsg, error_bad_request);
119 INFO("method %s called for %d", method, *runid);
124 * Sends the reply 'resp' to the request 'smsg' if 'resp' is not NULL.
125 * Otherwise, when 'resp' is NULL replies the error string 'errstr'.
127 static void reply(struct sd_bus_message *smsg, struct json_object *resp,
131 jbus_reply_j(smsg, resp);
133 jbus_reply_error_s(smsg, errstr);
137 * Sends the reply "true" to the request 'smsg' if 'status' is zero.
138 * Otherwise, when 'status' is not zero replies the error string 'errstr'.
140 static void reply_status(struct sd_bus_message *smsg, int status, const char *errstr)
143 jbus_reply_error_s(smsg, errstr);
145 jbus_reply_s(smsg, "true");
149 * On query "runnables" from 'smsg' with parameters of 'obj'.
151 * Nothing is expected in 'obj' that can be anything.
153 static void on_runnables(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
155 struct json_object *resp;
156 INFO("method runnables called");
157 resp = afm_db_application_list(afdb);
158 jbus_reply_j(smsg, resp);
159 json_object_put(resp);
163 * On query "detail" from 'smsg' with parameters of 'obj'.
165 static void on_detail(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
168 struct json_object *resp;
170 /* get the parameters */
171 if (j_read_string(obj, &appid))
172 ; /* appid as a string */
173 else if (j_read_string_at(obj, "id", &appid))
174 ; /* appid as obj.id string */
176 INFO("method detail called but bad request!");
177 jbus_reply_error_s(smsg, error_bad_request);
181 /* wants details for appid */
182 INFO("method detail called for %s", appid);
183 resp = afm_db_get_application_public(afdb, appid);
184 reply(smsg, resp, error_not_found);
185 json_object_put(resp);
189 * On query "start" from 'smsg' with parameters of 'obj'.
191 static void on_start(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
193 const char *appid, *modestr;
195 struct json_object *appli, *resp;
198 enum afm_launch_mode mode;
200 /* get the parameters */
201 mode = invalid_launch_mode;
202 if (j_read_string(obj, &appid)) {
203 mode = get_default_launch_mode();
204 } else if (j_read_string_at(obj, "id", &appid)) {
205 if (j_read_string_at(obj, "mode", &modestr)) {
206 mode = launch_mode_of_name(modestr);
208 mode = get_default_launch_mode();
211 if (!is_valid_launch_mode(mode)) {
212 jbus_reply_error_s(smsg, error_bad_request);
216 /* get the application */
217 INFO("method start called for %s mode=%s", appid,
218 name_of_launch_mode(mode));
219 appli = afm_db_get_application(afdb, appid);
221 jbus_reply_error_s(smsg, error_not_found);
225 /* launch the application */
227 runid = afm_run_start(appli, mode, &uri);
229 jbus_reply_error_s(smsg, error_cant_start);
235 /* returns only the runid */
236 snprintf(runidstr, sizeof runidstr, "%d", runid);
237 runidstr[sizeof runidstr - 1] = 0;
238 jbus_reply_s(smsg, runidstr);
242 /* returns the runid and its uri */
243 resp = json_object_new_object();
244 if (resp != NULL && j_add_integer(resp, "runid", runid)
245 && j_add_string(resp, "uri", uri))
246 jbus_reply_j(smsg, resp);
248 afm_run_terminate(runid);
249 jbus_reply_error_s(smsg, error_system);
251 json_object_put(resp);
256 * On query "once" from 'smsg' with parameters of 'obj'.
258 static void on_once(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
261 struct json_object *appli, *resp;
264 /* get the parameters */
265 if (!j_read_string(obj, &appid) && !j_read_string_at(obj, "id", &appid)) {
266 jbus_reply_error_s(smsg, error_bad_request);
270 /* get the application */
271 INFO("method once called for %s", appid);
272 appli = afm_db_get_application(afdb, appid);
274 jbus_reply_error_s(smsg, error_not_found);
278 /* launch the application */
279 runid = afm_run_once(appli);
281 jbus_reply_error_s(smsg, error_cant_start);
285 /* returns the state */
286 resp = afm_run_state(runid);
287 reply(smsg, resp, error_not_found);
288 json_object_put(resp);
292 * On query "pause" from 'smsg' with parameters of 'obj'.
294 static void on_pause(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
297 if (onrunid(smsg, obj, "pause", &runid)) {
298 status = afm_run_pause(runid);
299 reply_status(smsg, status, error_not_found);
304 * On query "resume" from 'smsg' with parameters of 'obj'.
306 static void on_resume(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
309 if (onrunid(smsg, obj, "resume", &runid)) {
310 status = afm_run_resume(runid);
311 reply_status(smsg, status, error_not_found);
316 * On query "stop" from 'smsg' with parameters of 'obj'.
318 static void on_stop(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
320 NOTICE("call to obsolete 'stop'");
321 on_pause(smsg, obj, unused);
325 * On query "continue" from 'smsg' with parameters of 'obj'.
327 static void on_continue(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
329 NOTICE("call to obsolete 'continue'");
330 on_resume(smsg, obj, unused);
334 * On query "terminate" from 'smsg' with parameters of 'obj'.
336 static void on_terminate(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
339 if (onrunid(smsg, obj, "terminate", &runid)) {
340 status = afm_run_terminate(runid);
341 reply_status(smsg, status, error_not_found);
346 * On query "runners" from 'smsg' with parameters of 'obj'.
348 static void on_runners(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
350 struct json_object *resp;
351 INFO("method runners called");
352 resp = afm_run_list();
353 jbus_reply_j(smsg, resp);
354 json_object_put(resp);
358 * On query "state" from 'smsg' with parameters of 'obj'.
360 static void on_state(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
363 struct json_object *resp;
364 if (onrunid(smsg, obj, "state", &runid)) {
365 resp = afm_run_state(runid);
366 reply(smsg, resp, error_not_found);
367 json_object_put(resp);
372 * Calls the system daemon to achieve application management of
373 * the 'method' gotten from 'smsg' with the parameter's string 'msg'.
375 * The principle is very simple: call the corresponding system method
376 * and reply its response to the caller.
378 * The request and reply is synchronous and is blocking.
379 * It is possible to implment it in an asynchrounous way but it
380 * would brake the common behaviour. It would be a call like
381 * jbus_call_ss(system_bus, method, msg, callback, smsg)
383 static void propagate(struct sd_bus_message *smsg, const char *msg, const char *method)
386 INFO("method %s propagated with %s", method, msg);
387 reply = jbus_call_ss_sync(system_bus, method, msg);
389 jbus_reply_s(smsg, reply);
393 jbus_reply_error_s(smsg, error_system);
396 #if defined(EXPLICIT_CALL)
398 * On query "install" from 'smsg' with parameters of 'msg'.
400 static void on_install(struct sd_bus_message *smsg, const char *msg, void *unused)
402 return propagate(smsg, msg, "install");
406 * On query "uninstall" from 'smsg' with parameters of 'msg'.
408 static void on_uninstall(struct sd_bus_message *smsg, const char *msg, void *unused)
410 return propagate(smsg, msg, "uninstall");
415 * On system signaling that applications list changed
417 static void on_signal_changed(struct json_object *obj, void *unused)
419 /* update the database */
420 afm_db_update_applications(afdb);
421 /* re-propagate now */
422 jbus_send_signal_j(user_bus, "changed", obj);
426 * Tiny routine to daemonize the program
427 * Return 0 on success or -1 on failure.
429 static int daemonize()
440 * Opens a sd-bus connection and returns it in 'ret'.
441 * The sd-bus connexion is intended to be for user if 'isuser'
442 * is not null. The adress is the default address when 'address'
443 * is NULL or, otherwise, the given address.
444 * It might be necessary to pass the address as an argument because
445 * library systemd uses secure_getenv to retrieves the default
446 * addresses and secure_getenv might return NULL in some cases.
448 static int open_bus(sd_bus **ret, int isuser, const char *address)
454 return (isuser ? sd_bus_default_user : sd_bus_default_system)(ret);
460 rc = sd_bus_set_address(b, address);
464 sd_bus_set_bus_client(b, 1);
466 rc = sd_bus_start(b);
479 * ENTRY POINT OF AFM-USER-DAEMON
481 int main(int ac, char **av)
483 int i, daemon = 0, rc;
484 enum afm_launch_mode mode;
485 struct sd_event *evloop;
486 struct sd_bus *sysbus, *usrbus;
487 const char *sys_bus_addr, *usr_bus_addr;
491 /* first interpretation of arguments */
494 while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
497 printf(usagestr, appname);
514 mode = launch_mode_of_name(optarg);
515 if (!is_valid_launch_mode(mode)) {
516 ERROR("invalid mode '%s'", optarg);
519 set_default_launch_mode(mode);
522 usr_bus_addr = optarg;
525 sys_bus_addr = optarg;
528 ERROR("missing argument value");
531 ERROR("unrecognized option");
536 /* init random generator */
537 srandom((unsigned int)time(NULL));
540 if (afm_run_init()) {
541 ERROR("afm_run_init failed");
546 afdb = afm_db_create();
548 ERROR("afm_create failed");
551 if (afm_db_add_root(afdb, FWK_APP_DIR)) {
552 ERROR("can't add root %s", FWK_APP_DIR);
556 /* second interpretation of arguments */
558 while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
561 if (afm_db_add_root(afdb, optarg)) {
562 ERROR("can't add root %s", optarg);
567 if (afm_db_add_application(afdb, optarg)) {
568 ERROR("can't add application %s", optarg);
575 /* update the database */
576 if (afm_db_update_applications(afdb)) {
577 ERROR("afm_update_applications failed");
581 /* daemonize if requested */
582 if (daemon && daemonize()) {
583 ERROR("daemonization failed");
587 /* get systemd objects */
588 rc = sd_event_new(&evloop);
590 ERROR("can't create event loop");
593 rc = open_bus(&sysbus, 0, sys_bus_addr);
595 ERROR("can't create system bus");
598 rc = sd_bus_attach_event(sysbus, evloop, 0);
600 ERROR("can't attach system bus to event loop");
603 rc = open_bus(&usrbus, 1, usr_bus_addr);
605 ERROR("can't create user bus");
608 rc = sd_bus_attach_event(usrbus, evloop, 0);
610 ERROR("can't attach user bus to event loop");
614 /* connects to the system bus */
615 system_bus = create_jbus(sysbus, AFM_SYSTEM_DBUS_PATH);
617 ERROR("create_jbus failed for system");
621 /* observe signals of system */
622 if(jbus_on_signal_j(system_bus, "changed", on_signal_changed, NULL)) {
623 ERROR("adding signal observer failed");
627 /* connect to the session bus */
628 user_bus = create_jbus(usrbus, AFM_USER_DBUS_PATH);
630 ERROR("create_jbus failed");
635 if (jbus_add_service_j(user_bus, "runnables", on_runnables, NULL)
636 || jbus_add_service_j(user_bus, "detail", on_detail, NULL)
637 || jbus_add_service_j(user_bus, "start", on_start, NULL)
638 || jbus_add_service_j(user_bus, "once", on_once, NULL)
639 || jbus_add_service_j(user_bus, "terminate", on_terminate, NULL)
640 || jbus_add_service_j(user_bus, "pause", on_pause, NULL)
641 || jbus_add_service_j(user_bus, "resume", on_resume, NULL)
642 || jbus_add_service_j(user_bus, "stop", on_stop, NULL)
643 || jbus_add_service_j(user_bus, "continue", on_continue, NULL)
644 || jbus_add_service_j(user_bus, "runners", on_runners, NULL)
645 || jbus_add_service_j(user_bus, "state", on_state, NULL)
646 #if defined(EXPLICIT_CALL)
647 || jbus_add_service_s(user_bus, "install", on_install, NULL)
648 || jbus_add_service_s(user_bus, "uninstall", on_uninstall, NULL)
650 || jbus_add_service_s(user_bus, "install", (void (*)(struct sd_bus_message *, const char *, void *))propagate, "install")
651 || jbus_add_service_s(user_bus, "uninstall", (void (*)(struct sd_bus_message *, const char *, void *))propagate, "uninstall")
654 ERROR("adding services failed");
658 /* start servicing */
659 if (jbus_start_serving(user_bus) < 0) {
660 ERROR("can't start server");
664 /* run until error */
666 sd_event_run(evloop, (uint64_t)-1);