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