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