1 /*
2  *	BIRD Internet Routing Daemon -- Unix Entry Point
3  *
4  *	(c) 1998--2000 Martin Mares <mj@ucw.cz>
5  *
6  *	Can be freely distributed and used under the terms of the GNU GPL.
7  */
8 
9 #undef LOCAL_DEBUG
10 
11 #ifndef _GNU_SOURCE
12 #define _GNU_SOURCE
13 #endif
14 
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <fcntl.h>
18 #include <unistd.h>
19 #include <signal.h>
20 #include <pwd.h>
21 #include <grp.h>
22 #include <sys/stat.h>
23 #include <libgen.h>
24 
25 #include "nest/bird.h"
26 #include "lib/lists.h"
27 #include "lib/resource.h"
28 #include "lib/socket.h"
29 #include "lib/event.h"
30 #include "lib/string.h"
31 #include "nest/route.h"
32 #include "nest/protocol.h"
33 #include "nest/iface.h"
34 #include "nest/cli.h"
35 #include "nest/locks.h"
36 #include "conf/conf.h"
37 #include "filter/filter.h"
38 
39 #include "unix.h"
40 #include "krt.h"
41 
42 /*
43  *	Debugging
44  */
45 
46 #ifdef DEBUGGING
47 static int debug_flag = 1;
48 #else
49 static int debug_flag = 0;
50 #endif
51 
52 void
async_dump(void)53 async_dump(void)
54 {
55   debug("INTERNAL STATE DUMP\n\n");
56 
57   rdump(&root_pool);
58   sk_dump_all();
59   tm_dump_all();
60   if_dump_all();
61   neigh_dump_all();
62   rta_dump_all();
63   rt_dump_all();
64   protos_dump_all();
65 
66   debug("\n");
67 }
68 
69 /*
70  *	Dropping privileges
71  */
72 
73 #ifdef CONFIG_RESTRICTED_PRIVILEGES
74 #include "lib/syspriv.h"
75 #else
76 
77 static inline void
drop_uid(uid_t uid UNUSED)78 drop_uid(uid_t uid UNUSED)
79 {
80   die("Cannot change user on this platform");
81 }
82 
83 #endif
84 
85 static inline void
drop_gid(gid_t gid)86 drop_gid(gid_t gid)
87 {
88   if (setgid(gid) < 0)
89     die("setgid: %m");
90 
91   if (setgroups(0, NULL) < 0)
92     die("setgroups: %m");
93 }
94 
95 /*
96  *	Reading the Configuration
97  */
98 
99 #ifdef PATH_IPROUTE_DIR
100 
101 static inline void
add_num_const(char * name,int val)102 add_num_const(char *name, int val)
103 {
104   struct symbol *s = cf_get_symbol(name);
105   s->class = SYM_CONSTANT | T_INT;
106   s->def = cfg_allocz(sizeof(struct f_val));
107   SYM_TYPE(s) = T_INT;
108   SYM_VAL(s).i = val;
109 }
110 
111 /* the code of read_iproute_table() is based on
112    rtnl_tab_initialize() from iproute2 package */
113 static void
read_iproute_table(char * file,char * prefix,int max)114 read_iproute_table(char *file, char *prefix, int max)
115 {
116   char buf[512], namebuf[512];
117   char *name;
118   int val;
119   FILE *fp;
120 
121   strcpy(namebuf, prefix);
122   name = namebuf + strlen(prefix);
123 
124   fp = fopen(file, "r");
125   if (!fp)
126     return;
127 
128   while (fgets(buf, sizeof(buf), fp))
129   {
130     char *p = buf;
131 
132     while (*p == ' ' || *p == '\t')
133       p++;
134 
135     if (*p == '#' || *p == '\n' || *p == 0)
136       continue;
137 
138     if (sscanf(p, "0x%x %s\n", &val, name) != 2 &&
139 	sscanf(p, "0x%x %s #", &val, name) != 2 &&
140 	sscanf(p, "%d %s\n", &val, name) != 2 &&
141 	sscanf(p, "%d %s #", &val, name) != 2)
142       continue;
143 
144     if (val < 0 || val > max)
145       continue;
146 
147     for(p = name; *p; p++)
148       if ((*p < 'a' || *p > 'z') && (*p < '0' || *p > '9') && (*p != '_'))
149 	*p = '_';
150 
151     add_num_const(namebuf, val);
152   }
153 
154   fclose(fp);
155 }
156 
157 #endif // PATH_IPROUTE_DIR
158 
159 
160 static char *config_name = PATH_CONFIG_FILE;
161 
162 static int
cf_read(byte * dest,uint len,int fd)163 cf_read(byte *dest, uint len, int fd)
164 {
165   int l = read(fd, dest, len);
166   if (l < 0)
167     cf_error("Read error");
168   return l;
169 }
170 
171 void
sysdep_preconfig(struct config * c)172 sysdep_preconfig(struct config *c)
173 {
174   init_list(&c->logfiles);
175 
176   c->latency_limit = UNIX_DEFAULT_LATENCY_LIMIT;
177   c->watchdog_warning = UNIX_DEFAULT_WATCHDOG_WARNING;
178 
179 #ifdef PATH_IPROUTE_DIR
180   read_iproute_table(PATH_IPROUTE_DIR "/rt_protos", "ipp_", 256);
181   read_iproute_table(PATH_IPROUTE_DIR "/rt_realms", "ipr_", 256);
182   read_iproute_table(PATH_IPROUTE_DIR "/rt_scopes", "ips_", 256);
183   read_iproute_table(PATH_IPROUTE_DIR "/rt_tables", "ipt_", 256);
184 #endif
185 }
186 
187 int
sysdep_commit(struct config * new,struct config * old UNUSED)188 sysdep_commit(struct config *new, struct config *old UNUSED)
189 {
190   log_switch(debug_flag, &new->logfiles, new->syslog_name);
191   return 0;
192 }
193 
194 static int
unix_read_config(struct config ** cp,char * name)195 unix_read_config(struct config **cp, char *name)
196 {
197   struct config *conf = config_alloc(name);
198   int ret;
199 
200   *cp = conf;
201   conf->file_fd = open(name, O_RDONLY);
202   if (conf->file_fd < 0)
203     return 0;
204   cf_read_hook = cf_read;
205   ret = config_parse(conf);
206   close(conf->file_fd);
207   return ret;
208 }
209 
210 static struct config *
read_config(void)211 read_config(void)
212 {
213   struct config *conf;
214 
215   if (!unix_read_config(&conf, config_name))
216     {
217       if (conf->err_msg)
218 	die("%s:%d:%d %s", conf->err_file_name, conf->err_lino, conf->err_chno, conf->err_msg);
219       else
220 	die("Unable to open configuration file %s: %m", config_name);
221     }
222 
223   return conf;
224 }
225 
226 void
async_config(void)227 async_config(void)
228 {
229   struct config *conf;
230 
231   log(L_INFO "Reconfiguration requested by SIGHUP");
232   if (!unix_read_config(&conf, config_name))
233     {
234       if (conf->err_msg)
235 	log(L_ERR "%s:%d:%d %s", conf->err_file_name, conf->err_lino, conf->err_chno, conf->err_msg);
236       else
237 	log(L_ERR "Unable to open configuration file %s: %m", config_name);
238       config_free(conf);
239     }
240   else
241     config_commit(conf, RECONFIG_HARD, 0);
242 }
243 
244 static struct config *
cmd_read_config(char * name)245 cmd_read_config(char *name)
246 {
247   struct config *conf;
248 
249   if (!name)
250     name = config_name;
251 
252   cli_msg(-2, "Reading configuration from %s", name);
253   if (!unix_read_config(&conf, name))
254     {
255       if (conf->err_msg)
256 	cli_msg(8002, "%s:%d:%d %s", conf->err_file_name, conf->err_lino, conf->err_chno, conf->err_msg);
257       else
258 	cli_msg(8002, "%s: %m", name);
259       config_free(conf);
260       conf = NULL;
261     }
262 
263   return conf;
264 }
265 
266 void
cmd_check_config(char * name)267 cmd_check_config(char *name)
268 {
269   struct config *conf = cmd_read_config(name);
270   if (!conf)
271     return;
272 
273   cli_msg(20, "Configuration OK");
274   config_free(conf);
275 }
276 
277 static void
cmd_reconfig_msg(int r)278 cmd_reconfig_msg(int r)
279 {
280   switch (r)
281     {
282     case CONF_DONE:	cli_msg( 3, "Reconfigured"); break;
283     case CONF_PROGRESS: cli_msg( 4, "Reconfiguration in progress"); break;
284     case CONF_QUEUED:	cli_msg( 5, "Reconfiguration already in progress, queueing new config"); break;
285     case CONF_UNQUEUED:	cli_msg(17, "Reconfiguration already in progress, removing queued config"); break;
286     case CONF_CONFIRM:	cli_msg(18, "Reconfiguration confirmed"); break;
287     case CONF_SHUTDOWN:	cli_msg( 6, "Reconfiguration ignored, shutting down"); break;
288     case CONF_NOTHING:	cli_msg(19, "Nothing to do"); break;
289     default:		break;
290     }
291 }
292 
293 /* Hack for scheduled undo notification */
294 cli *cmd_reconfig_stored_cli;
295 
296 void
cmd_reconfig_undo_notify(void)297 cmd_reconfig_undo_notify(void)
298 {
299   if (cmd_reconfig_stored_cli)
300     {
301       cli *c = cmd_reconfig_stored_cli;
302       cli_printf(c, CLI_ASYNC_CODE, "Config timeout expired, starting undo");
303       cli_write_trigger(c);
304     }
305 }
306 
307 void
cmd_reconfig(char * name,int type,int timeout)308 cmd_reconfig(char *name, int type, int timeout)
309 {
310   if (cli_access_restricted())
311     return;
312 
313   struct config *conf = cmd_read_config(name);
314   if (!conf)
315     return;
316 
317   int r = config_commit(conf, type, timeout);
318 
319   if ((r >= 0) && (timeout > 0))
320     {
321       cmd_reconfig_stored_cli = this_cli;
322       cli_msg(-22, "Undo scheduled in %d s", timeout);
323     }
324 
325   cmd_reconfig_msg(r);
326 }
327 
328 void
cmd_reconfig_confirm(void)329 cmd_reconfig_confirm(void)
330 {
331   if (cli_access_restricted())
332     return;
333 
334   int r = config_confirm();
335   cmd_reconfig_msg(r);
336 }
337 
338 void
cmd_reconfig_undo(void)339 cmd_reconfig_undo(void)
340 {
341   if (cli_access_restricted())
342     return;
343 
344   cli_msg(-21, "Undo requested");
345 
346   int r = config_undo();
347   cmd_reconfig_msg(r);
348 }
349 
350 /*
351  *	Command-Line Interface
352  */
353 
354 static sock *cli_sk;
355 static char *path_control_socket = PATH_CONTROL_SOCKET;
356 
357 
358 static void
cli_write(cli * c)359 cli_write(cli *c)
360 {
361   sock *s = c->priv;
362 
363   while (c->tx_pos)
364     {
365       struct cli_out *o = c->tx_pos;
366 
367       int len = o->wpos - o->outpos;
368       s->tbuf = o->outpos;
369       o->outpos = o->wpos;
370 
371       if (sk_send(s, len) <= 0)
372 	return;
373 
374       c->tx_pos = o->next;
375     }
376 
377   /* Everything is written */
378   s->tbuf = NULL;
379   cli_written(c);
380 }
381 
382 void
cli_write_trigger(cli * c)383 cli_write_trigger(cli *c)
384 {
385   sock *s = c->priv;
386 
387   if (s->tbuf == NULL)
388     cli_write(c);
389 }
390 
391 static void
cli_tx(sock * s)392 cli_tx(sock *s)
393 {
394   cli_write(s->data);
395 }
396 
397 int
cli_get_command(cli * c)398 cli_get_command(cli *c)
399 {
400   sock *s = c->priv;
401   byte *t = c->rx_aux ? : s->rbuf;
402   byte *tend = s->rpos;
403   byte *d = c->rx_pos;
404   byte *dend = c->rx_buf + CLI_RX_BUF_SIZE - 2;
405 
406   while (t < tend)
407     {
408       if (*t == '\r')
409 	t++;
410       else if (*t == '\n')
411 	{
412 	  t++;
413 	  c->rx_pos = c->rx_buf;
414 	  c->rx_aux = t;
415 	  *d = 0;
416 	  return (d < dend) ? 1 : -1;
417 	}
418       else if (d < dend)
419 	*d++ = *t++;
420     }
421   c->rx_aux = s->rpos = s->rbuf;
422   c->rx_pos = d;
423   return 0;
424 }
425 
426 static int
cli_rx(sock * s,uint size UNUSED)427 cli_rx(sock *s, uint size UNUSED)
428 {
429   cli_kick(s->data);
430   return 0;
431 }
432 
433 static void
cli_err(sock * s,int err)434 cli_err(sock *s, int err)
435 {
436   if (config->cli_debug)
437     {
438       if (err)
439 	log(L_INFO "CLI connection dropped: %s", strerror(err));
440       else
441 	log(L_INFO "CLI connection closed");
442     }
443   cli_free(s->data);
444 }
445 
446 static int
cli_connect(sock * s,uint size UNUSED)447 cli_connect(sock *s, uint size UNUSED)
448 {
449   cli *c;
450 
451   if (config->cli_debug)
452     log(L_INFO "CLI connect");
453   s->rx_hook = cli_rx;
454   s->tx_hook = cli_tx;
455   s->err_hook = cli_err;
456   s->data = c = cli_new(s);
457   s->pool = c->pool;		/* We need to have all the socket buffers allocated in the cli pool */
458   s->fast_rx = 1;
459   c->rx_pos = c->rx_buf;
460   c->rx_aux = NULL;
461   rmove(s, c->pool);
462   return 1;
463 }
464 
465 static void
cli_init_unix(uid_t use_uid,gid_t use_gid)466 cli_init_unix(uid_t use_uid, gid_t use_gid)
467 {
468   sock *s;
469 
470   cli_init();
471   s = cli_sk = sk_new(cli_pool);
472   s->type = SK_UNIX_PASSIVE;
473   s->rx_hook = cli_connect;
474   s->rbsize = 1024;
475   s->fast_rx = 1;
476 
477   /* Return value intentionally ignored */
478   unlink(path_control_socket);
479 
480   if (sk_open_unix(s, path_control_socket) < 0)
481     die("Cannot create control socket %s: %m", path_control_socket);
482 
483   if (use_uid || use_gid)
484     if (chown(path_control_socket, use_uid, use_gid) < 0)
485       die("chown: %m");
486 
487   if (chmod(path_control_socket, 0660) < 0)
488     die("chmod: %m");
489 }
490 
491 /*
492  *	PID file
493  */
494 
495 static char *pid_file;
496 static int pid_fd;
497 
498 static inline void
open_pid_file(void)499 open_pid_file(void)
500 {
501   if (!pid_file)
502     return;
503 
504   pid_fd = open(pid_file, O_WRONLY|O_CREAT, 0664);
505   if (pid_fd < 0)
506     die("Cannot create PID file %s: %m", pid_file);
507 }
508 
509 static inline void
write_pid_file(void)510 write_pid_file(void)
511 {
512   int pl, rv;
513   char ps[24];
514 
515   if (!pid_file)
516     return;
517 
518   /* We don't use PID file for uniqueness, so no need for locking */
519 
520   pl = bsnprintf(ps, sizeof(ps), "%ld\n", (long) getpid());
521   if (pl < 0)
522     bug("PID buffer too small");
523 
524   rv = ftruncate(pid_fd, 0);
525   if (rv < 0)
526     die("fruncate: %m");
527 
528   rv = write(pid_fd, ps, pl);
529   if(rv < 0)
530     die("write: %m");
531 
532   close(pid_fd);
533 }
534 
535 static inline void
unlink_pid_file(void)536 unlink_pid_file(void)
537 {
538   if (pid_file)
539     unlink(pid_file);
540 }
541 
542 
543 /*
544  *	Shutdown
545  */
546 
547 void
cmd_shutdown(void)548 cmd_shutdown(void)
549 {
550   if (cli_access_restricted())
551     return;
552 
553   cli_msg(7, "Shutdown requested");
554   order_shutdown();
555 }
556 
557 void
async_shutdown(void)558 async_shutdown(void)
559 {
560   DBG("Shutting down...\n");
561   order_shutdown();
562 }
563 
564 void
sysdep_shutdown_done(void)565 sysdep_shutdown_done(void)
566 {
567   unlink_pid_file();
568   unlink(path_control_socket);
569   log_msg(L_FATAL "Shutdown completed");
570   exit(0);
571 }
572 
573 /*
574  *	Signals
575  */
576 
577 static void
handle_sighup(int sig UNUSED)578 handle_sighup(int sig UNUSED)
579 {
580   DBG("Caught SIGHUP...\n");
581   async_config_flag = 1;
582 }
583 
584 static void
handle_sigusr(int sig UNUSED)585 handle_sigusr(int sig UNUSED)
586 {
587   DBG("Caught SIGUSR...\n");
588   async_dump_flag = 1;
589 }
590 
591 static void
handle_sigterm(int sig UNUSED)592 handle_sigterm(int sig UNUSED)
593 {
594   DBG("Caught SIGTERM...\n");
595   async_shutdown_flag = 1;
596 }
597 
598 void watchdog_sigalrm(int sig UNUSED);
599 
600 static void
signal_init(void)601 signal_init(void)
602 {
603   struct sigaction sa;
604 
605   bzero(&sa, sizeof(sa));
606   sa.sa_handler = handle_sigusr;
607   sa.sa_flags = SA_RESTART;
608   sigaction(SIGUSR1, &sa, NULL);
609   sa.sa_handler = handle_sighup;
610   sa.sa_flags = SA_RESTART;
611   sigaction(SIGHUP, &sa, NULL);
612   sa.sa_handler = handle_sigterm;
613   sa.sa_flags = SA_RESTART;
614   sigaction(SIGTERM, &sa, NULL);
615   sa.sa_handler = watchdog_sigalrm;
616   sa.sa_flags = 0;
617   sigaction(SIGALRM, &sa, NULL);
618   signal(SIGPIPE, SIG_IGN);
619 }
620 
621 /*
622  *	Parsing of command-line arguments
623  */
624 
625 static char *opt_list = "c:dD:ps:P:u:g:flRh";
626 static int parse_and_exit;
627 char *bird_name;
628 static char *use_user;
629 static char *use_group;
630 static int run_in_foreground = 0;
631 
632 static void
display_usage(void)633 display_usage(void)
634 {
635   fprintf(stderr, "Usage: %s [--version] [--help] [-c <config-file>] [OPTIONS]\n", bird_name);
636 }
637 
638 static void
display_help(void)639 display_help(void)
640 {
641   display_usage();
642 
643   fprintf(stderr,
644     "\n"
645     "Options: \n"
646     "  -c <config-file>     Use given configuration file instead\n"
647     "                       of prefix/etc/bird.conf\n"
648     "  -d                   Enable debug messages and run bird in foreground\n"
649     "  -D <debug-file>      Log debug messages to given file instead of stderr\n"
650     "  -f                   Run bird in foreground\n"
651     "  -g <group>           Use given group ID\n"
652     "  -h, --help           Display this information\n"
653     "  -l                   Look for a configuration file and a communication socket\n"
654     "                       file in the current working directory\n"
655     "  -p                   Test configuration file and exit without start\n"
656     "  -P <pid-file>        Create a PID file with given filename\n"
657     "  -R                   Apply graceful restart recovery after start\n"
658     "  -s <control-socket>  Use given filename for a control socket\n"
659     "  -u <user>            Drop privileges and use given user ID\n"
660     "  --version            Display version of BIRD\n");
661 
662   exit(0);
663 }
664 
665 static void
display_version(void)666 display_version(void)
667 {
668   fprintf(stderr, "BIRD version " BIRD_VERSION "\n");
669   exit(0);
670 }
671 
672 static inline char *
get_bird_name(char * s,char * def)673 get_bird_name(char *s, char *def)
674 {
675   char *t;
676   if (!s)
677     return def;
678   t = strrchr(s, '/');
679   if (!t)
680     return s;
681   if (!t[1])
682     return def;
683   return t+1;
684 }
685 
686 static inline uid_t
get_uid(const char * s)687 get_uid(const char *s)
688 {
689   struct passwd *pw;
690   char *endptr;
691   long int rv;
692 
693   if (!s)
694     return 0;
695 
696   errno = 0;
697   rv = strtol(s, &endptr, 10);
698 
699   if (!errno && !*endptr)
700     return rv;
701 
702   pw = getpwnam(s);
703   if (!pw)
704     die("Cannot find user '%s'", s);
705 
706   return pw->pw_uid;
707 }
708 
709 static inline gid_t
get_gid(const char * s)710 get_gid(const char *s)
711 {
712   struct group *gr;
713   char *endptr;
714   long int rv;
715 
716   if (!s)
717     return 0;
718 
719   errno = 0;
720   rv = strtol(s, &endptr, 10);
721 
722   if (!errno && !*endptr)
723     return rv;
724 
725   gr = getgrnam(s);
726   if (!gr)
727     die("Cannot find group '%s'", s);
728 
729   return gr->gr_gid;
730 }
731 
732 static void
parse_args(int argc,char ** argv)733 parse_args(int argc, char **argv)
734 {
735   int config_changed = 0;
736   int socket_changed = 0;
737   int c;
738 
739   bird_name = get_bird_name(argv[0], "bird");
740   if (argc == 2)
741     {
742       if (!strcmp(argv[1], "--version"))
743 	display_version();
744       if (!strcmp(argv[1], "--help"))
745 	display_help();
746     }
747   while ((c = getopt(argc, argv, opt_list)) >= 0)
748     switch (c)
749       {
750       case 'c':
751 	config_name = optarg;
752 	config_changed = 1;
753 	break;
754       case 'd':
755 	debug_flag |= 1;
756 	break;
757       case 'D':
758 	log_init_debug(optarg);
759 	debug_flag |= 2;
760 	break;
761       case 'p':
762 	parse_and_exit = 1;
763 	break;
764       case 's':
765 	path_control_socket = optarg;
766 	socket_changed = 1;
767 	break;
768       case 'P':
769 	pid_file = optarg;
770 	break;
771       case 'u':
772 	use_user = optarg;
773 	break;
774       case 'g':
775 	use_group = optarg;
776 	break;
777       case 'f':
778 	run_in_foreground = 1;
779 	break;
780       case 'l':
781 	if (!config_changed)
782 	  config_name = xbasename(config_name);
783 	if (!socket_changed)
784 	  path_control_socket = xbasename(path_control_socket);
785 	break;
786       case 'R':
787 	graceful_restart_recovery();
788 	break;
789       case 'h':
790 	display_help();
791 	break;
792       default:
793 	fputc('\n', stderr);
794 	display_usage();
795 	exit(1);
796       }
797   if (optind < argc)
798    {
799      display_usage();
800      exit(1);
801    }
802 }
803 
804 /*
805  *	Hic Est main()
806  */
807 
808 int
main(int argc,char ** argv)809 main(int argc, char **argv)
810 {
811 #ifdef HAVE_LIBDMALLOC
812   if (!getenv("DMALLOC_OPTIONS"))
813     dmalloc_debug(0x2f03d00);
814 #endif
815 
816   parse_args(argc, argv);
817   if (debug_flag == 1)
818     log_init_debug("");
819   log_switch(debug_flag, NULL, NULL);
820 
821   resource_init();
822   olock_init();
823   io_init();
824   rt_init();
825   if_init();
826   roa_init();
827   config_init();
828 
829   uid_t use_uid = get_uid(use_user);
830   gid_t use_gid = get_gid(use_group);
831 
832   if (!parse_and_exit)
833   {
834     test_old_bird(path_control_socket);
835     cli_init_unix(use_uid, use_gid);
836   }
837 
838   if (use_gid)
839     drop_gid(use_gid);
840 
841   if (use_uid)
842     drop_uid(use_uid);
843 
844   if (!parse_and_exit)
845     open_pid_file();
846 
847   protos_build();
848   proto_build(&proto_unix_kernel);
849   proto_build(&proto_unix_iface);
850 
851   struct config *conf = read_config();
852 
853   if (parse_and_exit)
854     exit(0);
855 
856   if (!(debug_flag||run_in_foreground))
857     {
858       pid_t pid = fork();
859       if (pid < 0)
860 	die("fork: %m");
861       if (pid)
862 	return 0;
863       setsid();
864       close(0);
865       if (open("/dev/null", O_RDWR) < 0)
866 	die("Cannot open /dev/null: %m");
867       dup2(0, 1);
868       dup2(0, 2);
869     }
870 
871   main_thread_init();
872 
873   write_pid_file();
874 
875   signal_init();
876 
877   config_commit(conf, RECONFIG_HARD, 0);
878 
879   graceful_restart_init();
880 
881 #ifdef LOCAL_DEBUG
882   async_dump_flag = 1;
883 #endif
884 
885   log(L_INFO "Started");
886   DBG("Entering I/O loop.\n");
887 
888   io_loop();
889   bug("I/O loop died");
890 }
891