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