e71e011278d8a98abcc36a9553da34b0bd2f6fd8
[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 /*
190  * On query "start" from 'smsg' with parameters of 'obj'.
191  */
192 static void on_start(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
193 {
194         const char *appid, *modestr;
195         char *uri;
196         struct json_object *appli, *resp;
197         int runid;
198         char runidstr[20];
199         enum afm_launch_mode mode;
200
201         /* get the parameters */
202         mode = invalid_launch_mode;
203         if (j_read_string(obj, &appid)) {
204                 mode = get_default_launch_mode();
205         } else if (j_read_string_at(obj, "id", &appid)) {
206                 if (j_read_string_at(obj, "mode", &modestr)) {
207                         mode = launch_mode_of_name(modestr);
208                 } else {
209                         mode = get_default_launch_mode();
210                 }
211         }
212         if (!is_valid_launch_mode(mode)) {
213                 jbus_reply_error_s(smsg, error_bad_request);
214                 return;
215         }
216
217         /* get the application */
218         INFO("method start called for %s mode=%s", appid,
219                                                 name_of_launch_mode(mode));
220         appli = afm_db_get_application(afdb, appid);
221         if (appli == NULL) {
222                 jbus_reply_error_s(smsg, error_not_found);
223                 return;
224         }
225
226         /* launch the application */
227         uri = NULL;
228         runid = afm_run_start(appli, mode, &uri);
229         if (runid <= 0) {
230                 jbus_reply_error_s(smsg, error_cant_start);
231                 free(uri);
232                 return;
233         }
234
235         if (uri == NULL) {
236                 /* returns only the runid */
237                 snprintf(runidstr, sizeof runidstr, "%d", runid);
238                 runidstr[sizeof runidstr - 1] = 0;
239                 jbus_reply_s(smsg, runidstr);
240                 return;
241         }
242
243         /* returns the runid and its uri */
244         resp = json_object_new_object();
245         if (resp != NULL && j_add_integer(resp, "runid", runid)
246                                         && j_add_string(resp, "uri", uri))
247                 jbus_reply_j(smsg, resp);
248         else {
249                 afm_run_terminate(runid);
250                 jbus_reply_error_s(smsg, error_system);
251         }
252         json_object_put(resp);
253         free(uri);
254 }
255
256 /*
257  * On query "pause" from 'smsg' with parameters of 'obj'.
258  */
259 static void on_pause(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
260 {
261         int runid, status;
262         if (onrunid(smsg, obj, "pause", &runid)) {
263                 status = afm_run_pause(runid);
264                 reply_status(smsg, status, error_not_found);
265         }
266 }
267
268 /*
269  * On query "resume" from 'smsg' with parameters of 'obj'.
270  */
271 static void on_resume(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
272 {
273         int runid, status;
274         if (onrunid(smsg, obj, "resume", &runid)) {
275                 status = afm_run_resume(runid);
276                 reply_status(smsg, status, error_not_found);
277         }
278 }
279
280 /*
281  * On query "stop" from 'smsg' with parameters of 'obj'.
282  */
283 static void on_stop(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
284 {
285         NOTICE("call to obsolete 'stop'");
286         on_pause(smsg, obj, unused);
287 }
288
289 /*
290  * On query "continue" from 'smsg' with parameters of 'obj'.
291  */
292 static void on_continue(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
293 {
294         NOTICE("call to obsolete 'continue'");
295         on_resume(smsg, obj, unused);
296 }
297
298 /*
299  * On query "terminate" from 'smsg' with parameters of 'obj'.
300  */
301 static void on_terminate(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
302 {
303         int runid, status;
304         if (onrunid(smsg, obj, "terminate", &runid)) {
305                 status = afm_run_terminate(runid);
306                 reply_status(smsg, status, error_not_found);
307         }
308 }
309
310 /*
311  * On query "runners" from 'smsg' with parameters of 'obj'.
312  */
313 static void on_runners(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
314 {
315         struct json_object *resp;
316         INFO("method runners called");
317         resp = afm_run_list();
318         jbus_reply_j(smsg, resp);
319         json_object_put(resp);
320 }
321
322 /*
323  * On query "state" from 'smsg' with parameters of 'obj'.
324  */
325 static void on_state(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
326 {
327         int runid;
328         struct json_object *resp;
329         if (onrunid(smsg, obj, "state", &runid)) {
330                 resp = afm_run_state(runid);
331                 reply(smsg, resp, error_not_found);
332                 json_object_put(resp);
333         }
334 }
335
336 /*
337  * Calls the system daemon to achieve application management of
338  * the 'method' gotten from 'smsg' with the parameter's string 'msg'.
339  *
340  * The principle is very simple: call the corresponding system method
341  * and reply its response to the caller.
342  *
343  * The request and reply is synchronous and is blocking.
344  * It is possible to implment it in an asynchrounous way but it
345  * would brake the common behaviour. It would be a call like
346  * jbus_call_ss(system_bus, method, msg, callback, smsg)
347  */
348 static void propagate(struct sd_bus_message *smsg, const char *msg, const char *method)
349 {
350         char *reply;
351         INFO("method %s propagated with %s", method, msg);
352         reply = jbus_call_ss_sync(system_bus, method, msg);
353         if (reply) {
354                 jbus_reply_s(smsg, reply);
355                 free(reply);
356         }
357         else
358                 jbus_reply_error_s(smsg, error_system);
359 }
360
361 #if defined(EXPLICIT_CALL)
362 /*
363  * On query "install" from 'smsg' with parameters of 'msg'.
364  */
365 static void on_install(struct sd_bus_message *smsg, const char *msg, void *unused)
366 {
367         return propagate(smsg, msg, "install");
368 }
369
370 /*
371  * On query "uninstall" from 'smsg' with parameters of 'msg'.
372  */
373 static void on_uninstall(struct sd_bus_message *smsg, const char *msg, void *unused)
374 {
375         return propagate(smsg, msg, "uninstall");
376 }
377 #endif
378
379 /*
380  * On system signaling that applications list changed
381  */
382 static void on_signal_changed(struct json_object *obj, void *unused)
383 {
384         /* update the database */
385         afm_db_update_applications(afdb);
386         /* re-propagate now */
387         jbus_send_signal_j(user_bus, "changed", obj);
388 }
389
390 /*
391  * Tiny routine to daemonize the program
392  * Return 0 on success or -1 on failure.
393  */
394 static int daemonize()
395 {
396         int rc = fork();
397         if (rc < 0)
398                 return rc;
399         if (rc)
400                 _exit(0);
401         return 0;
402 }
403
404 /*
405  * Opens a sd-bus connection and returns it in 'ret'.
406  * The sd-bus connexion is intended to be for user if 'isuser'
407  * is not null. The adress is the default address when 'address'
408  * is NULL or, otherwise, the given address.
409  * It might be necessary to pass the address as an argument because
410  * library systemd uses secure_getenv to retrieves the default
411  * addresses and secure_getenv might return NULL in some cases.
412  */
413 static int open_bus(sd_bus **ret, int isuser, const char *address)
414 {
415         sd_bus *b;
416         int rc;
417
418         if (address == NULL)
419                 return (isuser ? sd_bus_open_user : sd_bus_open_system)(ret);
420
421         rc = sd_bus_new(&b);
422         if (rc < 0)
423                 return rc;
424
425         rc = sd_bus_set_address(b, address);
426         if (rc < 0)
427                 goto fail;
428
429         sd_bus_set_bus_client(b, 1);
430
431         rc = sd_bus_start(b);
432         if (rc < 0)
433                 goto fail;
434
435         *ret = b;
436         return 0;
437
438 fail:
439         sd_bus_unref(b);
440         return rc;
441 }
442
443 /*
444  * ENTRY POINT OF AFM-USER-DAEMON
445  */
446 int main(int ac, char **av)
447 {
448         int i, daemon = 0, rc;
449         enum afm_launch_mode mode;
450         struct sd_event *evloop;
451         struct sd_bus *sysbus, *usrbus;
452         const char *sys_bus_addr, *usr_bus_addr;
453
454         LOGAUTH(appname);
455
456         /* first interpretation of arguments */
457         sys_bus_addr = NULL;
458         usr_bus_addr = NULL;
459         while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
460                 switch (i) {
461                 case 'h':
462                         printf(usagestr, appname);
463                         return 0;
464                 case 'q':
465                         if (verbosity)
466                                 verbosity--;
467                         break;
468                 case 'v':
469                         verbosity++;
470                         break;
471                 case 'd':
472                         daemon = 1;
473                         break;
474                 case 'r':
475                         break;
476                 case 'a':
477                         break;
478                 case 'm':
479                         mode = launch_mode_of_name(optarg);
480                         if (!is_valid_launch_mode(mode)) {
481                                 ERROR("invalid mode '%s'", optarg);
482                                 return 1;
483                         }
484                         set_default_launch_mode(mode);
485                         break;
486                 case 'u':
487                         usr_bus_addr = optarg;
488                         break;
489                 case 's':
490                         sys_bus_addr = optarg;
491                         break;
492                 case ':':
493                         ERROR("missing argument value");
494                         return 1;
495                 default:
496                         ERROR("unrecognized option");
497                         return 1;
498                 }
499         }
500
501         /* init random generator */
502         srandom((unsigned int)time(NULL));
503
504         /* init runners */
505         if (afm_run_init()) {
506                 ERROR("afm_run_init failed");
507                 return 1;
508         }
509
510         /* init framework */
511         afdb = afm_db_create();
512         if (!afdb) {
513                 ERROR("afm_create failed");
514                 return 1;
515         }
516         if (afm_db_add_root(afdb, FWK_APP_DIR)) {
517                 ERROR("can't add root %s", FWK_APP_DIR);
518                 return 1;
519         }
520
521         /* second interpretation of arguments */
522         optind = 1;
523         while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
524                 switch (i) {
525                 case 'r':
526                         if (afm_db_add_root(afdb, optarg)) {
527                                 ERROR("can't add root %s", optarg);
528                                 return 1;
529                         }
530                         break;
531                 case 'a':
532                         if (afm_db_add_application(afdb, optarg)) {
533                                 ERROR("can't add application %s", optarg);
534                                 return 1;
535                         }
536                         break;
537                 }
538         }
539
540         /* update the database */
541         if (afm_db_update_applications(afdb)) {
542                 ERROR("afm_update_applications failed");
543                 return 1;
544         }
545
546         /* daemonize if requested */
547         if (daemon && daemonize()) {
548                 ERROR("daemonization failed");
549                 return 1;
550         }
551
552         /* get systemd objects */
553         rc = sd_event_new(&evloop);
554         if (rc < 0) {
555                 ERROR("can't create event loop");
556                 return 1;
557         }
558         rc = open_bus(&sysbus, 0, sys_bus_addr);
559         if (rc < 0) {
560                 ERROR("can't create system bus");
561                 return 1;
562         }
563         rc = sd_bus_attach_event(sysbus, evloop, 0);
564         if (rc < 0) {
565                 ERROR("can't attach system bus to event loop");
566                 return 1;
567         }
568         rc = open_bus(&usrbus, 1, usr_bus_addr);
569         if (rc < 0) {
570                 ERROR("can't create user bus");
571                 return 1;
572         }
573         rc = sd_bus_attach_event(usrbus, evloop, 0);
574         if (rc < 0) {
575                 ERROR("can't attach user bus to event loop");
576                 return 1;
577         }
578
579         /* connects to the system bus */
580         system_bus = create_jbus(sysbus, AFM_SYSTEM_DBUS_PATH);
581         if (!system_bus) {
582                 ERROR("create_jbus failed for system");
583                 return 1;
584         }
585
586         /* observe signals of system */
587         if(jbus_on_signal_j(system_bus, "changed", on_signal_changed, NULL)) {
588                 ERROR("adding signal observer failed");
589                 return 1;
590         }
591
592         /* connect to the session bus */
593         user_bus = create_jbus(usrbus, AFM_USER_DBUS_PATH);
594         if (!user_bus) {
595                 ERROR("create_jbus failed");
596                 return 1;
597         }
598
599         /* init services */
600         if (jbus_add_service_j(user_bus, "runnables", on_runnables, NULL)
601          || jbus_add_service_j(user_bus, "detail",    on_detail, NULL)
602          || jbus_add_service_j(user_bus, "start",     on_start, NULL)
603          || jbus_add_service_j(user_bus, "terminate", on_terminate, NULL)
604          || jbus_add_service_j(user_bus, "pause",     on_pause, NULL)
605          || jbus_add_service_j(user_bus, "resume",    on_resume, NULL)
606          || jbus_add_service_j(user_bus, "stop",      on_stop, NULL)
607          || jbus_add_service_j(user_bus, "continue",  on_continue, NULL)
608          || jbus_add_service_j(user_bus, "runners",   on_runners, NULL)
609          || jbus_add_service_j(user_bus, "state",     on_state, NULL)
610 #if defined(EXPLICIT_CALL)
611          || jbus_add_service_s(user_bus, "install",   on_install, NULL)
612          || jbus_add_service_s(user_bus, "uninstall", on_uninstall, NULL)
613 #else
614          || jbus_add_service_s(user_bus, "install",   (void (*)(struct sd_bus_message *, const char *, void *))propagate, "install")
615          || jbus_add_service_s(user_bus, "uninstall", (void (*)(struct sd_bus_message *, const char *, void *))propagate, "uninstall")
616 #endif
617          ) {
618                 ERROR("adding services failed");
619                 return 1;
620         }
621
622         /* start servicing */
623         if (jbus_start_serving(user_bus) < 0) {
624                 ERROR("can't start server");
625                 return 1;
626         }
627
628         /* run until error */
629         for(;;)
630                 sd_event_run(evloop, (uint64_t)-1);
631         return 0;
632 }
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647