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