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