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"
57 * Option definition for getopt_long
59 static const char options_s[] = "hdqvr:a:m:";
60 static struct option options_l[] = {
61 { "root", required_argument, NULL, 'r' },
62 { "application", required_argument, NULL, 'a' },
63 { "mode", required_argument, NULL, 'm' },
64 { "daemon", no_argument, NULL, 'd' },
65 { "quiet", no_argument, NULL, 'q' },
66 { "verbose", no_argument, NULL, 'v' },
67 { "help", no_argument, NULL, 'h' },
72 * Connections to D-Bus
73 * This is an array for using the function
74 * jbus_read_write_dispatch_multiple
75 * directly without transformations.
77 static struct jbus *jbuses[2];
78 #define system_bus jbuses[0]
79 #define user_bus jbuses[1]
82 * Handle to the database of applications
84 static struct afm_db *afdb;
87 * Returned error strings
89 const char error_nothing[] = "[]";
90 const char error_bad_request[] = "\"bad request\"";
91 const char error_not_found[] = "\"not found\"";
92 const char error_cant_start[] = "\"can't start\"";
93 const char error_system[] = "\"system error\"";
97 * retrieves the 'runid' in 'obj' parameters received with the
98 * request 'smsg' for the 'method'.
100 * Returns 1 in case of success.
101 * Otherwise, if the 'runid' can't be retrived, an error stating
102 * the bad request is replied for 'smsg' and 0 is returned.
104 static int onrunid(struct sd_bus_message *smsg, struct json_object *obj,
105 const char *method, int *runid)
107 if (!j_read_integer(obj, runid)
108 && !j_read_integer_at(obj, "runid", runid)) {
109 INFO("bad request method %s: %s", method,
110 json_object_to_json_string(obj));
111 jbus_reply_error_s(smsg, error_bad_request);
115 INFO("method %s called for %d", method, *runid);
120 * Sends the reply 'resp' to the request 'smsg' if 'resp' is not NULL.
121 * Otherwise, when 'resp' is NULL replies the error string 'errstr'.
123 static void reply(struct sd_bus_message *smsg, struct json_object *resp,
127 jbus_reply_j(smsg, resp);
129 jbus_reply_error_s(smsg, errstr);
133 * Sends the reply "true" to the request 'smsg' if 'status' is zero.
134 * Otherwise, when 'status' is not zero replies the error string 'errstr'.
136 static void reply_status(struct sd_bus_message *smsg, int status, const char *errstr)
139 jbus_reply_error_s(smsg, errstr);
141 jbus_reply_s(smsg, "true");
145 * On query "runnables" from 'smsg' with parameters of 'obj'.
147 * Nothing is expected in 'obj' that can be anything.
149 static void on_runnables(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
151 struct json_object *resp;
152 INFO("method runnables called");
153 resp = afm_db_application_list(afdb);
154 jbus_reply_j(smsg, resp);
155 json_object_put(resp);
159 * On query "detail" from 'smsg' with parameters of 'obj'.
161 static void on_detail(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
164 struct json_object *resp;
166 /* get the parameters */
167 if (j_read_string(obj, &appid))
168 ; /* appid as a string */
169 else if (j_read_string_at(obj, "id", &appid))
170 ; /* appid as obj.id string */
172 INFO("method detail called but bad request!");
173 jbus_reply_error_s(smsg, error_bad_request);
177 /* wants details for appid */
178 INFO("method detail called for %s", appid);
179 resp = afm_db_get_application_public(afdb, appid);
180 reply(smsg, resp, error_not_found);
181 json_object_put(resp);
186 * On query "start" from 'smsg' with parameters of 'obj'.
188 static void on_start(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
190 const char *appid, *modestr;
192 struct json_object *appli, *resp;
195 enum afm_launch_mode mode;
197 /* get the parameters */
198 mode = invalid_launch_mode;
199 if (j_read_string(obj, &appid)) {
200 mode = get_default_launch_mode();
201 } else if (j_read_string_at(obj, "id", &appid)) {
202 if (j_read_string_at(obj, "mode", &modestr)) {
203 mode = launch_mode_of_name(modestr);
205 mode = get_default_launch_mode();
208 if (!is_valid_launch_mode(mode)) {
209 jbus_reply_error_s(smsg, error_bad_request);
213 /* get the application */
214 INFO("method start called for %s mode=%s", appid,
215 name_of_launch_mode(mode));
216 appli = afm_db_get_application(afdb, appid);
218 jbus_reply_error_s(smsg, error_not_found);
222 /* launch the application */
224 runid = afm_run_start(appli, mode, &uri);
226 jbus_reply_error_s(smsg, error_cant_start);
232 /* returns only the runid */
233 snprintf(runidstr, sizeof runidstr, "%d", runid);
234 runidstr[sizeof runidstr - 1] = 0;
235 jbus_reply_s(smsg, runidstr);
239 /* returns the runid and its uri */
240 resp = json_object_new_object();
241 if (resp != NULL && j_add_integer(resp, "runid", runid)
242 && j_add_string(resp, "uri", uri))
243 jbus_reply_j(smsg, resp);
246 jbus_reply_error_s(smsg, error_system);
248 json_object_put(resp);
253 * On query "stop" from 'smsg' with parameters of 'obj'.
255 static void on_stop(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
258 if (onrunid(smsg, obj, "stop", &runid)) {
259 status = afm_run_stop(runid);
260 reply_status(smsg, status, error_not_found);
265 * On query "continue" from 'smsg' with parameters of 'obj'.
267 static void on_continue(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
270 if (onrunid(smsg, obj, "continue", &runid)) {
271 status = afm_run_continue(runid);
272 reply_status(smsg, status, error_not_found);
277 * On query "terminate" from 'smsg' with parameters of 'obj'.
279 static void on_terminate(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
282 if (onrunid(smsg, obj, "terminate", &runid)) {
283 status = afm_run_terminate(runid);
284 reply_status(smsg, status, error_not_found);
289 * On query "runners" from 'smsg' with parameters of 'obj'.
291 static void on_runners(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
293 struct json_object *resp;
294 INFO("method runners called");
295 resp = afm_run_list();
296 jbus_reply_j(smsg, resp);
297 json_object_put(resp);
301 * On query "state" from 'smsg' with parameters of 'obj'.
303 static void on_state(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
306 struct json_object *resp;
307 if (onrunid(smsg, obj, "state", &runid)) {
308 resp = afm_run_state(runid);
309 reply(smsg, resp, error_not_found);
310 json_object_put(resp);
315 * Calls the system daemon to achieve application management of
316 * the 'method' gotten from 'smsg' with the parameter's string 'msg'.
318 * The principle is very simple: call the corresponding system method
319 * and reply its response to the caller.
321 * The request and reply is synchronous and is blocking.
322 * It is possible to implment it in an asynchrounous way but it
323 * would brake the common behaviour. It would be a call like
324 * jbus_call_ss(system_bus, method, msg, callback, smsg)
326 static void propagate(struct sd_bus_message *smsg, const char *msg, const char *method)
329 INFO("method %s propagated with %s", method, msg);
330 reply = jbus_call_ss_sync(system_bus, method, msg);
332 jbus_reply_s(smsg, reply);
336 jbus_reply_error_s(smsg, error_system);
339 #if defined(EXPLICIT_CALL)
341 * On query "install" from 'smsg' with parameters of 'msg'.
343 static void on_install(struct sd_bus_message *smsg, const char *msg, void *unused)
345 return propagate(smsg, msg, "install");
349 * On query "uninstall" from 'smsg' with parameters of 'msg'.
351 static void on_uninstall(struct sd_bus_message *smsg, const char *msg, void *unused)
353 return propagate(smsg, msg, "uninstall");
358 * On system signaling that applications list changed
360 static void on_signal_changed(struct json_object *obj, void *unused)
362 /* update the database */
363 afm_db_update_applications(afdb);
364 /* re-propagate now */
365 jbus_send_signal_j(user_bus, "changed", obj);
369 * Tiny routine to daemonize the program
370 * Return 0 on success or -1 on failure.
372 static int daemonize()
383 * ENTRY POINT OF AFM-USER-DAEMON
385 int main(int ac, char **av)
387 int i, daemon = 0, rc;
388 enum afm_launch_mode mode;
389 struct sd_event *evloop;
390 struct sd_bus *sysbus, *usrbus;
395 /* first interpretation of arguments */
396 while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
399 printf(usagestr, appname);
416 mode = launch_mode_of_name(optarg);
417 if (!is_valid_launch_mode(mode)) {
418 ERROR("invalid mode '%s'", optarg);
421 set_default_launch_mode(mode);
424 ERROR("missing argument value");
427 ERROR("unrecognized option");
432 /* init random generator */
433 srandom((unsigned int)time(NULL));
436 if (afm_run_init()) {
437 ERROR("afm_run_init failed");
442 afdb = afm_db_create();
444 ERROR("afm_create failed");
447 if (afm_db_add_root(afdb, FWK_APP_DIR)) {
448 ERROR("can't add root %s", FWK_APP_DIR);
452 /* second interpretation of arguments */
454 while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
457 if (afm_db_add_root(afdb, optarg)) {
458 ERROR("can't add root %s", optarg);
463 if (afm_db_add_application(afdb, optarg)) {
464 ERROR("can't add application %s", optarg);
471 /* update the database */
472 if (afm_db_update_applications(afdb)) {
473 ERROR("afm_update_applications failed");
477 /* daemonize if requested */
478 if (daemon && daemonize()) {
479 ERROR("daemonization failed");
483 /* get systemd objects */
484 rc = sd_event_new(&evloop);
486 ERROR("can't create event loop");
489 rc = sd_bus_open_system(&sysbus);
491 ERROR("can't create system bus");
494 rc = sd_bus_attach_event(sysbus, evloop, 0);
496 ERROR("can't attach system bus to event loop");
499 rc = sd_bus_open_user(&usrbus);
501 ERROR("can't create user bus");
504 rc = sd_bus_attach_event(usrbus, evloop, 0);
506 ERROR("can't attach user bus to event loop");
510 /* connects to the system bus */
511 system_bus = create_jbus(sysbus, AFM_SYSTEM_DBUS_PATH);
513 ERROR("create_jbus failed for system");
517 /* observe signals of system */
518 if(jbus_on_signal_j(system_bus, "changed", on_signal_changed, NULL)) {
519 ERROR("adding signal observer failed");
523 /* connect to the session bus */
524 user_bus = create_jbus(usrbus, AFM_USER_DBUS_PATH);
526 ERROR("create_jbus failed");
531 if (jbus_add_service_j(user_bus, "runnables", on_runnables, NULL)
532 || jbus_add_service_j(user_bus, "detail", on_detail, NULL)
533 || jbus_add_service_j(user_bus, "start", on_start, NULL)
534 || jbus_add_service_j(user_bus, "terminate", on_terminate, NULL)
535 || jbus_add_service_j(user_bus, "stop", on_stop, NULL)
536 || jbus_add_service_j(user_bus, "continue", on_continue, NULL)
537 || jbus_add_service_j(user_bus, "runners", on_runners, NULL)
538 || jbus_add_service_j(user_bus, "state", on_state, NULL)
539 #if defined(EXPLICIT_CALL)
540 || jbus_add_service_s(user_bus, "install", on_install, NULL)
541 || jbus_add_service_s(user_bus, "uninstall", on_uninstall, NULL)) {
543 || jbus_add_service_s(user_bus, "install", (void (*)(struct sd_bus_message *, const char *, void *))propagate, "install")
544 || jbus_add_service_s(user_bus, "uninstall", (void (*)(struct sd_bus_message *, const char *, void *))propagate, "uninstall")) {
546 ERROR("adding services failed");
550 /* start servicing */
551 if (jbus_start_serving(user_bus) < 0) {
552 ERROR("can't start server");
556 /* run until error */
558 sd_event_run(evloop, (uint64_t)-1);