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);
249 afm_run_terminate(runid);
250 jbus_reply_error_s(smsg, error_system);
252 json_object_put(resp);
257 * On query "pause" from 'smsg' with parameters of 'obj'.
259 static void on_pause(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
262 if (onrunid(smsg, obj, "pause", &runid)) {
263 status = afm_run_pause(runid);
264 reply_status(smsg, status, error_not_found);
269 * On query "resume" from 'smsg' with parameters of 'obj'.
271 static void on_resume(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
274 if (onrunid(smsg, obj, "resume", &runid)) {
275 status = afm_run_resume(runid);
276 reply_status(smsg, status, error_not_found);
281 * On query "stop" from 'smsg' with parameters of 'obj'.
283 static void on_stop(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
285 NOTICE("call to obsolete 'stop'");
286 on_pause(smsg, obj, unused);
290 * On query "continue" from 'smsg' with parameters of 'obj'.
292 static void on_continue(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
294 NOTICE("call to obsolete 'continue'");
295 on_resume(smsg, obj, unused);
299 * On query "terminate" from 'smsg' with parameters of 'obj'.
301 static void on_terminate(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
304 if (onrunid(smsg, obj, "terminate", &runid)) {
305 status = afm_run_terminate(runid);
306 reply_status(smsg, status, error_not_found);
311 * On query "runners" from 'smsg' with parameters of 'obj'.
313 static void on_runners(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
315 struct json_object *resp;
316 INFO("method runners called");
317 resp = afm_run_list();
318 jbus_reply_j(smsg, resp);
319 json_object_put(resp);
323 * On query "state" from 'smsg' with parameters of 'obj'.
325 static void on_state(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
328 struct json_object *resp;
329 if (onrunid(smsg, obj, "state", &runid)) {
330 resp = afm_run_state(runid);
331 reply(smsg, resp, error_not_found);
332 json_object_put(resp);
337 * Calls the system daemon to achieve application management of
338 * the 'method' gotten from 'smsg' with the parameter's string 'msg'.
340 * The principle is very simple: call the corresponding system method
341 * and reply its response to the caller.
343 * The request and reply is synchronous and is blocking.
344 * It is possible to implment it in an asynchrounous way but it
345 * would brake the common behaviour. It would be a call like
346 * jbus_call_ss(system_bus, method, msg, callback, smsg)
348 static void propagate(struct sd_bus_message *smsg, const char *msg, const char *method)
351 INFO("method %s propagated with %s", method, msg);
352 reply = jbus_call_ss_sync(system_bus, method, msg);
354 jbus_reply_s(smsg, reply);
358 jbus_reply_error_s(smsg, error_system);
361 #if defined(EXPLICIT_CALL)
363 * On query "install" from 'smsg' with parameters of 'msg'.
365 static void on_install(struct sd_bus_message *smsg, const char *msg, void *unused)
367 return propagate(smsg, msg, "install");
371 * On query "uninstall" from 'smsg' with parameters of 'msg'.
373 static void on_uninstall(struct sd_bus_message *smsg, const char *msg, void *unused)
375 return propagate(smsg, msg, "uninstall");
380 * On system signaling that applications list changed
382 static void on_signal_changed(struct json_object *obj, void *unused)
384 /* update the database */
385 afm_db_update_applications(afdb);
386 /* re-propagate now */
387 jbus_send_signal_j(user_bus, "changed", obj);
391 * Tiny routine to daemonize the program
392 * Return 0 on success or -1 on failure.
394 static int daemonize()
405 * Opens a sd-bus connection and returns it in 'ret'.
406 * The sd-bus connexion is intended to be for user if 'isuser'
407 * is not null. The adress is the default address when 'address'
408 * is NULL or, otherwise, the given address.
409 * It might be necessary to pass the address as an argument because
410 * library systemd uses secure_getenv to retrieves the default
411 * addresses and secure_getenv might return NULL in some cases.
413 static int open_bus(sd_bus **ret, int isuser, const char *address)
419 return (isuser ? sd_bus_open_user : sd_bus_open_system)(ret);
425 rc = sd_bus_set_address(b, address);
429 sd_bus_set_bus_client(b, 1);
431 rc = sd_bus_start(b);
444 * ENTRY POINT OF AFM-USER-DAEMON
446 int main(int ac, char **av)
448 int i, daemon = 0, rc;
449 enum afm_launch_mode mode;
450 struct sd_event *evloop;
451 struct sd_bus *sysbus, *usrbus;
452 const char *sys_bus_addr, *usr_bus_addr;
456 /* first interpretation of arguments */
459 while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
462 printf(usagestr, appname);
479 mode = launch_mode_of_name(optarg);
480 if (!is_valid_launch_mode(mode)) {
481 ERROR("invalid mode '%s'", optarg);
484 set_default_launch_mode(mode);
487 usr_bus_addr = optarg;
490 sys_bus_addr = optarg;
493 ERROR("missing argument value");
496 ERROR("unrecognized option");
501 /* init random generator */
502 srandom((unsigned int)time(NULL));
505 if (afm_run_init()) {
506 ERROR("afm_run_init failed");
511 afdb = afm_db_create();
513 ERROR("afm_create failed");
516 if (afm_db_add_root(afdb, FWK_APP_DIR)) {
517 ERROR("can't add root %s", FWK_APP_DIR);
521 /* second interpretation of arguments */
523 while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
526 if (afm_db_add_root(afdb, optarg)) {
527 ERROR("can't add root %s", optarg);
532 if (afm_db_add_application(afdb, optarg)) {
533 ERROR("can't add application %s", optarg);
540 /* update the database */
541 if (afm_db_update_applications(afdb)) {
542 ERROR("afm_update_applications failed");
546 /* daemonize if requested */
547 if (daemon && daemonize()) {
548 ERROR("daemonization failed");
552 /* get systemd objects */
553 rc = sd_event_new(&evloop);
555 ERROR("can't create event loop");
558 rc = open_bus(&sysbus, 0, sys_bus_addr);
560 ERROR("can't create system bus");
563 rc = sd_bus_attach_event(sysbus, evloop, 0);
565 ERROR("can't attach system bus to event loop");
568 rc = open_bus(&usrbus, 1, usr_bus_addr);
570 ERROR("can't create user bus");
573 rc = sd_bus_attach_event(usrbus, evloop, 0);
575 ERROR("can't attach user bus to event loop");
579 /* connects to the system bus */
580 system_bus = create_jbus(sysbus, AFM_SYSTEM_DBUS_PATH);
582 ERROR("create_jbus failed for system");
586 /* observe signals of system */
587 if(jbus_on_signal_j(system_bus, "changed", on_signal_changed, NULL)) {
588 ERROR("adding signal observer failed");
592 /* connect to the session bus */
593 user_bus = create_jbus(usrbus, AFM_USER_DBUS_PATH);
595 ERROR("create_jbus failed");
600 if (jbus_add_service_j(user_bus, "runnables", on_runnables, NULL)
601 || jbus_add_service_j(user_bus, "detail", on_detail, NULL)
602 || jbus_add_service_j(user_bus, "start", on_start, NULL)
603 || jbus_add_service_j(user_bus, "terminate", on_terminate, NULL)
604 || jbus_add_service_j(user_bus, "pause", on_pause, NULL)
605 || jbus_add_service_j(user_bus, "resume", on_resume, NULL)
606 || jbus_add_service_j(user_bus, "stop", on_stop, NULL)
607 || jbus_add_service_j(user_bus, "continue", on_continue, NULL)
608 || jbus_add_service_j(user_bus, "runners", on_runners, NULL)
609 || jbus_add_service_j(user_bus, "state", on_state, NULL)
610 #if defined(EXPLICIT_CALL)
611 || jbus_add_service_s(user_bus, "install", on_install, NULL)
612 || jbus_add_service_s(user_bus, "uninstall", on_uninstall, NULL)
614 || jbus_add_service_s(user_bus, "install", (void (*)(struct sd_bus_message *, const char *, void *))propagate, "install")
615 || jbus_add_service_s(user_bus, "uninstall", (void (*)(struct sd_bus_message *, const char *, void *))propagate, "uninstall")
618 ERROR("adding services failed");
622 /* start servicing */
623 if (jbus_start_serving(user_bus) < 0) {
624 ERROR("can't start server");
628 /* run until error */
630 sd_event_run(evloop, (uint64_t)-1);