2 Copyright 2015, 2016 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);
190 * On query "start" from 'smsg' with parameters of 'obj'.
192 static void on_start(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
194 const char *appid, *modestr;
196 struct json_object *appli, *resp;
199 enum afm_launch_mode mode;
201 /* get the parameters */
202 mode = invalid_launch_mode;
203 if (j_read_string(obj, &appid)) {
204 mode = get_default_launch_mode();
205 } else if (j_read_string_at(obj, "id", &appid)) {
206 if (j_read_string_at(obj, "mode", &modestr)) {
207 mode = launch_mode_of_name(modestr);
209 mode = get_default_launch_mode();
212 if (!is_valid_launch_mode(mode)) {
213 jbus_reply_error_s(smsg, error_bad_request);
217 /* get the application */
218 INFO("method start called for %s mode=%s", appid,
219 name_of_launch_mode(mode));
220 appli = afm_db_get_application(afdb, appid);
222 jbus_reply_error_s(smsg, error_not_found);
226 /* launch the application */
228 runid = afm_run_start(appli, mode, &uri);
230 jbus_reply_error_s(smsg, error_cant_start);
236 /* returns only the runid */
237 snprintf(runidstr, sizeof runidstr, "%d", runid);
238 runidstr[sizeof runidstr - 1] = 0;
239 jbus_reply_s(smsg, runidstr);
243 /* returns the runid and its uri */
244 resp = json_object_new_object();
245 if (resp != NULL && j_add_integer(resp, "runid", runid)
246 && j_add_string(resp, "uri", uri))
247 jbus_reply_j(smsg, resp);
250 jbus_reply_error_s(smsg, error_system);
252 json_object_put(resp);
257 * On query "stop" from 'smsg' with parameters of 'obj'.
259 static void on_stop(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
262 if (onrunid(smsg, obj, "stop", &runid)) {
263 status = afm_run_stop(runid);
264 reply_status(smsg, status, error_not_found);
269 * On query "continue" from 'smsg' with parameters of 'obj'.
271 static void on_continue(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
274 if (onrunid(smsg, obj, "continue", &runid)) {
275 status = afm_run_continue(runid);
276 reply_status(smsg, status, error_not_found);
281 * On query "terminate" from 'smsg' with parameters of 'obj'.
283 static void on_terminate(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
286 if (onrunid(smsg, obj, "terminate", &runid)) {
287 status = afm_run_terminate(runid);
288 reply_status(smsg, status, error_not_found);
293 * On query "runners" from 'smsg' with parameters of 'obj'.
295 static void on_runners(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
297 struct json_object *resp;
298 INFO("method runners called");
299 resp = afm_run_list();
300 jbus_reply_j(smsg, resp);
301 json_object_put(resp);
305 * On query "state" from 'smsg' with parameters of 'obj'.
307 static void on_state(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
310 struct json_object *resp;
311 if (onrunid(smsg, obj, "state", &runid)) {
312 resp = afm_run_state(runid);
313 reply(smsg, resp, error_not_found);
314 json_object_put(resp);
319 * Calls the system daemon to achieve application management of
320 * the 'method' gotten from 'smsg' with the parameter's string 'msg'.
322 * The principle is very simple: call the corresponding system method
323 * and reply its response to the caller.
325 * The request and reply is synchronous and is blocking.
326 * It is possible to implment it in an asynchrounous way but it
327 * would brake the common behaviour. It would be a call like
328 * jbus_call_ss(system_bus, method, msg, callback, smsg)
330 static void propagate(struct sd_bus_message *smsg, const char *msg, const char *method)
333 INFO("method %s propagated with %s", method, msg);
334 reply = jbus_call_ss_sync(system_bus, method, msg);
336 jbus_reply_s(smsg, reply);
340 jbus_reply_error_s(smsg, error_system);
343 #if defined(EXPLICIT_CALL)
345 * On query "install" from 'smsg' with parameters of 'msg'.
347 static void on_install(struct sd_bus_message *smsg, const char *msg, void *unused)
349 return propagate(smsg, msg, "install");
353 * On query "uninstall" from 'smsg' with parameters of 'msg'.
355 static void on_uninstall(struct sd_bus_message *smsg, const char *msg, void *unused)
357 return propagate(smsg, msg, "uninstall");
362 * On system signaling that applications list changed
364 static void on_signal_changed(struct json_object *obj, void *unused)
366 /* update the database */
367 afm_db_update_applications(afdb);
368 /* re-propagate now */
369 jbus_send_signal_j(user_bus, "changed", obj);
373 * Tiny routine to daemonize the program
374 * Return 0 on success or -1 on failure.
376 static int daemonize()
387 * Opens a sd-bus connection and returns it in 'ret'.
388 * The sd-bus connexion is intended to be for user if 'isuser'
389 * is not null. The adress is the default address when 'address'
390 * is NULL or, otherwise, the given address.
391 * It might be necessary to pass the address as an argument because
392 * library systemd uses secure_getenv to retrieves the default
393 * addresses and secure_getenv might return NULL in some cases.
395 static int open_bus(sd_bus **ret, int isuser, const char *address)
401 return (isuser ? sd_bus_open_user : sd_bus_open_system)(ret);
407 rc = sd_bus_set_address(b, address);
411 sd_bus_set_bus_client(b, 1);
413 rc = sd_bus_start(b);
426 * ENTRY POINT OF AFM-USER-DAEMON
428 int main(int ac, char **av)
430 int i, daemon = 0, rc;
431 enum afm_launch_mode mode;
432 struct sd_event *evloop;
433 struct sd_bus *sysbus, *usrbus;
434 const char *sys_bus_addr, *usr_bus_addr;
438 /* first interpretation of arguments */
441 while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
444 printf(usagestr, appname);
461 mode = launch_mode_of_name(optarg);
462 if (!is_valid_launch_mode(mode)) {
463 ERROR("invalid mode '%s'", optarg);
466 set_default_launch_mode(mode);
469 usr_bus_addr = optarg;
472 sys_bus_addr = optarg;
475 ERROR("missing argument value");
478 ERROR("unrecognized option");
483 /* init random generator */
484 srandom((unsigned int)time(NULL));
487 if (afm_run_init()) {
488 ERROR("afm_run_init failed");
493 afdb = afm_db_create();
495 ERROR("afm_create failed");
498 if (afm_db_add_root(afdb, FWK_APP_DIR)) {
499 ERROR("can't add root %s", FWK_APP_DIR);
503 /* second interpretation of arguments */
505 while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
508 if (afm_db_add_root(afdb, optarg)) {
509 ERROR("can't add root %s", optarg);
514 if (afm_db_add_application(afdb, optarg)) {
515 ERROR("can't add application %s", optarg);
522 /* update the database */
523 if (afm_db_update_applications(afdb)) {
524 ERROR("afm_update_applications failed");
528 /* daemonize if requested */
529 if (daemon && daemonize()) {
530 ERROR("daemonization failed");
534 /* get systemd objects */
535 rc = sd_event_new(&evloop);
537 ERROR("can't create event loop");
540 rc = open_bus(&sysbus, 0, sys_bus_addr);
542 ERROR("can't create system bus");
545 rc = sd_bus_attach_event(sysbus, evloop, 0);
547 ERROR("can't attach system bus to event loop");
550 rc = open_bus(&usrbus, 1, usr_bus_addr);
552 ERROR("can't create user bus");
555 rc = sd_bus_attach_event(usrbus, evloop, 0);
557 ERROR("can't attach user bus to event loop");
561 /* connects to the system bus */
562 system_bus = create_jbus(sysbus, AFM_SYSTEM_DBUS_PATH);
564 ERROR("create_jbus failed for system");
568 /* observe signals of system */
569 if(jbus_on_signal_j(system_bus, "changed", on_signal_changed, NULL)) {
570 ERROR("adding signal observer failed");
574 /* connect to the session bus */
575 user_bus = create_jbus(usrbus, AFM_USER_DBUS_PATH);
577 ERROR("create_jbus failed");
582 if (jbus_add_service_j(user_bus, "runnables", on_runnables, NULL)
583 || jbus_add_service_j(user_bus, "detail", on_detail, NULL)
584 || jbus_add_service_j(user_bus, "start", on_start, NULL)
585 || jbus_add_service_j(user_bus, "terminate", on_terminate, NULL)
586 || jbus_add_service_j(user_bus, "stop", on_stop, NULL)
587 || jbus_add_service_j(user_bus, "continue", on_continue, NULL)
588 || jbus_add_service_j(user_bus, "runners", on_runners, NULL)
589 || jbus_add_service_j(user_bus, "state", on_state, NULL)
590 #if defined(EXPLICIT_CALL)
591 || jbus_add_service_s(user_bus, "install", on_install, NULL)
592 || jbus_add_service_s(user_bus, "uninstall", on_uninstall, NULL)) {
594 || jbus_add_service_s(user_bus, "install", (void (*)(struct sd_bus_message *, const char *, void *))propagate, "install")
595 || jbus_add_service_s(user_bus, "uninstall", (void (*)(struct sd_bus_message *, const char *, void *))propagate, "uninstall")) {
597 ERROR("adding services failed");
601 /* start servicing */
602 if (jbus_start_serving(user_bus) < 0) {
603 ERROR("can't start server");
607 /* run until error */
609 sd_event_run(evloop, (uint64_t)-1);