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.
28 #include "utils-jbus.h"
29 #include "utils-json.h"
32 #include "afm-launch-mode.h"
36 * name of the application
38 static const char appname[] = "afm-user-daemon";
41 * string for printing usage
43 static const char usagestr[] =
44 "usage: %s [-q] [-v] [-m mode] [-r rootdir]... [-a appdir]...\n"
46 " -a appdir adds an application directory\n"
47 " -r rootdir adds a root directory of applications\n"
48 " -m mode set default launch mode (local or remote)\n"
49 " -d run as a daemon\n"
55 * Option definition for getopt_long
57 static const char options_s[] = "hdqvr:a:m:";
58 static struct option options_l[] = {
59 { "root", required_argument, NULL, 'r' },
60 { "application", required_argument, NULL, 'a' },
61 { "mode", required_argument, NULL, 'm' },
62 { "daemon", no_argument, NULL, 'd' },
63 { "quiet", no_argument, NULL, 'q' },
64 { "verbose", no_argument, NULL, 'v' },
65 { "help", no_argument, NULL, 'h' },
70 * Connections to D-Bus
71 * This is an array for using the function
72 * jbus_read_write_dispatch_multiple
73 * directly without transformations.
75 static struct jbus *jbuses[2];
76 #define system_bus jbuses[0]
77 #define user_bus jbuses[1]
80 * Handle to the database of applications
82 static struct afm_db *afdb;
85 * Returned error strings
87 const char error_nothing[] = "[]";
88 const char error_bad_request[] = "\"bad request\"";
89 const char error_not_found[] = "\"not found\"";
90 const char error_cant_start[] = "\"can't start\"";
91 const char error_system[] = "\"system error\"";
95 * retrieves the 'runid' in 'obj' parameters received with the
96 * request 'jreq' for the 'method'.
98 * Returns 1 in case of success.
99 * Otherwise, if the 'runid' can't be retrived, an error stating
100 * the bad request is replied for 'jreq' and 0 is returned.
102 static int onrunid(struct jreq *jreq, struct json_object *obj,
103 const char *method, int *runid)
105 if (!j_read_integer(obj, runid)
106 && !j_read_integer_at(obj, "runid", runid)) {
107 INFO("bad request method %s: %s", method,
108 json_object_to_json_string(obj));
109 jbus_reply_error_s(jreq, error_bad_request);
113 INFO("method %s called for %d", method, *runid);
118 * Sends the reply 'resp' to the request 'jreq' if 'resp' is not NULL.
119 * Otherwise, when 'resp' is NULL replies the error string 'errstr'.
121 static void reply(struct jreq *jreq, struct json_object *resp,
125 jbus_reply_j(jreq, resp);
127 jbus_reply_error_s(jreq, errstr);
131 * Sends the reply "true" to the request 'jreq' if 'status' is zero.
132 * Otherwise, when 'status' is not zero replies the error string 'errstr'.
134 static void reply_status(struct jreq *jreq, int status, const char *errstr)
137 jbus_reply_error_s(jreq, errstr);
139 jbus_reply_s(jreq, "true");
143 * On query "runnables" from 'jreq' with parameters of 'obj'.
145 * Nothing is expected in 'obj' that can be anything.
147 static void on_runnables(struct jreq *jreq, struct json_object *obj)
149 struct json_object *resp;
150 INFO("method runnables called");
151 resp = afm_db_application_list(afdb);
152 jbus_reply_j(jreq, resp);
153 json_object_put(resp);
157 * On query "detail" from 'jreq' with parameters of 'obj'.
159 static void on_detail(struct jreq *jreq, struct json_object *obj)
162 struct json_object *resp;
164 /* get the parameters */
165 if (j_read_string(obj, &appid))
166 ; /* appid as a string */
167 else if (j_read_string_at(obj, "id", &appid))
168 ; /* appid as obj.id string */
170 INFO("method detail called but bad request!");
171 jbus_reply_error_s(jreq, error_bad_request);
175 /* wants details for appid */
176 INFO("method detail called for %s", appid);
177 resp = afm_db_get_application_public(afdb, appid);
178 reply(jreq, resp, error_not_found);
179 json_object_put(resp);
184 * On query "start" from 'jreq' with parameters of 'obj'.
186 static void on_start(struct jreq *jreq, struct json_object *obj)
188 const char *appid, *modestr;
190 struct json_object *appli, *resp;
193 enum afm_launch_mode mode;
195 /* get the parameters */
196 mode = invalid_launch_mode;
197 if (j_read_string(obj, &appid)) {
198 mode = get_default_launch_mode();
199 } else if (j_read_string_at(obj, "id", &appid)) {
200 if (j_read_string_at(obj, "mode", &modestr)) {
201 mode = launch_mode_of_name(modestr);
203 mode = get_default_launch_mode();
206 if (!is_valid_launch_mode(mode)) {
207 jbus_reply_error_s(jreq, error_bad_request);
211 /* get the application */
212 INFO("method start called for %s mode=%s", appid,
213 name_of_launch_mode(mode));
214 appli = afm_db_get_application(afdb, appid);
216 jbus_reply_error_s(jreq, error_not_found);
220 /* launch the application */
222 runid = afm_run_start(appli, mode, &uri);
224 jbus_reply_error_s(jreq, error_cant_start);
230 /* returns only the runid */
231 snprintf(runidstr, sizeof runidstr, "%d", runid);
232 runidstr[sizeof runidstr - 1] = 0;
233 jbus_reply_s(jreq, runidstr);
237 /* returns the runid and its uri */
238 resp = json_object_new_object();
239 if (resp != NULL && j_add_integer(resp, "runid", runid)
240 && j_add_string(resp, "uri", uri))
241 jbus_reply_j(jreq, resp);
244 jbus_reply_error_s(jreq, error_system);
246 json_object_put(resp);
251 * On query "stop" from 'jreq' with parameters of 'obj'.
253 static void on_stop(struct jreq *jreq, struct json_object *obj)
256 if (onrunid(jreq, obj, "stop", &runid)) {
257 status = afm_run_stop(runid);
258 reply_status(jreq, status, error_not_found);
263 * On query "continue" from 'jreq' with parameters of 'obj'.
265 static void on_continue(struct jreq *jreq, struct json_object *obj)
268 if (onrunid(jreq, obj, "continue", &runid)) {
269 status = afm_run_continue(runid);
270 reply_status(jreq, status, error_not_found);
275 * On query "terminate" from 'jreq' with parameters of 'obj'.
277 static void on_terminate(struct jreq *jreq, struct json_object *obj)
280 if (onrunid(jreq, obj, "terminate", &runid)) {
281 status = afm_run_terminate(runid);
282 reply_status(jreq, status, error_not_found);
287 * On query "runners" from 'jreq' with parameters of 'obj'.
289 static void on_runners(struct jreq *jreq, struct json_object *obj)
291 struct json_object *resp;
292 INFO("method runners called");
293 resp = afm_run_list();
294 jbus_reply_j(jreq, resp);
295 json_object_put(resp);
299 * On query "state" from 'jreq' with parameters of 'obj'.
301 static void on_state(struct jreq *jreq, struct json_object *obj)
304 struct json_object *resp;
305 if (onrunid(jreq, obj, "state", &runid)) {
306 resp = afm_run_state(runid);
307 reply(jreq, resp, error_not_found);
308 json_object_put(resp);
313 * Calls the system daemon to achieve application management of
314 * the 'method' gotten from 'jreq' with the parameter's string 'msg'.
316 * The principle is very simple: call the corresponding system method
317 * and reply its response to the caller.
319 * The request and reply is synchronous and is blocking.
320 * It is possible to implment it in an asynchrounous way but it
321 * would brake the common behaviour. It would be a call like
322 * jbus_call_ss(system_bus, method, msg, callback, jreq)
324 static void propagate(struct jreq *jreq, const char *msg, const char *method)
327 INFO("method %s propagated with %s", method, msg);
328 reply = jbus_call_ss_sync(system_bus, method, msg);
330 jbus_reply_s(jreq, reply);
334 jbus_reply_error_s(jreq, error_system);
338 * On query "install" from 'jreq' with parameters of 'msg'.
340 static void on_install(struct jreq *jreq, const char *msg)
342 return propagate(jreq, msg, "install");
346 * On query "uninstall" from 'jreq' with parameters of 'msg'.
348 static void on_uninstall(struct jreq *jreq, const char *msg)
350 return propagate(jreq, msg, "uninstall");
354 * On system signaling that applications list changed
356 static void on_signal_changed(struct json_object *obj)
358 /* update the database */
359 afm_db_update_applications(afdb);
360 /* re-propagate now */
361 jbus_send_signal_j(user_bus, "changed", obj);
365 * Tiny routine to daemonize the program
366 * Return 0 on success or -1 on failure.
368 static int daemonize()
379 * ENTRY POINT OF AFM-USER-DAEMON
381 int main(int ac, char **av)
384 enum afm_launch_mode mode;
388 /* first interpretation of arguments */
389 while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
392 printf(usagestr, appname);
409 mode = launch_mode_of_name(optarg);
410 if (!is_valid_launch_mode(mode)) {
411 ERROR("invalid mode '%s'", optarg);
414 set_default_launch_mode(mode);
417 ERROR("missing argument value");
420 ERROR("unrecognized option");
425 /* init random generator */
426 srandom((unsigned int)time(NULL));
429 if (afm_run_init()) {
430 ERROR("afm_run_init failed");
435 afdb = afm_db_create();
437 ERROR("afm_create failed");
440 if (afm_db_add_root(afdb, FWK_APP_DIR)) {
441 ERROR("can't add root %s", FWK_APP_DIR);
445 /* second interpretation of arguments */
447 while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
450 if (afm_db_add_root(afdb, optarg)) {
451 ERROR("can't add root %s", optarg);
456 if (afm_db_add_application(afdb, optarg)) {
457 ERROR("can't add application %s", optarg);
464 /* update the database */
465 if (afm_db_update_applications(afdb)) {
466 ERROR("afm_update_applications failed");
470 /* daemonize if requested */
471 if (daemon && daemonize()) {
472 ERROR("daemonization failed");
476 /* connects to the system bus */
477 system_bus = create_jbus_system(AFM_SYSTEM_DBUS_PATH);
479 ERROR("create_jbus failed for system");
483 /* observe signals of system */
484 if(jbus_on_signal_j(system_bus, "changed", on_signal_changed)) {
485 ERROR("adding signal observer failed");
489 /* connect to the session bus */
490 user_bus = create_jbus_session(AFM_USER_DBUS_PATH);
492 ERROR("create_jbus failed");
497 if (jbus_add_service_j(user_bus, "runnables", on_runnables)
498 || jbus_add_service_j(user_bus, "detail", on_detail)
499 || jbus_add_service_j(user_bus, "start", on_start)
500 || jbus_add_service_j(user_bus, "terminate", on_terminate)
501 || jbus_add_service_j(user_bus, "stop", on_stop)
502 || jbus_add_service_j(user_bus, "continue", on_continue)
503 || jbus_add_service_j(user_bus, "runners", on_runners)
504 || jbus_add_service_j(user_bus, "state", on_state)
505 || jbus_add_service_s(user_bus, "install", on_install)
506 || jbus_add_service_s(user_bus, "uninstall", on_uninstall)) {
507 ERROR("adding services failed");
511 /* start servicing */
512 if (jbus_start_serving(user_bus)) {
513 ERROR("can't start server");
517 /* run until error */
518 while (jbus_read_write_dispatch_multiple(jbuses, 2, -1, 20) >= 0);