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"
38 * name of the application
40 static const char appname[] = "afm-user-daemon";
43 * string for printing version
45 static const char versionstr[] =
47 " %s version="AFM_VERSION"\n"
49 " Copyright (C) 2015, 2016, 2017 \"IoT.bzh\"\n"
50 " AFB comes with ABSOLUTELY NO WARRANTY.\n"
55 * string for printing usage
57 static const char usagestr[] =
58 "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"
69 * Option definition for getopt_long
71 static const char options_s[] = "hdqvV";
72 static struct option options_l[] = {
73 { "user-dbus", required_argument, NULL, 'u' },
74 { "system-dbus", required_argument, NULL, 's' },
75 { "daemon", no_argument, NULL, 'd' },
76 { "quiet", no_argument, NULL, 'q' },
77 { "verbose", no_argument, NULL, 'v' },
78 { "help", no_argument, NULL, 'h' },
79 { "version", no_argument, NULL, 'V' },
84 * Connections to D-Bus
85 * This is an array for using the function
86 * jbus_read_write_dispatch_multiple
87 * directly without transformations.
89 static struct jbus *jbuses[2];
90 #define system_bus jbuses[0]
91 #define user_bus jbuses[1]
94 * Handle to the database of applications
96 static struct afm_udb *afudb;
99 * Returned error strings
101 const char error_nothing[] = "[]";
102 const char error_bad_request[] = "\"bad request\"";
103 const char error_not_found[] = "\"not found\"";
104 const char error_cant_start[] = "\"can't start\"";
105 const char error_system[] = "\"system error\"";
109 * retrieves the 'runid' in 'obj' parameters received with the
110 * request 'smsg' for the 'method'.
112 * Returns 1 in case of success.
113 * Otherwise, if the 'runid' can't be retrived, an error stating
114 * the bad request is replied for 'smsg' and 0 is returned.
116 static int onrunid(struct sd_bus_message *smsg, struct json_object *obj,
117 const char *method, int *runid)
119 if (!j_read_integer(obj, runid)
120 && !j_read_integer_at(obj, "runid", runid)) {
121 INFO("bad request method %s: %s", method,
122 json_object_to_json_string(obj));
123 jbus_reply_error_s(smsg, error_bad_request);
127 INFO("method %s called for %d", method, *runid);
132 * Sends the reply 'resp' to the request 'smsg' if 'resp' is not NULL.
133 * Otherwise, when 'resp' is NULL replies the error string 'errstr'.
135 static void reply(struct sd_bus_message *smsg, struct json_object *resp,
139 jbus_reply_j(smsg, resp);
141 jbus_reply_error_s(smsg, errstr);
145 * Sends the reply "true" to the request 'smsg' if 'status' is zero.
146 * Otherwise, when 'status' is not zero replies the error string 'errstr'.
148 static void reply_status(struct sd_bus_message *smsg, int status, const char *errstr)
151 jbus_reply_error_s(smsg, errstr);
153 jbus_reply_s(smsg, "true");
157 * On query "runnables" from 'smsg' with parameters of 'obj'.
159 * Nothing is expected in 'obj' that can be anything.
161 static void on_runnables(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
163 struct json_object *resp;
164 INFO("method runnables called");
165 resp = afm_udb_applications_public(afudb);
166 jbus_reply_j(smsg, resp);
167 json_object_put(resp);
171 * On query "detail" from 'smsg' with parameters of 'obj'.
173 static void on_detail(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
176 struct json_object *resp;
178 /* get the parameters */
179 if (j_read_string(obj, &appid))
180 ; /* appid as a string */
181 else if (j_read_string_at(obj, "id", &appid))
182 ; /* appid as obj.id string */
184 INFO("method detail called but bad request!");
185 jbus_reply_error_s(smsg, error_bad_request);
189 /* wants details for appid */
190 INFO("method detail called for %s", appid);
191 resp = afm_udb_get_application_public(afudb, appid);
192 reply(smsg, resp, error_not_found);
193 json_object_put(resp);
197 * On query "start" from 'smsg' with parameters of 'obj'.
199 static void on_start(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
203 struct json_object *appli, *resp;
207 /* get the parameters */
208 if (!j_read_string(obj, &appid)) {
209 if (!j_read_string_at(obj, "id", &appid)) {
210 jbus_reply_error_s(smsg, error_bad_request);
215 /* get the application */
216 INFO("method start called for %s", appid);
217 appli = afm_udb_get_application_private(afudb, appid);
219 jbus_reply_error_s(smsg, error_not_found);
223 /* launch the application */
225 runid = afm_urun_start(appli);
227 jbus_reply_error_s(smsg, error_cant_start);
233 /* returns only the runid */
234 snprintf(runidstr, sizeof runidstr, "%d", runid);
235 runidstr[sizeof runidstr - 1] = 0;
236 jbus_reply_s(smsg, runidstr);
240 /* returns the runid and its uri */
241 resp = json_object_new_object();
242 if (resp != NULL && j_add_integer(resp, "runid", runid)
243 && j_add_string(resp, "uri", uri))
244 jbus_reply_j(smsg, resp);
246 afm_urun_terminate(runid);
247 jbus_reply_error_s(smsg, error_system);
249 json_object_put(resp);
254 * On query "once" from 'smsg' with parameters of 'obj'.
256 static void on_once(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
259 struct json_object *appli, *resp;
262 /* get the parameters */
263 if (!j_read_string(obj, &appid) && !j_read_string_at(obj, "id", &appid)) {
264 jbus_reply_error_s(smsg, error_bad_request);
268 /* get the application */
269 INFO("method once called for %s", appid);
270 appli = afm_udb_get_application_private(afudb, appid);
272 jbus_reply_error_s(smsg, error_not_found);
276 /* launch the application */
277 runid = afm_urun_once(appli);
279 jbus_reply_error_s(smsg, error_cant_start);
283 /* returns the state */
284 resp = afm_urun_state(afudb, runid);
285 reply(smsg, resp, error_not_found);
286 json_object_put(resp);
290 * On query "pause" from 'smsg' with parameters of 'obj'.
292 static void on_pause(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
295 if (onrunid(smsg, obj, "pause", &runid)) {
296 status = afm_urun_pause(runid);
297 reply_status(smsg, status, error_not_found);
302 * On query "resume" from 'smsg' with parameters of 'obj'.
304 static void on_resume(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
307 if (onrunid(smsg, obj, "resume", &runid)) {
308 status = afm_urun_resume(runid);
309 reply_status(smsg, status, error_not_found);
314 * On query "stop" from 'smsg' with parameters of 'obj'.
316 static void on_stop(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
318 NOTICE("call to obsolete 'stop'");
319 on_pause(smsg, obj, unused);
323 * On query "continue" from 'smsg' with parameters of 'obj'.
325 static void on_continue(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
327 NOTICE("call to obsolete 'continue'");
328 on_resume(smsg, obj, unused);
332 * On query "terminate" from 'smsg' with parameters of 'obj'.
334 static void on_terminate(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
337 if (onrunid(smsg, obj, "terminate", &runid)) {
338 status = afm_urun_terminate(runid);
339 reply_status(smsg, status, error_not_found);
344 * On query "runners" from 'smsg' with parameters of 'obj'.
346 static void on_runners(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
348 struct json_object *resp;
349 INFO("method runners called");
350 resp = afm_urun_list(afudb);
351 jbus_reply_j(smsg, resp);
352 json_object_put(resp);
356 * On query "state" from 'smsg' with parameters of 'obj'.
358 static void on_state(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
361 struct json_object *resp;
362 if (onrunid(smsg, obj, "state", &runid)) {
363 resp = afm_urun_state(afudb, runid);
364 reply(smsg, resp, error_not_found);
365 json_object_put(resp);
370 * Calls the system daemon to achieve application management of
371 * the 'method' gotten from 'smsg' with the parameter's string 'msg'.
373 * The principle is very simple: call the corresponding system method
374 * and reply its response to the caller.
376 * The request and reply is synchronous and is blocking.
377 * It is possible to implment it in an asynchrounous way but it
378 * would brake the common behaviour. It would be a call like
379 * jbus_call_ss(system_bus, method, msg, callback, smsg)
381 static void propagate(struct sd_bus_message *smsg, const char *msg, const char *method)
384 INFO("method %s propagated with %s", method, msg);
385 reply = jbus_call_ss_sync(system_bus, method, msg);
387 jbus_reply_s(smsg, reply);
391 jbus_reply_error_s(smsg, error_system);
394 #if defined(EXPLICIT_CALL)
396 * On query "install" from 'smsg' with parameters of 'msg'.
398 static void on_install(struct sd_bus_message *smsg, const char *msg, void *unused)
400 return propagate(smsg, msg, "install");
404 * On query "uninstall" from 'smsg' with parameters of 'msg'.
406 static void on_uninstall(struct sd_bus_message *smsg, const char *msg, void *unused)
408 return propagate(smsg, msg, "uninstall");
413 * On system signaling that applications list changed
415 static void on_signal_changed(struct json_object *obj, void *unused)
417 /* enforce daemon reload */
418 systemd_daemon_reload(1);
419 systemd_unit_restart_name(1, "sockets.target");
421 /* update the database */
422 afm_udb_update(afudb);
424 /* re-propagate now */
425 jbus_send_signal_j(user_bus, "changed", obj);
429 * Tiny routine to daemonize the program
430 * Return 0 on success or -1 on failure.
432 static int daemonize()
443 * Opens a sd-bus connection and returns it in 'ret'.
444 * The sd-bus connexion is intended to be for user if 'isuser'
445 * is not null. The adress is the default address when 'address'
446 * is NULL or, otherwise, the given address.
447 * It might be necessary to pass the address as an argument because
448 * library systemd uses secure_getenv to retrieves the default
449 * addresses and secure_getenv might return NULL in some cases.
451 static int open_bus(sd_bus **ret, int isuser, const char *address)
457 return (isuser ? sd_bus_default_user : sd_bus_default_system)(ret);
463 rc = sd_bus_set_address(b, address);
467 sd_bus_set_bus_client(b, 1);
469 rc = sd_bus_start(b);
482 * ENTRY POINT OF AFM-USER-DAEMON
484 int main(int ac, char **av)
486 int i, daemon = 0, rc;
487 struct sd_event *evloop;
488 struct sd_bus *sysbus, *usrbus;
489 const char *sys_bus_addr, *usr_bus_addr;
493 /* first interpretation of arguments */
496 while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
499 printf(usagestr, appname);
502 printf(versionstr, appname);
515 usr_bus_addr = optarg;
518 sys_bus_addr = optarg;
521 ERROR("missing argument value");
524 ERROR("unrecognized option");
529 /* init random generator */
530 srandom((unsigned int)time(NULL));
533 afudb = afm_udb_create(1, 1, "afm-appli-");
535 ERROR("afm_udb_create failed");
539 /* daemonize if requested */
540 if (daemon && daemonize()) {
541 ERROR("daemonization failed");
545 /* get systemd objects */
546 rc = sd_event_new(&evloop);
548 ERROR("can't create event loop");
551 rc = open_bus(&sysbus, 0, sys_bus_addr);
553 ERROR("can't create system bus");
556 rc = sd_bus_attach_event(sysbus, evloop, 0);
558 ERROR("can't attach system bus to event loop");
561 rc = open_bus(&usrbus, 1, usr_bus_addr);
563 ERROR("can't create user bus");
566 rc = sd_bus_attach_event(usrbus, evloop, 0);
568 ERROR("can't attach user bus to event loop");
572 /* connects to the system bus */
573 system_bus = create_jbus(sysbus, AFM_SYSTEM_DBUS_PATH);
575 ERROR("create_jbus failed for system");
579 /* observe signals of system */
580 if(jbus_on_signal_j(system_bus, "changed", on_signal_changed, NULL)) {
581 ERROR("adding signal observer failed");
585 /* connect to the session bus */
586 user_bus = create_jbus(usrbus, AFM_USER_DBUS_PATH);
588 ERROR("create_jbus failed");
593 if (jbus_add_service_j(user_bus, "runnables", on_runnables, NULL)
594 || jbus_add_service_j(user_bus, "detail", on_detail, NULL)
595 || jbus_add_service_j(user_bus, "start", on_start, NULL)
596 || jbus_add_service_j(user_bus, "once", on_once, NULL)
597 || jbus_add_service_j(user_bus, "terminate", on_terminate, NULL)
598 || jbus_add_service_j(user_bus, "pause", on_pause, NULL)
599 || jbus_add_service_j(user_bus, "resume", on_resume, NULL)
600 || jbus_add_service_j(user_bus, "stop", on_stop, NULL)
601 || jbus_add_service_j(user_bus, "continue", on_continue, NULL)
602 || jbus_add_service_j(user_bus, "runners", on_runners, NULL)
603 || jbus_add_service_j(user_bus, "state", on_state, NULL)
604 #if defined(EXPLICIT_CALL)
605 || jbus_add_service_s(user_bus, "install", on_install, NULL)
606 || jbus_add_service_s(user_bus, "uninstall", on_uninstall, NULL)
608 || jbus_add_service_s(user_bus, "install", (void (*)(struct sd_bus_message *, const char *, void *))propagate, "install")
609 || jbus_add_service_s(user_bus, "uninstall", (void (*)(struct sd_bus_message *, const char *, void *))propagate, "uninstall")
612 ERROR("adding services failed");
616 /* start servicing */
617 if (jbus_start_serving(user_bus) < 0) {
618 ERROR("can't start server");
622 /* run until error */
624 sd_event_run(evloop, (uint64_t)-1);