<chapt>Remote control
<label id="remote-control">
+<sect>Overview
+<label id="remote-control-overview">
+
<p>You can use the command-line client <file>birdc</file> to talk with a running
-BIRD. Communication is done using a <file/bird.ctl/ UNIX domain socket (unless
-changed with the <tt/-s/ option given to both the server and the client). The
+BIRD. Communication is done using the appropriate UNIX domain socket. The
commands can perform simple actions such as enabling/disabling of protocols,
telling BIRD to show various information, telling it to show routing table
filtered by filter, or asking BIRD to reconfigure. Press <tt/?/ at any time to
This is useful for running BIRD in resource constrained environments, where
Readline library (required for regular BIRD client) is not available.
-<p>Many commands have the <m/name/ of the protocol instance as an argument.
-This argument can be omitted if there exists only a single instance.
+<sect>Configuration
+<label id="remote-control-configuration">
+
+<p>By default, BIRD opens <file/bird.ctl/ UNIX domain socket and the CLI tool
+connects to it. If changed on the command line by the <tt/-s/ option,
+BIRD or the CLI tool connects there instead.
+
+<p>It's also possible to configure additional remote control sockets in the
+configuration file by <cf/cli "name";/ and you can open how many
+sockets you wish. There are no checks whether the user configured the same
+socket multiple times and BIRD may behave weirdly if this happens. On shutdown,
+the additional sockets get removed immediately and only the main socket stays
+until the very end.
-<p>Here is a brief list of supported functions:
+<sect>Usage
+<label id="remote-control-usage">
+
+<p>Here is a brief list of supported functions.
+
+<p>Note: Many commands have the <m/name/ of the protocol instance as an argument.
+This argument can be omitted if there exists only a single instance.
<descrip>
<tag><label id="cli-show-status">show status</tag>
#include "lib/socket.h"
#include "lib/event.h"
#include "lib/timer.h"
+#include "lib/tlists.h"
#include "lib/string.h"
#include "nest/route.h"
#include "nest/protocol.h"
return l;
}
+static void cli_preconfig(struct config *c);
+
void
sysdep_preconfig(struct config *c)
{
read_iproute_table(c, PATH_IPROUTE_DIR "/rt_scopes", "ips_", 255);
read_iproute_table(c, PATH_IPROUTE_DIR "/rt_tables", "ipt_", 0xffffffff);
#endif
+
+ cli_preconfig(c);
}
+static void cli_commit(struct config *new, struct config *old);
+
int
-sysdep_commit(struct config *new, struct config *old UNUSED)
+sysdep_commit(struct config *new, struct config *old)
{
log_switch(0, &new->logfiles, new->syslog_name);
+ cli_commit(new, old);
return 0;
}
* Command-Line Interface
*/
-static char *path_control_socket = PATH_CONTROL_SOCKET;
+static struct cli_config initial_control_socket_config = {
+ .name = PATH_CONTROL_SOCKET,
+ .mode = 0660,
+};
+#define path_control_socket initial_control_socket_config.name
+
static struct cli_config *main_control_socket_config = NULL;
+
+#define TLIST_PREFIX cli_listener
+#define TLIST_TYPE struct cli_listener
+#define TLIST_ITEM n
+#define TLIST_WANT_ADD_TAIL
+#define TLIST_WANT_WALK
static struct cli_listener {
+ TLIST_DEFAULT_NODE;
sock *s;
struct cli_config *config;
} *main_control_socket = NULL;
+#include "lib/tlists.h"
+
+static TLIST_LIST(cli_listener) cli_listeners;
+
static void
cli_write(cli *c)
{
}
static struct cli_listener *
-cli_listener(struct cli_config *cf)
+cli_listen(struct cli_config *cf)
{
struct cli_listener *l = mb_allocz(cli_pool, sizeof *l);
l->config = cf;
return NULL;
}
+ cli_listener_add_tail(&cli_listeners, l);
+
return l;
}
+static void
+cli_deafen(struct cli_listener *l)
+{
+ rfree(l->s);
+ unlink(l->config->name);
+ cli_listener_rem_node(&cli_listeners, l);
+ mb_free(l);
+}
+
static void
cli_init_unix(uid_t use_uid, gid_t use_gid)
{
ASSERT_DIE(main_control_socket_config == NULL);
+ cli_init();
- main_control_socket_config = mb_alloc(&root_pool, sizeof *main_control_socket_config);
- *main_control_socket_config = (struct cli_config) {
- .name = path_control_socket,
- .uid = use_uid,
- .gid = use_gid,
- .mode = 0660,
- };
+ main_control_socket_config = &initial_control_socket_config;
+ main_control_socket_config->uid = use_uid;
+ main_control_socket_config->gid = use_gid;
ASSERT_DIE(main_control_socket == NULL);
- cli_init();
- main_control_socket = cli_listener(main_control_socket_config);
+ main_control_socket = cli_listen(main_control_socket_config);
if (!main_control_socket)
die("Won't run without control socket");
}
+static void
+cli_preconfig(struct config *c)
+{
+ struct cli_config *ccf = mb_alloc(cli_pool, sizeof *ccf);
+ memcpy(ccf, main_control_socket_config, sizeof *ccf);
+ ccf->n = (struct cli_config_node) {};
+ ccf->config = c;
+ cli_config_add_tail(&c->cli, ccf);
+}
+
+static void
+cli_commit(struct config *new, struct config *old)
+{
+ if (new->shutdown)
+ {
+ /* Keep the main CLI throughout the shutdown */
+ initial_control_socket_config.config = new;
+ main_control_socket->config = &initial_control_socket_config;
+ }
+
+ WALK_TLIST(cli_config, c, &new->cli)
+ {
+ _Bool seen = 0;
+ WALK_TLIST(cli_listener, l, &cli_listeners)
+ if (l->config->config != new)
+ if (!strcmp(l->config->name, c->name))
+ {
+ ASSERT_DIE(l->config->config == old);
+ l->config = c;
+ seen = 1;
+ break;
+ }
+
+ if (!seen)
+ cli_listen(c);
+ }
+
+ WALK_TLIST_DELSAFE(cli_listener, l, &cli_listeners)
+ if (l->config->config != new)
+ cli_deafen(l);
+}
+
/*
* PID file
sysdep_shutdown_done(void)
{
unlink_pid_file();
- unlink(path_control_socket);
+ cli_deafen(main_control_socket);
log_msg(L_FATAL "Shutdown completed");
exit(0);
}