0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-11-15 07:38:43 +00:00

Merge branch 'mq-coro' into mq-config-new

This commit is contained in:
Jan Maria Matejka 2018-09-14 14:49:44 +02:00
commit a0bd04c04a
10 changed files with 705 additions and 238 deletions

26
aclocal.m4 vendored
View File

@ -17,8 +17,6 @@ AC_DEFUN([BIRD_CHECK_PTHREADS],
[ [
pthread_t pt; pthread_t pt;
pthread_create(&pt, NULL, NULL, NULL); pthread_create(&pt, NULL, NULL, NULL);
pthread_spinlock_t lock;
pthread_spin_lock(&lock);
] ]
) )
], ],
@ -31,6 +29,30 @@ AC_DEFUN([BIRD_CHECK_PTHREADS],
CFLAGS="$bird_tmp_cflags" CFLAGS="$bird_tmp_cflags"
]) ])
dnl ** Android API before version 24 doesn't implement spinlocks.
AC_DEFUN([BIRD_CHECK_PTHREAD_SPINLOCK],
[
AC_CACHE_CHECK(
[whether POSIX threads provide spinlocks],
[bird_cv_lib_pthread_spinlock],
[
AC_LINK_IFELSE(
[
AC_LANG_PROGRAM(
[ #include <pthread.h> ],
[
pthread_spinlock_t lock;
pthread_spin_lock(&lock);
]
)
],
[bird_cv_lib_pthread_spinlock=yes],
[bird_cv_lib_pthread_spinlock=no]
)
]
)
])
AC_DEFUN([BIRD_CHECK_MPLS_KERNEL], AC_DEFUN([BIRD_CHECK_MPLS_KERNEL],
[ [
AC_CACHE_CHECK( AC_CACHE_CHECK(

View File

@ -120,7 +120,13 @@ if test "$enable_pthreads" != no ; then
AC_DEFINE([USE_PTHREADS], [1], [Define to 1 if pthreads are enabled]) AC_DEFINE([USE_PTHREADS], [1], [Define to 1 if pthreads are enabled])
CFLAGS="$CFLAGS -pthread" CFLAGS="$CFLAGS -pthread"
LDFLAGS="$LDFLAGS -pthread" LDFLAGS="$LDFLAGS -pthread"
BIRD_CHECK_PTHREAD_SPINLOCK
if test "$bird_cv_lib_pthread_spinlock" = yes ; then
AC_DEFINE([USE_PTHREAD_SPINLOCK], [1], [Define to 1 if pthreads provide spinlocks])
proto_bfd=bfd proto_bfd=bfd
fi
elif test "$enable_pthreads" = yes ; then elif test "$enable_pthreads" = yes ; then
AC_MSG_ERROR([POSIX threads not available.]) AC_MSG_ERROR([POSIX threads not available.])
fi fi

View File

@ -653,7 +653,7 @@ agreement").
<tag><label id="proto-description">description "<m/text/"</tag> <tag><label id="proto-description">description "<m/text/"</tag>
This is an optional description of the protocol. It is displayed as a This is an optional description of the protocol. It is displayed as a
part of the output of 'show route all' command. part of the output of 'show protocols all' command.
<tag><label id="proto-vrf">vrf "<m/text/"</tag> <tag><label id="proto-vrf">vrf "<m/text/"</tag>
Associate the protocol with specific VRF. The protocol will be Associate the protocol with specific VRF. The protocol will be

26
lib/coroutine.h Normal file
View File

@ -0,0 +1,26 @@
/*
* BIRD Coroutines
*
* (c) 2017 Martin Mares <mj@ucw.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#ifndef _BIRD_COROUTINE_H_
#define _BIRD_COROUTINE_H_
#include "lib/event.h"
// The structure is completely opaque, implemented by sysdep
typedef struct coroutine coroutine;
coroutine *coro_new(struct pool *pool, void (*entry_point)(void *arg), void *arg);
void coro_suspend(void);
void coro_resume(coroutine *c);
void coro_done(event *e) NORET;
struct birdsock;
int coro_sk_read(struct birdsock *s);
void coro_sk_write(struct birdsock *s, unsigned len);
#endif

View File

@ -36,6 +36,8 @@ struct ssh_sock {
}; };
#endif #endif
struct coroutine;
typedef struct birdsock { typedef struct birdsock {
resource r; resource r;
pool *pool; /* Pool where incoming connections should be allocated (for SK_xxx_PASSIVE) */ pool *pool; /* Pool where incoming connections should be allocated (for SK_xxx_PASSIVE) */
@ -79,6 +81,9 @@ typedef struct birdsock {
char *password; /* Password for MD5 authentication */ char *password; /* Password for MD5 authentication */
const char *err; /* Error message */ const char *err; /* Error message */
struct ssh_sock *ssh; /* Used in SK_SSH */ struct ssh_sock *ssh; /* Used in SK_SSH */
struct coroutine *rx_coroutine;
struct coroutine *tx_coroutine;
} sock; } sock;
sock *sock_new(pool *); /* Allocate new socket */ sock *sock_new(pool *); /* Allocate new socket */

View File

@ -1,7 +1,7 @@
/* /*
* BIRD Internet Routing Daemon -- Command-Line Interface * BIRD Internet Routing Daemon -- Command-Line Interface
* *
* (c) 1999--2000 Martin Mares <mj@ucw.cz> * (c) 1999--2017 Martin Mares <mj@ucw.cz>
* *
* Can be freely distributed and used under the terms of the GNU GPL. * Can be freely distributed and used under the terms of the GNU GPL.
*/ */
@ -32,44 +32,55 @@
* Each CLI session is internally represented by a &cli structure and a * Each CLI session is internally represented by a &cli structure and a
* resource pool containing all resources associated with the connection, * resource pool containing all resources associated with the connection,
* so that it can be easily freed whenever the connection gets closed, not depending * so that it can be easily freed whenever the connection gets closed, not depending
* on the current state of command processing. * on the current state of command processing. A socket is associated with
* the session, over which requests and replies are sent.
* *
* The CLI commands are declared as a part of the configuration grammar * The CLI commands are declared as a part of the configuration grammar
* by using the |CF_CLI| macro. When a command is received, it is processed * by using the |CF_CLI| macro. When a command is received, it is processed
* by the same lexical analyzer and parser as used for the configuration, but * by the same lexical analyzer and parser as used for the configuration, but
* it's switched to a special mode by prepending a fake token to the text, * it's switched to a special mode by prepending a fake token to the text,
* so that it uses only the CLI command rules. Then the parser invokes * so that it uses only the CLI command rules. Then the parser invokes
* an execution routine corresponding to the command, which either constructs * an execution routine corresponding to the command, which constructs the
* the whole reply and returns it back or (in case it expects the reply will be long) * reply.
* it prints a partial reply and asks the CLI module (using the @cont hook) *
* to call it again when the output is transferred to the user. * Replies are buffered in memory and then sent asynchronously. Commands
* which produce long outputs must split them to pieces and yield to other
* operations between pieces. To simplify this (and possibly also complex
* parsing of input), the CLI session runs in a coroutine with its own
* execution context. At any time, cli_yield() can be called to interrupt
* the current coroutine and have the buffered output sent.
*
* Alternatively, a long sequence of replies can be split to parts
* using the @cont hook, which translates to yielding internally.
* *
* The @this_cli variable points to a &cli structure of the session being * The @this_cli variable points to a &cli structure of the session being
* currently parsed, but it's of course available only in command handlers * currently parsed, but it's available only before the first yield.
* not entered using the @cont hook.
*
* TX buffer management works as follows: At cli.tx_buf there is a
* list of TX buffers (struct cli_out), cli.tx_write is the buffer
* currently used by the producer (cli_printf(), cli_alloc_out()) and
* cli.tx_pos is the buffer currently used by the consumer
* (cli_write(), in system dependent code). The producer uses
* cli_out.wpos ptr as the current write position and the consumer
* uses cli_out.outpos ptr as the current read position. When the
* producer produces something, it calls cli_write_trigger(). If there
* is not enough space in the current buffer, the producer allocates
* the new one. When the consumer processes everything in the buffer
* queue, it calls cli_written(), tha frees all buffers (except the
* first one) and schedules cli.event .
* *
* A note on transmit buffer management: cli.tx_buf is a head of a list
* of TX buffers (struct cli_out). A buffer pointed to by cli.tx_write
* is the one currently written to using cli_printf() and cli_alloc_out(),
* its wpos field points to the position of the write head in that buffer.
* On the other side, cli.tx_pos is the buffer being set to the socket
* and its outpos field is the position of the read head.
*/ */
#define LOCAL_DEBUG 1
#include "nest/bird.h" #include "nest/bird.h"
#include "nest/cli.h" #include "nest/cli.h"
#include "conf/conf.h" #include "conf/conf.h"
#include "lib/coroutine.h"
#include "lib/string.h" #include "lib/string.h"
pool *cli_pool; pool *cli_pool;
/* Hack for scheduled undo notification */
extern cli *cmd_reconfig_stored_cli;
/*
* Output buffering
*/
static byte * static byte *
cli_alloc_out(cli *c, int size) cli_alloc_out(cli *c, int size)
{ {
@ -229,114 +240,54 @@ cli_free_out(cli *c)
c->async_msg_size = 0; c->async_msg_size = 0;
} }
void static void
cli_written(cli *c) cli_write(cli *c)
{ {
sock *s = c->socket;
while (c->tx_pos)
{
struct cli_out *o = c->tx_pos;
int len = o->wpos - o->outpos;
s->tbuf = o->outpos;
o->outpos = o->wpos;
coro_sk_write(s, len);
c->tx_pos = o->next;
}
/* Everything is written */
s->tbuf = NULL;
cli_free_out(c); cli_free_out(c);
ev_schedule(c->event); ev_schedule(c->event);
} }
struct cli *this_cli;
struct cli_conf_order {
struct conf_order co;
struct cli *cli;
};
static void
cli_cmd_error(struct conf_order *co, const char *msg, va_list args)
{
struct cli_conf_order *cco = (struct cli_conf_order *) co;
cli_vprintf(cco->cli, 9001, msg, args);
}
static void
cli_command(struct cli *c)
{
struct conf_state state = {
.name = "",
.lino = 1
};
struct cli_conf_order o = {
.co = {
.ctx = NULL,
.state = &state,
.buf = c->rx_buf,
.len = strlen(c->rx_buf),
.cf_include = NULL,
.cf_outclude = NULL,
.cf_error = cli_cmd_error,
.lp = c->parser_pool,
.pool = c->pool,
},
.cli = c,
};
if (config->cli_debug > 1)
log(L_TRACE "CLI: %s", c->rx_buf);
lp_flush(c->parser_pool);
this_cli = c;
cli_parse(&(o.co));
}
static void
cli_event(void *data)
{
cli *c = data;
int err;
while (c->ring_read != c->ring_write &&
c->async_msg_size < CLI_MAX_ASYNC_QUEUE)
cli_copy_message(c);
if (c->tx_pos)
;
else if (c->cont)
c->cont(c);
else
{
err = cli_get_command(c);
if (!err)
return;
if (err < 0)
cli_printf(c, 9000, "Command too long");
else
cli_command(c);
}
cli_write_trigger(c);
}
cli *
cli_new(void *priv)
{
pool *p = rp_new(cli_pool, "CLI");
cli *c = mb_alloc(p, sizeof(cli));
bzero(c, sizeof(cli));
c->pool = p;
c->priv = priv;
c->event = ev_new(p);
c->event->hook = cli_event;
c->event->data = c;
c->cont = cli_hello;
c->show_pool = lp_new_default(c->pool);
c->parser_pool = lp_new_default(c->pool);
c->rx_buf = mb_alloc(c->pool, CLI_RX_BUF_SIZE);
ev_schedule(c->event);
return c;
}
void void
cli_kick(cli *c) cli_write_trigger(cli *c)
{ {
if (!c->cont && !c->tx_pos) if (c->tx_pos && c->socket->tbuf == NULL)
ev_schedule(c->event); cli_write(c);
} }
static void
cli_err_hook(sock *s, int err)
{
if (config->cli_debug)
{
if (err)
log(L_INFO "CLI connection dropped: %s", strerror(err));
else
log(L_INFO "CLI connection closed");
}
cli_free(s->data);
}
/*
* Echoing of asynchronous messages
*/
static list cli_log_hooks; static list cli_log_hooks;
static int cli_log_inited; static int cli_log_inited;
@ -407,12 +358,209 @@ cli_echo(uint class, byte *msg)
} }
} }
/* Hack for scheduled undo notification */ /*
extern cli *cmd_reconfig_stored_cli; * Reading of input
*/
static int
cli_getchar(cli *c)
{
sock *s = c->socket;
if (c->rx_aux == s->rpos)
{
DBG("CLI: Waiting on read\n");
c->rx_aux = s->rpos = s->rbuf;
c->state = CLI_STATE_WAIT_RX;
int n = coro_sk_read(s);
c->state = CLI_STATE_RUN;
DBG("CLI: Read returned %d bytes\n", n);
ASSERT(n);
}
return *c->rx_aux++;
}
static int
cli_read_line(cli *c)
{
byte *d = c->rx_buf;
byte *dend = c->rx_buf + CLI_RX_BUF_SIZE - 2;
for (;;)
{
int ch = cli_getchar(c);
if (ch == '\r')
;
else if (ch == '\n')
break;
else if (d < dend)
*d++ = ch;
}
if (d >= dend)
return 0;
*d = 0;
return 1;
}
/*
* Execution of commands
*/
struct cli *this_cli;
struct cli_conf_order {
struct conf_order co;
struct cli *cli;
};
static void
cli_cmd_error(struct conf_order *co, const char *msg, va_list args)
{
struct cli_conf_order *cco = (struct cli_conf_order *) co;
cli_vprintf(cco->cli, 9001, msg, args);
}
static void
cli_command(struct cli *c)
{
struct conf_state state = {
.name = "",
.lino = 1
};
struct cli_conf_order o = {
.co = {
.ctx = NULL,
.state = &state,
.buf = c->rx_buf,
.len = strlen(c->rx_buf),
.cf_include = NULL,
.cf_outclude = NULL,
.cf_error = cli_cmd_error,
.lp = c->parser_pool,
.pool = c->pool,
},
.cli = c,
};
if (config->cli_debug > 1)
log(L_TRACE "CLI: %s", c->rx_buf);
lp_flush(c->parser_pool);
this_cli = c;
cli_parse(&(o.co));
}
/*
* Session control
*/
static void
cli_event(void *data)
{
cli *c = data;
DBG("CLI: Event in state %u\n", (int) c->state);
while (c->ring_read != c->ring_write &&
c->async_msg_size < CLI_MAX_ASYNC_QUEUE)
cli_copy_message(c);
cli_write_trigger(c);
if (c->state == CLI_STATE_YIELD ||
c->state == CLI_STATE_WAIT_TX && !c->tx_pos)
coro_resume(c->coro);
}
void
cli_yield(cli *c)
{
c->state = CLI_STATE_YIELD;
DBG("CLI: Yielding\n");
ev_schedule(c->event);
coro_suspend();
c->state = CLI_STATE_RUN;
DBG("CLI: Yield resumed\n");
}
static void
cli_coroutine(void *_c)
{
cli *c = _c;
sock *s = c->socket;
DBG("CLI: Coroutine started\n");
c->rx_aux = s->rbuf;
for (;;)
{
while (c->tx_pos)
{
DBG("CLI: Sleeping on write\n");
c->state = CLI_STATE_WAIT_TX;
coro_suspend();
c->state = CLI_STATE_RUN;
DBG("CLI: Woke up on write\n");
}
if (c->cont)
{
c->cont(c);
cli_write_trigger(c);
cli_yield(c);
continue;
}
if (!cli_read_line(c))
cli_printf(c, 9000, "Command too long");
else
cli_command(c);
cli_write_trigger(c);
}
}
cli *
cli_new(sock *s)
{
pool *p = rp_new(cli_pool, "CLI session");
cli *c = mb_alloc(p, sizeof(cli));
DBG("CLI: Created new session\n");
bzero(c, sizeof(cli));
c->pool = p;
c->socket = s;
c->event = ev_new(p);
c->event->hook = cli_event;
c->event->data = c;
c->cont = cli_hello;
c->parser_pool = lp_new_default(c->pool);
c->show_pool = lp_new_default(c->pool);
c->rx_buf = mb_alloc(c->pool, CLI_RX_BUF_SIZE);
s->pool = c->pool; /* We need to have all the socket buffers allocated in the cli pool */
rmove(s, c->pool);
s->err_hook = cli_err_hook;
s->data = c;
return c;
}
void
cli_run(cli *c)
{
DBG("CLI: Running\n");
c->state = CLI_STATE_RUN;
c->rx_pos = c->rx_buf;
c->rx_aux = NULL;
c->coro = coro_new(c->pool, cli_coroutine, c);
coro_resume(c->coro);
}
void void
cli_free(cli *c) cli_free(cli *c)
{ {
DBG("CLI: Destroying session\n");
cli_set_log_echo(c, 0, 0); cli_set_log_echo(c, 0, 0);
if (c->cleanup) if (c->cleanup)
c->cleanup(c); c->cleanup(c);

View File

@ -1,7 +1,7 @@
/* /*
* BIRD Internet Routing Daemon -- Command-Line Interface * BIRD Internet Routing Daemon -- Command-Line Interface
* *
* (c) 1999--2000 Martin Mares <mj@ucw.cz> * (c) 1999--2017 Martin Mares <mj@ucw.cz>
* *
* Can be freely distributed and used under the terms of the GNU GPL. * Can be freely distributed and used under the terms of the GNU GPL.
*/ */
@ -10,7 +10,9 @@
#define _BIRD_CLI_H_ #define _BIRD_CLI_H_
#include "lib/resource.h" #include "lib/resource.h"
#include "lib/coroutine.h"
#include "lib/event.h" #include "lib/event.h"
#include "lib/socket.h"
#define CLI_RX_BUF_SIZE 4096 #define CLI_RX_BUF_SIZE 4096
#define CLI_TX_BUF_SIZE 4096 #define CLI_TX_BUF_SIZE 4096
@ -25,20 +27,38 @@ struct cli_out {
byte buf[0]; byte buf[0];
}; };
enum cli_state {
CLI_STATE_INIT,
CLI_STATE_RUN,
CLI_STATE_WAIT_RX,
CLI_STATE_WAIT_TX,
CLI_STATE_YIELD,
};
typedef struct cli { typedef struct cli {
node n; /* Node in list of all log hooks */ node n; /* Node in list of all log hooks */
pool *pool; pool *pool;
void *priv; /* Private to sysdep layer */ coroutine *coro;
byte *rx_buf, *rx_pos, *rx_aux; /* sysdep */ enum cli_state state;
int restricted; /* CLI is restricted to read-only commands */
/* I/O */
sock *socket;
byte *rx_buf, *rx_pos, *rx_aux;
struct cli_out *tx_buf, *tx_pos, *tx_write; struct cli_out *tx_buf, *tx_pos, *tx_write;
event *event; event *event;
/* Continuation mechanism */
void (*cont)(struct cli *c); void (*cont)(struct cli *c);
void (*cleanup)(struct cli *c); void (*cleanup)(struct cli *c);
void *rover; /* Private to continuation routine */ void *rover; /* Private to continuation routine */
int last_reply; int last_reply;
int restricted; /* CLI is restricted to read-only commands */
/* Pools */
struct linpool *parser_pool; /* Pool used during parsing */ struct linpool *parser_pool; /* Pool used during parsing */
struct linpool *show_pool; /* Pool used during route show */ struct linpool *show_pool; /* Pool used during route show */
/* Asynchronous messages */
byte *ring_buf; /* Ring buffer for asynchronous messages */ byte *ring_buf; /* Ring buffer for asynchronous messages */
byte *ring_end, *ring_read, *ring_write; /* Pointers to the ring buffer */ byte *ring_end, *ring_read, *ring_write; /* Pointers to the ring buffer */
uint ring_overflow; /* Counter of ring overflows */ uint ring_overflow; /* Counter of ring overflows */
@ -56,15 +76,16 @@ extern struct cli *this_cli; /* Used during parsing */
void cli_printf(cli *, int, char *, ...); void cli_printf(cli *, int, char *, ...);
#define cli_msg(x...) cli_printf(this_cli, x) #define cli_msg(x...) cli_printf(this_cli, x)
void cli_write_trigger(cli *c);
void cli_set_log_echo(cli *, uint mask, uint size); void cli_set_log_echo(cli *, uint mask, uint size);
void cli_yield(cli *c);
/* Functions provided to sysdep layer */ /* Functions provided to sysdep layer */
cli *cli_new(void *);
void cli_init(void); void cli_init(void);
cli *cli_new(sock *s);
void cli_run(cli *);
void cli_free(cli *); void cli_free(cli *);
void cli_kick(cli *);
void cli_written(cli *);
void cli_echo(uint class, byte *msg); void cli_echo(uint class, byte *msg);
static inline int cli_access_restricted(void) static inline int cli_access_restricted(void)
@ -75,9 +96,4 @@ static inline int cli_access_restricted(void)
return 0; return 0;
} }
/* Functions provided by sysdep layer */
void cli_write_trigger(cli *);
int cli_get_command(cli *);
#endif #endif

View File

@ -1,4 +1,4 @@
src := io.c krt.c log.c main.c random.c conf.c src := conf.c coroutine.c io.c krt.c log.c main.c random.c
obj := $(src-o-files) obj := $(src-o-files)
$(all-daemon) $(all-daemon)
$(cf-local) $(cf-local)

332
sysdep/unix/coroutine.c Normal file
View File

@ -0,0 +1,332 @@
/*
* BIRD Coroutines
*
* (c) 2017 Martin Mares <mj@ucw.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdlib.h>
#include "nest/bird.h"
#include "lib/coroutine.h"
#include "lib/resource.h"
#include "lib/socket.h"
#include "lib/timer.h"
#include "sysdep/unix/unix.h"
#define CORO_STACK_SIZE 65536
#if ! USE_PTHREADS
/*
* Implementation of coroutines based on <ucontext.h>
*/
#include <ucontext.h>
struct coroutine {
resource r;
ucontext_t ctx;
void *stack;
void (*entry_point)(void *arg);
void *arg;
event *ev;
};
static ucontext_t *main_context;
static coroutine *coro_current; // NULL for main context
static void
coro_free(resource *r)
{
coroutine *c = (coroutine *) r;
xfree(c->stack);
}
static void
coro_dump(resource *r UNUSED)
{
debug("\n");
}
static size_t
coro_memsize(resource *r)
{
coroutine *c = (coroutine *) r;
return sizeof(*c) + CORO_STACK_SIZE + 2*ALLOC_OVERHEAD;
}
static struct resclass coro_class = {
.name = "Coroutine",
.size = sizeof(struct coroutine),
.free = coro_free,
.dump = coro_dump,
.memsize = coro_memsize,
};
static void
coro_do_start(void)
{
ASSERT(coro_current);
coro_current->entry_point(coro_current->arg);
bug("Coroutine returned unexpectedly");
}
struct coroutine *
coro_new(pool *p, void (*entry_point)(void *), void *arg)
{
if (!main_context)
{
main_context = xmalloc(sizeof(*main_context));
if (getcontext(main_context) < 0)
bug("getcontext() failed");
}
coroutine *c = ralloc(p, &coro_class);
c->entry_point = entry_point;
c->arg = arg;
if (getcontext(&c->ctx) < 0)
bug("getcontext() failed");
c->stack = xmalloc(CORO_STACK_SIZE);
c->ctx.uc_stack.ss_sp = c->stack;
c->ctx.uc_stack.ss_size = CORO_STACK_SIZE;
makecontext(&c->ctx, coro_do_start, 0);
return c;
}
void
coro_done(event *e)
{
ASSERT(coro_inited);
ASSERT(coro_current);
if (e)
ev_schedule(e);
c->ev = e;
coroutine *c = coro_current;
coro_suspend();
bug("Coroutine suspend after coro_done() should never return");
}
void
coro_suspend(void)
{
ASSERT(coro_current);
ASSERT(main_context);
coroutine *c = coro_current;
coro_current = NULL;
swapcontext(&c->ctx, main_context);
ASSERT(coro_current == c);
}
void
coro_resume(coroutine *c)
{
ASSERT(!coro_current);
coro_current = c;
swapcontext(main_context, &c->ctx);
ASSERT(!coro_current);
}
#else
/*
* Implementation of coroutines based on POSIX threads
*/
#include <pthread.h>
#include <semaphore.h>
#define CORO_STOP 1 /* The coroutine should stop at first coro_suspend(). */
#define CORO_DONE 2 /* The coroutine has already stopped. */
struct coroutine {
resource r;
pthread_t thread;
void (*entry_point)(void *arg);
void *arg;
event *ev;
sem_t sem;
uint flags;
};
static coroutine *coro_current; // NULL for main context
static int coro_inited;
static sem_t coro_main_sem;
static pthread_attr_t coro_thread_attrs;
static void
coro_free(resource *r)
{
coroutine *c = (coroutine *) r;
ASSERT(coro_current != c);
if (!(c->flags & CORO_DONE))
{
c->flags |= CORO_STOP;
coro_resume(c);
}
ASSERT(c->flags & CORO_DONE);
pthread_join(c->thread, NULL);
}
static void
coro_dump(resource *r UNUSED)
{
debug("\n");
}
static size_t
coro_memsize(resource *r)
{
coroutine *c = (coroutine *) r;
return sizeof(*c) + CORO_STACK_SIZE + 2*ALLOC_OVERHEAD;
}
static struct resclass coro_class = {
.name = "Coroutine",
.size = sizeof(struct coroutine),
.free = coro_free,
.dump = coro_dump,
.memsize = coro_memsize,
};
extern pthread_key_t current_time_key;
static void *
coro_do_start(void *c_)
{
coroutine *c = c_;
pthread_setspecific(current_time_key, &main_timeloop);
while (sem_wait(&c->sem) < 0)
;
coro_current = c;
c->entry_point(c->arg);
bug("Coroutine returned unexpectedly");
}
struct coroutine *
coro_new(pool *p, void (*entry_point)(void *), void *arg)
{
if (!coro_inited)
{
if (sem_init(&coro_main_sem, 0, 0) < 0)
bug("sem_init() failed");
if (pthread_attr_init(&coro_thread_attrs))
bug("pthread_attr_init() failed");
if (pthread_attr_setstacksize(&coro_thread_attrs, CORO_STACK_SIZE))
bug("pthread_attr_setstacksize() failed");
coro_inited = 1;
}
coroutine *c = ralloc(p, &coro_class);
c->entry_point = entry_point;
c->arg = arg;
if (sem_init(&c->sem, 0, 0) < 0)
bug("sem_init() failed");
if (pthread_create(&c->thread, &coro_thread_attrs, coro_do_start, c))
bug("pthread_create() failed");
return c;
}
static inline void
coro_check_stop(void)
{
ASSERT(coro_inited);
ASSERT(coro_current);
coroutine *c = coro_current;
if (c->flags & CORO_STOP)
coro_done(NULL);
}
void
coro_done(event *e)
{
ASSERT(coro_inited);
ASSERT(coro_current);
coroutine *c = coro_current;
c->flags |= CORO_DONE;
c->ev = e;
if (e)
ev_schedule(e);
sem_post(&coro_main_sem);
pthread_exit(NULL);
bug("pthread_exit should never return");
}
void
coro_suspend(void)
{
ASSERT(coro_inited);
ASSERT(coro_current);
coroutine *c = coro_current;
coro_check_stop();
sem_post(&coro_main_sem);
while (sem_wait(&c->sem) < 0)
;
coro_current = c;
coro_check_stop();
}
void
coro_resume(coroutine *c)
{
ASSERT(coro_inited);
ASSERT(!coro_current);
sem_post(&c->sem);
while (sem_wait(&coro_main_sem) < 0)
;
coro_current = NULL;
}
#endif
/* Coroutine-based I/O */
static int
coro_sk_rx_hook(sock *sk, uint size UNUSED)
{
ASSERT(sk->rx_coroutine);
ASSERT(!coro_current);
coro_resume(sk->rx_coroutine);
return 0;
}
static void
coro_sk_tx_hook(sock *sk)
{
ASSERT(sk->tx_coroutine);
ASSERT(!coro_current);
coro_resume(sk->tx_coroutine);
}
int
coro_sk_read(sock *s)
{
ASSERT(coro_current);
s->rx_coroutine = coro_current;
s->rx_hook = coro_sk_rx_hook;
coro_suspend();
s->rx_hook = NULL;
return s->rpos - s->rbuf;
}
void
coro_sk_write(sock *s, unsigned len)
{
ASSERT(coro_current);
s->tx_coroutine = coro_current;
s->tx_hook = coro_sk_tx_hook;
s->ttx = s->tbuf;
s->tpos = s->tbuf + len;
coro_suspend();
s->tx_hook = NULL;
}

View File

@ -23,6 +23,7 @@
#include <libgen.h> #include <libgen.h>
#include "nest/bird.h" #include "nest/bird.h"
#include "lib/coroutine.h"
#include "lib/lists.h" #include "lib/lists.h"
#include "lib/resource.h" #include "lib/resource.h"
#include "lib/socket.h" #include "lib/socket.h"
@ -98,95 +99,6 @@ drop_gid(gid_t gid)
static sock *cli_sk; static sock *cli_sk;
static char *path_control_socket = PATH_CONTROL_SOCKET; static char *path_control_socket = PATH_CONTROL_SOCKET;
static void
cli_write(cli *c)
{
sock *s = c->priv;
while (c->tx_pos)
{
struct cli_out *o = c->tx_pos;
int len = o->wpos - o->outpos;
s->tbuf = o->outpos;
o->outpos = o->wpos;
if (sk_send(s, len) <= 0)
return;
c->tx_pos = o->next;
}
/* Everything is written */
s->tbuf = NULL;
cli_written(c);
}
void
cli_write_trigger(cli *c)
{
sock *s = c->priv;
if (s->tbuf == NULL)
cli_write(c);
}
static void
cli_tx(sock *s)
{
cli_write(s->data);
}
int
cli_get_command(cli *c)
{
sock *s = c->priv;
byte *t = c->rx_aux ? : s->rbuf;
byte *tend = s->rpos;
byte *d = c->rx_pos;
byte *dend = c->rx_buf + CLI_RX_BUF_SIZE - 2;
while (t < tend)
{
if (*t == '\r')
t++;
else if (*t == '\n')
{
t++;
c->rx_pos = c->rx_buf;
c->rx_aux = t;
*d = 0;
return (d < dend) ? 1 : -1;
}
else if (d < dend)
*d++ = *t++;
}
c->rx_aux = s->rpos = s->rbuf;
c->rx_pos = d;
return 0;
}
static int
cli_rx(sock *s, uint size UNUSED)
{
cli_kick(s->data);
return 0;
}
static void
cli_err(sock *s, int err)
{
if (config->cli_debug)
{
if (err)
log(L_INFO "CLI connection dropped: %s", strerror(err));
else
log(L_INFO "CLI connection closed");
}
cli_free(s->data);
}
static int static int
cli_connect(sock *s, uint size UNUSED) cli_connect(sock *s, uint size UNUSED)
{ {
@ -194,15 +106,9 @@ cli_connect(sock *s, uint size UNUSED)
if (config->cli_debug) if (config->cli_debug)
log(L_INFO "CLI connect"); log(L_INFO "CLI connect");
s->rx_hook = cli_rx; c = cli_new(s);
s->tx_hook = cli_tx;
s->err_hook = cli_err;
s->data = c = cli_new(s);
s->pool = c->pool; /* We need to have all the socket buffers allocated in the cli pool */
s->fast_rx = 1; s->fast_rx = 1;
c->rx_pos = c->rx_buf; cli_run(c);
c->rx_aux = NULL;
rmove(s, c->pool);
return 1; return 1;
} }
@ -370,7 +276,7 @@ signal_init(void)
* Parsing of command-line arguments * Parsing of command-line arguments
*/ */
static char *opt_list = "c:dD:ps:P:u:g:flRh"; static char *opt_list = "bc:dD:ps:P:u:g:flRh";
static int parse_and_exit; static int parse_and_exit;
char *bird_name; char *bird_name;
static char *use_user; static char *use_user;
@ -391,6 +297,7 @@ display_help(void)
fprintf(stderr, fprintf(stderr,
"\n" "\n"
"Options: \n" "Options: \n"
" -b Run bird in background\n"
" -c <config-file> Use given configuration file instead\n" " -c <config-file> Use given configuration file instead\n"
" of prefix/etc/bird.conf\n" " of prefix/etc/bird.conf\n"
" -d Enable debug messages and run bird in foreground\n" " -d Enable debug messages and run bird in foreground\n"
@ -495,16 +402,21 @@ parse_args(int argc, char **argv)
while ((c = getopt(argc, argv, opt_list)) >= 0) while ((c = getopt(argc, argv, opt_list)) >= 0)
switch (c) switch (c)
{ {
case 'b':
run_in_foreground = 0;
break;
case 'c': case 'c':
config_name = optarg; config_name = optarg;
config_changed = 1; config_changed = 1;
break; break;
case 'd': case 'd':
debug_flag |= 1; debug_flag |= 1;
run_in_foreground = 1;
break; break;
case 'D': case 'D':
log_init_debug(optarg); log_init_debug(optarg);
debug_flag |= 2; debug_flag |= 2;
run_in_foreground = 1;
break; break;
case 'p': case 'p':
parse_and_exit = 1; parse_and_exit = 1;
@ -603,7 +515,7 @@ main(int argc, char **argv)
if (parse_and_exit) if (parse_and_exit)
exit(0); exit(0);
if (!(debug_flag||run_in_foreground)) if (!run_in_foreground)
{ {
pid_t pid = fork(); pid_t pid = fork();
if (pid < 0) if (pid < 0)