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"
33 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
38 #include "afm-launch-mode.h"
42 * name of the application
44 static const char appname[] = "afm-user-daemon";
47 * string for printing usage
49 static const char usagestr[] =
50 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
51 "usage: %s [-q] [-v] [-m mode] [-r rootdir]... [-a appdir]...\n"
53 " -a appdir adds an application directory\n"
54 " -r rootdir adds a root directory of applications\n"
55 " -m mode set default launch mode (local or remote)\n"
57 "usage: %s [option(s)]\n"
60 " -d run as a daemon\n"
61 " -u addr address of user D-Bus to use\n"
62 " -s addr address of system D-Bus to use\n"
68 * Option definition for getopt_long
70 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
71 static const char options_s[] = "hdqvr:a:m:";
72 static struct option options_l[] = {
73 { "root", required_argument, NULL, 'r' },
74 { "application", required_argument, NULL, 'a' },
75 { "mode", required_argument, NULL, 'm' },
77 static const char options_s[] = "hdqv";
78 static struct option options_l[] = {
80 { "user-dbus", required_argument, NULL, 'u' },
81 { "system-dbus", required_argument, NULL, 's' },
82 { "daemon", no_argument, NULL, 'd' },
83 { "quiet", no_argument, NULL, 'q' },
84 { "verbose", no_argument, NULL, 'v' },
85 { "help", no_argument, NULL, 'h' },
90 * Connections to D-Bus
91 * This is an array for using the function
92 * jbus_read_write_dispatch_multiple
93 * directly without transformations.
95 static struct jbus *jbuses[2];
96 #define system_bus jbuses[0]
97 #define user_bus jbuses[1]
100 * Handle to the database of applications
102 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
103 static struct afm_db *afdb;
105 static struct afm_udb *afudb;
109 * Returned error strings
111 const char error_nothing[] = "[]";
112 const char error_bad_request[] = "\"bad request\"";
113 const char error_not_found[] = "\"not found\"";
114 const char error_cant_start[] = "\"can't start\"";
115 const char error_system[] = "\"system error\"";
119 * retrieves the 'runid' in 'obj' parameters received with the
120 * request 'smsg' for the 'method'.
122 * Returns 1 in case of success.
123 * Otherwise, if the 'runid' can't be retrived, an error stating
124 * the bad request is replied for 'smsg' and 0 is returned.
126 static int onrunid(struct sd_bus_message *smsg, struct json_object *obj,
127 const char *method, int *runid)
129 if (!j_read_integer(obj, runid)
130 && !j_read_integer_at(obj, "runid", runid)) {
131 INFO("bad request method %s: %s", method,
132 json_object_to_json_string(obj));
133 jbus_reply_error_s(smsg, error_bad_request);
137 INFO("method %s called for %d", method, *runid);
142 * Sends the reply 'resp' to the request 'smsg' if 'resp' is not NULL.
143 * Otherwise, when 'resp' is NULL replies the error string 'errstr'.
145 static void reply(struct sd_bus_message *smsg, struct json_object *resp,
149 jbus_reply_j(smsg, resp);
151 jbus_reply_error_s(smsg, errstr);
155 * Sends the reply "true" to the request 'smsg' if 'status' is zero.
156 * Otherwise, when 'status' is not zero replies the error string 'errstr'.
158 static void reply_status(struct sd_bus_message *smsg, int status, const char *errstr)
161 jbus_reply_error_s(smsg, errstr);
163 jbus_reply_s(smsg, "true");
167 * On query "runnables" from 'smsg' with parameters of 'obj'.
169 * Nothing is expected in 'obj' that can be anything.
171 static void on_runnables(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
173 struct json_object *resp;
174 INFO("method runnables called");
175 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
176 resp = afm_db_application_list(afdb);
178 resp = afm_udb_applications_public(afudb);
180 jbus_reply_j(smsg, resp);
181 json_object_put(resp);
185 * On query "detail" from 'smsg' with parameters of 'obj'.
187 static void on_detail(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
190 struct json_object *resp;
192 /* get the parameters */
193 if (j_read_string(obj, &appid))
194 ; /* appid as a string */
195 else if (j_read_string_at(obj, "id", &appid))
196 ; /* appid as obj.id string */
198 INFO("method detail called but bad request!");
199 jbus_reply_error_s(smsg, error_bad_request);
203 /* wants details for appid */
204 INFO("method detail called for %s", appid);
205 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
206 resp = afm_db_get_application_public(afdb, appid);
208 resp = afm_udb_get_application_public(afudb, appid);
210 reply(smsg, resp, error_not_found);
211 json_object_put(resp);
215 * On query "start" from 'smsg' with parameters of 'obj'.
217 static void on_start(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
219 const char *appid, *modestr;
221 struct json_object *appli, *resp;
224 enum afm_launch_mode mode;
226 /* get the parameters */
227 mode = invalid_launch_mode;
228 if (j_read_string(obj, &appid)) {
229 mode = get_default_launch_mode();
230 } else if (j_read_string_at(obj, "id", &appid)) {
231 if (j_read_string_at(obj, "mode", &modestr)) {
232 mode = launch_mode_of_name(modestr);
234 mode = get_default_launch_mode();
237 if (!is_valid_launch_mode(mode)) {
238 jbus_reply_error_s(smsg, error_bad_request);
242 /* get the application */
243 INFO("method start called for %s mode=%s", appid,
244 name_of_launch_mode(mode));
245 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
246 appli = afm_db_get_application(afdb, appid);
248 appli = afm_udb_get_application_private(afudb, appid);
251 jbus_reply_error_s(smsg, error_not_found);
255 /* launch the application */
257 runid = afm_run_start(appli, mode, &uri);
259 jbus_reply_error_s(smsg, error_cant_start);
265 /* returns only the runid */
266 snprintf(runidstr, sizeof runidstr, "%d", runid);
267 runidstr[sizeof runidstr - 1] = 0;
268 jbus_reply_s(smsg, runidstr);
272 /* returns the runid and its uri */
273 resp = json_object_new_object();
274 if (resp != NULL && j_add_integer(resp, "runid", runid)
275 && j_add_string(resp, "uri", uri))
276 jbus_reply_j(smsg, resp);
278 afm_run_terminate(runid);
279 jbus_reply_error_s(smsg, error_system);
281 json_object_put(resp);
286 * On query "once" from 'smsg' with parameters of 'obj'.
288 static void on_once(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
291 struct json_object *appli, *resp;
294 /* get the parameters */
295 if (!j_read_string(obj, &appid) && !j_read_string_at(obj, "id", &appid)) {
296 jbus_reply_error_s(smsg, error_bad_request);
300 /* get the application */
301 INFO("method once called for %s", appid);
302 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
303 appli = afm_db_get_application(afdb, appid);
305 appli = afm_udb_get_application_private(afudb, appid);
308 jbus_reply_error_s(smsg, error_not_found);
312 /* launch the application */
313 runid = afm_run_once(appli);
315 jbus_reply_error_s(smsg, error_cant_start);
319 /* returns the state */
320 resp = afm_run_state(runid);
321 reply(smsg, resp, error_not_found);
322 json_object_put(resp);
326 * On query "pause" from 'smsg' with parameters of 'obj'.
328 static void on_pause(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
331 if (onrunid(smsg, obj, "pause", &runid)) {
332 status = afm_run_pause(runid);
333 reply_status(smsg, status, error_not_found);
338 * On query "resume" from 'smsg' with parameters of 'obj'.
340 static void on_resume(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
343 if (onrunid(smsg, obj, "resume", &runid)) {
344 status = afm_run_resume(runid);
345 reply_status(smsg, status, error_not_found);
350 * On query "stop" from 'smsg' with parameters of 'obj'.
352 static void on_stop(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
354 NOTICE("call to obsolete 'stop'");
355 on_pause(smsg, obj, unused);
359 * On query "continue" from 'smsg' with parameters of 'obj'.
361 static void on_continue(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
363 NOTICE("call to obsolete 'continue'");
364 on_resume(smsg, obj, unused);
368 * On query "terminate" from 'smsg' with parameters of 'obj'.
370 static void on_terminate(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
373 if (onrunid(smsg, obj, "terminate", &runid)) {
374 status = afm_run_terminate(runid);
375 reply_status(smsg, status, error_not_found);
380 * On query "runners" from 'smsg' with parameters of 'obj'.
382 static void on_runners(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
384 struct json_object *resp;
385 INFO("method runners called");
386 resp = afm_run_list();
387 jbus_reply_j(smsg, resp);
388 json_object_put(resp);
392 * On query "state" from 'smsg' with parameters of 'obj'.
394 static void on_state(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
397 struct json_object *resp;
398 if (onrunid(smsg, obj, "state", &runid)) {
399 resp = afm_run_state(runid);
400 reply(smsg, resp, error_not_found);
401 json_object_put(resp);
406 * Calls the system daemon to achieve application management of
407 * the 'method' gotten from 'smsg' with the parameter's string 'msg'.
409 * The principle is very simple: call the corresponding system method
410 * and reply its response to the caller.
412 * The request and reply is synchronous and is blocking.
413 * It is possible to implment it in an asynchrounous way but it
414 * would brake the common behaviour. It would be a call like
415 * jbus_call_ss(system_bus, method, msg, callback, smsg)
417 static void propagate(struct sd_bus_message *smsg, const char *msg, const char *method)
420 INFO("method %s propagated with %s", method, msg);
421 reply = jbus_call_ss_sync(system_bus, method, msg);
423 jbus_reply_s(smsg, reply);
427 jbus_reply_error_s(smsg, error_system);
430 #if defined(EXPLICIT_CALL)
432 * On query "install" from 'smsg' with parameters of 'msg'.
434 static void on_install(struct sd_bus_message *smsg, const char *msg, void *unused)
436 return propagate(smsg, msg, "install");
440 * On query "uninstall" from 'smsg' with parameters of 'msg'.
442 static void on_uninstall(struct sd_bus_message *smsg, const char *msg, void *unused)
444 return propagate(smsg, msg, "uninstall");
449 * On system signaling that applications list changed
451 static void on_signal_changed(struct json_object *obj, void *unused)
453 /* update the database */
454 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
455 afm_db_update_applications(afdb);
457 afm_udb_update(afudb);
459 /* re-propagate now */
460 jbus_send_signal_j(user_bus, "changed", obj);
464 * Tiny routine to daemonize the program
465 * Return 0 on success or -1 on failure.
467 static int daemonize()
478 * Opens a sd-bus connection and returns it in 'ret'.
479 * The sd-bus connexion is intended to be for user if 'isuser'
480 * is not null. The adress is the default address when 'address'
481 * is NULL or, otherwise, the given address.
482 * It might be necessary to pass the address as an argument because
483 * library systemd uses secure_getenv to retrieves the default
484 * addresses and secure_getenv might return NULL in some cases.
486 static int open_bus(sd_bus **ret, int isuser, const char *address)
492 return (isuser ? sd_bus_default_user : sd_bus_default_system)(ret);
498 rc = sd_bus_set_address(b, address);
502 sd_bus_set_bus_client(b, 1);
504 rc = sd_bus_start(b);
517 * ENTRY POINT OF AFM-USER-DAEMON
519 int main(int ac, char **av)
521 int i, daemon = 0, rc;
522 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
523 enum afm_launch_mode mode;
525 struct sd_event *evloop;
526 struct sd_bus *sysbus, *usrbus;
527 const char *sys_bus_addr, *usr_bus_addr;
531 /* first interpretation of arguments */
534 while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
537 printf(usagestr, appname);
549 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
555 mode = launch_mode_of_name(optarg);
556 if (!is_valid_launch_mode(mode)) {
557 ERROR("invalid mode '%s'", optarg);
560 set_default_launch_mode(mode);
564 usr_bus_addr = optarg;
567 sys_bus_addr = optarg;
570 ERROR("missing argument value");
573 ERROR("unrecognized option");
578 /* init random generator */
579 srandom((unsigned int)time(NULL));
582 if (afm_run_init()) {
583 ERROR("afm_run_init failed");
588 #ifdef LEGACY_MODE_WITHOUT_SYSTEMD
589 afdb = afm_db_create();
591 ERROR("afm_db_create failed");
594 if (afm_db_add_root(afdb, FWK_APP_DIR)) {
595 ERROR("can't add root %s", FWK_APP_DIR);
599 /* second interpretation of arguments */
601 while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
604 if (afm_db_add_root(afdb, optarg)) {
605 ERROR("can't add root %s", optarg);
610 if (afm_db_add_application(afdb, optarg)) {
611 ERROR("can't add application %s", optarg);
618 /* update the database */
619 if (afm_db_update_applications(afdb)) {
620 ERROR("afm_update_applications failed");
624 afudb = afm_udb_create(0, 1, "afm-appli-");
626 ERROR("afm_udb_create failed");
631 /* daemonize if requested */
632 if (daemon && daemonize()) {
633 ERROR("daemonization failed");
637 /* get systemd objects */
638 rc = sd_event_new(&evloop);
640 ERROR("can't create event loop");
643 rc = open_bus(&sysbus, 0, sys_bus_addr);
645 ERROR("can't create system bus");
648 rc = sd_bus_attach_event(sysbus, evloop, 0);
650 ERROR("can't attach system bus to event loop");
653 rc = open_bus(&usrbus, 1, usr_bus_addr);
655 ERROR("can't create user bus");
658 rc = sd_bus_attach_event(usrbus, evloop, 0);
660 ERROR("can't attach user bus to event loop");
664 /* connects to the system bus */
665 system_bus = create_jbus(sysbus, AFM_SYSTEM_DBUS_PATH);
667 ERROR("create_jbus failed for system");
671 /* observe signals of system */
672 if(jbus_on_signal_j(system_bus, "changed", on_signal_changed, NULL)) {
673 ERROR("adding signal observer failed");
677 /* connect to the session bus */
678 user_bus = create_jbus(usrbus, AFM_USER_DBUS_PATH);
680 ERROR("create_jbus failed");
685 if (jbus_add_service_j(user_bus, "runnables", on_runnables, NULL)
686 || jbus_add_service_j(user_bus, "detail", on_detail, NULL)
687 || jbus_add_service_j(user_bus, "start", on_start, NULL)
688 || jbus_add_service_j(user_bus, "once", on_once, NULL)
689 || jbus_add_service_j(user_bus, "terminate", on_terminate, NULL)
690 || jbus_add_service_j(user_bus, "pause", on_pause, NULL)
691 || jbus_add_service_j(user_bus, "resume", on_resume, NULL)
692 || jbus_add_service_j(user_bus, "stop", on_stop, NULL)
693 || jbus_add_service_j(user_bus, "continue", on_continue, NULL)
694 || jbus_add_service_j(user_bus, "runners", on_runners, NULL)
695 || jbus_add_service_j(user_bus, "state", on_state, NULL)
696 #if defined(EXPLICIT_CALL)
697 || jbus_add_service_s(user_bus, "install", on_install, NULL)
698 || jbus_add_service_s(user_bus, "uninstall", on_uninstall, NULL)
700 || jbus_add_service_s(user_bus, "install", (void (*)(struct sd_bus_message *, const char *, void *))propagate, "install")
701 || jbus_add_service_s(user_bus, "uninstall", (void (*)(struct sd_bus_message *, const char *, void *))propagate, "uninstall")
704 ERROR("adding services failed");
708 /* start servicing */
709 if (jbus_start_serving(user_bus) < 0) {
710 ERROR("can't start server");
714 /* run until error */
716 sd_event_run(evloop, (uint64_t)-1);