d4a955830f907b7a3bb7abc5fa9ffbf9202115f5
[src/app-framework-main.git] / src / afm-user-daemon.c
1 /*
2  Copyright 2015, 2016 IoT.bzh
3
4  author: José Bollo <jose.bollo@iot.bzh>
5
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
9
10      http://www.apache.org/licenses/LICENSE-2.0
11
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.
17 */
18
19 #include <unistd.h>
20 #include <stdio.h>
21 #include <time.h>
22 #include <getopt.h>
23 #include <string.h>
24
25 #include <systemd/sd-bus.h>
26 #include <systemd/sd-event.h>
27 #include <json-c/json.h>
28
29 #include "verbose.h"
30 #include "utils-jbus.h"
31 #include "utils-json.h"
32 #include "afm.h"
33 #include "afm-db.h"
34 #include "afm-launch-mode.h"
35 #include "afm-run.h"
36
37 /*
38  * name of the application
39  */
40 static const char appname[] = "afm-user-daemon";
41
42 /*
43  * string for printing usage
44  */
45 static const char usagestr[] =
46         "usage: %s [-q] [-v] [-m mode] [-r rootdir]... [-a appdir]...\n"
47         "\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"
54         "   -q           quiet\n"
55         "   -v           verbose\n"
56         "\n";
57
58 /*
59  * Option definition for getopt_long
60  */
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' },
72         { NULL, 0, NULL, 0 }
73 };
74
75 /*
76  * Connections to D-Bus
77  * This is an array for using the function
78  *    jbus_read_write_dispatch_multiple
79  * directly without transformations.
80  */
81 static struct jbus *jbuses[2];
82 #define system_bus  jbuses[0]
83 #define user_bus    jbuses[1]
84
85 /*
86  * Handle to the database of applications
87  */
88 static struct afm_db *afdb;
89
90 /*
91  * Returned error strings
92  */
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\"";
98
99
100 /*
101  * retrieves the 'runid' in 'obj' parameters received with the
102  * request 'smsg' for the 'method'.
103  *
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.
107  */
108 static int onrunid(struct sd_bus_message *smsg, struct json_object *obj,
109                                                 const char *method, int *runid)
110 {
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);
116                 return 0;
117         }
118
119         INFO("method %s called for %d", method, *runid);
120         return 1;
121 }
122
123 /*
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'.
126  */
127 static void reply(struct sd_bus_message *smsg, struct json_object *resp,
128                                                 const char *errstr)
129 {
130         if (resp)
131                 jbus_reply_j(smsg, resp);
132         else
133                 jbus_reply_error_s(smsg, errstr);
134 }
135
136 /*
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'.
139  */
140 static void reply_status(struct sd_bus_message *smsg, int status, const char *errstr)
141 {
142         if (status)
143                 jbus_reply_error_s(smsg, errstr);
144         else
145                 jbus_reply_s(smsg, "true");
146 }
147
148 /*
149  * On query "runnables" from 'smsg' with parameters of 'obj'.
150  *
151  * Nothing is expected in 'obj' that can be anything.
152  */
153 static void on_runnables(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
154 {
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);
160 }
161
162 /*
163  * On query "detail" from 'smsg' with parameters of 'obj'.
164  */
165 static void on_detail(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
166 {
167         const char *appid;
168         struct json_object *resp;
169
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 */
175         else {
176                 INFO("method detail called but bad request!");
177                 jbus_reply_error_s(smsg, error_bad_request);
178                 return;
179         }
180
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);
186 }
187
188 /*
189  * On query "start" from 'smsg' with parameters of 'obj'.
190  */
191 static void on_start(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
192 {
193         const char *appid, *modestr;
194         char *uri;
195         struct json_object *appli, *resp;
196         int runid;
197         char runidstr[20];
198         enum afm_launch_mode mode;
199
200         /* get the parameters */
201         mode = invalid_launch_mode;
202         if (j_read_string(obj, &appid)) {
203                 mode = get_default_launch_mode();
204         } else if (j_read_string_at(obj, "id", &appid)) {
205                 if (j_read_string_at(obj, "mode", &modestr)) {
206                         mode = launch_mode_of_name(modestr);
207                 } else {
208                         mode = get_default_launch_mode();
209                 }
210         }
211         if (!is_valid_launch_mode(mode)) {
212                 jbus_reply_error_s(smsg, error_bad_request);
213                 return;
214         }
215
216         /* get the application */
217         INFO("method start called for %s mode=%s", appid,
218                                                 name_of_launch_mode(mode));
219         appli = afm_db_get_application(afdb, appid);
220         if (appli == NULL) {
221                 jbus_reply_error_s(smsg, error_not_found);
222                 return;
223         }
224
225         /* launch the application */
226         uri = NULL;
227         runid = afm_run_start(appli, mode, &uri);
228         if (runid <= 0) {
229                 jbus_reply_error_s(smsg, error_cant_start);
230                 free(uri);
231                 return;
232         }
233
234         if (uri == NULL) {
235                 /* returns only the runid */
236                 snprintf(runidstr, sizeof runidstr, "%d", runid);
237                 runidstr[sizeof runidstr - 1] = 0;
238                 jbus_reply_s(smsg, runidstr);
239                 return;
240         }
241
242         /* returns the runid and its uri */
243         resp = json_object_new_object();
244         if (resp != NULL && j_add_integer(resp, "runid", runid)
245                                         && j_add_string(resp, "uri", uri))
246                 jbus_reply_j(smsg, resp);
247         else {
248                 afm_run_terminate(runid);
249                 jbus_reply_error_s(smsg, error_system);
250         }
251         json_object_put(resp);
252         free(uri);
253 }
254
255 /*
256  * On query "once" from 'smsg' with parameters of 'obj'.
257  */
258 static void on_once(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
259 {
260         const char *appid;
261         struct json_object *appli, *resp;
262         int runid;
263
264         /* get the parameters */
265         if (!j_read_string(obj, &appid) && !j_read_string_at(obj, "id", &appid)) {
266                 jbus_reply_error_s(smsg, error_bad_request);
267                 return;
268         }
269
270         /* get the application */
271         INFO("method once called for %s", appid);
272         appli = afm_db_get_application(afdb, appid);
273         if (appli == NULL) {
274                 jbus_reply_error_s(smsg, error_not_found);
275                 return;
276         }
277
278         /* launch the application */
279         runid = afm_run_once(appli);
280         if (runid <= 0) {
281                 jbus_reply_error_s(smsg, error_cant_start);
282                 return;
283         }
284
285         /* returns the state */
286         resp = afm_run_state(runid);
287         reply(smsg, resp, error_not_found);
288         json_object_put(resp);
289 }
290
291 /*
292  * On query "pause" from 'smsg' with parameters of 'obj'.
293  */
294 static void on_pause(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
295 {
296         int runid, status;
297         if (onrunid(smsg, obj, "pause", &runid)) {
298                 status = afm_run_pause(runid);
299                 reply_status(smsg, status, error_not_found);
300         }
301 }
302
303 /*
304  * On query "resume" from 'smsg' with parameters of 'obj'.
305  */
306 static void on_resume(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
307 {
308         int runid, status;
309         if (onrunid(smsg, obj, "resume", &runid)) {
310                 status = afm_run_resume(runid);
311                 reply_status(smsg, status, error_not_found);
312         }
313 }
314
315 /*
316  * On query "stop" from 'smsg' with parameters of 'obj'.
317  */
318 static void on_stop(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
319 {
320         NOTICE("call to obsolete 'stop'");
321         on_pause(smsg, obj, unused);
322 }
323
324 /*
325  * On query "continue" from 'smsg' with parameters of 'obj'.
326  */
327 static void on_continue(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
328 {
329         NOTICE("call to obsolete 'continue'");
330         on_resume(smsg, obj, unused);
331 }
332
333 /*
334  * On query "terminate" from 'smsg' with parameters of 'obj'.
335  */
336 static void on_terminate(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
337 {
338         int runid, status;
339         if (onrunid(smsg, obj, "terminate", &runid)) {
340                 status = afm_run_terminate(runid);
341                 reply_status(smsg, status, error_not_found);
342         }
343 }
344
345 /*
346  * On query "runners" from 'smsg' with parameters of 'obj'.
347  */
348 static void on_runners(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
349 {
350         struct json_object *resp;
351         INFO("method runners called");
352         resp = afm_run_list();
353         jbus_reply_j(smsg, resp);
354         json_object_put(resp);
355 }
356
357 /*
358  * On query "state" from 'smsg' with parameters of 'obj'.
359  */
360 static void on_state(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
361 {
362         int runid;
363         struct json_object *resp;
364         if (onrunid(smsg, obj, "state", &runid)) {
365                 resp = afm_run_state(runid);
366                 reply(smsg, resp, error_not_found);
367                 json_object_put(resp);
368         }
369 }
370
371 /*
372  * Calls the system daemon to achieve application management of
373  * the 'method' gotten from 'smsg' with the parameter's string 'msg'.
374  *
375  * The principle is very simple: call the corresponding system method
376  * and reply its response to the caller.
377  *
378  * The request and reply is synchronous and is blocking.
379  * It is possible to implment it in an asynchrounous way but it
380  * would brake the common behaviour. It would be a call like
381  * jbus_call_ss(system_bus, method, msg, callback, smsg)
382  */
383 static void propagate(struct sd_bus_message *smsg, const char *msg, const char *method)
384 {
385         char *reply;
386         INFO("method %s propagated with %s", method, msg);
387         reply = jbus_call_ss_sync(system_bus, method, msg);
388         if (reply) {
389                 jbus_reply_s(smsg, reply);
390                 free(reply);
391         }
392         else
393                 jbus_reply_error_s(smsg, error_system);
394 }
395
396 #if defined(EXPLICIT_CALL)
397 /*
398  * On query "install" from 'smsg' with parameters of 'msg'.
399  */
400 static void on_install(struct sd_bus_message *smsg, const char *msg, void *unused)
401 {
402         return propagate(smsg, msg, "install");
403 }
404
405 /*
406  * On query "uninstall" from 'smsg' with parameters of 'msg'.
407  */
408 static void on_uninstall(struct sd_bus_message *smsg, const char *msg, void *unused)
409 {
410         return propagate(smsg, msg, "uninstall");
411 }
412 #endif
413
414 /*
415  * On system signaling that applications list changed
416  */
417 static void on_signal_changed(struct json_object *obj, void *unused)
418 {
419         /* update the database */
420         afm_db_update_applications(afdb);
421         /* re-propagate now */
422         jbus_send_signal_j(user_bus, "changed", obj);
423 }
424
425 /*
426  * Tiny routine to daemonize the program
427  * Return 0 on success or -1 on failure.
428  */
429 static int daemonize()
430 {
431         int rc = fork();
432         if (rc < 0)
433                 return rc;
434         if (rc)
435                 _exit(0);
436         return 0;
437 }
438
439 /*
440  * Opens a sd-bus connection and returns it in 'ret'.
441  * The sd-bus connexion is intended to be for user if 'isuser'
442  * is not null. The adress is the default address when 'address'
443  * is NULL or, otherwise, the given address.
444  * It might be necessary to pass the address as an argument because
445  * library systemd uses secure_getenv to retrieves the default
446  * addresses and secure_getenv might return NULL in some cases.
447  */
448 static int open_bus(sd_bus **ret, int isuser, const char *address)
449 {
450         sd_bus *b;
451         int rc;
452
453         if (address == NULL)
454                 return (isuser ? sd_bus_open_user : sd_bus_open_system)(ret);
455
456         rc = sd_bus_new(&b);
457         if (rc < 0)
458                 return rc;
459
460         rc = sd_bus_set_address(b, address);
461         if (rc < 0)
462                 goto fail;
463
464         sd_bus_set_bus_client(b, 1);
465
466         rc = sd_bus_start(b);
467         if (rc < 0)
468                 goto fail;
469
470         *ret = b;
471         return 0;
472
473 fail:
474         sd_bus_unref(b);
475         return rc;
476 }
477
478 /*
479  * ENTRY POINT OF AFM-USER-DAEMON
480  */
481 int main(int ac, char **av)
482 {
483         int i, daemon = 0, rc;
484         enum afm_launch_mode mode;
485         struct sd_event *evloop;
486         struct sd_bus *sysbus, *usrbus;
487         const char *sys_bus_addr, *usr_bus_addr;
488
489         LOGAUTH(appname);
490
491         /* first interpretation of arguments */
492         sys_bus_addr = NULL;
493         usr_bus_addr = NULL;
494         while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
495                 switch (i) {
496                 case 'h':
497                         printf(usagestr, appname);
498                         return 0;
499                 case 'q':
500                         if (verbosity)
501                                 verbosity--;
502                         break;
503                 case 'v':
504                         verbosity++;
505                         break;
506                 case 'd':
507                         daemon = 1;
508                         break;
509                 case 'r':
510                         break;
511                 case 'a':
512                         break;
513                 case 'm':
514                         mode = launch_mode_of_name(optarg);
515                         if (!is_valid_launch_mode(mode)) {
516                                 ERROR("invalid mode '%s'", optarg);
517                                 return 1;
518                         }
519                         set_default_launch_mode(mode);
520                         break;
521                 case 'u':
522                         usr_bus_addr = optarg;
523                         break;
524                 case 's':
525                         sys_bus_addr = optarg;
526                         break;
527                 case ':':
528                         ERROR("missing argument value");
529                         return 1;
530                 default:
531                         ERROR("unrecognized option");
532                         return 1;
533                 }
534         }
535
536         /* init random generator */
537         srandom((unsigned int)time(NULL));
538
539         /* init runners */
540         if (afm_run_init()) {
541                 ERROR("afm_run_init failed");
542                 return 1;
543         }
544
545         /* init framework */
546         afdb = afm_db_create();
547         if (!afdb) {
548                 ERROR("afm_create failed");
549                 return 1;
550         }
551         if (afm_db_add_root(afdb, FWK_APP_DIR)) {
552                 ERROR("can't add root %s", FWK_APP_DIR);
553                 return 1;
554         }
555
556         /* second interpretation of arguments */
557         optind = 1;
558         while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
559                 switch (i) {
560                 case 'r':
561                         if (afm_db_add_root(afdb, optarg)) {
562                                 ERROR("can't add root %s", optarg);
563                                 return 1;
564                         }
565                         break;
566                 case 'a':
567                         if (afm_db_add_application(afdb, optarg)) {
568                                 ERROR("can't add application %s", optarg);
569                                 return 1;
570                         }
571                         break;
572                 }
573         }
574
575         /* update the database */
576         if (afm_db_update_applications(afdb)) {
577                 ERROR("afm_update_applications failed");
578                 return 1;
579         }
580
581         /* daemonize if requested */
582         if (daemon && daemonize()) {
583                 ERROR("daemonization failed");
584                 return 1;
585         }
586
587         /* get systemd objects */
588         rc = sd_event_new(&evloop);
589         if (rc < 0) {
590                 ERROR("can't create event loop");
591                 return 1;
592         }
593         rc = open_bus(&sysbus, 0, sys_bus_addr);
594         if (rc < 0) {
595                 ERROR("can't create system bus");
596                 return 1;
597         }
598         rc = sd_bus_attach_event(sysbus, evloop, 0);
599         if (rc < 0) {
600                 ERROR("can't attach system bus to event loop");
601                 return 1;
602         }
603         rc = open_bus(&usrbus, 1, usr_bus_addr);
604         if (rc < 0) {
605                 ERROR("can't create user bus");
606                 return 1;
607         }
608         rc = sd_bus_attach_event(usrbus, evloop, 0);
609         if (rc < 0) {
610                 ERROR("can't attach user bus to event loop");
611                 return 1;
612         }
613
614         /* connects to the system bus */
615         system_bus = create_jbus(sysbus, AFM_SYSTEM_DBUS_PATH);
616         if (!system_bus) {
617                 ERROR("create_jbus failed for system");
618                 return 1;
619         }
620
621         /* observe signals of system */
622         if(jbus_on_signal_j(system_bus, "changed", on_signal_changed, NULL)) {
623                 ERROR("adding signal observer failed");
624                 return 1;
625         }
626
627         /* connect to the session bus */
628         user_bus = create_jbus(usrbus, AFM_USER_DBUS_PATH);
629         if (!user_bus) {
630                 ERROR("create_jbus failed");
631                 return 1;
632         }
633
634         /* init services */
635         if (jbus_add_service_j(user_bus, "runnables", on_runnables, NULL)
636          || jbus_add_service_j(user_bus, "detail",    on_detail, NULL)
637          || jbus_add_service_j(user_bus, "start",     on_start, NULL)
638          || jbus_add_service_j(user_bus, "once",      on_once, NULL)
639          || jbus_add_service_j(user_bus, "terminate", on_terminate, NULL)
640          || jbus_add_service_j(user_bus, "pause",     on_pause, NULL)
641          || jbus_add_service_j(user_bus, "resume",    on_resume, NULL)
642          || jbus_add_service_j(user_bus, "stop",      on_stop, NULL)
643          || jbus_add_service_j(user_bus, "continue",  on_continue, NULL)
644          || jbus_add_service_j(user_bus, "runners",   on_runners, NULL)
645          || jbus_add_service_j(user_bus, "state",     on_state, NULL)
646 #if defined(EXPLICIT_CALL)
647          || jbus_add_service_s(user_bus, "install",   on_install, NULL)
648          || jbus_add_service_s(user_bus, "uninstall", on_uninstall, NULL)
649 #else
650          || jbus_add_service_s(user_bus, "install",   (void (*)(struct sd_bus_message *, const char *, void *))propagate, "install")
651          || jbus_add_service_s(user_bus, "uninstall", (void (*)(struct sd_bus_message *, const char *, void *))propagate, "uninstall")
652 #endif
653          ) {
654                 ERROR("adding services failed");
655                 return 1;
656         }
657
658         /* start servicing */
659         if (jbus_start_serving(user_bus) < 0) {
660                 ERROR("can't start server");
661                 return 1;
662         }
663
664         /* run until error */
665         for(;;)
666                 sd_event_run(evloop, (uint64_t)-1);
667         return 0;
668 }
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683