diff --git a/.travis.yml b/.travis.yml
index b2e3c56..121b3ac 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,10 +3,8 @@ dist: trusty
language: c
install:
- sudo apt-add-repository "deb http://archive.ubuntu.com/ubuntu trusty-backports main restricted universe multiverse"
- - curl http://mirror.openio.io/pub/repo/openio/APT-GPG-KEY-OPENIO-0 | sudo apt-key add -
- - sudo apt-add-repository "deb http://mirror.openio.io/pub/repo/openio/sds/17.04/ubuntu/trusty ./"
- sudo apt-get update -qq
- - sudo apt-get install -y --force-yes libevent-2.0-5 libevent-dev make cmake libglib2.0-dev
+ - sudo apt-get install -y --force-yes autoconf automake libtool xutils-dev gcc make cmake libglib2.0-dev
env:
matrix:
- BUILD_TYPE=Debug
@@ -16,5 +14,5 @@ script:
- mkdir /tmp/oio
- export CMAKE_OPTS='-DCMAKE_INSTALL_PREFIX=/tmp/oio -DLD_LIBDIR=lib'
- cmake ${CMAKE_OPTS} -DCMAKE_BUILD_TYPE=$BUILD_TYPE .
- - make -j 4 all
+ - make all
- make install
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5c868c7..faaed13 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -37,6 +37,7 @@ if (CMAKE_COMPILER_IS_GNUCC)
# gcc >= 4.6
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wmissing-prototypes")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-missing-field-initializers")
#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Winline")
#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wconversion")
#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wswitch-enum")
@@ -77,27 +78,60 @@ else()
else()
set (LD_LIBDIR "lib")
endif()
- MESSAGE("LD_LIBDIR guessed to ${LD_LIBDIR}")
endif()
find_package(PkgConfig)
pkg_check_modules(GLIB2 REQUIRED glib-2.0 gthread-2.0 gmodule-2.0)
-pkg_check_modules(LIBEVENT REQUIRED libevent)
-### explicit plugin directory for alerting modules ###
-
-set(GRIDINIT_PLUGINS_DIRECTORY "${LD_LIBDIR}/gridinit")
-if(GRIDINIT_PLUGINS)
- set(GRIDINIT_PLUGINS_DIRECTORY "${GRIDINIT_PLUGINS}")
-endif(GRIDINIT_PLUGINS)
+add_custom_target(
+ libdill_s
+ ALL
+ COMMAND ./autogen.sh
+ COMMAND ./configure --enable-static --disable-shared
+ COMMAND make
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/vendor/sustrik/libdill
+ COMMENT "Building libdill")
+
+if (NOT (${CMAKE_BINARY_DIR} STREQUAL ${CMAKE_SOURCE_DIR}))
+add_custom_target(
+ libdill_source
+ ALL
+ COMMAND mkdir -p ${CMAKE_BINARY_DIR}/vendor/sustrik/libdill
+ COMMAND cd ${CMAKE_BINARY_DIR}/vendor/sustrik/libdill && lndir ${CMAKE_SOURCE_DIR}/vendor/sustrik/libdill
+ COMMENT "Preparing the source of libdill")
+ add_dependencies(libdill_s libdill_source)
+endif()
set(CMAKE_LIBRARY_PATH "")
set(CMAKE_INCLUDE_PATH "")
-include_directories(AFTER ${GLIB2_INCLUDE_DIRS})
+include_directories(AFTER
+ ${CMAKE_SOURCE_DIR}/vendor/sustrik/libdill
+ ${GLIB2_INCLUDE_DIRS})
add_definitions(-DLOG_DOMAIN="gridinit")
-add_definitions(-DGRIDINIT_DOMAIN="gridinit")
-
-add_subdirectory(./main)
+include_directories(BEFORE ${CMAKE_SOURCE_DIR}/main)
+
+add_executable(gridinit
+ main/gridinit.c
+ main/children.c
+ main/limits.c
+ main/uid.c
+ main/utils.c)
+target_link_libraries(gridinit
+ ${GLIB2_LIBRARIES}
+ ${CMAKE_BINARY_DIR}/vendor/sustrik/libdill/.libs/libdill.a
+ -pthread -lrt)
+add_dependencies(gridinit
+ libdill_s)
+
+
+add_executable(gridinit_cmd
+ main/gridinit_cmd.c
+ main/format_output.c
+ main/utils.c)
+target_link_libraries(gridinit_cmd ${GLIB2_LIBRARIES})
+
+install(TARGETS gridinit gridinit_cmd
+ RUNTIME DESTINATION bin)
diff --git a/README.md b/README.md
index 3580bcf..1da5677 100644
--- a/README.md
+++ b/README.md
@@ -7,8 +7,8 @@ Gridinit is a tool used to manage non-daemon processes.
### Dependencies
* cmake, make, gcc
-* libevent, libevent-devel
* glib, glib-devel
+* libdill (currently embedded)
### Compile-time configuration
diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt
deleted file mode 100644
index 18f9887..0000000
--- a/main/CMakeLists.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-include_directories(BEFORE .)
-
-add_executable(gridinit
- uid.c
- common.c
- limits.c
- children.c
- gridinit.c
- utils.c
- cnx.c)
-target_link_libraries(gridinit
- ${GLIB2_LIBRARIES} ${LIBEVENT_LIBRARIES})
-
-add_executable(gridinit_cmd
- gridinit_cmd.c
- format_output.c
- utils.c
- cnx.c)
-target_link_libraries(gridinit_cmd
- ${GLIB2_LIBRARIES} ${LIBEVENT_LIBRARIES})
-
-install(TARGETS gridinit gridinit_cmd
- LIBRARY DESTINATION ${LD_LIBDIR}
- PUBLIC_HEADER DESTINATION include
- RUNTIME DESTINATION bin)
diff --git a/main/children.c b/main/children.c
index 2e845c5..4c529c1 100644
--- a/main/children.c
+++ b/main/children.c
@@ -130,8 +130,6 @@ struct child_s {
static struct child_s SRV_BEACON = {};
-static supervisor_postfork_f *supervisor_cb_postfork = NULL;
-static void *supervisor_cb_postfork_udata = NULL;
static struct child_s *
supervisor_get_child(const gchar *key)
@@ -413,8 +411,6 @@ _child_start(struct child_s *sd, void *udata, supervisor_cb_f cb)
case 0: /* child */
setsid();
sd->pid = getpid();
- if (supervisor_cb_postfork != NULL)
- supervisor_cb_postfork(supervisor_cb_postfork_udata);
reset_sighandler();
/* change the rights before changing the working directory */
@@ -1174,10 +1170,3 @@ supervisor_children_set_delay_sigkill(const char *key, time_t delay)
return 0;
}
-void
-supervisor_set_callback_postfork(supervisor_postfork_f *cb, void *udata)
-{
- supervisor_cb_postfork_udata = udata;
- supervisor_cb_postfork = cb;
-}
-
diff --git a/main/cnx.c b/main/cnx.c
deleted file mode 100644
index 08ea573..0000000
--- a/main/cnx.c
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
-gridinit, a monitor for non-daemon processes.
-Copyright (C) 2013 AtoS Worldline, original work aside of Redcurrant
-Copyright (C) 2015-2018 OpenIO SAS
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program. If not, see .
-*/
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-
-#include "gridinit_internals.h"
-
-int
-__open_unix_client(const char *path)
-{
- int sock;
- struct sockaddr_un local = {};
-
- if (!path || strlen(path) >= sizeof(local.sun_path)) {
- errno = EINVAL;
- return -1;
- }
-
- /* Create ressources to monitor */
- sock = socket(PF_UNIX, SOCK_STREAM, 0);
- if (sock < 0)
- return -1;
-
- /* Bind to file */
- local.sun_family = AF_UNIX;
- g_strlcpy(local.sun_path, path, sizeof(local.sun_path)-1);
-
- if (-1 == connect(sock, (struct sockaddr *)&local, sizeof(local)))
- goto label_error;
-
- errno = 0;
- return sock;
-
-label_error:
- if (sock >= 0) {
- typeof(errno) errsav;
- errsav = errno;
- close(sock);
- errno = errsav;
- }
- return -1;
-}
-
diff --git a/main/common.c b/main/common.c
deleted file mode 100644
index 8754bb3..0000000
--- a/main/common.c
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
-gridinit-utils, a helper library for gridinit.
-Copyright (C) 2013 AtoS Worldline, original work aside of Redcurrant
-Copyright (C) 2015-2018 OpenIO SAS
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program. If not, see .
-*/
-
-#include
-#include
-
-#include "./gridinit-utils.h"
-
-GError*
-g_error_printf(const char *dom, int code, const char *fmt, ...)
-{
- GError *e;
- gchar *str;
- va_list va;
-
- va_start(va, fmt);
- str = g_strdup_vprintf (fmt, va);
- va_end(va);
-
- e = g_error_new(g_quark_from_static_string(dom), code, "%s", str);
- g_free(str);
- return e;
-}
-
diff --git a/main/gridinit-utils.h b/main/gridinit-utils.h
index e1b39d2..474e06c 100644
--- a/main/gridinit-utils.h
+++ b/main/gridinit-utils.h
@@ -39,20 +39,20 @@ along with this program. If not, see .
# define GRID_LOGLVL_WARN (2 << G_LOG_LEVEL_USER_SHIFT)
# define GRID_LOGLVL_ERROR (1 << G_LOG_LEVEL_USER_SHIFT)
-# define FATAL(Format,...) g_log(GRIDINIT_DOMAIN, GRID_LOGLVL_ERROR, Format, ##__VA_ARGS__)
-# define ALERT(Format,...) g_log(GRIDINIT_DOMAIN, GRID_LOGLVL_ERROR, Format, ##__VA_ARGS__)
-# define CRIT(Format,...) g_log(GRIDINIT_DOMAIN, GRID_LOGLVL_ERROR, Format, ##__VA_ARGS__)
-# define ERROR(Format,...) g_log(GRIDINIT_DOMAIN, GRID_LOGLVL_ERROR, Format, ##__VA_ARGS__)
-# define WARN(Format,...) g_log(GRIDINIT_DOMAIN, GRID_LOGLVL_WARN, Format, ##__VA_ARGS__)
-# define NOTICE(Format,...) g_log(GRIDINIT_DOMAIN, GRID_LOGLVL_NOTICE, Format, ##__VA_ARGS__)
-# define INFO(Format,...) g_log(GRIDINIT_DOMAIN, GRID_LOGLVL_INFO, Format, ##__VA_ARGS__)
-# define DEBUG(Format,...) g_log(GRIDINIT_DOMAIN, GRID_LOGLVL_DEBUG, Format, ##__VA_ARGS__)
-# define TRACE(Format,...) g_log(GRIDINIT_DOMAIN, GRID_LOGLVL_TRACE, Format, ##__VA_ARGS__)
-
-GError* g_error_printf(const char *dom, int code, const char *fmt, ...);
+# define FATAL(Format,...) g_log(LOG_DOMAIN, GRID_LOGLVL_ERROR, Format, ##__VA_ARGS__)
+# define ALERT(Format,...) g_log(LOG_DOMAIN, GRID_LOGLVL_ERROR, Format, ##__VA_ARGS__)
+# define CRIT(Format,...) g_log(LOG_DOMAIN, GRID_LOGLVL_ERROR, Format, ##__VA_ARGS__)
+# define ERROR(Format,...) g_log(LOG_DOMAIN, GRID_LOGLVL_ERROR, Format, ##__VA_ARGS__)
+# define WARN(Format,...) g_log(LOG_DOMAIN, GRID_LOGLVL_WARN, Format, ##__VA_ARGS__)
+# define NOTICE(Format,...) g_log(LOG_DOMAIN, GRID_LOGLVL_NOTICE, Format, ##__VA_ARGS__)
+# define INFO(Format,...) g_log(LOG_DOMAIN, GRID_LOGLVL_INFO, Format, ##__VA_ARGS__)
+# define DEBUG(Format,...) g_log(LOG_DOMAIN, GRID_LOGLVL_DEBUG, Format, ##__VA_ARGS__)
+# define TRACE(Format,...) g_log(LOG_DOMAIN, GRID_LOGLVL_TRACE, Format, ##__VA_ARGS__)
extern time_t supervisor_default_delay_KILL;
+extern GQuark gq_log;
+
/* Children monitoring ----------------------------------------------------- */
enum supervisor_limit_e {
@@ -86,16 +86,11 @@ struct child_info_s {
gboolean started;
};
-typedef void (supervisor_postfork_f) (void *udata);
-
typedef void (supervisor_cb_f) (void *udata, struct child_info_s *ci);
void supervisor_children_init(void);
-/* Sets an optional function that will be used just after the fork */
-void supervisor_set_callback_postfork(supervisor_postfork_f *cb, void *udata);
-
void supervisor_children_fini(void);
guint supervisor_children_cleanall(void);
diff --git a/main/gridinit.c b/main/gridinit.c
index 5fba4e5..8c95bba 100644
--- a/main/gridinit.c
+++ b/main/gridinit.c
@@ -18,63 +18,38 @@ along with this program. If not, see .
*/
#include
-#include
-#include
-#include
-#include
-#include
+#include
#include
-#include
#include
#include
-#include
-#include
-#include
#include
#include
#include
-#include
#include
#include
#include
-#include
#include
+#include
#include
#include "./gridinit_internals.h"
#define USERFLAG_PROCESS_DIED 0x00000002
#define USERFLAG_PROCESS_RESTARTED 0x00000004
+#define UNUSED __attribute__ ((unused))
#define BOOL(i) ((i)!=0)
-typedef int (*cmd_f)(struct bufferevent *bevent, int argc, char **argv);
-
-struct cmd_mapping_s {
- const gchar *cmd_name;
- cmd_f cmd_callback;
-};
-
-struct server_sock_s {
- int family;
- int fd;
- char *url;
- struct event event;
- struct stat unix_stat_path;
- struct stat unix_stat_sock;
-};
-
int main_log_level_default = 0x7F;
int main_log_level = 0x7F;
gint64 main_log_level_update = 0;
-static GList *list_of_servers = NULL;
-static GList *list_of_signals = NULL; /* list of libevent events */
+GQuark gq_log = 0;
static char syslog_id[256] = "";
-static char sock_path[1024] = "";
+static char sock_path[1024] = GRIDINIT_SOCK_PATH;
static char pidfile_path[1024] = "";
static char default_working_directory[1024] = "";
static char *config_path = NULL;
@@ -148,11 +123,6 @@ glvl_allowed(register GLogLevelFlags lvl)
|| (ALLOWED_LEVEL() >= REAL_LEVEL(lvl)));
}
-static struct event timer_event;
-
-static void timer_event_arm(gboolean first);
-static void timer_event_cb(int i, short s, void *p);
-
static void
_str_set_array(gboolean concat, gchar ***dst, gchar *str)
{
@@ -187,24 +157,6 @@ _str_set_array(gboolean concat, gchar ***dst, gchar *str)
g_strfreev(tokens);
}
-static void
-timer_event_cb(int i, short s, void *p)
-{
- (void)i;
- (void)s;
- (void)p;
- timer_event_arm(FALSE);
-}
-
-static void
-timer_event_arm(gboolean first)
-{
- struct timeval tv;
- if (first)
- evtimer_set(&timer_event, timer_event_cb, NULL);
- tv.tv_sec = tv.tv_usec = 1L;
- evtimer_add(&timer_event, &tv);
-}
/* Process management helpers ---------------------------------------------- */
@@ -270,152 +222,134 @@ thread_ignore_signals(void)
/* COMMANDS management ----------------------------------------------------- */
+
static void
-service_run_groupv(int nb_groups, char **groupv, void *udata, supervisor_cb_f cb)
+service_run_groupv(int nb_groups, char **groupv, GString *out, supervisor_cb_f cb)
{
guint count;
- struct bufferevent *bevent;
void group_filter(void *u1, struct child_info_s *ci) {
- if (!gridinit_group_in_set((gchar*)u1, ci->group)) {
+ const char *group = u1;
+ if (group && !gridinit_group_in_set(group, ci->group)) {
TRACE("start: Skipping [%s] with group [%s]", ci->key, ci->group);
- return;
+ } else {
+ TRACE("Calback on service [%s]", ci->key);
+ cb(out, ci);
+ ++ count;
}
- TRACE("Calback on service [%s]", ci->key);
- cb(udata, ci);
- ++ count;
}
- bevent = udata;
-
- if (!nb_groups || !groupv)
- supervisor_run_services(NULL, cb);
- else {
- int i;
- char *what;
- struct child_info_s ci;
-
- for (i=0; ikey);
switch (supervisor_children_status(ci->key, TRUE)) {
case 0:
INFO("Already started [%s]", ci->key);
- evbuffer_add_printf(bufferevent_get_output(bevent), "%d %s\n", EALREADY, ci->key);
+ g_string_append_printf(out, "%d %s\n", EALREADY, ci->key);
return;
case 1:
INFO("Started [%s]", ci->key);
- evbuffer_add_printf(bufferevent_get_output(bevent), "%d %s\n", 0, ci->key);
+ g_string_append_printf(out, "%d %s\n", 0, ci->key);
return;
default:
- WARN("Cannot start [%s] : %s", ci->key, strerror(errno));
- evbuffer_add_printf(bufferevent_get_output(bevent), "%d %s\n", errno, ci->key);
+ WARN("Cannot start [%s]: %s", ci->key, strerror(errno));
+ g_string_append_printf(out, "%d %s\n", errno, ci->key);
return;
}
}
- service_run_groupv(argc, argv, bevent, start_process);
- bufferevent_enable(bevent, EV_WRITE);
- bufferevent_flush(bevent, EV_WRITE, BEV_FINISHED);
- return 0;
+ g_assert_nonnull(out);
+ return service_run_groupv(argc, argv, out, start_process);
}
-static int
-command_stop(struct bufferevent *bevent, int argc, char **argv)
+static void
+command_stop(GString *out, int argc, char **argv)
{
- void stop_process(void *udata, struct child_info_s *ci) {
- (void) udata;
+ void stop_process(void *u UNUSED, struct child_info_s *ci) {
switch (supervisor_children_status(ci->key, FALSE)) {
case 0:
INFO("Already stopped [%s]", ci->key);
- evbuffer_add_printf(bufferevent_get_output(bevent), "%d %s\n", EALREADY, ci->key);
+ g_string_append_printf(out, "%d %s\n", EALREADY, ci->key);
return;
case 1:
INFO("Stopped [%s]", ci->key);
- evbuffer_add_printf(bufferevent_get_output(bevent), "%d %s\n", 0, ci->key);
+ g_string_append_printf(out, "%d %s\n", 0, ci->key);
return;
default:
- WARN("Cannot stop [%s] : %s", ci->key, strerror(errno));
- evbuffer_add_printf(bufferevent_get_output(bevent), "%d %s\n", errno, ci->key);
+ WARN("Cannot stop [%s]: %s", ci->key, strerror(errno));
+ g_string_append_printf(out, "%d %s\n", errno, ci->key);
return;
}
}
- service_run_groupv(argc, argv, bevent, stop_process);
- bufferevent_enable(bevent, EV_WRITE);
- bufferevent_flush(bevent, EV_WRITE, BEV_FINISHED);
- return 0;
+ g_assert_nonnull(out);
+ return service_run_groupv(argc, argv, out, stop_process);
}
-static int
-command_restart(struct bufferevent *bevent, int argc, char **argv)
+static void
+command_restart(GString *out, int argc, char **argv)
{
- void restart_process(void *udata, struct child_info_s *ci) {
- (void) udata;
+ void restart_process(void *u UNUSED, struct child_info_s *ci) {
switch (supervisor_children_restart(ci->key)) {
case 0:
INFO("Already restarted [%s]", ci->key);
- evbuffer_add_printf(bufferevent_get_output(bevent), "%d %s\n", EALREADY, ci->key);
+ g_string_append_printf(out, "%d %s\n", EALREADY, ci->key);
return;
case 1:
INFO("Restart [%s]", ci->key);
- evbuffer_add_printf(bufferevent_get_output(bevent), "%d %s\n", 0, ci->key);
+ g_string_append_printf(out, "%d %s\n", 0, ci->key);
return;
default:
- WARN("Cannot restart [%s] : %s", ci->key, strerror(errno));
- evbuffer_add_printf(bufferevent_get_output(bevent), "%d %s\n", errno, ci->key);
+ WARN("Cannot restart [%s]: %s", ci->key, strerror(errno));
+ g_string_append_printf(out, "%d %s\n", errno, ci->key);
return;
}
}
- service_run_groupv(argc, argv, bevent, restart_process);
- bufferevent_enable(bevent, EV_WRITE);
- bufferevent_flush(bevent, EV_WRITE, BEV_FINISHED);
- return 0;
+ g_assert_nonnull(out);
+ return service_run_groupv(argc, argv, out, restart_process);
}
-
-static int
-command_show(struct bufferevent *bevent, int argc, char **argv)
+static void
+command_show(GString *out, int argc UNUSED, char **argv UNUSED)
{
- void print_process(void *udata, struct child_info_s *ci) {
- (void) udata;
- evbuffer_add_printf(bufferevent_get_output(bevent),
+ void print_process(void *u UNUSED, struct child_info_s *ci) {
+ g_string_append_printf(out,
"%d "
"%d %d %d "
"%u %u "
@@ -432,552 +366,229 @@ command_show(struct bufferevent *bevent, int argc, char **argv)
ci->key, ci->group, ci->cmd);
}
- (void) argc;
- (void) argv;
-
- service_run_groupv(0, NULL, NULL, print_process);
- bufferevent_enable(bevent, EV_WRITE);
- bufferevent_flush(bevent, EV_WRITE, BEV_FINISHED);
- return 0;
+ g_assert_nonnull(out);
+ return service_run_groupv(0, NULL, out, print_process);
}
-static int
-command_repair(struct bufferevent *bevent, int argc, char **argv)
+static void
+command_repair(GString *out, int argc, char **argv)
{
- void repair_process(void *udata, struct child_info_s *ci) {
- (void) udata;
+ void repair_process(void *u UNUSED, struct child_info_s *ci) {
if (0 == supervisor_children_repair(ci->key)) {
INFO("Repaired [%s]", ci->key);
- evbuffer_add_printf(bufferevent_get_output(bevent), "%d %s\n", 0, ci->key);
- }
- else {
- WARN("Failed to repair [%s] : %s", ci->key, strerror(errno));
- evbuffer_add_printf(bufferevent_get_output(bevent), "%d %s\n", errno, ci->key);
+ g_string_append_printf(out, "%d %s\n", 0, ci->key);
+ } else {
+ WARN("Failed to repair [%s]: %s", ci->key, strerror(errno));
+ g_string_append_printf(out, "%d %s\n", errno, ci->key);
}
}
- service_run_groupv(argc, argv, bevent, repair_process);
- bufferevent_enable(bevent, EV_WRITE);
- bufferevent_flush(bevent, EV_WRITE, BEV_FINISHED);
- return 0;
+ g_assert_nonnull(out);
+ return service_run_groupv(argc, argv, out, repair_process);
}
-static int
-command_reload(struct bufferevent *bevent, int argc, char **argv)
+static void
+command_reload(GString *out, int argc UNUSED, char **argv UNUSED)
{
- GError *error_local = NULL;
- guint count;
+ GError *err = NULL;
- (void) argc;
- (void) argv;
+ g_assert_nonnull(out);
- count = supervisor_children_mark_obsolete();
+ guint count = supervisor_children_mark_obsolete();
+ g_string_append_printf(out, "%d obsoleted %u processes\n", 0, count);
TRACE("Marked %u obsolete services\n", count);
- evbuffer_add_printf(bufferevent_get_output(bevent), "%d obsoleted %u processes\n", 0, count);
-
- if (!_cfg_reload(TRUE, &error_local)) {
- WARN("error: Failed to reload the configuration from [%s]\n", config_path);
- WARN("cause: %s\n", error_local ? error_local->message : "NULL");
- evbuffer_add_printf(bufferevent_get_output(bevent), "%d reload\n", error_local ? error_local->code : EINVAL);
- }
- else {
- evbuffer_add_printf(bufferevent_get_output(bevent), "0 reload\n");
+ if (!_cfg_reload(TRUE, &err)) {
+ WARN("error: Failed to reload the configuration from [%s]: (%d) %s\n",
+ config_path, err ? err->code : 0, err ? err->message : "?");
+ g_string_append_printf(out, "%d reload\n", err ? err->code : EINVAL);
+ } else {
+ g_string_append(out, "0 reload\n");
count = supervisor_children_disable_obsolete();
- evbuffer_add_printf(bufferevent_get_output(bevent), "0 disabled %u obsolete processes\n", count);
+ g_string_append_printf(out, "0 disabled %u obsolete processes\n", count);
if (count)
NOTICE("Services refreshed, %u disabled\n", count);
else
TRACE("Services refreshed, %u disabled\n", count);
-
}
- return 0;
}
-static struct cmd_mapping_s COMMANDS [] = {
- {"status", command_show },
- {"repair", command_repair },
- {"start", command_start },
- {"stop", command_stop },
- {"restart", command_restart },
- {"reload", command_reload },
- {NULL, NULL}
-};
+typedef void (*cmd_f) (GString *out, int argc, char **argv);
static cmd_f
__resolve_command(const gchar *n)
{
- int i;
- for (i=0; ;i++) {
- struct cmd_mapping_s *cmd = COMMANDS + i;
- if (!cmd->cmd_name)
- return NULL;
+ static struct cmd_mapping_s {
+ const gchar *cmd_name;
+ cmd_f cmd_callback;
+ } COMMANDS [] = {
+ {"status", command_show },
+ {"repair", command_repair },
+ {"start", command_start },
+ {"stop", command_stop },
+ {"restart", command_restart },
+ {"reload", command_reload },
+ {NULL, NULL}
+ };
+
+ for (struct cmd_mapping_s *cmd = COMMANDS; cmd->cmd_name ;cmd++) {
if (0 == g_ascii_strcasecmp(n, cmd->cmd_name))
return cmd->cmd_callback;
}
+ return NULL;
}
-
-/* Libevent callbacks ------------------------------------------------------ */
-
-static void
-supervisor_signal_handler(int s, short flags, void *udata)
-{
- (void) udata, (void) flags;
-
- switch (s) {
- case SIGUSR1:
- flag_more_verbose = ~0;
- return;
- case SIGUSR2: /* ignored */
- case SIGPIPE: /* ignored */
- return;
- case SIGINT:
- case SIGQUIT:
- case SIGKILL:
- case SIGTERM:
- flag_running = 0;
- return;
- case SIGCHLD:
- return;
- case SIGALRM:
- return;
- }
-}
-
-static void
-__bevent_error(struct bufferevent *bevent, short what, void *udata)
-{
- (void) udata;
- if (what & BEV_EVENT_CONNECTED) {
- TRACE("Connection established for fd=%d what=%04X", bufferevent_getfd(bevent), what);
- bufferevent_enable(bevent, EV_READ|EV_WRITE);
- }
- if (what & ~BEV_EVENT_CONNECTED) {
- int fd, sock_err;
- socklen_t sock_err_len;
-
- fd = bufferevent_getfd(bevent);
- sock_err_len = sizeof(sock_err);
- if (0 != getsockopt(fd, SOL_SOCKET, SO_ERROR, &sock_err, &sock_err_len))
- TRACE("Error on fd=%d what=%04X : getsockopt() error : %s", fd, what, strerror(errno));
- else
- TRACE("Error on fd=%d what=%04X : %s", fd, what, strerror(sock_err));
- bufferevent_flush(bevent, EV_READ, BEV_FINISHED);
- bufferevent_flush(bevent, EV_WRITE, BEV_FINISHED);
- bufferevent_enable(bevent, EV_READ|EV_WRITE);
- }
-}
-
-static void
-__event_command_in(struct bufferevent *bevent, void *udata)
+static GString *
+_command_execute(const char *cmd)
{
int argc = 0;
gchar **argv = NULL;
- char *cmd;
- size_t cmd_len = 0;
-
- (void) udata;
- TRACE("Data available from fd=%d", bufferevent_getfd(bevent));
-
- cmd = evbuffer_readln(bufferevent_get_input(bevent), &cmd_len, EVBUFFER_EOL_CRLF);
- if (!cmd) {
- TRACE("Read error from fd=%d", bufferevent_getfd(bevent));
- bufferevent_disable(bevent, EV_WRITE);
- bufferevent_enable(bevent, EV_READ);
- return;
- }
- else {
- cmd_f callback;
-
- if (!g_shell_parse_argv(cmd, &argc, &argv, NULL))
- TRACE("Invalid request from fd=%d", bufferevent_getfd(bevent));
- else {
- TRACE("Executing request [%s] from fd=%d", argv[0], bufferevent_getfd(bevent));
- if (NULL != (callback = __resolve_command(argv[0])))
- (callback)(bevent, argc-1, argv+1);
- g_strfreev(argv);
- }
- free(cmd);
-
- bufferevent_flush(bevent, EV_WRITE|EV_READ, BEV_FINISHED);
- bufferevent_disable(bevent, EV_READ);
- bufferevent_enable(bevent, EV_WRITE);
- }
-}
-
-static void
-__event_command_out(struct bufferevent *bevent, void *udata)
-{
- (void) udata;
- TRACE("Closing client connection fd=%d", bufferevent_getfd(bevent));
- bufferevent_disable(bevent, EV_READ|EV_WRITE);
- close(bufferevent_getfd(bevent));
- bufferevent_setfd(bevent, -1);
- bufferevent_free(bevent);
-}
-
-static void
-__event_accept(int fd, short flags, void *udata)
-{
- struct bufferevent *bevent = NULL;
- struct linger ls = {1,0};
- socklen_t ss_len;
- struct sockaddr_storage ss;
- int fd_client;
- int i_opt, i_rc;
- struct event_base *libevents_handle;
-
- (void) flags;
- libevents_handle = udata;
-
- ss_len = sizeof(ss);
- fd_client = accept(fd, (struct sockaddr*)&ss, &ss_len);
- if (fd_client < 0) {
- WARN("accept error on fd=%d : %s", fd, strerror(errno));
- return;
+ GString *out = g_string_sized_new(2048);
+
+ if (!g_shell_parse_argv(cmd, &argc, &argv, NULL)) {
+ g_string_append(out, "1 malformed request\n");
+ } else {
+ cmd_f callback = __resolve_command(argv[0]);
+ if (NULL != callback)
+ (callback)(out, argc-1, argv+1);
+ else
+ g_string_append(out, "1 unexpected request\n");
}
- /*SO_LINGER*/
- ls.l_onoff = 1;
- ls.l_linger = 0;
- i_rc = setsockopt(fd_client, SOL_SOCKET, SO_LINGER, (void *) &ls, sizeof(ls));
- if (i_rc == -1)
- WARN("fd=%i Cannot set the linger behaviour (%s)", fd_client, strerror(errno));
-
- /*SO_REUSEADDR*/
- i_opt = 1;
- i_rc = setsockopt(fd_client, SOL_SOCKET, SO_REUSEADDR, (void*) &i_opt, sizeof(i_opt));
- if (i_rc == -1)
- WARN("fd=%i Cannot set the REUSEADDR flag (%s)", fd_client, strerror(errno));
-
- /*SO_KEEPALIVE*/
- i_opt = 1;
- i_rc = setsockopt(fd_client, SOL_SOCKET, SO_KEEPALIVE, (void*) &i_opt, sizeof(i_opt));
- if (i_rc == -1)
- WARN("fd=%i Cannot trigger the tcp keepalive behaviour (%s)", fd_client, strerror(errno));
-
- /* TCP-specific options */
- socklen_t opt_len = sizeof(i_opt);
- i_rc = getsockopt(fd_client, SOL_SOCKET, SO_TYPE, (void*)&i_opt, &opt_len);
- if (i_rc == -1)
- WARN("fd=%i Cannot check the socket type (%s)", fd_client, strerror(errno));
- else if (i_opt == SOCK_STREAM && ss.ss_family == AF_INET) {
-
- /* TCP_QUICKACK */
- i_opt = 1;
- i_rc = setsockopt(fd_client, IPPROTO_TCP, TCP_QUICKACK, (void*)&i_opt, sizeof(i_opt));
- if (i_rc == -1)
- WARN("fd=%i Cannot set TCP_QUICKACK mode on socket (%s)", fd_client, strerror(errno));
-
- /* TCP_NODELAY */
- i_opt = 1;
- i_rc = setsockopt(fd_client, IPPROTO_TCP, TCP_NODELAY, (void*)&i_opt, sizeof(i_opt));
- if (i_rc == -1)
- WARN("fd=%i Cannot set TCP_NODELAY mode on socket (%s)", fd_client, strerror(errno));
- }
-
- evutil_make_socket_closeonexec(fd_client);
-
- /* Now manage this connection */
- bevent = bufferevent_new(fd_client, __event_command_in, __event_command_out, __bevent_error, NULL);
- bufferevent_settimeout(bevent, 1000, 4000);
- bufferevent_enable(bevent, EV_READ);
- bufferevent_disable(bevent, EV_WRITE);
- bufferevent_base_set(libevents_handle, bevent);
- TRACE("Connection accepted server=%d client=%d", fd, fd_client);
+ if (argv) g_strfreev(argv);
+ return out;
}
/* Server socket pool management ------------------------------------------- */
-static int
-servers_is_unix(struct server_sock_s *server)
+static GString *
+_read_line(int ldh, const int64_t dl)
{
- struct sockaddr_storage ss;
- socklen_t ss_len;
-
- bzero(&ss, sizeof(ss));
- ss_len = sizeof(ss);
-
- if (server->fd < 0)
- return FALSE;
-
- if (0 == getsockname(server->fd, (struct sockaddr*) &ss, &ss_len)) {
- if (ss.ss_family==AF_UNIX || ss.ss_family==AF_LOCAL) {
- return TRUE;
- }
+ GString *out = g_string_new("");
+ gchar c;
+ int r;
+
+label_retry:
+ c = '\0';
+ r = dill_brecv(ldh, &c, 1, dl);
+ if (r < 0) {
+ g_string_free(out, TRUE);
+ return NULL;
}
-
- errno = 0;
- return FALSE;
+ if (c != '\n') {
+ g_string_append_c(out, c);
+ goto label_retry;
+ }
+ return out;
}
-static int
-servers_is_the_same(struct server_sock_s *server)
+static dill_coroutine void
+_client_run(int ch, int ldh_client)
{
- struct stat stat_sock, stat_path;
-
- bzero(&stat_sock, sizeof(stat_sock));
- bzero(&stat_path, sizeof(stat_path));
-
- return ((0 == stat(server->url, &stat_path))
- && (0 == fstat(server->fd, &stat_sock))
- && (stat_path.st_ino == server->unix_stat_path.st_ino)
- && (stat_sock.st_ino == server->unix_stat_sock.st_ino));
+ TRACE("client dill_now running h=%d", ldh_client);
+ GString *in = _read_line(ldh_client, dill_now() + 5000);
+ if (!in) {
+ WARN("Client failed: (%d) %s", errno, strerror(errno));
+ } else {
+ TRACE("Client h=%d recv=%" G_GSIZE_FORMAT " [%s]", ldh_client, in ? in->len : 0, in ? in->str : NULL);
+ GString *out = _command_execute(in->str);
+ while (out->len > 0 && out->str[out->len-1] == '\n')
+ g_string_truncate(out, out->len - 1);
+ dill_bsend(ldh_client, out->str, out->len, dill_now() + 5000);
+ g_string_free(in, TRUE);
+ g_string_free(out, TRUE);
+ }
+
+ TRACE("client dill_now exiting h=%d", ldh_client);
+ int rc = dill_hclose(ldh_client);
+ g_assert(rc == 0);
+
+ int v = 0;
+ (void) dill_chsend(ch, &v, sizeof(v), 0);
}
-static void
-servers_unmonitor_one(struct server_sock_s *server)
+static dill_coroutine void
+_server_run(int ch, const char *path)
{
- if (server->fd < 0) {
- /* server socket already stopped */
- return ;
- }
+ int workers = dill_bundle();
+ g_assert(workers >= 0);
- /* Stop the libevent management right now */
- if (event_pending(&(server->event), EV_READ, NULL))
- event_del(&(server->event));
-
- /* If the current socket is a UNIX socket, remove the socket file
- * on disk only if this file is exactly the same that the file
- * this socket created. We must avoid deleting a socket file
- * opened by another process */
- if (servers_is_unix(server) && servers_is_the_same(server))
- unlink(server->url);
-
- shutdown(server->fd, SHUT_RDWR);
- close(server->fd);
- server->fd = -1;
-}
-
-/* starts the server monitoring with the libevent.
- * The inner file descriptor must be a valid socket filedes */
-static gboolean
-servers_monitor_one(struct server_sock_s *server)
-{
- struct sockaddr_storage ss;
- socklen_t ss_len;
-
- if (!server || server->fd < 0) {
- errno = EINVAL;
- return FALSE;
+ int ldh_server = dill_ipc_listen(path, 1024);
+ if (ldh_server < 0) {
+ ERROR("Failed to listen to the commands socket: (%d) %s", errno, strerror(errno));
+ flag_running = 0;
+ return;
}
- memset(&ss, 0x00, sizeof(ss));
- ss_len = sizeof(ss);
- if (-1 == getsockname(server->fd, (struct sockaddr*) &ss, &ss_len)) {
- if (!flag_quiet)
- g_printerr("Error with socket [%s]\n", server->url);
- return FALSE;
- }
+ DEBUG("Initiated a server socket on [%s] h=%d", sock_path, ldh_server);
- /* For unix sockets, remember the stat for further checks */
- if (ss.ss_family==AF_UNIX || ss.ss_family==AF_LOCAL) {
- if (0 != stat(((struct sockaddr_un*)&ss)->sun_path, &(server->unix_stat_path))) {
- if (!flag_quiet)
- g_printerr("Error with socket [%s]\n", server->url);
- return FALSE;
- }
- if (0 != fstat(server->fd, &(server->unix_stat_sock))) {
- if (!flag_quiet)
- g_printerr("Error with socket [%s]\n", server->url);
- return FALSE;
+ while (flag_running) {
+ int ldh_client = dill_ipc_accept(ldh_server, dill_now() + 30000);
+ if (ldh_client >= 0) {
+ TRACE("Client accepted h=%d", ldh_client);
+ dill_bundle_go(workers, _client_run(ch, ldh_client));
+ } else if (errno != EAGAIN && errno != EINTR && errno != ETIMEDOUT) {
+ WARN("accept error: (%d) %s", errno, strerror(errno));
+ flag_running = 0;
}
}
- event_set(&(server->event), server->fd, EV_READ|EV_PERSIST, __event_accept, NULL);
- event_add(&(server->event), NULL);
- errno = 0;
- if (!flag_quiet)
- g_printerr("Socket opened [%s]\n", server->url);
- return TRUE;
-}
-
-static int
-servers_monitor_none(void)
-{
- GList *l;
-
- TRACE("About to stop all the server sockets");
- for (l=list_of_servers; l ;l=l->next) {
- struct server_sock_s *s = l->data;
- servers_unmonitor_one(s);
- }
-
- errno = 0;
- return TRUE;
-}
-
-static int
-servers_monitor_all(void)
-{
- GList *l;
-
- TRACE("About to monitor all the server sockets");
-
- for (l=list_of_servers; l ;l=l->next) {
- struct server_sock_s *s = l->data;
- if (!servers_monitor_one(s))
- return FALSE;
- }
+ int rc = dill_hclose(ldh_server);
+ g_assert(rc == 0);
- errno = 0;
- return TRUE;
+ dill_bundle_wait(workers, dill_now() + 30000);
+ dill_hclose(workers);
}
-/**
- * Creates a server structure based on the file descriptor and
- * add it to the list
- */
-static gboolean
-servers_save_fd(int fd, const char *url)
+static void
+_single_check(void)
{
- struct server_sock_s *server;
+ guint proc_count = supervisor_children_catharsis(NULL, alert_proc_died);
+ if (proc_count > 0)
+ DEBUG("%u services died", proc_count);
- if (fd < 0)
- return FALSE;
+ /* alert for the services that died */
+ supervisor_run_services(NULL, alert_send_deferred);
- /* should check if the socket is not already monitored */
- server = g_malloc0(sizeof(*server));
- server->fd = fd;
- server->url = g_strdup(url);
+ proc_count = supervisor_children_kill_disabled();
+ if (proc_count)
+ DEBUG("Killed %u disabled/stopped services", proc_count);
- list_of_servers = g_list_prepend(list_of_servers, server);
- return TRUE;
-}
+ proc_count = supervisor_children_start_enabled(NULL, alert_proc_started);
+ if (proc_count)
+ DEBUG("Started %u enabled services", proc_count);
-static int
-__open_unix_server(const char *path)
-{
- struct sockaddr_un local = {};
-
- if (!path || strlen(path) >= sizeof(local.sun_path)) {
- errno = EINVAL;
- return -1;
+ if (flag_more_verbose) {
+ NOTICE("Increasing verbosity for 15 minutes");
+ logger_verbose();
+ flag_more_verbose = 0;
}
- /* Create ressources to monitor */
-#ifdef SOCK_CLOEXEC
-# define SOCK_FLAGS SOCK_CLOEXEC
-#else
-# define SOCK_FLAGS 0
-#endif
-
- int sock = socket(PF_UNIX, SOCK_STREAM | SOCK_FLAGS, 0);
- if (sock < 0)
- return -1;
-
-#ifndef SOCK_CLOEXEC
-# ifdef FD_CLOEXEC
- (void) fcntl(sock, F_SETFD, fcntl(sock, F_GETFD)|FD_CLOEXEC);
-# endif
-#endif
-
- /* Bind to file */
- local.sun_family = AF_UNIX;
- g_strlcpy(local.sun_path, path, sizeof(local.sun_path)-1);
-
- if (-1 == bind(sock, (struct sockaddr *)&local, sizeof(local)))
- goto label_error;
-
- /* Listen on that socket */
- if (-1 == listen(sock, 65536))
- goto label_error;
-
- errno = 0;
- return sock;
-
-label_error:
- if (sock >= 0) {
- typeof(errno) errsav;
- errsav = errno;
- close(sock);
- errno = errsav;
+ if (main_log_level_update) {
+ gint64 when = g_get_monotonic_time() - (15 * G_TIME_SPAN_MINUTE);
+ if (main_log_level_update < when) {
+ NOTICE("Verbosity reset to its default value");
+ main_log_level = main_log_level_default;
+ main_log_level_update = 0;
+ }
}
- return -1;
}
-/**
- * Opens a UNIX server socket then manage a server based on it
- */
-static int
-servers_save_unix(const char *path)
-{
- int sock;
-
- if (-1 == (sock = __open_unix_server(path)))
- goto label_error;
-
- if (!servers_save_fd(sock, path))
- goto label_error;
-
- errno = 0;
- return sock;
-
-label_error:
- if (sock >= 0) {
- typeof(errno) errsav;
- errsav = errno;
- shutdown(sock, SHUT_RDWR);
- close(sock);
- errno = errsav;
- }
- return -1;
-}
-
-/**
- * Stops the server socket then
- */
static void
-servers_clean(void)
+_routine_check(int ch)
{
- GList *l;
-
- /* stop */
- servers_monitor_none();
-
- /* clean */
- for (l=list_of_servers; l ; l=l->next) {
- struct server_sock_s *p_server = l->data;
-
- if (p_server->url)
- g_free(p_server->url);
-
- memset(p_server, 0x00, sizeof(*p_server));
- g_free(p_server);
- l->data = NULL;
+ int v = 0;
+ while (flag_running) {
+ _single_check();
+ /* wake-up periodically or upon a ping from a cliet worker */
+ int rc = dill_chrecv(ch, &v, sizeof(v), dill_now() + 1000);
+ if (rc < 0 && errno != ETIMEDOUT)
+ return;
}
-
- g_list_free(list_of_servers);
- list_of_servers = NULL;
-}
-
-/* Signals management ------------------------------------------------------ */
-
-static void
-signals_manage(int s)
-{
- struct event *signal_event;
- signal_event = g_malloc0(sizeof(*signal_event));
- event_set(signal_event, s, EV_SIGNAL|EV_PERSIST, supervisor_signal_handler, NULL);
- event_add(signal_event, NULL);
- list_of_signals = g_list_prepend(list_of_signals, signal_event);
}
-static void
-signals_clean(void)
-{
- GList *l;
- for (l=list_of_signals; l ;l=l->next) {
- struct event *signal_event = l->data;
- g_free(signal_event);
- l->data = NULL;
- }
- g_list_free(list_of_signals);
- list_of_signals = NULL;
-}
/* Configuration ----------------------------------------------------------- */
@@ -1124,8 +735,6 @@ _cfg_service_load_env(GKeyFile *kf, const gchar *section, const gchar *str_key)
static gboolean
_group_is_accepted(gchar *str_key, gchar *str_group)
{
- gchar **p_group, **which;
-
if (!groups_only_cli && !groups_only_cfg) {
TRACE("Service [%s] accepted : gridinit not restricted to some groups", str_key);
return TRUE;
@@ -1135,8 +744,8 @@ _group_is_accepted(gchar *str_key, gchar *str_group)
return FALSE;
}
- which = groups_only_cli ? groups_only_cli : groups_only_cfg;
- for (p_group=which; *p_group ;p_group++) {
+ gchar **which = groups_only_cli ? groups_only_cli : groups_only_cfg;
+ for (gchar **p_group=which; *p_group ;p_group++) {
if (0 == g_ascii_strcasecmp(*p_group, str_group)) {
TRACE("Service [%s] accepted : belongs to an allowed group", str_key);
return TRUE;
@@ -1193,13 +802,13 @@ _cfg_section_service(GKeyFile *kf, const gchar *section, GError **err)
if (!uid_exists(str_uid, &uid)) {
/* Invalid user */
- *err = g_error_printf(LOG_DOMAIN, EINVAL, "Service [%s] cannot cannot receive UID [%s] : errno=%d %s",
+ *err = g_error_new(gq_log, EINVAL, "Service [%s] cannot cannot receive UID [%s] : errno=%d %s",
str_key, str_uid, errno, strerror(errno));
goto label_exit;
}
if (!gid_exists(str_gid, &gid)) {
/* Invalid group */
- *err = g_error_printf(LOG_DOMAIN, EINVAL, "Service [%s] cannot cannot receive GID [%s] : errno=%d %s",
+ *err = g_error_new(gq_log, EINVAL, "Service [%s] cannot cannot receive GID [%s] : errno=%d %s",
str_key, str_gid, errno, strerror(errno));
goto label_exit;
}
@@ -1217,7 +826,7 @@ _cfg_section_service(GKeyFile *kf, const gchar *section, GError **err)
/* Enables or not. This is a lock controlled by the configuration
* that overrides all other child states. */
if (0 > supervisor_children_enable(str_key, _cfg_value_is_true(str_enabled))) {
- *err = g_error_printf(LOG_DOMAIN, errno, "Service [%s] cannot be marked [%s] : %s",
+ *err = g_error_new(gq_log, errno, "Service [%s] cannot be marked [%s] : %s",
str_key, (_cfg_value_is_true(str_enabled)?"ENABLED":"DISABLED"),
strerror(errno));
goto label_exit;
@@ -1454,16 +1063,14 @@ static gboolean
_cfg_reload_file(GKeyFile *kf, gboolean services_only, GError **err)
{
gboolean rc = FALSE;
- gchar **groups=NULL, **p_group=NULL;
-
- groups = g_key_file_get_groups(kf, NULL);
+ gchar **groups = g_key_file_get_groups(kf, NULL);
if (!groups) {
- *err = g_error_new(g_quark_from_static_string("gridinit"), EINVAL, "no group");
+ *err = g_error_new(gq_log, EINVAL, "no group");
return FALSE;
}
- for (p_group=groups; *p_group ;p_group++) {
+ for (gchar **p_group=groups; *p_group ;p_group++) {
TRACE("Reading section [%s]", *p_group);
@@ -1498,9 +1105,7 @@ static gboolean
_cfg_reload(gboolean services_only, GError **err)
{
gboolean rc = FALSE;
- GKeyFile *kf = NULL;
-
- kf = g_key_file_new();
+ GKeyFile *kf = g_key_file_new();
if (!g_key_file_load_from_file(kf, config_path, 0, err)) {
SETERRNO(err);
@@ -1521,12 +1126,11 @@ _cfg_reload(gboolean services_only, GError **err)
NOTICE("errno=%d %s : %s", en, path, strerror(en));
return 0;
}
- int glob_rc;
glob_t subfiles_glob = {};
DEBUG("Loading services files matching [%s]", config_subdir);
- glob_rc = glob(config_subdir,
+ int glob_rc = glob(config_subdir,
GLOB_BRACE|GLOB_NOSORT|GLOB_MARK,
notify_error, &subfiles_glob);
if (glob_rc != 0) {
@@ -1724,9 +1328,7 @@ static void
__parse_options(int argc, char ** args)
{
GError *error_local = NULL;
- GOptionContext *context;
-
- context = g_option_context_new(" CONFIG_PATH [LOG4C_PATH]");
+ GOptionContext *context = g_option_context_new(" CONFIG_PATH [LOG4C_PATH]");
g_option_context_add_main_entries(context, entries, NULL);
if (!g_option_context_parse(context, &argc, &args, &error_local)) {
g_print("option parsing failed: %s\n", error_local->message);
@@ -1751,6 +1353,8 @@ __parse_options(int argc, char ** args)
exit(1);
return;
}
+ g_option_context_free(context);
+ context = NULL;
if (*syslog_id) {
openlog(g_get_prgname(), LOG_PID, LOG_LOCAL0);
@@ -1823,25 +1427,78 @@ is_gridinit_running(const gchar *path)
return FALSE;
}
-int
-main(int argc, char ** args)
+static void _signal_handler(int s)
{
- guint proc_count;
- int rc = 1;
- struct event_base *libevents_handle = NULL;
+ switch (s) {
+ case SIGUSR1:
+ flag_more_verbose = ~0;
+ return;
+ case SIGINT:
+ case SIGQUIT:
+ case SIGKILL:
+ case SIGTERM:
+ flag_running = 0;
+ return;
+ case SIGUSR2:
+ case SIGPIPE:
+ case SIGCHLD:
+ case SIGALRM:
+ return;
+ }
+}
+
+static int
+_action(void)
+{
+ int rc, ch[2] = {-1, -1};
+
+ rc = dill_chmake(ch);
+ if (rc != 0)
+ return rc;
+
+ rc = dill_chdone(ch[1]); /* one way only */
+ if (rc != 0)
+ goto label_error;
+
+ /* Kickoff the network service */
+ int ldr_server = dill_go(_server_run(ch[0], sock_path));
+ if (ldr_server < 0) {
+ ERROR("Failed to kickoff the server coroutine: (%d) %s",
+ errno, strerror(errno));
+ rc = -1;
+ goto label_error;
+ } else {
+ DEBUG("Started the server coroutine h=%d", ldr_server);
+ }
- void postfork(void *udata) {
- (void) udata;
- if (libevents_handle)
- event_reinit(libevents_handle);
+ /* start all the enabled processes, then run the main loop */
+ rc = 0;
+ guint proc_count = supervisor_children_start_enabled(NULL, NULL);
+ DEBUG("First started %u processes", proc_count);
+
+ _routine_check(ch[1]);
+ rc = 0;
+
+ /* Stop the server coroutine and handle */
+ DEBUG("Stopping the server");
+ if (ldr_server >= 0) {
+ dill_hclose(ldr_server);
+ ldr_server = -1;
}
- groups_only_cli = NULL;
- groups_only_cfg = NULL;
- bzero(pidfile_path, sizeof(pidfile_path));
- bzero(default_working_directory, sizeof(default_working_directory));
+label_error:
+ dill_chdone(ch[0]);
+ dill_hclose(ch[0]);
+ dill_hclose(ch[1]);
+ return rc;
+}
- g_strlcpy(sock_path, GRIDINIT_SOCK_PATH, sizeof(sock_path));
+int
+main(int argc, char ** args)
+{
+ int rc = 1;
+
+ gq_log = g_quark_from_static_string(LOG_DOMAIN);
logger_init_level(GRID_LOGLVL_INFO);
g_log_set_default_handler(logger_stderr, NULL);
@@ -1869,109 +1526,34 @@ main(int argc, char ** args)
write_pid_file();
}
- if (-1 == servers_save_unix(sock_path)) {
- ERROR("Failed to open the UNIX socket for commands : %s",
- strerror(errno));
- goto label_exit;
- }
-
- /* Starts the network and the signal management */
- DEBUG("Initiating the network and signals management");
- libevents_handle = event_init();
+ /* Signal management */
+ signal(SIGTERM, _signal_handler);
+ signal(SIGABRT, _signal_handler);
+ signal(SIGINT, _signal_handler);
+ signal(SIGALRM, _signal_handler);
+ signal(SIGQUIT, _signal_handler);
+ signal(SIGUSR1, _signal_handler);
+ signal(SIGPIPE, _signal_handler);
+ signal(SIGUSR2, _signal_handler);
+ signal(SIGCHLD, _signal_handler);
- supervisor_set_callback_postfork(postfork, NULL);
-
- signals_manage(SIGTERM);
- signals_manage(SIGABRT);
- signals_manage(SIGINT);
- signals_manage(SIGALRM);
- signals_manage(SIGQUIT);
- signals_manage(SIGUSR1);
- signals_manage(SIGPIPE);
- signals_manage(SIGUSR2);
- signals_manage(SIGCHLD);
- if (!servers_monitor_all()) {
- ERROR("Failed to monitor the server sockets");
- goto label_exit;
- }
-
- timer_event_arm(TRUE);
-
- DEBUG("Starting the event loop!");
-
- /* start all the enabled processes */
- proc_count = supervisor_children_start_enabled(NULL, NULL);
- DEBUG("First started %u processes", proc_count);
-
- while (flag_running) {
-
- proc_count = supervisor_children_catharsis(NULL, alert_proc_died);
- if (proc_count > 0)
- DEBUG("%u services died", proc_count);
-
- /* alert for the services that died */
- supervisor_run_services(NULL, alert_send_deferred);
-
- proc_count = supervisor_children_kill_disabled();
- if (proc_count)
- DEBUG("Killed %u disabled/stopped services", proc_count);
-
- proc_count = supervisor_children_start_enabled(NULL, alert_proc_started);
- if (proc_count)
- DEBUG("Started %u enabled services", proc_count);
-
- if (!flag_running)
- break;
- if (flag_more_verbose) {
- NOTICE("Increasing verbosity for 15 minutes");
- logger_verbose();
- flag_more_verbose = 0;
- }
-
- if (main_log_level_update) {
- gint64 when = g_get_monotonic_time() - (15 * G_TIME_SPAN_MINUTE);
- if (main_log_level_update < when) {
- NOTICE("Verbosity reset to its default value");
- main_log_level = main_log_level_default;
- main_log_level_update = 0;
- }
- }
-
- /* Be sure to wake */
- alarm(1);
-
- /* Manages the connections pool */
- if (0 > event_loop(EVLOOP_ONCE)) {
- ERROR("event_loop() error : %s", strerror(errno));
- break;
- }
- }
-
- rc = 0;
+ rc = _action();
label_exit:
- /* stop all the processes */
+ thread_ignore_signals();
+
DEBUG("Stopping all the children");
(void) supervisor_children_stopall(1);
- thread_ignore_signals();
DEBUG("Waiting for them to die");
while (supervisor_children_kill_disabled() > 0)
sleep(1);
- /* clean the working structures */
- thread_ignore_signals();
-
DEBUG("Cleaning the working structures");
- if (libevents_handle)
- event_base_free(libevents_handle);
-
supervisor_children_cleanall();
supervisor_children_fini();
- servers_clean();
- signals_clean();
- g_free(config_path);
+ g_free(config_path);
closelog();
return rc;
}
diff --git a/main/gridinit_cmd.c b/main/gridinit_cmd.c
index 2584d55..72f4db7 100644
--- a/main/gridinit_cmd.c
+++ b/main/gridinit_cmd.c
@@ -18,18 +18,18 @@ along with this program. If not, see .
*/
#include
-#include
+#include
#include
#include
-#include
#include
#include
#include
#include
-#include
-#include "./format_output.h"
+#include
+
#include
+#include "./format_output.h"
#include "./gridinit_internals.h"
#define MINI 0
@@ -37,7 +37,7 @@ along with this program. If not, see .
static gchar *sock_path = NULL;
-static gchar line[65536];
+static gchar line[8192] = "";
static gboolean flag_color = FALSE;
static gchar *format = NULL;
static gboolean flag_version = FALSE;
@@ -149,6 +149,33 @@ static const gchar description[] =
"group\n";
+static int
+__open_unix_client(const char *path)
+{
+ struct sockaddr_un local = {0};
+
+ if (!path || strlen(path) >= sizeof(local.sun_path)) {
+ errno = EINVAL;
+ return -1;
+ }
+ local.sun_family = AF_UNIX;
+ g_strlcpy(local.sun_path, path, sizeof(local.sun_path));
+
+ int sock = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0)
+ return -1;
+
+ if (-1 == connect(sock, (struct sockaddr *)&local, sizeof(local))) {
+ int errsav = errno;
+ close(sock);
+ errno = errsav;
+ return -1;
+ }
+
+ errno = 0;
+ return sock;
+}
+
static gint
compare_child_info(gconstpointer p1, gconstpointer p2)
{
@@ -187,8 +214,7 @@ static size_t
get_longest_key(GList *all_jobs)
{
size_t maxlen = 4;
- GList *l;
- for (l=all_jobs; l ;l=l->next) {
+ for (GList *l=all_jobs; l ;l=l->next) {
struct child_info_s *ci = l->data;
size_t len = strlen(ci->key);
if (len > maxlen)
@@ -197,36 +223,18 @@ get_longest_key(GList *all_jobs)
return maxlen;
}
-static size_t
-my_chomp(gchar *str)
-{
- gchar c;
- size_t len;
-
- len = strlen(str);
- while (len && (c=str[len-1]) && g_ascii_isspace(c))
- str[--len] = '\0';
- return len;
-}
-
static void
unpack_line(gchar *str, gchar **start, int *code)
{
- gchar c, *p = NULL;
-
*start = str;
*code = EINVAL;
- if (!str || !*str)
- return ;
- if (!my_chomp(str))
+ if (!str)
return ;
+ str = g_strstrip(str);
+ gchar *p = NULL;
*code = g_ascii_strtoll(str, &p, 10);
-
- if (p) {
- while ((c = *p) && g_ascii_isspace(c))
- p++;
- *start = p;
- }
+ if (p)
+ *start = g_strchug(p);
}
static GList*
@@ -236,10 +244,8 @@ read_services_list(FILE *in_stream)
while (!feof(in_stream) && !ferror(in_stream)) {
if (NULL != fgets(line, sizeof(line), in_stream)) {
-
- (void) my_chomp(line);
-
- gchar **tokens = g_strsplit_set(line, " \t\r\n", 15);
+ gchar *l = g_strstrip(line);
+ gchar **tokens = g_strsplit_set(l, " \t\r\n", 15);
if (tokens) {
if (g_strv_length(tokens) == 15) {
struct child_info_s ci;
@@ -273,10 +279,8 @@ read_services_list(FILE *in_stream)
static void
dump_as_is(FILE *in_stream, void *udata)
{
- int code;
- gchar *start;
gboolean first = TRUE;
- struct dump_as_is_arg_s *dump_args;
+ struct dump_as_is_arg_s *dump_args = udata;
FORMAT format_t = parse_format(format);
@@ -288,16 +292,14 @@ dump_as_is(FILE *in_stream, void *udata)
else
kw = &KEYWORDS_NORMAL;
-
- dump_args = udata;
-
print_header(format_t);
while (!feof(in_stream) && !ferror(in_stream)) {
bzero(line, sizeof(line));
if (NULL != fgets(line, sizeof(line), in_stream)) {
- start = NULL;
- (void)unpack_line(line, &start, &code);
+ int code = 0;
+ gchar *start = NULL;
+ unpack_line(line, &start, &code);
if (dump_args) {
if (code==0 || code==EALREADY)
diff --git a/main/gridinit_internals.h b/main/gridinit_internals.h
index 8cb8a71..fa7d7c9 100644
--- a/main/gridinit_internals.h
+++ b/main/gridinit_internals.h
@@ -80,10 +80,6 @@ along with this program. If not, see .
# define CFG_KEY_INHERIT "inherit_env"
#endif
-int __open_unix_client(const char *path);
-
-/* Groups matching */
-
gboolean gridinit_group_in_set(const gchar *group, const gchar *set);
#endif
diff --git a/main/uid.c b/main/uid.c
index 1dc6e71..41c1e9e 100644
--- a/main/uid.c
+++ b/main/uid.c
@@ -40,24 +40,24 @@ supervisor_rights_init(const char *user_name, const char *group_name, GError **
pwd = getpwnam(user_name);
if (pwd == NULL) {
- *error = g_error_printf(GRIDINIT_DOMAIN, errno, "User [%s] not found in /etc/passwd", user_name);
+ *error = g_error_new(gq_log, errno, "User [%s] not found in /etc/passwd", user_name);
return FALSE;
}
grp = getgrnam(group_name);
if (grp == NULL) {
- *error = g_error_printf(GRIDINIT_DOMAIN, errno, "Group [%s] not found in /etc/group", group_name);
+ *error = g_error_new(gq_log, errno, "Group [%s] not found in /etc/group", group_name);
return FALSE;
}
effective_gid = grp->gr_gid;
effective_uid = pwd->pw_uid;
NOTICE("rights_init : effective id set to %d:%d", effective_uid, effective_gid);
-
+
real_gid = getuid();
real_uid = getgid();
NOTICE("rights_init : real id saved (%d:%d)", real_uid, real_gid);
-
+
return TRUE;
}
diff --git a/vendor/sustrik/libdill/AUTHORS b/vendor/sustrik/libdill/AUTHORS
new file mode 100644
index 0000000..f284bd2
--- /dev/null
+++ b/vendor/sustrik/libdill/AUTHORS
@@ -0,0 +1,29 @@
+Full list of copyright holders:
+
+Alex Cornejo
+Bent Cardan
+Constantine Tarasenkov
+Eli Riggs
+Gonzalo Diethelm *
+Jean-Francois Smigielski
+Jim Jagielski
+Jim Schimpf
+Luca Barbato
+Martin Lucina
+Martin Sustrik *
+Maximilian Pudelko
+Michael Gehring
+Nick Desaulniers
+Nickolay Ilyushin
+Nir Soffer
+Paul Banks
+Petr Skocik
+Sergey Avseyev
+Tai Chi Minh Ralph Eastwood *
+Yue Xu
+Zach Banks
+
+* Future patches by this author are submitted under the MIT/X11 license
+
+Copyrights for DNS resolution library in dns subdirectory are owned by:
+William Ahern
diff --git a/vendor/sustrik/libdill/COPYING b/vendor/sustrik/libdill/COPYING
new file mode 100644
index 0000000..36e7426
--- /dev/null
+++ b/vendor/sustrik/libdill/COPYING
@@ -0,0 +1,19 @@
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom
+the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
+
diff --git a/vendor/sustrik/libdill/Makefile.am b/vendor/sustrik/libdill/Makefile.am
new file mode 100644
index 0000000..a1b199e
--- /dev/null
+++ b/vendor/sustrik/libdill/Makefile.am
@@ -0,0 +1,124 @@
+#
+# Copyright (c) 2016 Martin Sustrik All rights reserved.
+# Copyright (c) 2013 Luca Barbato
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom
+# the Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+ACLOCAL_AMFLAGS = -I m4
+
+################################################################################
+# libdill library #
+################################################################################
+
+dillincludedir = $(includedir)
+dillinclude_HEADERS = libdill.h libdillimpl.h
+
+lib_LTLIBRARIES = libdill.la
+
+libdill_la_SOURCES = \
+ ipc.c \
+ iol.h \
+ iol.c \
+ fd.h \
+ fd.c \
+ bsock.c \
+ chan.c \
+ cr.h \
+ cr.c \
+ epoll.h.inc \
+ epoll.c.inc \
+ handle.h \
+ handle.c \
+ kqueue.h.inc \
+ kqueue.c.inc \
+ libdill.c \
+ list.h \
+ now.h \
+ now.c \
+ poll.h.inc \
+ poll.c.inc \
+ pollset.h \
+ pollset.c \
+ qlist.h \
+ rbtree.h \
+ rbtree.c \
+ slist.h \
+ stack.h \
+ stack.c \
+ ctx.h \
+ ctx.c \
+ utils.h \
+ utils.c
+
+libdill_la_LDFLAGS = \
+ -no-undefined -version-info @DILL_LIBTOOL_VERSION@ @PTHREAD_LIBS@
+
+# Turn the source fortification in glibc off - otherwise it would panic
+# because of the stack pointer black magic in 'go' macro.
+libdill_la_CFLAGS = \
+ @PTHREAD_CFLAGS@ \
+ -fvisibility=hidden\
+ -DDILL_EXPORTS \
+ -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0
+
+check_HEADERS = \
+ tests/assert.h
+
+LDADD = libdill.la
+
+TESTS = $(check_PROGRAMS)
+
+################################################################################
+# tutorials #
+################################################################################
+
+noinst_PROGRAMS =
+
+################################################################################
+# additional packaging-related stuff #
+################################################################################
+
+# Generate ChangeLog file from git.
+# Also, there's no git availabe when building from the source package and
+# thus no way to obtain package version. Therefore, package version is
+# saved into a file when building a source package.
+dist-hook:
+ @if test -d "$(srcdir)/.git"; \
+ then \
+ echo Creating ChangeLog; \
+ cd "$(top_srcdir)"; \
+ (echo '# Generated by Makefile. Do not edit.'; echo; \
+ ./missing --run git log --decorate ) > ChangeLog.tmp; \
+ mv -f ChangeLog.tmp $(top_distdir)/ChangeLog; \
+ rm -f ChangeLog.tmp; \
+ else \
+ cp -f ChangeLog $(top_distdir)/ChangeLog || \
+ echo Failed to generate ChangeLog >&2; \
+ fi; \
+ $(srcdir)/package_version.sh > $(distdir)/.version
+
+EXTRA_DIST = \
+ ./abi_version.sh \
+ ./package_version.sh
+
+CLEANFILES = *.gcno *.gcda dns/*.gcno dns/*.gcda tests/*.gcno tests/*.gcda
+
+distclean-local:
+ -rm -f config.h
+
diff --git a/vendor/sustrik/libdill/README.md b/vendor/sustrik/libdill/README.md
new file mode 100644
index 0000000..f62cfb8
--- /dev/null
+++ b/vendor/sustrik/libdill/README.md
@@ -0,0 +1,13 @@
+# libdill: Structured Concurrency for C
+
+[![Build Status](https://travis-ci.org/sustrik/libdill.svg?branch=master)](https://travis-ci.org/sustrik/libdill)
+
+## Documentation
+
+For the documentation check the project website:
+
+http://libdill.org
+
+## License
+
+Libdill is licensed under MIT/X11 license.
diff --git a/vendor/sustrik/libdill/abi_version.sh b/vendor/sustrik/libdill/abi_version.sh
new file mode 100755
index 0000000..65affbd
--- /dev/null
+++ b/vendor/sustrik/libdill/abi_version.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+# Copyright (c) 2013 Luca Barbato
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom
+# the Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+if [ ! -f libdill.h ]; then
+ echo "abi_version.sh: error: libdill.h does not exist" 1>&2
+ exit 1
+fi
+
+CURRENT=`egrep '^#define +DILL_VERSION_CURRENT +[0-9]+$' libdill.h`
+REVISION=`egrep '^#define +DILL_VERSION_REVISION +[0-9]+$' libdill.h`
+AGE=`egrep '^#define +DILL_VERSION_AGE +[0-9]+$' libdill.h`
+
+if [ -z "$CURRENT" -o -z "$REVISION" -o -z "$AGE" ]; then
+ echo "abi_version.sh: error: could not extract version from libdill.h" 1>&2
+ exit 1
+fi
+
+CURRENT=`echo $CURRENT | awk '{ print $3 }'`
+REVISION=`echo $REVISION | awk '{ print $3 }'`
+AGE=`echo $AGE | awk '{ print $3 }'`
+
+case $1 in
+ -libtool)
+ printf '%s' "$CURRENT:$REVISION:$AGE"
+ ;;
+ *)
+ printf '%s' "$CURRENT.$REVISION.$AGE"
+ ;;
+esac
+
diff --git a/vendor/sustrik/libdill/autogen.sh b/vendor/sustrik/libdill/autogen.sh
new file mode 100755
index 0000000..c819af1
--- /dev/null
+++ b/vendor/sustrik/libdill/autogen.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+autoreconf -ifv
diff --git a/vendor/sustrik/libdill/bsock.c b/vendor/sustrik/libdill/bsock.c
new file mode 100644
index 0000000..e659a0d
--- /dev/null
+++ b/vendor/sustrik/libdill/bsock.c
@@ -0,0 +1,65 @@
+/*
+
+ Copyright (c) 2017 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#include
+#include
+
+#define DILL_DISABLE_RAW_NAMES
+#include "libdillimpl.h"
+#include "utils.h"
+
+dill_unique_id(dill_bsock_type);
+
+int dill_bsend(int s, const void *buf, size_t len, int64_t deadline) {
+ struct dill_bsock_vfs *b = dill_hquery(s, dill_bsock_type);
+ if(dill_slow(!b)) return -1;
+ struct dill_iolist iol = {(void*)buf, len, NULL, 0};
+ return b->bsendl(b, &iol, &iol, deadline);
+}
+
+int dill_brecv(int s, void *buf, size_t len, int64_t deadline) {
+ struct dill_bsock_vfs *b = dill_hquery(s, dill_bsock_type);
+ if(dill_slow(!b)) return -1;
+ struct dill_iolist iol = {buf, len, NULL, 0};
+ return b->brecvl(b, &iol, &iol, deadline);
+}
+
+int dill_bsendl(int s, struct dill_iolist *first, struct dill_iolist *last,
+ int64_t deadline) {
+ struct dill_bsock_vfs *b = dill_hquery(s, dill_bsock_type);
+ if(dill_slow(!b)) return -1;
+ if(dill_slow(!first || !last || last->iol_next)) {
+ errno = EINVAL; return -1;}
+ return b->bsendl(b, first, last, deadline);
+}
+
+int dill_brecvl(int s, struct dill_iolist *first, struct dill_iolist *last,
+ int64_t deadline) {
+ struct dill_bsock_vfs *b = dill_hquery(s, dill_bsock_type);
+ if(dill_slow(!b)) return -1;
+ if(dill_slow((first && !last) || (!first && last) || last->iol_next)) {
+ errno = EINVAL; return -1;}
+ return b->brecvl(b, first, last, deadline);
+}
+
diff --git a/vendor/sustrik/libdill/chan.c b/vendor/sustrik/libdill/chan.c
new file mode 100644
index 0000000..f422b51
--- /dev/null
+++ b/vendor/sustrik/libdill/chan.c
@@ -0,0 +1,348 @@
+/*
+
+ Copyright (c) 2018 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#include
+#include
+#include
+#include
+
+#include "cr.h"
+#include "ctx.h"
+#include "list.h"
+#include "utils.h"
+
+#define DILL_DISABLE_RAW_NAMES
+#include "libdillimpl.h"
+
+struct dill_halfchan {
+ /* Table of virtual functions. */
+ struct dill_hvfs vfs;
+ /* List of clauses wanting to receive from the inbound halfchannel. */
+ struct dill_list in;
+ /* List of clauses wanting to send to the inbound halfchannel. */
+ struct dill_list out;
+ /* Whether this is the fist or the second half-channel of the channel. */
+ unsigned int index : 1;
+ /* 1 if chdone() has been called on this channel. 0 otherwise. */
+ unsigned int done : 1;
+ /* 1 if the object was created with chmake_mem(). */
+ unsigned int mem : 1;
+ /* 1 if hclose() was already called for this half-channel. */
+ unsigned int closed : 1;
+};
+
+/* Channel clause. */
+struct dill_chclause {
+ struct dill_clause cl;
+ /* An item in either the dill_halfchan::in or dill_halfchan::out list. */
+ struct dill_list item;
+ /* The object being passed via the channel. */
+ void *val;
+ size_t len;
+};
+
+DILL_CT_ASSERT(sizeof(struct dill_chstorage) >=
+ sizeof(struct dill_halfchan) * 2);
+
+/******************************************************************************/
+/* Handle implementation. */
+/******************************************************************************/
+
+static const int dill_halfchan_type_placeholder = 0;
+const void *dill_halfchan_type = &dill_halfchan_type_placeholder;
+static void *dill_halfchan_query(struct dill_hvfs *vfs, const void *type);
+static void dill_halfchan_close(struct dill_hvfs *vfs);
+
+/******************************************************************************/
+/* Helpers. */
+/******************************************************************************/
+
+/* Return the other half-channel within the same channel. */
+#define dill_halfchan_other(self) (self->index ? self - 1 : self + 1)
+
+/******************************************************************************/
+/* Channel creation and deallocation. */
+/******************************************************************************/
+
+static void dill_halfchan_init(struct dill_halfchan *ch, int index) {
+ ch->vfs.query = dill_halfchan_query;
+ ch->vfs.close = dill_halfchan_close;
+ dill_list_init(&ch->in);
+ dill_list_init(&ch->out);
+ ch->index = index;
+ ch->done = 0;
+ ch->mem = 1;
+ ch->closed = 0;
+}
+
+int dill_chmake_mem(struct dill_chstorage *mem, int chv[2]) {
+ int err;
+ if(dill_slow(!mem)) {err = EINVAL; goto error1;}
+ struct dill_halfchan *ch = (struct dill_halfchan*)mem;
+ dill_halfchan_init(&ch[0], 0);
+ dill_halfchan_init(&ch[1], 1);
+ chv[0] = dill_hmake(&ch[0].vfs);
+ if(dill_slow(chv[0] < 0)) {err = errno; goto error1;}
+ chv[1] = dill_hmake(&ch[1].vfs);
+ if(dill_slow(chv[1] < 0)) {err = errno; goto error2;}
+ return 0;
+error2:
+ /* This closes the handle but leaves everything else alone given
+ that the second handle wasn't event created. */
+ dill_hclose(chv[0]);
+error1:
+ errno = err;
+ return -1;
+}
+
+int dill_chmake(int chv[2]) {
+ int err;
+ struct dill_chstorage *ch = malloc(sizeof(struct dill_chstorage));
+ if(dill_slow(!ch)) {err = ENOMEM; goto error1;}
+ int h = dill_chmake_mem(ch, chv);
+ if(dill_slow(h < 0)) {err = errno; goto error2;}
+ ((struct dill_halfchan*)ch)[0].mem = 0;
+ ((struct dill_halfchan*)ch)[1].mem = 0;
+ return h;
+error2:
+ free(ch);
+error1:
+ errno = err;
+ return -1;
+}
+
+static void *dill_halfchan_query(struct dill_hvfs *vfs, const void *type) {
+ if(dill_fast(type == dill_halfchan_type)) return vfs;
+ errno = ENOTSUP;
+ return NULL;
+}
+
+static void dill_halfchan_term(struct dill_halfchan *ch) {
+ /* Resume any remaining senders and receivers on the channel
+ with the EPIPE error. */
+ while(!dill_list_empty(&ch->in)) {
+ struct dill_chclause *chcl = dill_cont(dill_list_next(&ch->in),
+ struct dill_chclause, item);
+ dill_trigger(&chcl->cl, EPIPE);
+ }
+ while(!dill_list_empty(&ch->out)) {
+ struct dill_chclause *chcl = dill_cont(dill_list_next(&ch->out),
+ struct dill_chclause, item);
+ dill_trigger(&chcl->cl, EPIPE);
+ }
+}
+
+static void dill_halfchan_close(struct dill_hvfs *vfs) {
+ struct dill_halfchan *ch = (struct dill_halfchan*)vfs;
+ dill_assert(ch && !ch->closed);
+ /* If the other half of the channel is still open do nothing. */
+ if(!dill_halfchan_other(ch)->closed) {
+ ch->closed = 1;
+ return;
+ }
+ if(ch->index) ch = dill_halfchan_other(ch);
+ dill_halfchan_term(&ch[0]);
+ dill_halfchan_term(&ch[1]);
+ if(!ch->mem) free(ch);
+}
+
+/******************************************************************************/
+/* Sending and receiving. */
+/******************************************************************************/
+
+static void dill_chcancel(struct dill_clause *cl) {
+ struct dill_chclause *chcl = dill_cont(cl, struct dill_chclause, cl);
+ dill_list_erase(&chcl->item);
+}
+
+int dill_chsend(int h, const void *val, size_t len, int64_t deadline) {
+ int rc = dill_canblock();
+ if(dill_slow(rc < 0)) return -1;
+ /* Get the channel interface. */
+ struct dill_halfchan *ch = dill_hquery(h, dill_halfchan_type);
+ if(dill_slow(!ch)) return -1;
+ /* Sending is always done to the opposite side of the channel. */
+ ch = dill_halfchan_other(ch);
+ /* Check if the channel is done. */
+ if(dill_slow(ch->done)) {errno = EPIPE; return -1;}
+ /* Copy the message directly to the waiting receiver, if any. */
+ if(!dill_list_empty(&ch->in)) {
+ struct dill_chclause *chcl = dill_cont(dill_list_next(&ch->in),
+ struct dill_chclause, item);
+ if(dill_slow(len != chcl->len)) {
+ dill_trigger(&chcl->cl, EMSGSIZE);
+ errno = EMSGSIZE;
+ return -1;
+ }
+ memcpy(chcl->val, val, len);
+ dill_trigger(&chcl->cl, 0);
+ return 0;
+ }
+ /* The clause is not available immediately. */
+ if(dill_slow(deadline == 0)) {errno = ETIMEDOUT; return -1;}
+ /* Let's wait. */
+ struct dill_chclause chcl;
+ dill_list_insert(&chcl.item, &ch->out);
+ chcl.val = (void*)val;
+ chcl.len = len;
+ dill_waitfor(&chcl.cl, 0, dill_chcancel);
+ struct dill_tmclause tmcl;
+ dill_timer(&tmcl, 1, deadline);
+ int id = dill_wait();
+ if(dill_slow(id < 0)) return -1;
+ if(dill_slow(id == 1)) {errno = ETIMEDOUT; return -1;}
+ if(dill_slow(errno != 0)) return -1;
+ return 0;
+}
+
+int dill_chrecv(int h, void *val, size_t len, int64_t deadline) {
+ int rc = dill_canblock();
+ if(dill_slow(rc < 0)) return -1;
+ /* Get the channel interface. */
+ struct dill_halfchan *ch = dill_hquery(h, dill_halfchan_type);
+ if(dill_slow(!ch)) return -1;
+ /* Check whether the channel is done. */
+ if(dill_slow(ch->done)) {errno = EPIPE; return -1;}
+ /* If there's a sender waiting, copy the message directly
+ from the sender. */
+ if(!dill_list_empty(&ch->out)) {
+ struct dill_chclause *chcl = dill_cont(dill_list_next(&ch->out),
+ struct dill_chclause, item);
+ if(dill_slow(len != chcl->len)) {
+ dill_trigger(&chcl->cl, EMSGSIZE);
+ errno = EMSGSIZE;
+ return -1;
+ }
+ memcpy(val, chcl->val, len);
+ dill_trigger(&chcl->cl, 0);
+ return 0;
+ }
+ /* The clause is not immediately available. */
+ if(dill_slow(deadline == 0)) {errno = ETIMEDOUT; return -1;}
+ /* Let's wait. */
+ struct dill_chclause chcl;
+ dill_list_insert(&chcl.item, &ch->in);
+ chcl.val = val;
+ chcl.len = len;
+ dill_waitfor(&chcl.cl, 0, dill_chcancel);
+ struct dill_tmclause tmcl;
+ dill_timer(&tmcl, 1, deadline);
+ int id = dill_wait();
+ if(dill_slow(id < 0)) return -1;
+ if(dill_slow(id == 1)) {errno = ETIMEDOUT; return -1;}
+ if(dill_slow(errno != 0)) return -1;
+ return 0;
+}
+
+int dill_chdone(int h) {
+ struct dill_halfchan *ch = dill_hquery(h, dill_halfchan_type);
+ if(dill_slow(!ch)) return -1;
+ /* Done is always done to the opposite side of the channel. */
+ ch = dill_halfchan_other(ch);
+ if(ch->done) {errno = EPIPE; return -1;}
+ ch->done = 1;
+ /* Resume any remaining senders and receivers on the channel
+ with the EPIPE error. */
+ while(!dill_list_empty(&ch->in)) {
+ struct dill_chclause *chcl = dill_cont(dill_list_next(&ch->in),
+ struct dill_chclause, item);
+ dill_trigger(&chcl->cl, EPIPE);
+ }
+ while(!dill_list_empty(&ch->out)) {
+ struct dill_chclause *chcl = dill_cont(dill_list_next(&ch->out),
+ struct dill_chclause, item);
+ dill_trigger(&chcl->cl, EPIPE);
+ }
+ return 0;
+}
+
+int dill_choose(struct chclause *clauses, int nclauses, int64_t deadline) {
+ int rc = dill_canblock();
+ if(dill_slow(rc < 0)) return -1;
+ if(dill_slow(nclauses < 0 || (nclauses != 0 && !clauses))) {
+ errno = EINVAL; return -1;}
+ int i;
+ for(i = 0; i != nclauses; ++i) {
+ struct chclause *cl = &clauses[i];
+ struct dill_halfchan *ch = dill_hquery(cl->ch, dill_halfchan_type);
+ if(dill_slow(!ch)) return i;
+ if(dill_slow(cl->len > 0 && !cl->val)) {errno = EINVAL; return i;}
+ struct dill_chclause *chcl;
+ switch(cl->op) {
+ case DILL_CHSEND:
+ ch = dill_halfchan_other(ch);
+ if(dill_slow(ch->done)) {errno = EPIPE; return i;}
+ if(dill_list_empty(&ch->in)) break;
+ chcl = dill_cont(dill_list_next(&ch->in),
+ struct dill_chclause, item);
+ if(dill_slow(cl->len != chcl->len)) {
+ dill_trigger(&chcl->cl, EMSGSIZE);
+ errno = EMSGSIZE;
+ return i;
+ }
+ memcpy(chcl->val, cl->val, cl->len);
+ dill_trigger(&chcl->cl, 0);
+ errno = 0;
+ return i;
+ case DILL_CHRECV:
+ if(dill_slow(ch->done)) {errno = EPIPE; return i;}
+ if(dill_list_empty(&ch->out)) break;
+ chcl = dill_cont(dill_list_next(&ch->out),
+ struct dill_chclause, item);
+ if(dill_slow(cl->len != chcl->len)) {
+ dill_trigger(&chcl->cl, EMSGSIZE);
+ errno = EMSGSIZE;
+ return i;
+ }
+ memcpy(cl->val, chcl->val, cl->len);
+ dill_trigger(&chcl->cl, 0);
+ errno = 0;
+ return i;
+ default:
+ errno = EINVAL;
+ return i;
+ }
+ }
+ /* There are no clauses immediately available. */
+ if(dill_slow(deadline == 0)) {errno = ETIMEDOUT; return -1;}
+ /* Let's wait. */
+ struct dill_chclause chcls[nclauses];
+ for(i = 0; i != nclauses; ++i) {
+ struct dill_halfchan *ch = dill_hquery(clauses[i].ch,
+ dill_halfchan_type);
+ dill_assert(ch);
+ dill_list_insert(&chcls[i].item, clauses[i].op == DILL_CHRECV ?
+ &ch->in : &dill_halfchan_other(ch)->out);
+ chcls[i].val = clauses[i].val;
+ chcls[i].len = clauses[i].len;
+ dill_waitfor(&chcls[i].cl, i, dill_chcancel);
+ }
+ struct dill_tmclause tmcl;
+ dill_timer(&tmcl, nclauses, deadline);
+ int id = dill_wait();
+ if(dill_slow(id < 0)) return -1;
+ if(dill_slow(id == nclauses)) {errno = ETIMEDOUT; return -1;}
+ return id;
+}
+
diff --git a/vendor/sustrik/libdill/configure.ac b/vendor/sustrik/libdill/configure.ac
new file mode 100644
index 0000000..71b464b
--- /dev/null
+++ b/vendor/sustrik/libdill/configure.ac
@@ -0,0 +1,177 @@
+#
+# Copyright (c) 2013 Luca Barbato
+# Copyright (c) 2015 Martin Sustrik All rights reserved.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom
+# the Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+################################################################################
+# Start the configuration phase. #
+################################################################################
+
+AC_PREREQ([2.53])
+
+AC_INIT([libdill], [m4_esyscmd([./package_version.sh])],
+ [sustrik@250bpm.com], [libdill], [http://libdill.org/])
+AM_INIT_AUTOMAKE([1.6 foreign subdir-objects tar-ustar])
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+AC_CANONICAL_HOST
+
+################################################################################
+# Retrieve the versions. #
+################################################################################
+
+AC_PROG_SED
+AC_PROG_AWK
+
+DILL_ABI_VERSION=m4_esyscmd([./abi_version.sh])
+DILL_PACKAGE_VERSION=m4_esyscmd([./package_version.sh])
+DILL_LIBTOOL_VERSION=m4_esyscmd([./abi_version.sh -libtool])
+
+AC_SUBST(DILL_ABI_VERSION)
+AC_SUBST(DILL_PACKAGE_VERSION)
+AC_SUBST(DILL_LIBTOOL_VERSION)
+
+AC_MSG_NOTICE([libdill package version: $DILL_PACKAGE_VERSION])
+AC_MSG_NOTICE([libdill ABI version: $DILL_ABI_VERSION])
+
+################################################################################
+# Check the compilers. #
+################################################################################
+
+AC_PROG_CC_C99
+AM_PROG_CC_C_O
+
+################################################################################
+# --enable-debug #
+################################################################################
+
+AC_ARG_ENABLE([debug], [AS_HELP_STRING([--enable-debug],
+ [Enable debugging information [default=no]])])
+
+if test "x$enable_debug" = "xyes"; then
+ # Override original optimisation level - last option specified wins.
+ CFLAGS="$CFLAGS -g -O0"
+fi
+
+################################################################################
+# --enable-valgrind #
+################################################################################
+
+AC_ARG_ENABLE([valgrind], [AS_HELP_STRING([--enable-valgrind],
+ [Provide information about coroutine stacks to valgrind [default=no]])])
+
+if test "x$enable_valgrind" = "xyes"; then
+ AC_DEFINE(DILL_VALGRIND)
+fi
+
+################################################################################
+# --enable-gcov #
+################################################################################
+
+AC_ARG_ENABLE([gcov], [AS_HELP_STRING([--enable-gcov],
+ [Enable gcov [default=no]])])
+
+if test "x$enable_gcov" = "xyes"; then
+ CFLAGS="$CFLAGS --coverage --no-inline -O0"
+fi
+
+################################################################################
+# --enable-census #
+################################################################################
+
+AC_ARG_ENABLE([census], [AS_HELP_STRING([--enable-census],
+ [Print out information about space used on coroutine stacks [default=no]])])
+
+if test "x$enable_census" = "xyes"; then
+ AC_DEFINE(DILL_CENSUS)
+fi
+
+################################################################################
+# --disable-threads #
+################################################################################
+
+AC_ARG_ENABLE([threads], [AS_HELP_STRING([--disable-threads],
+ [Disable threading support [default=no]])])
+
+if test "x$enable_threads" = "xno"; then
+ AM_CONDITIONAL([DILL_THREADS], false)
+else
+ PTHREAD_LIBS=error
+ PTHREAD_CFLAGS=""
+ AC_CHECK_LIB([pthread], [pthread_attr_init], PTHREAD_LIBS="-lpthread")
+ if test "x$PTHREAD_LIBS" = "xerror"; then
+ AC_CHECK_LIB([c_r], [pthread_attr_init], PTHREAD_LIBS="-lc_r")
+ fi
+ if test "x$PTHREAD_LIBS" = "xerror"; then
+ AC_CHECK_FUNC([pthread_attr_init], PTHREAD_LIBS="")
+ fi
+ if test "x$PTHREAD_LIBS" = "xerror"; then
+ AC_MSG_ERROR([no thread implementation available])
+ else
+ PTHREAD_CFLAGS="-pthread"
+ AC_DEFINE(DILL_THREADS)
+ AC_DEFINE(DILL_PTHREAD)
+ AM_CONDITIONAL([DILL_THREADS], true)
+ fi
+ AC_SUBST(PTHREAD_LIBS)
+ AC_SUBST(PTHREAD_CFLAGS)
+fi
+
+################################################################################
+# Feature checks. #
+################################################################################
+
+AC_CHECK_FUNC([posix_memalign], [AC_DEFINE([HAVE_POSIX_MEMALIGN])])
+AC_CHECK_FUNC([mprotect], [AC_DEFINE([HAVE_MPROTECT])])
+AC_CHECK_LIB([rt], [clock_gettime])
+AC_CHECK_FUNCS([clock_gettime])
+AC_CHECK_LIB([socket], [socket])
+AC_CHECK_FUNCS([epoll_create], [AC_DEFINE([HAVE_EPOLL])])
+AC_CHECK_FUNCS([kqueue], [AC_DEFINE([HAVE_KQUEUE])])
+
+dnl Check if struct sockaddr contains sa_len member
+AC_CHECK_MEMBERS([struct sockaddr.sa_len], [], [], [
+# include
+# include
+])
+
+################################################################################
+# Libtool #
+################################################################################
+
+LT_INIT
+
+################################################################################
+# --enable-shared #
+################################################################################
+
+if test "x$enable_shared" = "xyes"; then
+ AC_DEFINE(DILL_SHARED)
+fi
+
+################################################################################
+# Finish the configuration phase. #
+################################################################################
+
+AC_CONFIG_MACRO_DIR([m4])
+
+AC_OUTPUT([Makefile])
+cp confdefs.h config.h
+
diff --git a/vendor/sustrik/libdill/cr.c b/vendor/sustrik/libdill/cr.c
new file mode 100644
index 0000000..b86d4d9
--- /dev/null
+++ b/vendor/sustrik/libdill/cr.c
@@ -0,0 +1,535 @@
+/*
+
+ Copyright (c) 2018 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#include
+#include
+#include
+#include
+
+#if defined DILL_VALGRIND
+#include
+#endif
+
+#include "cr.h"
+#include "pollset.h"
+#include "stack.h"
+#include "utils.h"
+#include "ctx.h"
+
+#if defined DILL_CENSUS
+
+/* When taking the stack size census, we will keep the maximum stack size
+ in a list indexed by the go() call, i.e., by file name and line number. */
+struct dill_census_item {
+ struct dill_slist crs;
+ const char *file;
+ int line;
+ size_t max_stack;
+};
+
+#endif
+
+/* Storage for the constant used by the go() macro. */
+volatile void *dill_unoptimisable = NULL;
+
+/******************************************************************************/
+/* Handle implementation. */
+/******************************************************************************/
+
+static const int dill_cr_type_placeholder = 0;
+static const void *dill_cr_type = &dill_cr_type_placeholder;
+static void *dill_cr_query(struct dill_hvfs *vfs, const void *type);
+static void dill_cr_close(struct dill_hvfs *vfs);
+
+/******************************************************************************/
+/* Bundle. */
+/******************************************************************************/
+
+static const int dill_bundle_type_placeholder = 0;
+const void *dill_bundle_type = &dill_bundle_type_placeholder;
+static void *dill_bundle_query(struct dill_hvfs *vfs, const void *type);
+static void dill_bundle_close(struct dill_hvfs *vfs);
+
+struct dill_bundle {
+ /* Table of virtual functions. */
+ struct dill_hvfs vfs;
+ /* List of coroutines in this bundle. */
+ struct dill_list crs;
+ /* If somebody is doing hdone() on this bundle, here's the clause
+ to trigger when all coroutines are finished. */
+ struct dill_clause *waiter;
+ /* If true, the bundle was created by bundle_mem. */
+ unsigned int mem : 1;
+};
+
+DILL_CT_ASSERT(sizeof(struct dill_bundle_storage) >=
+ sizeof(struct dill_bundle));
+
+int dill_bundle_mem(struct dill_bundle_storage *mem) {
+ int err;
+ if(dill_slow(!mem)) {err = EINVAL; return -1;}
+ struct dill_bundle *b = (struct dill_bundle*)mem;
+ b->vfs.query = dill_bundle_query;
+ b->vfs.close = dill_bundle_close;
+ dill_list_init(&b->crs);
+ b->waiter = NULL;
+ b->mem = 1;
+ return dill_hmake(&b->vfs);
+}
+
+int dill_bundle(void) {
+ int err;
+ struct dill_bundle *b = malloc(sizeof(struct dill_bundle));
+ if(dill_slow(!b)) {err = ENOMEM; goto error1;}
+ int h = dill_bundle_mem((struct dill_bundle_storage*)b);
+ if(dill_slow(h < 0)) {err = errno; goto error2;}
+ b->mem = 0;
+ return h;
+error2:
+ free(b);
+error1:
+ errno = err;
+ return -1;
+}
+
+static void *dill_bundle_query(struct dill_hvfs *vfs, const void *type) {
+ if(dill_fast(type == dill_bundle_type)) return vfs;
+ errno = ENOTSUP;
+ return NULL;
+}
+
+static void dill_bundle_close(struct dill_hvfs *vfs) {
+ struct dill_bundle *self = (struct dill_bundle*)vfs;
+ struct dill_list *it = &self->crs;
+ for(it = self->crs.next; it != &self->crs; it = dill_list_next(it)) {
+ struct dill_cr *cr = dill_cont(it, struct dill_cr, bundle);
+ dill_cr_close(&cr->vfs);
+ }
+ if(!self->mem) free(self);
+}
+
+int dill_bundle_wait(int h, int64_t deadline) {
+ int rc = dill_canblock();
+ if(dill_slow(rc < 0)) return -1;
+ struct dill_bundle *self = dill_hquery(h, dill_bundle_type);
+ if(dill_slow(!self)) return -1;
+ /* If there are no coroutines in the bundle succeed immediately. */
+ if(dill_list_empty(&self->crs)) return 0;
+ /* Otherwise wait for all coroutines to finish. */
+ struct dill_clause cl;
+ self->waiter = &cl;
+ dill_waitfor(&cl, 0, NULL);
+ struct dill_tmclause tmcl;
+ dill_timer(&tmcl, 1, deadline);
+ int id = dill_wait();
+ self->waiter = NULL;
+ if(dill_slow(id < 0)) return -1;
+ if(dill_slow(id == 1)) {errno = ETIMEDOUT; return -1;}
+ dill_assert(id == 0);
+ return 0;
+}
+
+/******************************************************************************/
+/* Helpers. */
+/******************************************************************************/
+
+static void dill_resume(struct dill_cr *cr, int id, int err) {
+ struct dill_ctx_cr *ctx = &dill_getctx->cr;
+ cr->id = id;
+ cr->err = err;
+ dill_qlist_push(&ctx->ready, &cr->ready);
+}
+
+int dill_canblock(void) {
+ struct dill_ctx_cr *ctx = &dill_getctx->cr;
+ if(dill_slow(ctx->r->no_blocking1 || ctx->r->no_blocking2)) {
+ errno = ECANCELED; return -1;}
+ return 0;
+}
+
+int dill_no_blocking(int val) {
+ struct dill_ctx_cr *ctx = &dill_getctx->cr;
+ int old = ctx->r->no_blocking2;
+ ctx->r->no_blocking2 = val;
+ return old;
+}
+
+/******************************************************************************/
+/* Context. */
+/******************************************************************************/
+
+int dill_ctx_cr_init(struct dill_ctx_cr *ctx) {
+ /* This function is definitely called from the main coroutine, given that
+ it's called only once and you can't even create a different coroutine
+ without calling it. */
+ ctx->r = &ctx->main;
+ dill_qlist_init(&ctx->ready);
+ dill_rbtree_init(&ctx->timers);
+ /* We can't use now() here as the context is still being intialized. */
+ ctx->last_poll = dill_mnow();
+ /* Initialize the main coroutine. */
+ memset(&ctx->main, 0, sizeof(ctx->main));
+ ctx->main.ready.next = NULL;
+ dill_slist_init(&ctx->main.clauses);
+#if defined DILL_CENSUS
+ dill_slist_init(&ctx->census);
+#endif
+ return 0;
+}
+
+void dill_ctx_cr_term(struct dill_ctx_cr *ctx) {
+#if defined DILL_CENSUS
+ struct dill_slist *it;
+ for(it = dill_slist_next(&ctx->census); it != &ctx->census;
+ it = dill_slist_next(it)) {
+ struct dill_census_item *ci =
+ dill_cont(it, struct dill_census_item, crs);
+ fprintf(stderr, "%s:%d - maximum stack size %zu B\n",
+ ci->file, ci->line, ci->max_stack);
+ }
+#endif
+}
+
+/******************************************************************************/
+/* Timers. */
+/******************************************************************************/
+
+static void dill_timer_cancel(struct dill_clause *cl) {
+ struct dill_ctx_cr *ctx = &dill_getctx->cr;
+ struct dill_tmclause *tmcl = dill_cont(cl, struct dill_tmclause, cl);
+ dill_rbtree_erase(&ctx->timers, &tmcl->item);
+ /* This is a safeguard. If an item isn't properly removed from the rb-tree,
+ we can spot the fact by seeing that the cr has been set to NULL. */
+ tmcl->cl.cr = NULL;
+}
+
+/* Adds a timer clause to the list of clauses being waited on. */
+void dill_timer(struct dill_tmclause *tmcl, int id, int64_t deadline) {
+ struct dill_ctx_cr *ctx = &dill_getctx->cr;
+ /* If the deadline is infinite, there's nothing to wait for. */
+ if(deadline < 0) return;
+ dill_rbtree_insert(&ctx->timers, deadline, &tmcl->item);
+ dill_waitfor(&tmcl->cl, id, dill_timer_cancel);
+}
+
+/******************************************************************************/
+/* Coroutine creation and termination */
+/******************************************************************************/
+
+static void dill_cancel(struct dill_cr *cr, int err);
+
+/* The initial part of go(). Allocates a new stack and bundle. */
+int dill_prologue(sigjmp_buf **jb, void **ptr, size_t len, int bndl,
+ const char *file, int line) {
+ int err;
+ struct dill_ctx_cr *ctx = &dill_getctx->cr;
+ /* Return ECANCELED if shutting down. */
+ int rc = dill_canblock();
+ if(dill_slow(rc < 0)) {err = ECANCELED; goto error1;}
+ /* If bundle is not supplied by the user create one. If user supplied a
+ memory to use put the bundle at the beginning of the block. */
+ int new_bundle = bndl < 0;
+ if(new_bundle) {
+ if(*ptr) {
+ bndl = dill_bundle_mem(*ptr);
+ *ptr = ((uint8_t*)*ptr) + sizeof(struct dill_bundle_storage);
+ len -= sizeof(struct dill_bundle_storage);
+ }
+ else {
+ bndl = dill_bundle();
+ }
+ if(dill_slow(bndl < 0)) {err = errno; goto error1;}
+ }
+ struct dill_bundle *bundle = dill_hquery(bndl, dill_bundle_type);
+ if(dill_slow(!bundle)) {err = errno; goto error2;}
+ /* Allocate a stack. */
+ struct dill_cr *cr;
+ size_t stacksz;
+ if(!*ptr) {
+ cr = (struct dill_cr*)dill_allocstack(&stacksz);
+ if(dill_slow(!cr)) {err = errno; goto error2;}
+ }
+ else {
+ /* The stack is supplied by the user.
+ Align the top of the stack to a 16-byte boundary. */
+ uintptr_t top = (uintptr_t)*ptr;
+ top += len;
+ top &= ~(uintptr_t)15;
+ stacksz = top - (uintptr_t)*ptr;
+ cr = (struct dill_cr*)top;
+ if(dill_slow(stacksz < sizeof(struct dill_cr))) {
+ err = ENOMEM; goto error2;}
+ }
+#if defined DILL_CENSUS
+ /* Mark the bytes in the stack as unused. */
+ uint8_t *bottom = ((char*)cr) - stacksz;
+ int i;
+ for(i = 0; i != stacksz; ++i)
+ bottom[i] = 0xa0 + (i % 13);
+#endif
+ --cr;
+ cr->vfs.query = dill_cr_query;
+ cr->vfs.close = dill_cr_close;
+ dill_list_insert(&cr->bundle, &bundle->crs);
+ cr->ready.next = NULL;
+ dill_slist_init(&cr->clauses);
+ cr->closer = NULL;
+ cr->no_blocking1 = 0;
+ cr->no_blocking2 = 0;
+ cr->done = 0;
+ cr->mem = *ptr ? 1 : 0;
+#if defined DILL_VALGRIND
+ cr->sid = VALGRIND_STACK_REGISTER((char*)(cr + 1) - stacksz, cr);
+#endif
+#if defined DILL_CENSUS
+ /* Find the appropriate census item if it exists. It's O(n) but meh. */
+ cr->census = NULL;
+ struct dill_slist *it;
+ for(it = dill_slist_next(&ctx->census); it != &ctx->census;
+ it = dill_slist_next(it)) {
+ cr->census = dill_cont(it, struct dill_census_item, crs);
+ if(cr->census->line == line && strcmp(cr->census->file, file) == 0)
+ break;
+ }
+ /* Allocate it if it does not exist. */
+ if(it == &ctx->census) {
+ cr->census = malloc(sizeof(struct dill_census_item));
+ dill_assert(cr->census);
+ dill_slist_push(&ctx->census, &cr->census->crs);
+ cr->census->file = file;
+ cr->census->line = line;
+ cr->census->max_stack = 0;
+ }
+ cr->stacksz = stacksz - sizeof(struct dill_cr);
+#endif
+ /* Return the context of the parent coroutine to the caller so that it can
+ store its current state. It can't be done here because we are at the
+ wrong stack frame here. */
+ *jb = &ctx->r->ctx;
+ /* Add parent coroutine to the list of coroutines ready for execution. */
+ dill_resume(ctx->r, 0, 0);
+ /* Mark the new coroutine as running. */
+ *ptr = ctx->r = cr;
+ /* In case of success go() returns the handle, bundle_go() returns 0. */
+ return new_bundle ? bndl : 0;
+error2:
+ if(new_bundle) {
+ rc = dill_hclose(bndl);
+ dill_assert(rc == 0);
+ }
+error1:
+ errno = err;
+ return -1;
+}
+
+/* The final part of go(). Gets called when the coroutine is finished. */
+void dill_epilogue(void) {
+ struct dill_ctx_cr *ctx = &dill_getctx->cr;
+ /* Mark the coroutine as finished. */
+ ctx->r->done = 1;
+ /* If there's a coroutine waiting for us to finish, unblock it now. */
+ if(ctx->r->closer)
+ dill_cancel(ctx->r->closer, 0);
+ /* Deallocate the coroutine, unless, of course, it is already
+ in the process of being closed. */
+ if(!ctx->r->no_blocking1) {
+ /* If this is the last coroutine in the bundle and there's someone
+ waiting for hdone() on the bundle unblock them. */
+ if(dill_list_oneitem(&ctx->r->bundle)) {
+ struct dill_bundle *b = dill_cont(ctx->r->bundle.next,
+ struct dill_bundle, crs);
+ if(b->waiter) dill_trigger(b->waiter, 0);
+ }
+ dill_list_erase(&ctx->r->bundle);
+ dill_cr_close(&ctx->r->vfs);
+ }
+ /* With no clauses added, this call will never return. */
+ dill_assert(dill_slist_empty(&ctx->r->clauses));
+ dill_wait();
+}
+
+static void *dill_cr_query(struct dill_hvfs *vfs, const void *type) {
+ struct dill_cr *cr = dill_cont(vfs, struct dill_cr, vfs);
+ if(dill_fast(type == dill_cr_type)) return cr;
+ errno = ENOTSUP;
+ return NULL;
+}
+
+/* Gets called when coroutine handle is closed. */
+static void dill_cr_close(struct dill_hvfs *vfs) {
+ struct dill_ctx_cr *ctx = &dill_getctx->cr;
+ struct dill_cr *cr = dill_cont(vfs, struct dill_cr, vfs);
+ /* If the coroutine has already finished, we are done. */
+ if(!cr->done) {
+ /* No blocking calls from this point on. */
+ cr->no_blocking1 = 1;
+ /* Resume the coroutine if it was blocked. */
+ if(!cr->ready.next)
+ dill_cancel(cr, ECANCELED);
+ /* Wait for the coroutine to stop executing. With no clauses added,
+ the only mechanism to resume is through dill_cancel(). This is not
+ really a blocking call, although it looks like one. Given that the
+ coroutine that is being shut down is not permitted to block, we
+ should get control back pretty quickly. */
+ cr->closer = ctx->r;
+ int rc = dill_wait();
+ dill_assert(rc == -1 && errno == 0);
+ }
+#if defined DILL_CENSUS
+ /* Find the first overwritten byte on the stack.
+ Determine stack usage based on that. */
+ uint8_t *bottom = ((uint8_t*)cr) - cr->stacksz;
+ int i;
+ for(i = 0; i != cr->stacksz; ++i) {
+ if(bottom[i] != 0xa0 + (i % 13)) {
+ /* dill_cr is located on the stack so we have to take that into
+ account. Also, it may be necessary to align the top of the stack
+ to a 16-byte boundary, so add 16 bytes to account for that. */
+ size_t used = cr->stacksz - i - sizeof(struct dill_cr) + 16;
+ if(used > cr->census->max_stack)
+ cr->census->max_stack = used;
+ break;
+ }
+ }
+#endif
+#if defined DILL_VALGRIND
+ VALGRIND_STACK_DEREGISTER(cr->sid);
+#endif
+ /* Now that the coroutine is finished, deallocate it. */
+ if(!cr->mem) dill_freestack(cr + 1);
+}
+
+/******************************************************************************/
+/* Suspend/resume functionality. */
+/******************************************************************************/
+
+void dill_waitfor(struct dill_clause *cl, int id,
+ void (*cancel)(struct dill_clause *cl)) {
+ struct dill_ctx_cr *ctx = &dill_getctx->cr;
+ /* Add a clause to the coroutine list of active clauses. */
+ cl->cr = ctx->r;
+ dill_slist_push(&ctx->r->clauses, &cl->item);
+ cl->id = id;
+ cl->cancel = cancel;
+}
+
+int dill_wait(void) {
+ struct dill_ctx_cr *ctx = &dill_getctx->cr;
+ /* Store the context of the current coroutine, if any. */
+ if(dill_setjmp(ctx->r->ctx)) {
+ /* We get here once the coroutine is resumed. */
+ dill_slist_init(&ctx->r->clauses);
+ errno = ctx->r->err;
+ return ctx->r->id;
+ }
+ /* For performance reasons, we want to avoid excessive checking of current
+ time, so we cache the value here. It will be recomputed only after
+ a blocking call. */
+ int64_t nw = dill_now();
+ /* Wait for timeouts and external events. However, if there are ready
+ coroutines there's no need to poll for external events every time.
+ Still, we'll do it at least once a second. The external signal may
+ very well be a deadline or a user-issued command that cancels the CPU
+ intensive operation. */
+ if(dill_qlist_empty(&ctx->ready) || nw > ctx->last_poll + 1000) {
+ int block = dill_qlist_empty(&ctx->ready);
+ while(1) {
+ /* Compute the timeout for the subsequent poll. */
+ int timeout = 0;
+ if(block) {
+ if(dill_rbtree_empty(&ctx->timers))
+ timeout = -1;
+ else {
+ int64_t deadline = dill_cont(
+ dill_rbtree_first(&ctx->timers),
+ struct dill_tmclause, item)->item.val;
+ timeout = (int) (nw >= deadline ? 0 : deadline - nw);
+ }
+ }
+ /* Wait for events. */
+ int fired = dill_pollset_poll(timeout);
+ if(timeout != 0) nw = dill_now();
+ if(dill_slow(fired < 0)) continue;
+ /* Fire all expired timers. */
+ if(!dill_rbtree_empty(&ctx->timers)) {
+ while(!dill_rbtree_empty(&ctx->timers)) {
+ struct dill_tmclause *tmcl = dill_cont(
+ dill_rbtree_first(&ctx->timers),
+ struct dill_tmclause, item);
+ if(tmcl->item.val > nw)
+ break;
+ dill_trigger(&tmcl->cl, ETIMEDOUT);
+ fired = 1;
+ }
+ }
+ /* Never retry the poll when in non-blocking mode. */
+ if(!block || fired)
+ break;
+ /* If the timeout was hit but there were no expired timers,
+ do the poll again. It can happen if the timers were canceled
+ in the meantime. */
+ }
+ ctx->last_poll = nw;
+ }
+ /* There's a coroutine ready to be executed so jump to it. */
+ struct dill_slist *it = dill_qlist_pop(&ctx->ready);
+ it->next = NULL;
+ ctx->r = dill_cont(it, struct dill_cr, ready);
+ /* dill_longjmp has to be at the end of a function body, otherwise stack
+ unwinding information will be trimmed if a crash occurs in this
+ function. */
+ dill_longjmp(ctx->r->ctx);
+ return 0;
+}
+
+static void dill_docancel(struct dill_cr *cr, int id, int err) {
+ /* Sanity check: Make sure that the coroutine was really suspended. */
+ dill_assert(!cr->ready.next);
+ /* Remove the clauses from endpoints' lists of waiting coroutines. */
+ struct dill_slist *it;
+ for(it = dill_slist_next(&cr->clauses); it != &cr->clauses;
+ it = dill_slist_next(it)) {
+ struct dill_clause *cl = dill_cont(it, struct dill_clause, item);
+ if(cl->cancel) cl->cancel(cl);
+ }
+ /* Schedule the newly unblocked coroutine for execution. */
+ dill_resume(cr, id, err);
+}
+
+void dill_trigger(struct dill_clause *cl, int err) {
+ dill_docancel(cl->cr, cl->id, err);
+}
+
+static void dill_cancel(struct dill_cr *cr, int err) {
+ dill_docancel(cr, -1, err);
+}
+
+int dill_yield(void) {
+ struct dill_ctx_cr *ctx = &dill_getctx->cr;
+ int rc = dill_canblock();
+ if(dill_slow(rc < 0)) return -1;
+ /* Put the current coroutine into the ready queue. */
+ dill_resume(ctx->r, 0, 0);
+ /* Suspend. */
+ return dill_wait();
+}
+
diff --git a/vendor/sustrik/libdill/cr.h b/vendor/sustrik/libdill/cr.h
new file mode 100644
index 0000000..627bd72
--- /dev/null
+++ b/vendor/sustrik/libdill/cr.h
@@ -0,0 +1,170 @@
+/*
+
+ Copyright (c) 2018 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#ifndef DILL_CR_INCLUDED
+#define DILL_CR_INCLUDED
+
+#include
+
+#include "list.h"
+#include "qlist.h"
+#include "rbtree.h"
+#include "slist.h"
+
+#define DILL_DISABLE_RAW_NAMES
+#include "libdillimpl.h"
+
+/* The coroutine. The memory layout looks like this:
+ +-------------------------------------------------------------+---------+
+ | stack | dill_cr |
+ +-------------------------------------------------------------+---------+
+ - dill_cr contains generic book-keeping info about the coroutine
+ - the stack is a standard C stack; it grows downwards (at the moment, libdill
+ doesn't support microarchitectures where stacks grow upwards)
+*/
+struct dill_cr {
+ /* When the coroutine is ready for execution but not running yet,
+ it lives on this list (ctx->ready). 'id' is the result value to return
+ from dill_wait() when the coroutine is resumed. Additionally, errno
+ will be set to 'err'. */
+ struct dill_slist ready;
+ /* Virtual function table. */
+ struct dill_hvfs vfs;
+ int id;
+ int err;
+ /* When the coroutine is suspended 'ctx' holds the context
+ (registers and such).*/
+ sigjmp_buf ctx;
+ /* If the coroutine is blocked, here's the list of the clauses it's
+ waiting for. */
+ struct dill_slist clauses;
+ /* A list of coroutines belonging to a particular bundle. */
+ struct dill_list bundle;
+ /* There are two possible reasons to disable blocking calls.
+ 1. The coroutine is being closed by its owner.
+ 2. The execution is happening within the context of an hclose() call. */
+ unsigned int no_blocking1 : 1;
+ unsigned int no_blocking2 : 1;
+ /* Set when the coroutine has finished its execution. */
+ unsigned int done : 1;
+ /* If true, the coroutine was launched with go_mem. */
+ unsigned int mem : 1;
+ /* When the coroutine handle is being closed, this points to the
+ coroutine that is doing the hclose() call. */
+ struct dill_cr *closer;
+#if defined DILL_VALGRIND
+ /* Valgrind stack identifier. This way, valgrind knows which areas of
+ memory are used as stacks, and so it doesn't produce spurious warnings.
+ Well, sort of. The mechanism is not perfect, but it's still better
+ than nothing. */
+ int sid;
+#endif
+#if defined DILL_CENSUS
+ /* Census record corresponding to this coroutine. */
+ struct dill_census_item *census;
+ size_t stacksz;
+#endif
+/* Clang assumes that the client stack is aligned to 16-bytes on x86-64
+ architectures. To achieve this, we align this structure (with the added
+ benefit of a minor optimization). */
+} __attribute__((aligned(16)));
+
+struct dill_ctx_cr {
+ /* Currently running coroutine. */
+ struct dill_cr *r;
+ /* List of coroutines ready for execution. */
+ struct dill_qlist ready;
+ /* All active timers. */
+ struct dill_rbtree timers;
+ /* Last time poll was performed. */
+ int64_t last_poll;
+ /* The main coroutine. We don't control the creation of the main coroutine's
+ stack, so we have to store this info here instead of the top of
+ the stack. */
+ struct dill_cr main;
+#if defined DILL_CENSUS
+ struct dill_slist census;
+#endif
+};
+
+struct dill_clause {
+ /* The coroutine that owns this clause. */
+ struct dill_cr *cr;
+ /* List of the clauses the coroutine is waiting on. See dill_cr::clauses. */
+ struct dill_slist item;
+ /* Number to return from dill_wait() if this clause triggers. */
+ int id;
+ /* Function to call when this clause is canceled. */
+ void (*cancel)(struct dill_clause *cl);
+};
+
+/* Timer clause. */
+struct dill_tmclause {
+ struct dill_clause cl;
+ /* An item in dill_ctx_cr::timers. */
+ struct dill_rbtree_item item;
+};
+
+/* File descriptor clause. */
+struct dill_fdclause;
+
+int dill_ctx_cr_init(struct dill_ctx_cr *ctx);
+void dill_ctx_cr_term(struct dill_ctx_cr *ctx);
+
+/* When dill_wait() is called next time, the coroutine will wait
+ (among other clauses) on this clause. 'id' must not be negative.
+ 'cancel' is a function to be called when the clause is canceled
+ without being triggered. */
+void dill_waitfor(struct dill_clause *cl, int id,
+ void (*cancel)(struct dill_clause *cl));
+
+/* Suspend running coroutine. Move to executing different coroutines.
+ The coroutine will be resumed once one of the clauses previously added by
+ dill_waitfor() is triggered. When that happens, all the clauses, whether
+ triggered or not, will be canceled. The function returns the ID of the
+ triggered clause or -1 on error. In either case, it sets errno to 0 indicate
+ success or non-zero value to indicate error. */
+int dill_wait(void);
+
+/* Schedule a previously suspended coroutine for execution. Keep in mind that
+ this doesn't immediately run it, it just puts it into the coroutine ready
+ queue. It will cause dill_wait() to return the id supplied in
+ dill_waitfor(). */
+void dill_trigger(struct dill_clause *cl, int err);
+
+/* Add a timer to the list of active clauses. */
+void dill_timer(struct dill_tmclause *tmcl, int id, int64_t deadline);
+
+/* Returns 0 if blocking functions are allowed.
+ Returns -1 and sets errno to ECANCELED otherwise. */
+int dill_canblock(void);
+
+/* When set to 1, blocking calls return ECANCELED.
+ Returns the old value of the flag */
+int dill_no_blocking(int val);
+
+/* Cleans cached info about the fd. */
+int dill_clean(int fd);
+
+#endif
+
+
diff --git a/vendor/sustrik/libdill/ctx.c b/vendor/sustrik/libdill/ctx.c
new file mode 100644
index 0000000..3df2e0d
--- /dev/null
+++ b/vendor/sustrik/libdill/ctx.c
@@ -0,0 +1,203 @@
+/*
+
+ Copyright (c) 2016 Tai Chi Minh Ralph Eastwood
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#include "ctx.h"
+
+#if !defined DILL_THREADS
+
+struct dill_ctx dill_ctx_ = {0};
+
+static void dill_ctx_atexit(void) {
+ dill_ctx_fd_term(&dill_ctx_.fd);
+ dill_ctx_pollset_term(&dill_ctx_.pollset);
+ dill_ctx_stack_term(&dill_ctx_.stack);
+ dill_ctx_handle_term(&dill_ctx_.handle);
+ dill_ctx_cr_term(&dill_ctx_.cr);
+ dill_ctx_now_term(&dill_ctx_.now);
+}
+
+struct dill_ctx *dill_ctx_init(void) {
+ int rc = dill_ctx_now_init(&dill_ctx_.now);
+ dill_assert(rc == 0);
+ rc = dill_ctx_cr_init(&dill_ctx_.cr);
+ dill_assert(rc == 0);
+ rc = dill_ctx_handle_init(&dill_ctx_.handle);
+ dill_assert(rc == 0);
+ rc = dill_ctx_stack_init(&dill_ctx_.stack);
+ dill_assert(rc == 0);
+ rc = dill_ctx_pollset_init(&dill_ctx_.pollset);
+ dill_assert(rc == 0);
+ rc = dill_ctx_fd_init(&dill_ctx_.fd);
+ dill_assert(rc == 0);
+ rc = atexit(dill_ctx_atexit);
+ dill_assert(rc == 0);
+ dill_ctx_.initialized = 1;
+ return &dill_ctx_;
+}
+
+#else
+
+#include
+
+/* Determine whether current thread is the main thread. */
+#if defined __linux__
+#define _GNU_SOURCE
+#include
+#include
+static int dill_ismain() {
+ return syscall(SYS_gettid) == getpid();
+}
+#elif defined __OpenBSD__ || defined __FreeBSD__ || \
+ defined __APPLE__ || defined __DragonFly__
+#if defined __FreeBSD__ || defined __OpenBSD__
+#include
+#endif
+static int dill_ismain() {
+ return pthread_main_np();
+}
+#elif defined __NetBSD__
+#include
+static int dill_ismain() {
+ return _lwp_self() == 1;
+}
+#elif defined __sun
+static int dill_ismain() {
+ return pthread_self() == 1;
+}
+#else
+#error "Cannot determine which thread is the main thread."
+#endif
+
+#if defined __GNUC__ && !defined DILL_THREAD_FALLBACK
+
+__thread struct dill_ctx dill_ctx_ = {0};
+
+static pthread_key_t dill_key;
+static pthread_once_t dill_keyonce = PTHREAD_ONCE_INIT;
+static void *dill_main = NULL;
+
+static void dill_ctx_term(void *ptr) {
+ struct dill_ctx *ctx = ptr;
+ dill_ctx_fd_term(&ctx->fd);
+ dill_ctx_pollset_term(&ctx->pollset);
+ dill_ctx_stack_term(&ctx->stack);
+ dill_ctx_handle_term(&ctx->handle);
+ dill_ctx_cr_term(&ctx->cr);
+ dill_ctx_now_term(&ctx->now);
+ if(dill_ismain()) dill_main = NULL;
+}
+
+static void dill_ctx_atexit(void) {
+ if(dill_main) dill_ctx_term(dill_main);
+}
+
+static void dill_makekey(void) {
+ int rc = pthread_key_create(&dill_key, dill_ctx_term);
+ dill_assert(!rc);
+}
+
+struct dill_ctx *dill_ctx_init(void) {
+ int rc = dill_ctx_now_init(&dill_ctx_.now);
+ dill_assert(rc == 0);
+ rc = dill_ctx_cr_init(&dill_ctx_.cr);
+ dill_assert(rc == 0);
+ rc = dill_ctx_handle_init(&dill_ctx_.handle);
+ dill_assert(rc == 0);
+ rc = dill_ctx_stack_init(&dill_ctx_.stack);
+ dill_assert(rc == 0);
+ rc = dill_ctx_pollset_init(&dill_ctx_.pollset);
+ dill_assert(rc == 0);
+ rc = dill_ctx_fd_init(&dill_ctx_.fd);
+ dill_assert(rc == 0);
+ rc = pthread_once(&dill_keyonce, dill_makekey);
+ dill_assert(rc == 0);
+ if(dill_ismain()) {
+ dill_main = &dill_ctx_;
+ rc = atexit(dill_ctx_atexit);
+ dill_assert(rc == 0);
+ }
+ rc = pthread_setspecific(dill_key, &dill_ctx_);
+ dill_assert(rc == 0);
+ dill_ctx_.initialized = 1;
+ return &dill_ctx_;
+}
+
+#else
+
+static pthread_key_t dill_key;
+static pthread_once_t dill_keyonce = PTHREAD_ONCE_INIT;
+static void *dill_main = NULL;
+
+static void dill_ctx_term(void *ptr) {
+ struct dill_ctx *ctx = ptr;
+ dill_ctx_fd_term(&ctx->fd);
+ dill_ctx_pollset_term(&ctx->pollset);
+ dill_ctx_stack_term(&ctx->stack);
+ dill_ctx_handle_term(&ctx->handle);
+ dill_ctx_cr_term(&ctx->cr);
+ dill_ctx_now_term(&ctx->now);
+ free(ctx);
+ if(dill_ismain()) dill_main = NULL;
+}
+
+static void dill_ctx_atexit(void) {
+ if(dill_main) dill_ctx_term(dill_main);
+}
+
+static void dill_makekey(void) {
+ int rc = pthread_key_create(&dill_key, dill_ctx_term);
+ dill_assert(!rc);
+}
+
+struct dill_ctx *dill_getctx_(void) {
+ int rc = pthread_once(&dill_keyonce, dill_makekey);
+ dill_assert(rc == 0);
+ struct dill_ctx *ctx = pthread_getspecific(dill_key);
+ if(dill_fast(ctx)) return ctx;
+ ctx = malloc(sizeof(struct dill_ctx));
+ dill_assert(ctx);
+ rc = dill_ctx_now_init(&ctx->now);
+ dill_assert(rc == 0);
+ rc = dill_ctx_cr_init(&ctx->cr);
+ dill_assert(rc == 0);
+ rc = dill_ctx_handle_init(&ctx->handle);
+ dill_assert(rc == 0);
+ rc = dill_ctx_stack_init(&ctx->stack);
+ dill_assert(rc == 0);
+ rc = dill_ctx_pollset_init(&ctx->pollset);
+ dill_assert(rc == 0);
+ rc = dill_ctx_fd_init(&ctx->fd);
+ dill_assert(rc == 0);
+ if(dill_ismain()) {
+ dill_main = ctx;
+ rc = atexit(dill_ctx_atexit);
+ dill_assert(rc == 0);
+ }
+ rc = pthread_setspecific(dill_key, ctx);
+ dill_assert(rc == 0);
+ return ctx;
+}
+
+#endif
+
+#endif
+
diff --git a/vendor/sustrik/libdill/ctx.h b/vendor/sustrik/libdill/ctx.h
new file mode 100644
index 0000000..b6dbb65
--- /dev/null
+++ b/vendor/sustrik/libdill/ctx.h
@@ -0,0 +1,67 @@
+/*
+
+ Copyright (c) 2016 Tai Chi Minh Ralph Eastwood
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#ifndef DILL_CTX_INCLUDED
+#define DILL_CTX_INCLUDED
+
+#include "cr.h"
+#include "fd.h"
+#include "handle.h"
+#include "now.h"
+#include "pollset.h"
+#include "stack.h"
+
+struct dill_ctx {
+#if !defined DILL_THREAD_FALLBACK
+ int initialized;
+#endif
+ struct dill_ctx_now now;
+ struct dill_ctx_cr cr;
+ struct dill_ctx_handle handle;
+ struct dill_ctx_stack stack;
+ struct dill_ctx_pollset pollset;
+ struct dill_ctx_fd fd;
+};
+
+struct dill_ctx *dill_ctx_init(void);
+
+#if !defined DILL_THREADS
+
+extern struct dill_ctx dill_ctx_;
+#define dill_getctx \
+ (dill_fast(dill_ctx_.initialized) ? &dill_ctx_ : dill_ctx_init())
+
+#elif defined __GNUC__ && !defined DILL_THREAD_FALLBACK
+
+extern __thread struct dill_ctx dill_ctx_;
+#define dill_getctx \
+ (dill_fast(dill_ctx_.initialized) ? &dill_ctx_ : dill_ctx_init())
+
+#else
+
+struct dill_ctx *dill_getctx_(void);
+#define dill_getctx (dill_getctx_())
+
+#endif
+
+#endif
+
diff --git a/vendor/sustrik/libdill/epoll.c.inc b/vendor/sustrik/libdill/epoll.c.inc
new file mode 100644
index 0000000..c925125
--- /dev/null
+++ b/vendor/sustrik/libdill/epoll.c.inc
@@ -0,0 +1,270 @@
+/*
+
+ Copyright (c) 2016 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#include
+#include
+#include
+#include
+#include
+
+#include "cr.h"
+#include "list.h"
+#include "pollset.h"
+#include "utils.h"
+#include "ctx.h"
+
+#define DILL_ENDLIST 0xffffffff
+
+#define DILL_EPOLLSETSIZE 128
+
+/* One of these is associated with each file descriptor. */
+struct dill_fdinfo {
+ /* A coroutines waiting to read from the fd or NULL. */
+ struct dill_fdclause *in;
+ /* A coroutines waiting to write to the fd or NULL. */
+ struct dill_fdclause *out;
+ /* Cached current state of epollset. */
+ uint32_t currevs;
+ /* 1-based index, 0 stands for "not part of the list", DILL_ENDLIST
+ stands for "no more elements in the list. */
+ uint32_t next;
+ /* 1 if the file descriptor is cached. 0 otherwise. */
+ unsigned int cached : 1;
+};
+
+int dill_ctx_pollset_init(struct dill_ctx_pollset *ctx) {
+ int err;
+ /* Allocate one info per fd. */
+ ctx->nfdinfos = dill_maxfds();
+ ctx->fdinfos = calloc(ctx->nfdinfos, sizeof(struct dill_fdinfo));
+ if(dill_slow(!ctx->fdinfos)) {err = ENOMEM; goto error1;}
+ /* Changelist is empty. */
+ ctx->changelist = DILL_ENDLIST;
+ /* Create the kernel-side pollset. */
+ ctx->efd = epoll_create(1);
+ if(dill_slow(ctx->efd < 0)) {err = errno; goto error2;}
+ return 0;
+error2:
+ free(ctx->fdinfos);
+ ctx->fdinfos = NULL;
+error1:
+ errno = err;
+ return -1;
+}
+
+void dill_ctx_pollset_term(struct dill_ctx_pollset *ctx) {
+ int rc = close(ctx->efd);
+ dill_assert(rc == 0);
+ free(ctx->fdinfos);
+}
+
+static void dill_fdcancelin(struct dill_clause *cl) {
+ struct dill_fdinfo *fdinfo =
+ dill_cont(cl, struct dill_fdclause, cl)->fdinfo;
+ fdinfo->in = NULL;
+ if(!fdinfo->next) {
+ struct dill_ctx_pollset *ctx = &dill_getctx->pollset;
+ fdinfo->next = ctx->changelist;
+ ctx->changelist = fdinfo - ctx->fdinfos + 1;
+ }
+}
+
+static void dill_fdcancelout(struct dill_clause *cl) {
+ struct dill_fdinfo *fdinfo =
+ dill_cont(cl, struct dill_fdclause, cl)->fdinfo;
+ fdinfo->out = NULL;
+ if(!fdinfo->next) {
+ struct dill_ctx_pollset *ctx = &dill_getctx->pollset;
+ fdinfo->next = ctx->changelist;
+ ctx->changelist = fdinfo - ctx->fdinfos + 1;
+ }
+}
+
+int dill_pollset_in(struct dill_fdclause *fdcl, int id, int fd) {
+ struct dill_ctx_pollset *ctx = &dill_getctx->pollset;
+ if(dill_slow(fd < 0 || fd >= ctx->nfdinfos)) {errno = EBADF; return -1;}
+ struct dill_fdinfo *fdi = &ctx->fdinfos[fd];
+ /* If not yet cached, check whether the fd exists and if it does,
+ add it to the pollset. */
+ if(dill_slow(!fdi->cached)) {
+ struct epoll_event ev;
+#ifdef DILL_VALGRIND
+ memset(&ev.data, 0, sizeof(ev.data)); //Keep Valgrind happy
+#endif
+ ev.data.fd = fd;
+ ev.events = EPOLLIN;
+ int rc = epoll_ctl(ctx->efd, EPOLL_CTL_ADD, fd, &ev);
+ if(dill_slow(rc < 0)) {
+ if(errno == ELOOP || errno == EPERM) {errno = ENOTSUP; return -1;}
+ return -1;
+ }
+ fdi->in = NULL;
+ fdi->out = NULL;
+ fdi->currevs = EPOLLIN;
+ fdi->next = 0;
+ fdi->cached = 1;
+ }
+ if(dill_slow(fdi->in)) {errno = EBUSY; return -1;}
+ /* If the fd is not yet in the pollset, add it there. */
+ else if(!fdi->next) {
+ fdi->next = ctx->changelist;
+ ctx->changelist = fd + 1;
+ }
+ fdcl->fdinfo = fdi;
+ fdi->in = fdcl;
+ dill_waitfor(&fdcl->cl, id, dill_fdcancelin);
+ return 0;
+}
+
+int dill_pollset_out(struct dill_fdclause *fdcl, int id, int fd) {
+ struct dill_ctx_pollset *ctx = &dill_getctx->pollset;
+ if(dill_slow(fd < 0 || fd >= ctx->nfdinfos)) {errno = EBADF; return -1;}
+ struct dill_fdinfo *fdi = &ctx->fdinfos[fd];
+ /* If not yet cached, check whether the fd exists and if it does,
+ add it to pollset. */
+ if(dill_slow(!fdi->cached)) {
+ struct epoll_event ev;
+#ifdef DILL_VALGRIND
+ memset(&ev.data, 0, sizeof(ev.data)); //Keep Valgrind happy
+#endif
+ ev.data.fd = fd;
+ ev.events = EPOLLOUT;
+ int rc = epoll_ctl(ctx->efd, EPOLL_CTL_ADD, fd, &ev);
+ if(dill_slow(rc < 0)) {
+ if(errno == ELOOP || errno == EPERM) {errno = ENOTSUP; return -1;}
+ return -1;
+ }
+ fdi->in = NULL;
+ fdi->out = NULL;
+ fdi->currevs = EPOLLOUT;
+ fdi->next = 0;
+ fdi->cached = 1;
+ }
+ if(dill_slow(fdi->out)) {errno = EBUSY; return -1;}
+ /* If the fd is not yet in the pollset, add it there. */
+ else if(!fdi->next) {
+ fdi->next = ctx->changelist;
+ ctx->changelist = fd + 1;
+ }
+ fdcl->fdinfo = fdi;
+ fdi->out = fdcl;
+ dill_waitfor(&fdcl->cl, id, dill_fdcancelout);
+ return 0;
+}
+
+int dill_pollset_clean(int fd) {
+ struct dill_ctx_pollset *ctx = &dill_getctx->pollset;
+ struct dill_fdinfo *fdi = &ctx->fdinfos[fd];
+ if(!fdi->cached) return 0;
+ /* We cannot clean an fd that someone is waiting for. */
+ if(dill_slow(fdi->in || fdi->out)) {errno = EBUSY; return -1;}
+ /* Remove the file descriptor from the pollset if it is still there. */
+ if(fdi->currevs) {
+ struct epoll_event ev;
+#ifdef DILL_VALGRIND
+ memset(&ev.data, 0, sizeof(ev.data)); //Keep Valgrind happy
+#endif
+ ev.data.fd = fd;
+ ev.events = 0;
+ int rc = epoll_ctl(ctx->efd, EPOLL_CTL_DEL, fd, &ev);
+ dill_assert(rc == 0 || errno == ENOENT);
+ fdi->currevs = 0;
+ }
+ /* If needed, remove the fd from the changelist. */
+ if(fdi->next) {
+ uint32_t *pidx = &ctx->changelist;
+ while(1) {
+ dill_assert(*pidx != 0 && *pidx != DILL_ENDLIST);
+ if(*pidx - 1 == fd) break;
+ pidx = &ctx->fdinfos[*pidx - 1].next;
+ }
+ *pidx = fdi->next;
+ fdi->next = 0;
+ }
+ /* Mark the fd as not used. */
+ fdi->cached = 0;
+ return 0;
+}
+
+int dill_pollset_poll(int timeout) {
+ struct dill_ctx_pollset *ctx = &dill_getctx->pollset;
+ /* Apply any changes to the pollset.
+ TODO: Use epoll_ctl_batch once available. */
+ while(ctx->changelist != DILL_ENDLIST) {
+ int fd = ctx->changelist - 1;
+ struct dill_fdinfo *fdi = &ctx->fdinfos[fd];
+ struct epoll_event ev;
+ ev.data.u64 = 0; //Keep Valgrind happy
+ ev.data.fd = fd;
+ ev.events = 0;
+ if(fdi->in)
+ ev.events |= EPOLLIN;
+ if(fdi->out)
+ ev.events |= EPOLLOUT;
+ if(fdi->currevs != ev.events) {
+ int op;
+ if(!ev.events)
+ op = EPOLL_CTL_DEL;
+ else if(!fdi->currevs)
+ op = EPOLL_CTL_ADD;
+ else
+ op = EPOLL_CTL_MOD;
+ fdi->currevs = ev.events;
+ int rc = epoll_ctl(ctx->efd, op, fd, &ev);
+ dill_assert(rc == 0);
+ }
+ ctx->changelist = fdi->next;
+ fdi->next = 0;
+ }
+ /* Wait for events. */
+ struct epoll_event evs[DILL_EPOLLSETSIZE];
+ int numevs = epoll_wait(ctx->efd, evs, DILL_EPOLLSETSIZE, timeout);
+ if(numevs < 0 && errno == EINTR) return -1;
+ dill_assert(numevs >= 0);
+ /* Fire file descriptor events. */
+ int i;
+ for(i = 0; i != numevs; ++i) {
+ int fd = evs[i].data.fd;
+ struct dill_fdinfo *fdi = &ctx->fdinfos[fd];
+ /* Resume blocked coroutines. */
+ if(fdi->in && (evs[i].events & (EPOLLIN | EPOLLERR | EPOLLHUP))) {
+ dill_trigger(&fdi->in->cl, 0);
+ /* Remove the fd from the pollset if needed. */
+ if(!fdi->in && !fdi->next) {
+ fdi->next = ctx->changelist;
+ ctx->changelist = fd + 1;
+ }
+ }
+ if(fdi->out && (evs[i].events & (EPOLLOUT | EPOLLERR | EPOLLHUP))) {
+ dill_trigger(&fdi->out->cl, 0);
+ /* Remove the fd from the pollset if needed. */
+ if(!fdi->out && !fdi->next) {
+ fdi->next = ctx->changelist;
+ ctx->changelist = fd + 1;
+ }
+ }
+ }
+ /* Return 0 on timeout or 1 if at least one coroutine was resumed. */
+ return numevs > 0 ? 1 : 0;
+}
diff --git a/vendor/sustrik/libdill/epoll.h.inc b/vendor/sustrik/libdill/epoll.h.inc
new file mode 100644
index 0000000..892cc54
--- /dev/null
+++ b/vendor/sustrik/libdill/epoll.h.inc
@@ -0,0 +1,46 @@
+/*
+
+ Copyright (c) 2016 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#ifndef DILL_EPOLL_INCLUDED
+#define DILL_EPOLL_INCLUDED
+
+#include
+
+#include "cr.h"
+#include "list.h"
+
+struct dill_fdinfo;
+
+struct dill_fdclause {
+ struct dill_clause cl;
+ struct dill_fdinfo *fdinfo;
+
+};
+
+struct dill_ctx_pollset {
+ int efd;
+ struct dill_fdinfo *fdinfos;
+ size_t nfdinfos;
+ uint32_t changelist;
+};
+
+#endif
diff --git a/vendor/sustrik/libdill/fd.c b/vendor/sustrik/libdill/fd.c
new file mode 100644
index 0000000..4e3bad1
--- /dev/null
+++ b/vendor/sustrik/libdill/fd.c
@@ -0,0 +1,392 @@
+/*
+
+ Copyright (c) 2017 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#include
+#include
+#include
+#include
+
+#include "ctx.h"
+#include "fd.h"
+#include "iol.h"
+#include "utils.h"
+
+#define DILL_FD_CACHESIZE 32
+#define DILL_FD_BUFSIZE 1984
+
+#if defined MSG_NOSIGNAL
+#define FD_NOSIGNAL MSG_NOSIGNAL
+#else
+#define FD_NOSIGNAL 0
+#endif
+
+int dill_ctx_fd_init(struct dill_ctx_fd *ctx) {
+ ctx->count = 0;
+ dill_slist_init(&ctx->cache);
+ return 0;
+}
+
+void dill_ctx_fd_term(struct dill_ctx_fd *ctx) {
+ while(1) {
+ struct dill_slist *it = dill_slist_pop(&ctx->cache);
+ if(it == &ctx->cache) break;
+ free(it);
+ }
+}
+
+static uint8_t *dill_fd_allocbuf(void) {
+ struct dill_ctx_fd *ctx = &dill_getctx->fd;
+ struct dill_slist *it = dill_slist_pop(&ctx->cache);
+ if(dill_fast(it != &ctx->cache)) {
+ ctx->count--;
+ return (uint8_t*)it;
+ }
+ uint8_t *p = malloc(DILL_FD_BUFSIZE);
+ if(dill_slow(!p)) {errno = ENOMEM; return NULL;}
+ return p;
+}
+
+static void dill_fd_freebuf(uint8_t *buf) {
+ struct dill_ctx_fd *ctx = &dill_getctx->fd;
+ if(ctx->count >= DILL_FD_CACHESIZE) {
+ free(buf);
+ return;
+ }
+ dill_slist_push(&ctx->cache, (struct dill_slist*)buf);
+ ctx->count++;
+}
+
+void dill_fd_initrxbuf(struct dill_fd_rxbuf *rxbuf) {
+ dill_assert(rxbuf);
+ rxbuf->len = 0;
+ rxbuf->pos = 0;
+ rxbuf->buf = NULL;
+}
+
+void dill_fd_termrxbuf(struct dill_fd_rxbuf *rxbuf) {
+ if(rxbuf->buf) dill_fd_freebuf(rxbuf->buf);
+}
+
+int dill_fd_unblock(int s) {
+ /* Switch to non-blocking mode. */
+ int opt = fcntl(s, F_GETFL, 0);
+ if (opt == -1)
+ opt = 0;
+ int rc = fcntl(s, F_SETFL, opt | O_NONBLOCK);
+ dill_assert(rc == 0);
+ /* Allow re-using the same local address rapidly. */
+ opt = 1;
+ rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt));
+ dill_assert(rc == 0);
+ /* If possible, prevent SIGPIPE signal when writing to the connection
+ already closed by the peer. */
+#ifdef SO_NOSIGPIPE
+ opt = 1;
+ rc = setsockopt (s, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof (opt));
+ dill_assert (rc == 0 || errno == EINVAL);
+#endif
+ return 0;
+}
+
+int dill_fd_connect(int s, const struct sockaddr *addr, socklen_t addrlen,
+ int64_t deadline) {
+ /* Initiate connect. */
+ int rc = connect(s, addr, addrlen);
+ if(rc == 0) return 0;
+ if(dill_slow(errno != EINPROGRESS)) return -1;
+ /* Connect is in progress. Let's wait till it's done. */
+ rc = dill_fdout(s, deadline);
+ if(dill_slow(rc == -1)) return -1;
+ /* Retrieve the error from the socket, if any. */
+ int err = 0;
+ socklen_t errsz = sizeof(err);
+ rc = getsockopt(s, SOL_SOCKET, SO_ERROR, (void*)&err, &errsz);
+ if(dill_slow(rc != 0)) return -1;
+ if(dill_slow(err != 0)) {errno = err; return -1;}
+ return 0;
+}
+
+int dill_fd_accept(int s, struct sockaddr *addr, socklen_t *addrlen,
+ int64_t deadline) {
+ int as;
+ while(1) {
+ /* Try to accept new connection synchronously. */
+ as = accept(s, addr, addrlen);
+ if(dill_fast(as >= 0))
+ break;
+ /* If connection was aborted by the peer grab the next one. */
+ if(dill_slow(errno == ECONNABORTED)) continue;
+ /* Propagate other errors to the caller. */
+ if(dill_slow(errno != EAGAIN && errno != EWOULDBLOCK)) return -1;
+ /* Operation is in progress. Wait till new connection is available. */
+ int rc = dill_fdin(s, deadline);
+ if(dill_slow(rc < 0)) return -1;
+ }
+ int rc = dill_fd_unblock(as);
+ dill_assert(rc == 0);
+ return as;
+}
+
+int dill_fd_send(int s, struct dill_iolist *first, struct dill_iolist *last,
+ int64_t deadline) {
+ /* Make a local iovec array. */
+ /* TODO: This is dangerous, it may cause stack overflow.
+ There should probably be a on-heap per-socket buffer for that. */
+ size_t niov;
+ int rc = dill_iolcheck(first, last, &niov, NULL);
+ if(dill_slow(rc < 0)) return -1;
+ struct iovec iov[niov];
+ dill_ioltoiov(first, iov);
+ /* Message header will act as an iterator in the following loop. */
+ struct msghdr hdr;
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.msg_iov = iov;
+ hdr.msg_iovlen = niov;
+ /* It is very likely that at least one byte can be sent. Therefore,
+ to improve efficiency, try to send and resort to fdout() only after
+ send failed. */
+ while(1) {
+ while(hdr.msg_iovlen && !hdr.msg_iov[0].iov_len) {
+ hdr.msg_iov++;
+ hdr.msg_iovlen--;
+ }
+ if(!hdr.msg_iovlen) return 0;
+ ssize_t sz = sendmsg(s, &hdr, FD_NOSIGNAL);
+ dill_assert(sz != 0);
+ if(sz < 0) {
+ if(dill_slow(errno != EWOULDBLOCK && errno != EAGAIN)) {
+ if(errno == EPIPE) errno = ECONNRESET;
+ return -1;
+ }
+ sz = 0;
+ }
+ /* Adjust the iovec array so that it doesn't contain data
+ that was already sent. */
+ while(sz) {
+ struct iovec *head = &hdr.msg_iov[0];
+ if(head->iov_len > sz) {
+ head->iov_base += sz;
+ head->iov_len -= sz;
+ break;
+ }
+ sz -= head->iov_len;
+ hdr.msg_iov++;
+ hdr.msg_iovlen--;
+ if(!hdr.msg_iovlen) return 0;
+ }
+ /* Wait till more data can be sent. */
+ int rc = dill_fdout(s, deadline);
+ if(dill_slow(rc < 0)) return -1;
+ }
+}
+
+/* Same as dill_fd_recv() but with no rx buffering. */
+static int dill_fd_recv_(int s, struct dill_iolist *first,
+ struct dill_iolist *last, int64_t deadline) {
+ /* Make a local iovec array. */
+ /* TODO: This is dangerous, it may cause stack overflow.
+ There should probably be a on-heap per-socket buffer for that. */
+ size_t niov;
+ int rc = dill_iolcheck(first, last, &niov, NULL);
+ if(dill_slow(rc < 0)) return -1;
+ struct iovec iov[niov];
+ dill_ioltoiov(first, iov);
+ /* Message header will act as an iterator in the following loop. */
+ struct msghdr hdr;
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.msg_iov = iov;
+ hdr.msg_iovlen = niov;
+ while(1) {
+ ssize_t sz = recvmsg(s, &hdr, 0);
+ if(dill_slow(sz == 0)) {errno = EPIPE; return -1;}
+ if(sz < 0) {
+ if(dill_slow(errno != EWOULDBLOCK && errno != EAGAIN)) {
+ if(errno == EPIPE) errno = ECONNRESET;
+ return -1;
+ }
+ sz = 0;
+ }
+ /* Adjust the iovec array so that it doesn't contain buffers
+ that ware already filled in. */
+ while(sz) {
+ struct iovec *head = &hdr.msg_iov[0];
+ if(head->iov_len > sz) {
+ head->iov_base += sz;
+ head->iov_len -= sz;
+ break;
+ }
+ sz -= head->iov_len;
+ hdr.msg_iov++;
+ hdr.msg_iovlen--;
+ if(!hdr.msg_iovlen) return 0;
+ }
+ /* Wait for more data. */
+ int rc = dill_fdin(s, deadline);
+ if(dill_slow(rc < 0)) return -1;
+ }
+}
+
+/* Skip len bytes. If len is negative skip until error occurs. */
+static int dill_fd_skip(int s, ssize_t len, int64_t deadline) {
+ uint8_t buf[512];
+ while(len) {
+ size_t to_recv = len < 0 || len > sizeof(buf) ? sizeof(buf) : len;
+ struct dill_iolist iol = {buf, to_recv, NULL, 0};
+ int rc = dill_fd_recv_(s, &iol, &iol, deadline);
+ if(dill_slow(rc < 0)) return -1;
+ if(len >= 0) len -= to_recv;
+ }
+ return 0;
+}
+
+/* Copy data from rxbuf to one dill_iolist structure.
+ Returns number of bytes copied. */
+static size_t dill_fd_copy(struct dill_fd_rxbuf *rxbuf,
+ struct dill_iolist *iol) {
+ size_t rmn = rxbuf->len - rxbuf->pos;
+ if(!rmn) return 0;
+ if(rmn < iol->iol_len) {
+ if(dill_fast(iol->iol_base))
+ memcpy(iol->iol_base, rxbuf->buf + rxbuf->pos, rmn);
+ rxbuf->len = 0;
+ rxbuf->pos = 0;
+ dill_fd_freebuf(rxbuf->buf);
+ rxbuf->buf = NULL;
+ return rmn;
+ }
+ if(dill_fast(iol->iol_base))
+ memcpy(iol->iol_base, rxbuf->buf + rxbuf->pos, iol->iol_len);
+ rxbuf->pos += iol->iol_len;
+ return iol->iol_len;
+}
+
+int dill_fd_recv(int s, struct dill_fd_rxbuf *rxbuf, struct dill_iolist *first,
+ struct dill_iolist *last, int64_t deadline) {
+ /* Skip all data until error occurs. */
+ if(dill_slow(!first && !last)) return dill_fd_skip(s, -1, deadline);
+ /* Fill in data from the rxbuf. */
+ size_t sz;
+ while(1) {
+ sz = dill_fd_copy(rxbuf, first);
+ if(sz < first->iol_len) break;
+ first = first->iol_next;
+ if(!first) return 0;
+ }
+ /* Copy the current iolist element so that we can modify it without
+ changing the original list. */
+ struct dill_iolist curr;
+ curr.iol_base = first->iol_base ? first->iol_base + sz : NULL;
+ curr.iol_len = first->iol_len - sz;
+ curr.iol_next = first->iol_next;
+ curr.iol_rsvd = 0;
+ /* Find out how much data is still missing. */
+ size_t miss = 0;
+ struct dill_iolist *it = &curr;
+ while(it) {
+ miss += it->iol_len;
+ it = it->iol_next;
+ }
+ /* If requested amount of data is larger than rx buffer avoid the copy
+ and read it directly into user's buffer. */
+ if(miss > DILL_FD_BUFSIZE)
+ return dill_fd_recv_(s, &curr, curr.iol_next ? last : &curr, deadline);
+ /* If small amount of data is requested use rx buffer. */
+ while(1) {
+ /* Read as much data as possible to the buffer to avoid extra
+ syscalls. Do the speculative recv() first to avoid extra
+ polling. Do fdin() only after recv() fails to get data. */
+ if(!rxbuf->buf) {
+ rxbuf->buf = dill_fd_allocbuf();
+ if(dill_slow(!rxbuf->buf)) return -1;
+ }
+ ssize_t sz = recv(s, rxbuf->buf, DILL_FD_BUFSIZE, 0);
+ if(dill_slow(sz == 0)) {errno = EPIPE; return -1;}
+ if(sz < 0) {
+ if(dill_slow(errno != EWOULDBLOCK && errno != EAGAIN)) {
+ if(errno == EPIPE) errno = ECONNRESET;
+ return -1;
+ }
+ sz = 0;
+ }
+ rxbuf->len = sz;
+ rxbuf->pos = 0;
+ /* Copy the data from rxbuffer to the iolist. */
+ while(1) {
+ sz = dill_fd_copy(rxbuf, &curr);
+ if(sz < curr.iol_len) break;
+ if(!curr.iol_next) return 0;
+ curr = *curr.iol_next;
+ }
+ if(curr.iol_base) curr.iol_base += sz;
+ curr.iol_len -= sz;
+ /* Wait for more data. */
+ int rc = dill_fdin(s, deadline);
+ if(dill_slow(rc < 0)) return -1;
+ }
+}
+
+void dill_fd_close(int s) {
+ int rc = dill_fdclean(s);
+ dill_assert(rc == 0);
+ /* Discard any pending outbound data. If SO_LINGER option cannot
+ be set, never mind and continue anyway. */
+ struct linger lng;
+ lng.l_onoff=1;
+ lng.l_linger=0;
+ setsockopt(s, SOL_SOCKET, SO_LINGER, (void*)&lng, sizeof(lng));
+ /* We are not checking the error here. close() has inconsistent behaviour
+ and leaking a file descriptor is better than crashing the entire
+ program. */
+ close(s);
+}
+
+int dill_fd_own(int s) {
+ int n = dup(s);
+ if(dill_slow(n < 0)) return -1;
+ dill_fd_close(s);
+ return n;
+}
+
+int dill_fd_check(int s, int type, int family1, int family2, int listening) {
+ /* Check type. E.g. SOCK_STREAM vs. SOCK_DGRAM. */
+ int val;
+ socklen_t valsz = sizeof(val);
+ int rc = getsockopt(s, SOL_SOCKET, SO_TYPE, &val, &valsz);
+ if(dill_slow(rc < 0)) return -1;
+ if(dill_slow(val != type)) {errno = EINVAL; return -1;}
+ /* Check whether the socket is in listening mode.
+ Returns ENOPROTOOPT on OSX. */
+ rc = getsockopt(s, SOL_SOCKET, SO_ACCEPTCONN, &val, &valsz);
+ if(dill_slow(rc < 0 && errno != ENOPROTOOPT)) return -1;
+ if(dill_slow(rc >= 0 && val != listening)) {errno = EINVAL; return -1;}
+ /* Check family. E.g. AF_INET vs. AF_UNIX. */
+ struct sockaddr_storage ss;
+ socklen_t sssz = sizeof(ss);
+ rc = getsockname(s, (struct sockaddr*)&ss, &sssz);
+ if(dill_slow(rc < 0)) return -1;
+ if(dill_slow(ss.ss_family != family1 && ss.ss_family != family2)) {
+ errno = EINVAL; return -1;}
+ return 0;
+}
+
diff --git a/vendor/sustrik/libdill/fd.h b/vendor/sustrik/libdill/fd.h
new file mode 100644
index 0000000..58f65ea
--- /dev/null
+++ b/vendor/sustrik/libdill/fd.h
@@ -0,0 +1,89 @@
+/*
+
+ Copyright (c) 2016 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#ifndef DILL_FD_INCLUDED
+#define DILL_FD_INCLUDED
+
+#include
+#include
+#include
+
+#define DILL_DISABLE_RAW_NAMES
+#include "libdill.h"
+#include "slist.h"
+
+struct dill_ctx_fd {
+ int count;
+ struct dill_slist cache;
+};
+
+int dill_ctx_fd_init(struct dill_ctx_fd *ctx);
+void dill_ctx_fd_term(struct dill_ctx_fd *ctx);
+
+struct dill_fd_rxbuf {
+ size_t len;
+ size_t pos;
+ uint8_t *buf;
+};
+
+void dill_fd_initrxbuf(
+ struct dill_fd_rxbuf *rxbuf);
+void dill_fd_termrxbuf(
+ struct dill_fd_rxbuf *rxbuf);
+int dill_fd_unblock(
+ int s);
+int dill_fd_connect(
+ int s,
+ const struct sockaddr *addr,
+ socklen_t addrlen,
+ int64_t deadline);
+int dill_fd_accept(
+ int s,
+ struct sockaddr *addr,
+ socklen_t *addrlen,
+ int64_t deadline);
+int dill_fd_send(
+ int s,
+ struct dill_iolist *first,
+ struct dill_iolist *last,
+ int64_t deadline);
+int dill_fd_recv(
+ int s,
+ struct dill_fd_rxbuf *rxbuf,
+ struct dill_iolist *first,
+ struct dill_iolist *last,
+ int64_t deadline);
+void dill_fd_close(
+ int s);
+int dill_fd_own(
+ int s);
+int dill_fd_check(
+ int s,
+ int type,
+ int family1,
+ int family2,
+ int listening);
+
+#endif
+
diff --git a/vendor/sustrik/libdill/handle.c b/vendor/sustrik/libdill/handle.c
new file mode 100644
index 0000000..3d658ca
--- /dev/null
+++ b/vendor/sustrik/libdill/handle.c
@@ -0,0 +1,168 @@
+/*
+
+ Copyright (c) 2018 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "cr.h"
+#include "handle.h"
+#include "utils.h"
+#include "ctx.h"
+
+#define DILL_DISABLE_RAW_NAMES
+#include "libdillimpl.h"
+
+struct dill_handle {
+ /* Table of virtual functions. */
+ struct dill_hvfs *vfs;
+ /* Index of the next handle in the linked list of unused handles. -1 means
+ 'the end of the list'. -2 means 'handle is in use'. */
+ int next;
+ /* Cache the last call to hquery. */
+ const void *type;
+ void *ptr;
+};
+
+#define DILL_CHECKHANDLE(h, err) \
+ if(dill_slow((h) < 0 || (h) >= ctx->nhandles ||\
+ ctx->handles[(h)].next != -2)) {\
+ errno = EBADF; return (err);}\
+ struct dill_handle *hndl = &ctx->handles[(h)];
+
+int dill_ctx_handle_init(struct dill_ctx_handle *ctx) {
+ ctx->handles = NULL;
+ ctx->nhandles = 0;
+ ctx->nused = 0;
+ ctx->first = -1;
+ ctx->last = -1;
+ return 0;
+}
+
+void dill_ctx_handle_term(struct dill_ctx_handle *ctx) {
+ free(ctx->handles);
+}
+
+int dill_hmake(struct dill_hvfs *vfs) {
+ struct dill_ctx_handle *ctx = &dill_getctx->handle;
+ if(dill_slow(!vfs || !vfs->query || !vfs->close)) {
+ errno = EINVAL; return -1;}
+ /* Returns ECANCELED if shutting down. */
+ int rc = dill_canblock();
+ if(dill_slow(rc < 0)) return -1;
+ /* If there's no space for the new handle, expand the array.
+ Keep at least 8 handles unused so that there's at least some rotation
+ of handle numbers even if operating close to the current limit. */
+ if(dill_slow(ctx->nhandles - ctx->nused <= 8)) {
+ /* Start with 256 handles, double the size when needed. */
+ int sz = ctx->nhandles ? ctx->nhandles * 2 : 256;
+ struct dill_handle *hndls =
+ realloc(ctx->handles, sz * sizeof(struct dill_handle));
+ if(dill_slow(!hndls)) {errno = ENOMEM; return -1;}
+ /* Add newly allocated handles to the list of unused handles. */
+ int i;
+ for(i = ctx->nhandles; i != sz - 1; ++i)
+ hndls[i].next = i + 1;
+ hndls[sz - 1].next = -1;
+ ctx->first = ctx->nhandles;
+ ctx->last = sz - 1;
+ /* Adjust the array. */
+ ctx->handles = hndls;
+ ctx->nhandles = sz;
+ }
+ /* Return first handle from the list of unused handles. */
+ int h = ctx->first;
+ ctx->first = ctx->handles[h].next;
+ if(dill_slow(ctx->first) == -1) ctx->last = -1;
+ ctx->handles[h].vfs = vfs;
+ ctx->handles[h].next = -2;
+ ctx->handles[h].type = NULL;
+ ctx->handles[h].ptr = NULL;
+ ctx->nused++;
+ return h;
+}
+
+int dill_hown(int h) {
+ struct dill_ctx_handle *ctx = &dill_getctx->handle;
+ DILL_CHECKHANDLE(h, -1);
+ /* Create a new handle for the same object. */
+ int res = dill_hmake(hndl->vfs);
+ if(dill_slow(res < 0)) {
+ int rc = dill_hclose(h);
+ dill_assert(rc == 0);
+ return -1;
+ }
+ /* In case handle array was reallocated we have to recompute the pointer. */
+ hndl = &ctx->handles[h];
+ /* Return a handle to the shared pool. */
+ hndl->ptr = NULL;
+ hndl->next = -1;
+ if(ctx->first == -1) ctx->first = h;
+ else ctx->handles[ctx->last].next = h;
+ ctx->last = h;
+ ctx->nused--;
+ return res;
+}
+
+void *dill_hquery(int h, const void *type) {
+ struct dill_ctx_handle *ctx = &dill_getctx->handle;
+ DILL_CHECKHANDLE(h, NULL);
+ /* Try and use the cached pointer first; otherwise do the expensive virtual
+ call.*/
+ if(dill_fast(hndl->ptr != NULL && hndl->type == type))
+ return hndl->ptr;
+ else {
+ void *ptr = hndl->vfs->query(hndl->vfs, type);
+ if(dill_slow(!ptr)) return NULL;
+ /* Update cache. */
+ hndl->type = type;
+ hndl->ptr = ptr;
+ return ptr;
+ }
+}
+
+int dill_hclose(int h) {
+ struct dill_ctx_handle *ctx = &dill_getctx->handle;
+ DILL_CHECKHANDLE(h, -1);
+ /* This will guarantee that blocking functions cannot be called anywhere
+ inside the context of the close. */
+ int old = dill_no_blocking(1);
+ /* Send the stop signal to the handle. */
+ hndl->vfs->close(hndl->vfs);
+ /* Restore the previous state. */
+ dill_no_blocking(old);
+ /* Mark the cache as invalid. */
+ hndl->ptr = NULL;
+ /* Return a handle to the shared pool. */
+ hndl->next = -1;
+ if(ctx->first == -1) ctx->first = h;
+ else ctx->handles[ctx->last].next = h;
+ ctx->last = h;
+ ctx->nused--;
+ return 0;
+}
+
diff --git a/vendor/sustrik/libdill/handle.h b/vendor/sustrik/libdill/handle.h
new file mode 100644
index 0000000..f576ce7
--- /dev/null
+++ b/vendor/sustrik/libdill/handle.h
@@ -0,0 +1,43 @@
+/*
+
+ Copyright (c) 2018 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#ifndef DILL_HANDLE_INCLUDED
+#define DILL_HANDLE_INCLUDED
+
+struct dill_handle;
+
+struct dill_ctx_handle {
+ /* Array of handles. The size of the array is stored in 'nall'. */
+ struct dill_handle *handles;
+ int nhandles;
+ /* Number of allocated handles. */
+ int nused;
+ /* Points to the first and last item in the list of unused handles. */
+ int first;
+ int last;
+};
+
+int dill_ctx_handle_init(struct dill_ctx_handle *ctx);
+void dill_ctx_handle_term(struct dill_ctx_handle *ctx);
+
+#endif
+
diff --git a/vendor/sustrik/libdill/iol.c b/vendor/sustrik/libdill/iol.c
new file mode 100644
index 0000000..0593583
--- /dev/null
+++ b/vendor/sustrik/libdill/iol.c
@@ -0,0 +1,108 @@
+/*
+
+ Copyright (c) 2018 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#include
+
+#include "iol.h"
+#include "utils.h"
+
+int dill_iolcheck(struct iolist *first, struct iolist *last,
+ size_t *nbufs, size_t *nbytes) {
+ if(!first && !last) {
+ if(nbufs) *nbufs = 0;
+ if(nbytes) *nbytes = 0;
+ return 0;
+ }
+ if(dill_slow(!first || !last || last->iol_next)) {
+ errno = EINVAL; return -1;}
+ size_t nbf = 0, nbt = 0, res = 0;
+ struct iolist *it;
+ for(it = first; it; it = it->iol_next) {
+ if(dill_slow(it->iol_rsvd || (!it->iol_next && it != last)))
+ goto error;
+ it->iol_rsvd = 1;
+ nbf++;
+ nbt += it->iol_len;
+ }
+ for(it = first; it; it = it->iol_next) it->iol_rsvd = 0;
+ if(nbufs) *nbufs = nbf;
+ if(nbytes) *nbytes = nbt;
+ return 0;
+error:;
+ struct iolist *it2;
+ for(it2 = first; it2 != it; it2 = it2->iol_next) it->iol_rsvd = 0;
+ errno = EINVAL;
+ return -1;
+}
+
+void dill_ioltoiov(struct iolist *first, struct iovec *iov) {
+ while(first) {
+ iov->iov_base = first->iol_base;
+ iov->iov_len = first->iol_len;
+ ++iov;
+ first = first->iol_next;
+ }
+}
+
+int dill_ioltrim(struct iolist *first, size_t n, struct iolist *result) {
+ while(n) {
+ if(!first) return -1;
+ if(first->iol_len >= n) break;
+ n -= first->iol_len;
+ first = first->iol_next;
+ }
+ result->iol_base = first->iol_base ? ((uint8_t*)first->iol_base) + n : NULL;
+ result->iol_len = first->iol_len - n;
+ result->iol_next = first->iol_next;
+ result->iol_rsvd = 0;
+ return 0;
+}
+
+int dill_iolto(const void *src, size_t srclen, struct iolist *first) {
+ const uint8_t *p = src;
+ while(1) {
+ if(!srclen) return 0;
+ if(!first) return -1;
+ if(first->iol_len >= srclen) break;
+ if(first->iol_base) memcpy(first->iol_base, p, first->iol_len);
+ p += first->iol_len;
+ srclen -= first->iol_len;
+ first = first->iol_next;
+ }
+ if(first->iol_base) memcpy(first->iol_base, p, srclen);
+ return 0;
+}
+
+int dill_iolfrom(void *dst, size_t dstlen, struct dill_iolist *first) {
+ uint8_t *p = dst;
+ while(first) {
+ if(dstlen < first->iol_len) return -1;
+ memcpy(p, first->iol_base, first->iol_len);
+ p += first->iol_len;
+ dstlen -= first->iol_len;
+ first = first->iol_next;
+ }
+ return 0;
+}
+
diff --git a/vendor/sustrik/libdill/iol.h b/vendor/sustrik/libdill/iol.h
new file mode 100644
index 0000000..c157e63
--- /dev/null
+++ b/vendor/sustrik/libdill/iol.h
@@ -0,0 +1,58 @@
+/*
+
+ Copyright (c) 2018 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#ifndef DILL_IOL_INCLUDED
+#define DILL_IOL_INCLUDED
+
+#include
+
+#include "libdill.h"
+
+/* Checks whether iolist is valid. Returns 0 in case of success or -1 in case
+ of error. Fills in number of buffers in the list and overall number of bytes
+ if requested. */
+int dill_iolcheck(struct dill_iolist *first, struct dill_iolist *last,
+ size_t *nbufs, size_t *nbytes);
+
+/* Copy the iolist into an iovec. Iovec must have at least as much elements
+ as the iolist, otherwise undefined behaviour ensues. The data buffers
+ as such are not affected by this operation .*/
+void dill_ioltoiov(struct dill_iolist *first, struct iovec *iov);
+
+/* Trims first n bytes from the iolist. Returns the trimmed iolist. Keeps the
+ original iolist unchanged. Returns 0 in the case of success, -1 is there
+ are less than N bytes in the original iolist. */
+int dill_ioltrim(struct dill_iolist *first, size_t n,
+ struct dill_iolist *result);
+
+/* Copies supplied bytes into the iolist. Returns 0 on success, -1 if bytes
+ won't fit into the iolist. */
+int dill_iolto(const void *src, size_t srclen, struct dill_iolist *first);
+
+/* Copies supplied bytes from the iolist. Returns 0 on success, -1 is bytes
+ won't fit into the buffer. */
+int dill_iolfrom(void *dst, size_t dstlen, struct dill_iolist *first);
+
+#endif
+
diff --git a/vendor/sustrik/libdill/ipc.c b/vendor/sustrik/libdill/ipc.c
new file mode 100644
index 0000000..60606c8
--- /dev/null
+++ b/vendor/sustrik/libdill/ipc.c
@@ -0,0 +1,504 @@
+/*
+
+ Copyright (c) 2017 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#include
+#include
+#include
+#include
+#include
+
+#define DILL_DISABLE_RAW_NAMES
+#include "libdillimpl.h"
+#include "fd.h"
+#include "utils.h"
+
+static int dill_ipc_resolve(const char *addr, struct sockaddr_un *su);
+
+dill_unique_id(dill_ipc_listener_type);
+dill_unique_id(dill_ipc_type);
+
+/******************************************************************************/
+/* UNIX connection socket */
+/******************************************************************************/
+
+static void *dill_ipc_hquery(struct dill_hvfs *hvfs, const void *type);
+static void dill_ipc_hclose(struct dill_hvfs *hvfs);
+static int dill_ipc_bsendl(struct dill_bsock_vfs *bvfs,
+ struct dill_iolist *first, struct dill_iolist *last, int64_t deadline);
+static int dill_ipc_brecvl(struct dill_bsock_vfs *bvfs,
+ struct dill_iolist *first, struct dill_iolist *last, int64_t deadline);
+
+struct dill_ipc_conn {
+ struct dill_hvfs hvfs;
+ struct dill_bsock_vfs bvfs;
+ int fd;
+ struct dill_fd_rxbuf rxbuf;
+ unsigned int busy : 1;
+ unsigned int indone : 1;
+ unsigned int outdone : 1;
+ unsigned int inerr : 1;
+ unsigned int outerr : 1;
+ unsigned int mem : 1;
+};
+
+DILL_CHECK_STORAGE(dill_ipc_conn, dill_ipc_storage)
+
+static int dill_ipc_makeconn(int fd, void *mem) {
+ /* Create the object. */
+ struct dill_ipc_conn *self = (struct dill_ipc_conn*)mem;
+ self->hvfs.query = dill_ipc_hquery;
+ self->hvfs.close = dill_ipc_hclose;
+ self->bvfs.bsendl = dill_ipc_bsendl;
+ self->bvfs.brecvl = dill_ipc_brecvl;
+ self->fd = fd;
+ dill_fd_initrxbuf(&self->rxbuf);
+ self->busy = 0;
+ self->indone = 0;
+ self->outdone = 0;
+ self->inerr = 0;
+ self->outerr = 0;
+ self->mem = 1;
+ /* Create the handle. */
+ return dill_hmake(&self->hvfs);
+}
+
+static void *dill_ipc_hquery(struct dill_hvfs *hvfs, const void *type) {
+ struct dill_ipc_conn *self = (struct dill_ipc_conn*)hvfs;
+ if(type == dill_bsock_type) return &self->bvfs;
+ if(type == dill_ipc_type) return self;
+ errno = ENOTSUP;
+ return NULL;
+}
+
+int dill_ipc_fromfd_mem(int fd, struct dill_ipc_storage *mem) {
+ int err;
+ if(dill_slow(!mem || fd < 0)) {err = EINVAL; goto error1;}
+ /* Make sure that the supplied file descriptor is of correct type. */
+ int rc = dill_fd_check(fd, SOCK_STREAM, AF_UNIX, -1, 0);
+ if(dill_slow(rc < 0)) {err = errno; goto error1;}
+ /* Take ownership of the file descriptor. */
+ fd = dill_fd_own(fd);
+ if(dill_slow(fd < 0)) {err = errno; goto error1;}
+ /* Set the socket to non-blocking mode */
+ rc = dill_fd_unblock(fd);
+ if(dill_slow(rc < 0)) {err = errno; goto error1;}
+ /* Create the handle */
+ int h = dill_ipc_makeconn(fd, mem);
+ if(dill_slow(h < 0)) {err = errno; goto error1;}
+ /* Return the handle */
+ return h;
+error1:
+ errno = err;
+ return -1;
+}
+
+int dill_icp_fromfd(int fd) {
+ int err;
+ struct dill_ipc_conn *obj = malloc(sizeof(struct dill_ipc_conn));
+ if(dill_slow(!obj)) {err = ENOMEM; goto error1;}
+ int s = dill_ipc_fromfd_mem(fd, (struct dill_ipc_storage*)obj);
+ if (dill_slow(s < 0)) {err = errno; goto error2;}
+ obj->mem = 0;
+ return s;
+error2:
+ free(obj);
+error1:
+ errno = err;
+ return -1;
+}
+
+int dill_ipc_connect_mem(const char *addr, struct dill_ipc_storage *mem,
+ int64_t deadline) {
+ int err;
+ if(dill_slow(!mem)) {err = EINVAL; goto error1;}
+ /* Create a UNIX address out of the address string. */
+ struct sockaddr_un su;
+ int rc = dill_ipc_resolve(addr, &su);
+ if(rc < 0) {err = errno; goto error1;}
+ /* Open a socket. */
+ int s = socket(AF_UNIX, SOCK_STREAM, 0);
+ if(dill_slow(s < 0)) {err = errno; goto error1;}
+ /* Set it to non-blocking mode. */
+ rc = dill_fd_unblock(s);
+ if(dill_slow(rc < 0)) {err = errno; goto error2;}
+ /* Connect to the remote endpoint. */
+ rc = dill_fd_connect(s, (struct sockaddr*)&su, sizeof(su), deadline);
+ if(dill_slow(rc < 0)) {err = errno; goto error2;}
+ /* Create the handle. */
+ int h = dill_ipc_makeconn(s, mem);
+ if(dill_slow(h < 0)) {err = errno; goto error2;}
+ return h;
+error2:
+ dill_fd_close(s);
+error1:
+ errno = err;
+ return -1;
+}
+
+int dill_ipc_connect(const char *addr, int64_t deadline) {
+ int err;
+ struct dill_ipc_conn *obj = malloc(sizeof(struct dill_ipc_conn));
+ if(dill_slow(!obj)) {err = ENOMEM; goto error1;}
+ int s = dill_ipc_connect_mem(addr, (struct dill_ipc_storage*)obj, deadline);
+ if(dill_slow(s < 0)) {err = errno; goto error2;}
+ obj->mem = 0;
+ return s;
+error2:
+ free(obj);
+error1:
+ errno = err;
+ return -1;
+}
+
+static int dill_ipc_bsendl(struct dill_bsock_vfs *bvfs,
+ struct dill_iolist *first, struct dill_iolist *last, int64_t deadline) {
+ struct dill_ipc_conn *self = dill_cont(bvfs, struct dill_ipc_conn, bvfs);
+ if(dill_slow(self->busy)) {errno = EBUSY; return -1;}
+ if(dill_slow(self->outdone)) {errno = EPIPE; return -1;}
+ if(dill_slow(self->outerr)) {errno = ECONNRESET; return -1;}
+ self->busy = 1;
+ ssize_t sz = dill_fd_send(self->fd, first, last, deadline);
+ self->busy = 0;
+ if(dill_fast(sz >= 0)) return sz;
+ self->outerr = 1;
+ return -1;
+}
+
+static int dill_ipc_brecvl(struct dill_bsock_vfs *bvfs,
+ struct dill_iolist *first, struct dill_iolist *last, int64_t deadline) {
+ struct dill_ipc_conn *self = dill_cont(bvfs, struct dill_ipc_conn, bvfs);
+ if(dill_slow(self->busy)) {errno = EBUSY; return -1;}
+ if(dill_slow(self->indone)) {errno = EPIPE; return -1;}
+ if(dill_slow(self->inerr)) {errno = ECONNRESET; return -1;}
+ self->busy = 1;
+ int rc = dill_fd_recv(self->fd, &self->rxbuf, first, last, deadline);
+ self->busy = 0;
+ if(dill_fast(rc == 0)) return 0;
+ if(errno == EPIPE) self->indone = 1;
+ else self->inerr = 1;
+ return -1;
+}
+
+int dill_ipc_done(int s, int64_t deadline) {
+ struct dill_ipc_conn *self = dill_hquery(s, dill_ipc_type);
+ if(dill_slow(!self)) return -1;
+ if(dill_slow(self->outdone)) {errno = EPIPE; return -1;}
+ if(dill_slow(self->outerr)) {errno = ECONNRESET; return -1;}
+ /* Shutdown is done asynchronously on kernel level.
+ No need to use the deadline. */
+ int rc = shutdown(self->fd, SHUT_WR);
+ if(dill_slow(rc < 0)) {
+ if(errno == ENOTCONN) {self->outerr = 1; errno = ECONNRESET; return -1;}
+ if(errno == ENOBUFS) {self->outerr = 1; errno = ENOMEM; return -1;}
+ dill_assert(0);
+ }
+ self->outdone = 1;
+ return 0;
+}
+
+int dill_ipc_close(int s, int64_t deadline) {
+ int err;
+ /* Listener socket needs no special treatment. */
+ if(dill_hquery(s, dill_ipc_listener_type)) {
+ return dill_hclose(s);
+ }
+ struct dill_ipc_conn *self = dill_hquery(s, dill_ipc_type);
+ if(dill_slow(!self)) return -1;
+ if(dill_slow(self->inerr || self->outerr)) {err = ECONNRESET; goto error;}
+ /* If not done already, flush the outbound data and start the terminal
+ handshake. */
+ if(!self->outdone) {
+ int rc = dill_ipc_done(s, deadline);
+ if(dill_slow(rc < 0)) {err = errno; goto error;}
+ }
+ /* Now we are going to read all the inbound data until we reach end of the
+ stream. That way we can be sure that the peer either received all our
+ data or consciously closed the connection without reading all of it. */
+ int rc = dill_ipc_brecvl(&self->bvfs, NULL, NULL, deadline);
+ dill_assert(rc < 0);
+ if(dill_slow(errno != EPIPE)) {err = errno; goto error;}
+ dill_ipc_hclose(&self->hvfs);
+ return 0;
+error:
+ dill_ipc_hclose(&self->hvfs);
+ errno = err;
+ return -1;
+}
+
+static void dill_ipc_hclose(struct dill_hvfs *hvfs) {
+ struct dill_ipc_conn *self = (struct dill_ipc_conn*)hvfs;
+ dill_fd_close(self->fd);
+ dill_fd_termrxbuf(&self->rxbuf);
+ if(!self->mem) free(self);
+}
+
+/******************************************************************************/
+/* UNIX listener socket */
+/******************************************************************************/
+
+static void *dill_ipc_listener_hquery(struct dill_hvfs *hvfs, const void *type);
+static void dill_ipc_listener_hclose(struct dill_hvfs *hvfs);
+
+struct dill_ipc_listener {
+ struct dill_hvfs hvfs;
+ int fd;
+ unsigned int mem : 1;
+};
+
+DILL_CHECK_STORAGE(dill_ipc_listener, dill_ipc_listener_storage)
+
+static void *dill_ipc_listener_hquery(struct dill_hvfs *hvfs,
+ const void *type) {
+ struct dill_ipc_listener *self = (struct dill_ipc_listener*)hvfs;
+ if(type == dill_ipc_listener_type) return self;
+ errno = ENOTSUP;
+ return NULL;
+}
+
+static int dill_ipc_makelistener(int fd,
+ struct dill_ipc_listener_storage *mem) {
+ /* Create the object. */
+ struct dill_ipc_listener *self = (struct dill_ipc_listener*)mem;
+ self->hvfs.query = dill_ipc_listener_hquery;
+ self->hvfs.close = dill_ipc_listener_hclose;
+ self->fd = fd;
+ self->mem = 1;
+ /* Create the handle. */
+ return dill_hmake(&self->hvfs);
+}
+
+int dill_ipc_listener_fromfd(int fd) {
+ int err;
+ struct dill_ipc_listener *obj = malloc(sizeof(struct dill_ipc_listener));
+ if(dill_slow(!obj)) {err = ENOMEM; goto error1;}
+ int s = dill_ipc_listener_fromfd_mem(fd,
+ (struct dill_ipc_listener_storage*)obj);
+ if (dill_slow(s < 0)) {err = errno; goto error2;}
+ obj->mem = 0;
+ return s;
+error2:
+ free(obj);
+error1:
+ errno = err;
+ return -1;
+}
+
+int dill_ipc_listener_fromfd_mem(int fd,
+ struct dill_ipc_listener_storage *mem) {
+ int err;
+ if(dill_slow(!mem || fd < 0)) {err = EINVAL; goto error1;}
+ /* Make sure that the supplied file descriptor is of correct type. */
+ int rc = dill_fd_check(fd, SOCK_STREAM, AF_UNIX, -1, 1);
+ if(dill_slow(rc < 0)) {err = errno; goto error1;}
+ /* Take ownership of the file descriptor. */
+ fd = dill_fd_own(fd);
+ if(dill_slow(fd < 0)) {err = errno; goto error1;}
+ /* Set the socket to non-blocking mode */
+ rc = dill_fd_unblock(fd);
+ if(dill_slow(rc < 0)) {err = errno; goto error1;}
+ /* Create the handle */
+ int h = dill_ipc_makelistener(fd, mem);
+ if(dill_slow(h < 0)) {err = errno; goto error1;}
+ /* Return the handle */
+ return h;
+error1:
+ errno = err;
+ return -1;
+}
+
+int dill_ipc_listen_mem(const char *addr, int backlog,
+ struct dill_ipc_listener_storage *mem) {
+ int err;
+ /* Create a UNIX address out of the address string. */
+ struct sockaddr_un su;
+ int rc = dill_ipc_resolve(addr, &su);
+ if(rc < 0) {err = errno; goto error1;}
+ /* Open the listening socket. */
+ int s = socket(AF_UNIX, SOCK_STREAM, 0);
+ if(dill_slow(s < 0)) {err = errno; goto error1;}
+ /* Set it to non-blocking mode. */
+ rc = dill_fd_unblock(s);
+ if(dill_slow(rc < 0)) {err = errno; goto error2;}
+ /* Start listening for incoming connections. */
+ rc = bind(s, (struct sockaddr*)&su, sizeof(su));
+ if(dill_slow(rc < 0)) {err = errno; goto error2;}
+ rc = listen(s, backlog);
+ if(dill_slow(rc < 0)) {err = errno; goto error2;}
+ int h = dill_ipc_makelistener(s, mem);
+ if(dill_slow(h < 0)) {err = errno; goto error2;}
+ return h;
+error2:
+ close(s);
+error1:
+ errno = err;
+ return -1;
+}
+
+int dill_ipc_listen(const char *addr, int backlog) {
+ int err;
+ struct dill_ipc_listener *obj = malloc(sizeof(struct dill_ipc_listener));
+ if(dill_slow(!obj)) {err = ENOMEM; goto error1;}
+ int ls = dill_ipc_listen_mem(addr, backlog,
+ (struct dill_ipc_listener_storage*)obj);
+ if(dill_slow(ls < 0)) {err = errno; goto error2;}
+ obj->mem = 0;
+ return ls;
+error2:
+ free(obj);
+error1:
+ errno = err;
+ return -1;
+}
+
+int dill_ipc_accept_mem(int s, struct dill_ipc_storage *mem, int64_t deadline) {
+ int err;
+ if(dill_slow(!mem)) {err = EINVAL; goto error1;}
+ /* Retrieve the listener object. */
+ struct dill_ipc_listener *lst = dill_hquery(s, dill_ipc_listener_type);
+ if(dill_slow(!lst)) {err = errno; goto error1;}
+ /* Try to get new connection in a non-blocking way. */
+ int as = dill_fd_accept(lst->fd, NULL, NULL, deadline);
+ if(dill_slow(as < 0)) {err = errno; goto error1;}
+ /* Set it to non-blocking mode. */
+ int rc = dill_fd_unblock(as);
+ if(dill_slow(rc < 0)) {err = errno; goto error2;}
+ /* Create the handle. */
+ int h = dill_ipc_makeconn(as, (struct dill_ipc_conn*)mem);
+ if(dill_slow(h < 0)) {err = errno; goto error2;}
+ return h;
+error2:
+ dill_fd_close(as);
+error1:
+ errno = err;
+ return -1;
+}
+
+int dill_ipc_accept(int s, int64_t deadline) {
+ int err;
+ struct dill_ipc_conn *obj = malloc(sizeof(struct dill_ipc_conn));
+ if(dill_slow(!obj)) {err = ENOMEM; goto error1;}
+ int as = dill_ipc_accept_mem(s, (struct dill_ipc_storage*)obj, deadline);
+ if(dill_slow(as < 0)) {err = errno; goto error2;}
+ obj->mem = 0;
+ return as;
+error2:
+ free(obj);
+error1:
+ errno = err;
+ return -1;
+}
+
+static void dill_ipc_listener_hclose(struct dill_hvfs *hvfs) {
+ struct dill_ipc_listener *self = (struct dill_ipc_listener*)hvfs;
+ dill_fd_close(self->fd);
+ if(!self->mem) free(self);
+}
+
+/******************************************************************************/
+/* UNIX pair */
+/******************************************************************************/
+
+int dill_ipc_pair_mem(struct dill_ipc_pair_storage *mem, int s[2]) {
+ int err;
+ if(dill_slow(!mem)) {err = EINVAL; goto error1;}
+ /* Create the pair. */
+ int fds[2];
+ int rc = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
+ if(rc < 0) {err = errno; goto error1;}
+ /* Set the sockets to non-blocking mode. */
+ rc = dill_fd_unblock(fds[0]);
+ if(dill_slow(rc < 0)) {err = errno; goto error3;}
+ rc = dill_fd_unblock(fds[1]);
+ if(dill_slow(rc < 0)) {err = errno; goto error3;}
+ /* Create the handles. */
+ struct dill_ipc_conn *conns = (struct dill_ipc_conn*)mem;
+ s[0] = dill_ipc_makeconn(fds[0], &conns[0]);
+ if(dill_slow(s[0] < 0)) {err = errno; goto error3;}
+ s[1] = dill_ipc_makeconn(fds[1], &conns[1]);
+ if(dill_slow(s[1] < 0)) {err = errno; goto error4;}
+ return 0;
+error4:
+ rc = dill_hclose(s[0]);
+ goto error2;
+error3:
+ dill_fd_close(fds[0]);
+error2:
+ dill_fd_close(fds[1]);
+error1:
+ errno = err;
+ return -1;
+}
+
+int dill_ipc_pair(int s[2]) {
+ int err;
+ /* Create the pair. */
+ int fds[2];
+ int rc = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
+ if(rc < 0) {err = errno; goto error1;}
+ /* Set the sockets to non-blocking mode. */
+ rc = dill_fd_unblock(fds[0]);
+ if(dill_slow(rc < 0)) {err = errno; goto error3;}
+ rc = dill_fd_unblock(fds[1]);
+ if(dill_slow(rc < 0)) {err = errno; goto error3;}
+ /* Allocate the memory. */
+ struct dill_ipc_conn *conn0 = malloc(sizeof(struct dill_ipc_conn));
+ if(dill_slow(!conn0)) {err = ENOMEM; goto error3;}
+ struct dill_ipc_conn *conn1 = malloc(sizeof(struct dill_ipc_conn));
+ if(dill_slow(!conn1)) {err = ENOMEM; goto error4;}
+ /* Create the handles. */
+ s[0] = dill_ipc_makeconn(fds[0], conn0);
+ if(dill_slow(s[0] < 0)) {err = errno; goto error5;}
+ conn0->mem = 0;
+ s[1] = dill_ipc_makeconn(fds[1], conn1);
+ if(dill_slow(s[1] < 0)) {err = errno; goto error6;}
+ conn1->mem = 0;
+ return 0;
+error6:
+ rc = dill_hclose(s[0]);
+ goto error2;
+error5:
+ free(conn1);
+error4:
+ free(conn0);
+error3:
+ dill_fd_close(fds[0]);
+error2:
+ dill_fd_close(fds[1]);
+error1:
+ errno = err;
+ return -1;
+}
+
+/******************************************************************************/
+/* Helpers */
+/******************************************************************************/
+
+static int dill_ipc_resolve(const char *addr, struct sockaddr_un *su) {
+ dill_assert(su);
+ if(strlen(addr) >= sizeof(su->sun_path)) {errno = ENAMETOOLONG; return -1;}
+ su->sun_family = AF_UNIX;
+ strncpy(su->sun_path, addr, sizeof(su->sun_path));
+ return 0;
+}
+
diff --git a/vendor/sustrik/libdill/kqueue.c.inc b/vendor/sustrik/libdill/kqueue.c.inc
new file mode 100644
index 0000000..92a0674
--- /dev/null
+++ b/vendor/sustrik/libdill/kqueue.c.inc
@@ -0,0 +1,307 @@
+/*
+
+ Copyright (c) 2016 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "cr.h"
+#include "list.h"
+#include "pollset.h"
+#include "utils.h"
+#include "ctx.h"
+
+#define DILL_ENDLIST 0xffffffff
+
+#define DILL_CHNGSSIZE 128
+#define DILL_EVSSIZE 128
+
+#define FDW_IN 1
+#define FDW_OUT 2
+
+struct dill_fdinfo {
+ struct dill_fdclause *in;
+ struct dill_fdclause *out;
+ uint16_t currevs;
+ uint16_t firing;
+ /* 1-based index, 0 stands for "not part of the list", DILL_ENDLIST
+ stands for "no more elements in the list. */
+ uint32_t next;
+ /* 1 if the file descriptor is cached. 0 otherwise. */
+ unsigned int cached : 1;
+};
+
+int dill_ctx_pollset_init(struct dill_ctx_pollset *ctx) {
+ int err;
+ /* Allocate one info per fd. */
+ ctx->nfdinfos = dill_maxfds();
+ ctx->fdinfos = calloc(ctx->nfdinfos, sizeof(struct dill_fdinfo));
+ if(dill_slow(!ctx->fdinfos)) {err = ENOMEM; goto error1;}
+ /* Changelist is empty. */
+ ctx->changelist = DILL_ENDLIST;
+ /* Create kernel-side pollset. */
+ ctx->kfd = kqueue();
+ if(dill_slow(ctx->kfd < 0)) {err = errno; goto error2;}
+ return 0;
+error2:
+ free(ctx->fdinfos);
+ ctx->fdinfos = NULL;
+error1:
+ errno = err;
+ return -1;
+}
+
+void dill_ctx_pollset_term(struct dill_ctx_pollset *ctx) {
+ /* Kqueue documentation says that a kqueue descriptor won't
+ survive a fork. However, implementations seem to disagree.
+ On FreeBSD the following function succeeds. On OSX it returns
+ EACCESS. Therefore we ignore the return value. */
+ close(ctx->kfd);
+ free(ctx->fdinfos);
+}
+
+static void dill_fdcancelin(struct dill_clause *cl) {
+ struct dill_fdinfo *fdinfo =
+ dill_cont(cl, struct dill_fdclause, cl)->fdinfo;
+ fdinfo->in = NULL;
+ if(!fdinfo->next) {
+ struct dill_ctx_pollset *ctx = &dill_getctx->pollset;
+ fdinfo->next = ctx->changelist;
+ ctx->changelist = fdinfo - ctx->fdinfos + 1;
+ }
+}
+
+static void dill_fdcancelout(struct dill_clause *cl) {
+ struct dill_fdinfo *fdinfo =
+ dill_cont(cl, struct dill_fdclause, cl)->fdinfo;
+ fdinfo->out = NULL;
+ if(!fdinfo->next) {
+ struct dill_ctx_pollset *ctx = &dill_getctx->pollset;
+ fdinfo->next = ctx->changelist;
+ ctx->changelist = fdinfo - ctx->fdinfos + 1;
+ }
+}
+
+int dill_pollset_in(struct dill_fdclause *fdcl, int id, int fd) {
+ struct dill_ctx_pollset *ctx = &dill_getctx->pollset;
+ if(dill_slow(fd < 0 || fd >= ctx->nfdinfos)) {errno = EBADF; return -1;}
+ struct dill_fdinfo *fdi = &ctx->fdinfos[fd];
+ /* If not yet cached, check whether fd exists and if so add it
+ to pollset. */
+ if(dill_slow(!fdi->cached)) {
+ struct kevent ev;
+ EV_SET(&ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0);
+ int rc = kevent(ctx->kfd, &ev, 1, NULL, 0, NULL);
+ if(dill_slow(rc < 0 && errno == EBADF)) return -1;
+ dill_assert(rc >= 0);
+ fdi->in = NULL;
+ fdi->out = NULL;
+ fdi->currevs = FDW_IN;
+ fdi->firing = 0;
+ fdi->next = 0;
+ fdi->cached = 1;
+ }
+ if(dill_slow(fdi->in)) {errno = EBUSY; return -1;}
+ /* If fd is not yet in the pollset, add it there. */
+ else if(!fdi->next) {
+ fdi->next = ctx->changelist;
+ ctx->changelist = fd + 1;
+ }
+ fdcl->fdinfo = fdi;
+ fdi->in = fdcl;
+ dill_waitfor(&fdcl->cl, id, dill_fdcancelin);
+ return 0;
+}
+
+int dill_pollset_out(struct dill_fdclause *fdcl, int id, int fd) {
+ struct dill_ctx_pollset *ctx = &dill_getctx->pollset;
+ if(dill_slow(fd < 0 || fd >= ctx->nfdinfos)) {errno = EBADF; return -1;}
+ struct dill_fdinfo *fdi = &ctx->fdinfos[fd];
+ /* If not yet cached, check whether the fd exists and if it does,
+ add it to the pollset. */
+ if(dill_slow(!fdi->cached)) {
+ struct kevent ev;
+ EV_SET(&ev, fd, EVFILT_WRITE, EV_ADD, 0, 0, 0);
+ int rc = kevent(ctx->kfd, &ev, 1, NULL, 0, NULL);
+ if(dill_slow(rc < 0 && errno == EBADF)) return -1;
+ dill_assert(rc >= 0);
+ fdi->in = NULL;
+ fdi->out = NULL;
+ fdi->currevs = FDW_OUT;
+ fdi->firing = 0;
+ fdi->next = 0;
+ fdi->cached = 1;
+ }
+ if(dill_slow(fdi->out)) {errno = EBUSY; return -1;}
+ /* If the fd is not yet in the pollset, add it there. */
+ else if(!fdi->next) {
+ fdi->next = ctx->changelist;
+ ctx->changelist = fd + 1;
+ }
+ fdcl->fdinfo = fdi;
+ fdi->out = fdcl;
+ dill_waitfor(&fdcl->cl, id, dill_fdcancelout);
+ return 0;
+}
+
+int dill_pollset_clean(int fd) {
+ struct dill_ctx_pollset *ctx = &dill_getctx->pollset;
+ struct dill_fdinfo *fdi = &ctx->fdinfos[fd];
+ if(!fdi->cached) return 0;
+ /* We cannot clean an fd that someone is waiting for. */
+ if(dill_slow(fdi->in || fdi->out)) {errno = EBUSY; return -1;}
+ /* Remove the file descriptor from the pollset if it is still there. */
+ int nevs = 0;
+ struct kevent evs[2];
+ if(fdi->currevs & FDW_IN) {
+ EV_SET(&evs[nevs], fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
+ ++nevs;
+ }
+ if(fdi->currevs & FDW_OUT) {
+ EV_SET(&evs[nevs], fd, EVFILT_WRITE, EV_DELETE, 0, 0, 0);
+ ++nevs;
+ }
+ if(nevs) {
+ int rc = kevent(ctx->kfd, evs, nevs, NULL, 0, NULL);
+ dill_assert(rc != -1);
+ }
+ fdi->currevs = 0;
+ /* If needed, remove the fd from the changelist. */
+ if(fdi->next) {
+ uint32_t *pidx = &ctx->changelist;
+ while(1) {
+ dill_assert(*pidx != 0 && *pidx != DILL_ENDLIST);
+ if(*pidx - 1 == fd) break;
+ pidx = &ctx->fdinfos[*pidx - 1].next;
+ }
+ *pidx = fdi->next;
+ fdi->next = 0;
+ }
+ /* Mark the fd as not used. */
+ fdi->cached = 0;
+ return 0;
+}
+
+int dill_pollset_poll(int timeout) {
+ struct dill_ctx_pollset *ctx = &dill_getctx->pollset;
+ /* Apply any changes to the pollset. */
+ struct kevent chngs[DILL_CHNGSSIZE];
+ int nchngs = 0;
+ while(ctx->changelist != DILL_ENDLIST) {
+ /* Flush the changes to the pollset even if there is one empty entry
+ left in the changeset. That way, we make sure that both in & out
+ associated with the next file descriptor can be filled in if we
+ choose not to flush the changes yet. */
+ if(nchngs >= DILL_CHNGSSIZE - 1) {
+ int rc = kevent(ctx->kfd, chngs, nchngs, NULL, 0, NULL);
+ dill_assert(rc != -1);
+ nchngs = 0;
+ }
+ int fd = ctx->changelist - 1;
+ struct dill_fdinfo *fdi = &ctx->fdinfos[fd];
+ if(fdi->in) {
+ if(!(fdi->currevs & FDW_IN)) {
+ EV_SET(&chngs[nchngs], fd, EVFILT_READ, EV_ADD, 0, 0, 0);
+ fdi->currevs |= FDW_IN;
+ ++nchngs;
+ }
+ }
+ else {
+ if(fdi->currevs & FDW_IN) {
+ EV_SET(&chngs[nchngs], fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
+ fdi->currevs &= ~FDW_IN;
+ ++nchngs;
+ }
+ }
+ if(fdi->out) {
+ if(!(fdi->currevs & FDW_OUT)) {
+ EV_SET(&chngs[nchngs], fd, EVFILT_WRITE, EV_ADD, 0, 0, 0);
+ fdi->currevs |= FDW_OUT;
+ ++nchngs;
+ }
+ }
+ else {
+ if(fdi->currevs & FDW_OUT) {
+ EV_SET(&chngs[nchngs], fd, EVFILT_WRITE, EV_DELETE, 0, 0, 0);
+ fdi->currevs &= ~FDW_OUT;
+ ++nchngs;
+ }
+ }
+ fdi->firing = 0;
+ ctx->changelist = fdi->next;
+ fdi->next = 0;
+ }
+ /* Wait for events. */
+ struct kevent evs[DILL_EVSSIZE];
+ struct timespec ts;
+ if(timeout >= 0) {
+ ts.tv_sec = timeout / 1000;
+ ts.tv_nsec = (((long)timeout) % 1000) * 1000000;
+ }
+ int nevs = kevent(ctx->kfd, chngs, nchngs, evs, DILL_EVSSIZE,
+ timeout < 0 ? NULL : &ts);
+ if(nevs < 0 && errno == EINTR) return -1;
+ dill_assert(nevs >= 0);
+ /* Join events on file descriptor basis.
+ Put all the firing fds into the changelist. */
+ int i;
+ for(i = 0; i != nevs; ++i) {
+ dill_assert(evs[i].flags != EV_ERROR);
+ int fd = (int)evs[i].ident;
+ struct dill_fdinfo *fdi = &ctx->fdinfos[fd];
+ /* Add firing event to the result list. */
+ if(evs[i].flags == EV_EOF)
+ fdi->firing |= (FDW_IN | FDW_OUT);
+ else {
+ if(evs[i].filter == EVFILT_READ)
+ fdi->firing |= FDW_IN;
+ if(evs[i].filter == EVFILT_WRITE)
+ fdi->firing |= FDW_OUT;
+ }
+ if(!fdi->next) {
+ fdi->next = ctx->changelist;
+ ctx->changelist = fd + 1;
+ }
+ }
+ /* Resume blocked coroutines. */
+ uint32_t chl = ctx->changelist;
+ while(chl != DILL_ENDLIST) {
+ int fd = chl - 1;
+ struct dill_fdinfo *fdi = &ctx->fdinfos[fd];
+ if(fdi->in && (fdi->firing & FDW_IN))
+ dill_trigger(&fdi->in->cl, 0);
+ if(fdi->out && (fdi->firing & FDW_OUT))
+ dill_trigger(&fdi->out->cl, 0);
+ fdi->firing = 0;
+ chl = fdi->next;
+ }
+ /* Return 0 on timeout or 1 if at least one coroutine was resumed. */
+ return nevs > 0 ? 1 : 0;
+}
+
diff --git a/vendor/sustrik/libdill/kqueue.h.inc b/vendor/sustrik/libdill/kqueue.h.inc
new file mode 100644
index 0000000..869b0b2
--- /dev/null
+++ b/vendor/sustrik/libdill/kqueue.h.inc
@@ -0,0 +1,43 @@
+/*
+
+ Copyright (c) 2016 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#ifndef DILL_KQUEUE_INCLUDED
+#define DILL_KQUEUE_INCLUDED
+
+#include "cr.h"
+#include "list.h"
+
+struct dill_fdinfo;
+
+struct dill_fdclause {
+ struct dill_clause cl;
+ struct dill_fdinfo *fdinfo;
+};
+
+struct dill_ctx_pollset {
+ int kfd;
+ int nfdinfos;
+ struct dill_fdinfo *fdinfos;
+ uint32_t changelist;
+};
+
+#endif
diff --git a/vendor/sustrik/libdill/libdill.c b/vendor/sustrik/libdill/libdill.c
new file mode 100644
index 0000000..3760535
--- /dev/null
+++ b/vendor/sustrik/libdill/libdill.c
@@ -0,0 +1,83 @@
+/*
+
+ Copyright (c) 2017 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#include
+
+#include "cr.h"
+#include "pollset.h"
+#include "utils.h"
+
+#define DILL_DISABLE_RAW_NAMES
+#include "libdillimpl.h"
+
+int dill_msleep(int64_t deadline) {
+ /* Return ECANCELED if shutting down. */
+ int rc = dill_canblock();
+ if(dill_slow(rc < 0)) return -1;
+ /* Actual waiting. */
+ struct dill_tmclause tmcl;
+ dill_timer(&tmcl, 1, deadline);
+ int id = dill_wait();
+ if(dill_slow(id < 0)) return -1;
+ return 0;
+}
+
+int dill_fdin(int fd, int64_t deadline) {
+ /* Return ECANCELED if shutting down. */
+ int rc = dill_canblock();
+ if(dill_slow(rc < 0)) return -1;
+ /* Start waiting for the fd. */
+ struct dill_fdclause fdcl;
+ rc = dill_pollset_in(&fdcl, 1, fd);
+ if(dill_slow(rc < 0)) return -1;
+ /* Optionally, start waiting for a timer. */
+ struct dill_tmclause tmcl;
+ dill_timer(&tmcl, 2, deadline);
+ /* Block. */
+ int id = dill_wait();
+ if(dill_slow(id < 0)) return -1;
+ if(dill_slow(id == 2)) {errno = ETIMEDOUT; return -1;}
+ return 0;
+}
+
+int dill_fdout(int fd, int64_t deadline) {
+ /* Return ECANCELED if shutting down. */
+ int rc = dill_canblock();
+ if(dill_slow(rc < 0)) return -1;
+ /* Start waiting for the fd. */
+ struct dill_fdclause fdcl;
+ rc = dill_pollset_out(&fdcl, 1, fd);
+ if(dill_slow(rc < 0)) return -1;
+ /* Optionally, start waiting for a timer. */
+ struct dill_tmclause tmcl;
+ dill_timer(&tmcl, 2, deadline);
+ /* Block. */
+ int id = dill_wait();
+ if(dill_slow(id < 0)) return -1;
+ if(dill_slow(id == 2)) {errno = ETIMEDOUT; return -1;}
+ return 0;
+}
+
+int dill_fdclean(int fd) {
+ return dill_pollset_clean(fd);
+}
+
diff --git a/vendor/sustrik/libdill/libdill.h b/vendor/sustrik/libdill/libdill.h
new file mode 100644
index 0000000..593fa17
--- /dev/null
+++ b/vendor/sustrik/libdill/libdill.h
@@ -0,0 +1,1008 @@
+/*
+
+ Copyright (c) 2017 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#ifndef LIBDILL_H_INCLUDED
+#define LIBDILL_H_INCLUDED
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#if defined __linux__
+#include
+#endif
+
+/******************************************************************************/
+/* ABI versioning support */
+/******************************************************************************/
+
+/* Don't change this unless you know exactly what you're doing and have */
+/* read and understood the following documents: */
+/* www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html */
+/* www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html */
+
+/* The current interface version. */
+#define DILL_VERSION_CURRENT 23
+
+/* The latest revision of the current interface. */
+#define DILL_VERSION_REVISION 0
+
+/* How many past interface versions are still supported. */
+#define DILL_VERSION_AGE 2
+
+/******************************************************************************/
+/* Symbol visibility */
+/******************************************************************************/
+
+#if !defined __GNUC__ && !defined __clang__
+#error "Unsupported compiler!"
+#endif
+
+#if DILL_NO_EXPORTS
+#define DILL_EXPORT
+#else
+#define DILL_EXPORT __attribute__ ((visibility("default")))
+#endif
+
+/* Old versions of GCC don't support visibility attribute. */
+#if defined __GNUC__ && __GNUC__ < 4
+#undef DILL_EXPORT
+#define DILL_EXPORT
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/******************************************************************************/
+/* Helpers */
+/******************************************************************************/
+
+DILL_EXPORT int dill_fdclean(int fd);
+DILL_EXPORT int dill_fdin(int fd, int64_t deadline);
+DILL_EXPORT int dill_fdout(int fd, int64_t deadline);
+DILL_EXPORT int64_t dill_now(void);
+DILL_EXPORT int dill_msleep(int64_t deadline);
+
+#if !defined DILL_DISABLE_RAW_NAMES
+#define fdclean dill_fdclean
+#define fdin dill_fdin
+#define fdout dill_fdout
+#define now dill_now
+#define msleep dill_msleep
+#endif
+
+/******************************************************************************/
+/* Handles */
+/******************************************************************************/
+
+DILL_EXPORT int dill_hown(int h);
+DILL_EXPORT int dill_hclose(int h);
+
+#if !defined DILL_DISABLE_RAW_NAMES
+#define hown dill_hown
+#define hclose dill_hclose
+#endif
+
+/******************************************************************************/
+/* Coroutines */
+/******************************************************************************/
+
+#define dill_coroutine __attribute__((noinline))
+
+DILL_EXPORT extern volatile void *dill_unoptimisable;
+
+DILL_EXPORT __attribute__((noinline)) int dill_prologue(sigjmp_buf **ctx,
+ void **ptr, size_t len, int bndl, const char *file, int line);
+DILL_EXPORT __attribute__((noinline)) void dill_epilogue(void);
+
+/* The following macros use alloca(sizeof(size_t)) because clang
+ doesn't support alloca with size zero. */
+
+/* This assembly setjmp/longjmp mechanism is in the same order as glibc and
+ musl, but glibc implements pointer mangling, which is hard to support.
+ This should be binary-compatible with musl, though. */
+
+/* Stack-switching on X86-64. */
+#if defined(__x86_64__) && !defined DILL_ARCH_FALLBACK
+#define dill_setjmp(ctx) ({\
+ int ret;\
+ asm("lea LJMPRET%=(%%rip), %%rcx\n\t"\
+ "xor %%rax, %%rax\n\t"\
+ "mov %%rbx, (%%rdx)\n\t"\
+ "mov %%rbp, 8(%%rdx)\n\t"\
+ "mov %%r12, 16(%%rdx)\n\t"\
+ "mov %%r13, 24(%%rdx)\n\t"\
+ "mov %%r14, 32(%%rdx)\n\t"\
+ "mov %%r15, 40(%%rdx)\n\t"\
+ "mov %%rsp, 48(%%rdx)\n\t"\
+ "mov %%rcx, 56(%%rdx)\n\t"\
+ "LJMPRET%=:\n\t"\
+ : "=a" (ret)\
+ : "d" (ctx)\
+ : "memory", "rcx", "rsi", "rdi", "r8", "r9", "r10", "r11", "cc");\
+ ret;\
+})
+#define dill_longjmp(ctx) \
+ asm("movq 56(%%rdx), %%rcx\n\t"\
+ "movq 48(%%rdx), %%rsp\n\t"\
+ "movq 40(%%rdx), %%r15\n\t"\
+ "movq 32(%%rdx), %%r14\n\t"\
+ "movq 24(%%rdx), %%r13\n\t"\
+ "movq 16(%%rdx), %%r12\n\t"\
+ "movq 8(%%rdx), %%rbp\n\t"\
+ "movq (%%rdx), %%rbx\n\t"\
+ ".cfi_def_cfa %%rdx, 0 \n\t"\
+ ".cfi_offset %%rbx, 0 \n\t"\
+ ".cfi_offset %%rbp, 8 \n\t"\
+ ".cfi_offset %%r12, 16 \n\t"\
+ ".cfi_offset %%r13, 24 \n\t"\
+ ".cfi_offset %%r14, 32 \n\t"\
+ ".cfi_offset %%r15, 40 \n\t"\
+ ".cfi_offset %%rsp, 48 \n\t"\
+ ".cfi_offset %%rip, 56 \n\t"\
+ "jmp *%%rcx\n\t"\
+ : : "d" (ctx), "a" (1))
+#define dill_setsp(x) \
+ asm(""::"r"(alloca(sizeof(size_t))));\
+ asm volatile("leaq (%%rax), %%rsp"::"rax"(x));
+
+/* Stack switching on X86. */
+#elif defined(__i386__) && !defined DILL_ARCH_FALLBACK
+#define dill_setjmp(ctx) ({\
+ int ret;\
+ asm("movl $LJMPRET%=, %%ecx\n\t"\
+ "movl %%ebx, (%%edx)\n\t"\
+ "movl %%esi, 4(%%edx)\n\t"\
+ "movl %%edi, 8(%%edx)\n\t"\
+ "movl %%ebp, 12(%%edx)\n\t"\
+ "movl %%esp, 16(%%edx)\n\t"\
+ "movl %%ecx, 20(%%edx)\n\t"\
+ "xorl %%eax, %%eax\n\t"\
+ "LJMPRET%=:\n\t"\
+ : "=a" (ret) : "d" (ctx) : "memory");\
+ ret;\
+})
+#define dill_longjmp(ctx) \
+ asm("movl (%%edx), %%ebx\n\t"\
+ "movl 4(%%edx), %%esi\n\t"\
+ "movl 8(%%edx), %%edi\n\t"\
+ "movl 12(%%edx), %%ebp\n\t"\
+ "movl 16(%%edx), %%esp\n\t"\
+ "movl 20(%%edx), %%ecx\n\t"\
+ ".cfi_def_cfa %%edx, 0 \n\t"\
+ ".cfi_offset %%ebx, 0 \n\t"\
+ ".cfi_offset %%esi, 4 \n\t"\
+ ".cfi_offset %%edi, 8 \n\t"\
+ ".cfi_offset %%ebp, 12 \n\t"\
+ ".cfi_offset %%esp, 16 \n\t"\
+ ".cfi_offset %%eip, 20 \n\t"\
+ "jmp *%%ecx\n\t"\
+ : : "d" (ctx), "a" (1))
+#define dill_setsp(x) \
+ asm(""::"r"(alloca(sizeof(size_t))));\
+ asm volatile("leal (%%eax), %%esp"::"eax"(x));
+
+/* Stack-switching on other microarchitectures. */
+#else
+#define dill_setjmp(ctx) sigsetjmp(ctx, 0)
+#define dill_longjmp(ctx) siglongjmp(ctx, 1)
+/* For newer GCCs, -fstack-protector breaks on this; use -fno-stack-protector.
+ Alternatively, implement a custom dill_setsp for your microarchitecture. */
+#define dill_setsp(x) \
+ dill_unoptimisable = alloca((char*)alloca(sizeof(size_t)) - (char*)(x));
+#endif
+
+/* Statement expressions are a gcc-ism but they are also supported by clang.
+ Given that there's no other way to do this, screw other compilers for now.
+ See https://gcc.gnu.org/onlinedocs/gcc-3.2/gcc/Statement-Exprs.html */
+
+/* A bug in gcc have been observed where name clash between variable in the
+ outer scope and a local variable in this macro causes the variable to
+ get weird values. To avoid that, we use fancy names (dill_*__). */
+
+#define dill_go_(fn, ptr, len, bndl) \
+ ({\
+ sigjmp_buf *dill_ctx__;\
+ void *dill_stk__ = (ptr);\
+ int dill_handle__ = dill_prologue(&dill_ctx__, &dill_stk__, (len),\
+ (bndl), __FILE__, __LINE__);\
+ if(dill_handle__ >= 0) {\
+ if(!dill_setjmp(*dill_ctx__)) {\
+ dill_setsp(dill_stk__);\
+ fn;\
+ dill_epilogue();\
+ }\
+ }\
+ dill_handle__;\
+ })
+
+#define dill_go(fn) dill_go_(fn, NULL, 0, -1)
+#define dill_go_mem(fn, ptr, len) dill_go_(fn, ptr, len, -1)
+
+#define dill_bundle_go(bndl, fn) dill_go_(fn, NULL, 0, bndl)
+#define dill_bundle_go_mem(bndl, fn, ptr, len) dill_go_(fn, ptr, len, bndl)
+
+struct dill_bundle_storage {char _[64];};
+
+DILL_EXPORT int dill_bundle(void);
+DILL_EXPORT int dill_bundle_mem(struct dill_bundle_storage *mem);
+DILL_EXPORT int dill_bundle_wait(int h, int64_t deadline);
+DILL_EXPORT int dill_yield(void);
+
+#if !defined DILL_DISABLE_RAW_NAMES
+#define coroutine dill_coroutine
+#define go dill_go
+#define go_mem dill_go_mem
+#define bundle_go dill_bundle_go
+#define bundle_go_mem dill_bundle_go_mem
+#define bundle_storage dill_bundle_storage
+#define bundle dill_bundle
+#define bundle_mem dill_bundle_mem
+#define bundle_wait dill_bundle_wait
+#define yield dill_yield
+#endif
+
+/******************************************************************************/
+/* Channels */
+/******************************************************************************/
+
+#define DILL_CHSEND 1
+#define DILL_CHRECV 2
+
+struct chclause {
+ int op;
+ int ch;
+ void *val;
+ size_t len;
+};
+
+struct dill_chstorage {char _[144];};
+
+DILL_EXPORT int dill_chmake(
+ int chv[2]);
+DILL_EXPORT int dill_chmake_mem(
+ struct dill_chstorage *mem,
+ int chv[2]);
+DILL_EXPORT int dill_chsend(
+ int ch,
+ const void *val,
+ size_t len,
+ int64_t deadline);
+DILL_EXPORT int dill_chrecv(
+ int ch,
+ void *val,
+ size_t len,
+ int64_t deadline);
+DILL_EXPORT int dill_chdone(
+ int ch);
+DILL_EXPORT int dill_choose(
+ struct chclause *clauses,
+ int nclauses,
+ int64_t deadline);
+
+#if !defined DILL_DISABLE_RAW_NAMES
+#define CHSEND DILL_CHSEND
+#define CHRECV DILL_CHRECV
+#define chstorage dill_chstorage
+#define chmake dill_chmake
+#define chmake_mem dill_chmake_mem
+#define chsend dill_chsend
+#define chrecv dill_chrecv
+#define chdone dill_chdone
+#define choose dill_choose
+#endif
+
+#if !defined DILL_DISABLE_SOCKETS
+
+/******************************************************************************/
+/* Gather/scatter list. */
+/******************************************************************************/
+
+struct dill_iolist {
+ void *iol_base;
+ size_t iol_len;
+ struct dill_iolist *iol_next;
+ int iol_rsvd;
+};
+
+#if !defined DILL_DISABLE_RAW_NAMES
+#define iolist dill_iolist
+#endif
+
+/******************************************************************************/
+/* Bytestream sockets. */
+/******************************************************************************/
+
+DILL_EXPORT int dill_bsend(
+ int s,
+ const void *buf,
+ size_t len,
+ int64_t deadline);
+DILL_EXPORT int dill_brecv(
+ int s,
+ void *buf,
+ size_t len,
+ int64_t deadline);
+DILL_EXPORT int dill_bsendl(
+ int s,
+ struct dill_iolist *first,
+ struct dill_iolist *last,
+ int64_t deadline);
+DILL_EXPORT int dill_brecvl(
+ int s,
+ struct dill_iolist *first,
+ struct dill_iolist *last,
+ int64_t deadline);
+
+#if !defined DILL_DISABLE_RAW_NAMES
+#define bsend dill_bsend
+#define brecv dill_brecv
+#define bsendl dill_bsendl
+#define brecvl dill_brecvl
+#endif
+
+/******************************************************************************/
+/* Message sockets. */
+/******************************************************************************/
+
+DILL_EXPORT int dill_msend(
+ int s,
+ const void *buf,
+ size_t len,
+ int64_t deadline);
+DILL_EXPORT ssize_t dill_mrecv(
+ int s,
+ void *buf,
+ size_t len,
+ int64_t deadline);
+DILL_EXPORT int dill_msendl(
+ int s,
+ struct dill_iolist *first,
+ struct dill_iolist *last,
+ int64_t deadline);
+DILL_EXPORT ssize_t dill_mrecvl(
+ int s,
+ struct dill_iolist *first,
+ struct dill_iolist *last,
+ int64_t deadline);
+
+#if !defined DILL_DISABLE_RAW_NAMES
+#define msend dill_msend
+#define mrecv dill_mrecv
+#define msendl dill_msendl
+#define mrecvl dill_mrecvl
+#endif
+
+/******************************************************************************/
+/* IP address resolution. */
+/******************************************************************************/
+
+struct sockaddr;
+
+#define DILL_IPADDR_IPV4 1
+#define DILL_IPADDR_IPV6 2
+#define DILL_IPADDR_PREF_IPV4 3
+#define DILL_IPADDR_PREF_IPV6 4
+#define DILL_IPADDR_MAXSTRLEN 46
+
+struct dill_ipaddr {char _[32];};
+
+DILL_EXPORT int dill_ipaddr_local(
+ struct dill_ipaddr *addr,
+ const char *name,
+ int port,
+ int mode);
+DILL_EXPORT int dill_ipaddr_remote(
+ struct dill_ipaddr *addr,
+ const char *name,
+ int port,
+ int mode,
+ int64_t deadline);
+DILL_EXPORT const char *dill_ipaddr_str(
+ const struct dill_ipaddr *addr,
+ char *ipstr);
+DILL_EXPORT int dill_ipaddr_family(
+ const struct dill_ipaddr *addr);
+DILL_EXPORT const struct sockaddr *dill_ipaddr_sockaddr(
+ const struct dill_ipaddr *addr);
+DILL_EXPORT int dill_ipaddr_len(
+ const struct dill_ipaddr *addr);
+DILL_EXPORT int dill_ipaddr_port(
+ const struct dill_ipaddr *addr);
+DILL_EXPORT void dill_ipaddr_setport(
+ struct dill_ipaddr *addr,
+ int port);
+DILL_EXPORT int dill_ipaddr_equal(
+ const struct dill_ipaddr *addr1,
+ const struct dill_ipaddr *addr2,
+ int ignore_port);
+
+#if !defined DILL_DISABLE_RAW_NAMES
+#define IPADDR_IPV4 DILL_IPADDR_IPV4
+#define IPADDR_IPV6 DILL_IPADDR_IPV6
+#define IPADDR_PREF_IPV4 DILL_IPADDR_PREF_IPV4
+#define IPADDR_PREF_IPV6 DILL_IPADDR_PREF_IPV6
+#define IPADDR_MAXSTRLEN DILL_IPADDR_MAXSTRLEN
+#define ipaddr dill_ipaddr
+#define ipaddr_local dill_ipaddr_local
+#define ipaddr_remote dill_ipaddr_remote
+#define ipaddr_str dill_ipaddr_str
+#define ipaddr_family dill_ipaddr_family
+#define ipaddr_sockaddr dill_ipaddr_sockaddr
+#define ipaddr_len dill_ipaddr_len
+#define ipaddr_port dill_ipaddr_port
+#define ipaddr_setport dill_ipaddr_setport
+#define ipaddr_equal dill_ipaddr_equal
+#endif
+
+/******************************************************************************/
+/* TCP protocol. */
+/******************************************************************************/
+
+struct dill_tcp_listener_storage {char _[56];};
+
+struct dill_tcp_storage {char _[72];};
+
+DILL_EXPORT int dill_tcp_listen(
+ struct dill_ipaddr *addr,
+ int backlog);
+DILL_EXPORT int dill_tcp_listen_mem(
+ struct dill_ipaddr *addr,
+ int backlog,
+ struct dill_tcp_listener_storage *mem);
+DILL_EXPORT int dill_tcp_accept(
+ int s,
+ struct dill_ipaddr *addr,
+ int64_t deadline);
+DILL_EXPORT int dill_tcp_accept_mem(
+ int s,
+ struct dill_ipaddr *addr,
+ struct dill_tcp_storage *mem,
+ int64_t deadline);
+DILL_EXPORT int dill_tcp_connect(
+ const struct dill_ipaddr *addr,
+ int64_t deadline);
+DILL_EXPORT int dill_tcp_connect_mem(
+ const struct dill_ipaddr *addr,
+ struct dill_tcp_storage *mem,
+ int64_t deadline);
+DILL_EXPORT int dill_tcp_done(
+ int s,
+ int64_t deadline);
+DILL_EXPORT int dill_tcp_close(
+ int s,
+ int64_t deadline);
+DILL_EXPORT int dill_tcp_listener_fromfd(
+ int fd);
+DILL_EXPORT int dill_tcp_listener_fromfd_mem(
+ int fd,
+ struct dill_tcp_listener_storage *mem);
+DILL_EXPORT int dill_tcp_fromfd(
+ int fd);
+DILL_EXPORT int dill_tcp_fromfd_mem(
+ int fd,
+ struct dill_tcp_storage *mem);
+
+#if !defined DILL_DISABLE_RAW_NAMES
+#define tcp_listener_storage dill_tcp_listener_storage
+#define tcp_storage dill_tcp_storage
+#define tcp_listen dill_tcp_listen
+#define tcp_listen_mem dill_tcp_listen_mem
+#define tcp_accept dill_tcp_accept
+#define tcp_accept_mem dill_tcp_accept_mem
+#define tcp_connect dill_tcp_connect
+#define tcp_connect_mem dill_tcp_connect_mem
+#define tcp_done dill_tcp_done
+#define tcp_close dill_tcp_close
+#define tcp_listener_fromfd dill_tcp_listener_fromfd
+#define tcp_listener_fromfd_mem dill_tcp_listener_fromfd_mem
+#define tcp_fromfd dill_tcp_fromfd
+#define tcp_fromfd_mem dill_tcp_fromfd_mem
+#endif
+
+/******************************************************************************/
+/* IPC protocol. */
+/******************************************************************************/
+
+struct dill_ipc_listener_storage {char _[24];};
+
+struct dill_ipc_storage {char _[72];};
+
+struct dill_ipc_pair_storage {char _[144];};
+
+DILL_EXPORT int dill_ipc_listen(
+ const char *addr,
+ int backlog);
+DILL_EXPORT int dill_ipc_listen_mem(
+ const char *addr,
+ int backlog,
+ struct dill_ipc_listener_storage *mem);
+DILL_EXPORT int dill_ipc_accept(
+ int s,
+ int64_t deadline);
+DILL_EXPORT int dill_ipc_accept_mem(
+ int s,
+ struct dill_ipc_storage *mem,
+ int64_t deadline);
+DILL_EXPORT int dill_ipc_connect(
+ const char *addr,
+ int64_t deadline);
+DILL_EXPORT int dill_ipc_connect_mem(
+ const char *addr,
+ struct dill_ipc_storage *mem,
+ int64_t deadline);
+DILL_EXPORT int dill_ipc_done(
+ int s,
+ int64_t deadline);
+DILL_EXPORT int dill_ipc_close(
+ int s,
+ int64_t deadline);
+DILL_EXPORT int dill_ipc_listener_fromfd(
+ int fd);
+DILL_EXPORT int dill_ipc_listener_fromfd_mem(
+ int fd,
+ struct dill_ipc_listener_storage *mem);
+DILL_EXPORT int dill_ipc_fromfd(
+ int fd);
+DILL_EXPORT int dill_ipc_fromfd_mem(
+ int fd,
+ struct dill_ipc_storage *mem);
+DILL_EXPORT int dill_ipc_pair(
+ int s[2]);
+DILL_EXPORT int dill_ipc_pair_mem(
+ struct dill_ipc_pair_storage *mem,
+ int s[2]);
+
+#if !defined DILL_DISABLE_RAW_NAMES
+#define ipc_listener_storage dill_ipc_listener_storage
+#define ipc_storage dill_ipc_storage
+#define ipc_pair_storage dill_ipc_pair_storage
+#define ipc_listen dill_ipc_listen
+#define ipc_listen_mem dill_ipc_listen_mem
+#define ipc_accept dill_ipc_accept
+#define ipc_accept_mem dill_ipc_accept_mem
+#define ipc_connect dill_ipc_connect
+#define ipc_connect_mem dill_ipc_connect_mem
+#define ipc_done dill_ipc_done
+#define ipc_close dill_ipc_close
+#define ipc_listener_fromfd dill_ipc_listener_fromfd
+#define ipc_listener_fromfd_mem dill_ipc_listener_fromfd_mem
+#define ipc_fromfd dill_ipc_fromfd
+#define ipc_fromfd_mem dill_ipc_fromfd_mem
+#define ipc_pair dill_ipc_pair
+#define ipc_pair_mem dill_ipc_pair_mem
+#endif
+
+/******************************************************************************/
+/* PREFIX protocol. */
+/* Messages are prefixed by size. */
+/******************************************************************************/
+
+struct dill_prefix_storage {char _[56];};
+
+#define DILL_PREFIX_BIG_ENDIAN 0
+#define DILL_PREFIX_LITTLE_ENDIAN 1
+
+DILL_EXPORT int dill_prefix_attach(
+ int s,
+ size_t hdrlen,
+ int flags);
+DILL_EXPORT int dill_prefix_attach_mem(
+ int s,
+ size_t hdrlen,
+ int flags,
+ struct dill_prefix_storage *mem);
+DILL_EXPORT int dill_prefix_detach(
+ int s);
+
+#if !defined DILL_DISABLE_RAW_NAMES
+#define PREFIX_BIG_ENDIAN DILL_PREFIX_BIG_ENDIAN
+#define PREFIX_LITTLE_ENDIAN DILL_PREFIX_LITTLE_ENDIAN
+#define prefix_storage dill_prefix_storage
+#define prefix_attach dill_prefix_attach
+#define prefix_attach_mem dill_prefix_attach_mem
+#define prefix_detach dill_prefix_detach
+#endif
+
+/******************************************************************************/
+/* SUFFIX protocol. */
+/* Messages are suffixed by specified string of bytes. */
+/******************************************************************************/
+
+struct dill_suffix_storage {char _[128];};
+
+DILL_EXPORT int dill_suffix_attach(
+ int s,
+ const void *suffix,
+ size_t suffixlen);
+DILL_EXPORT int dill_suffix_attach_mem(
+ int s,
+ const void *suffix,
+ size_t suffixlen,
+ struct dill_suffix_storage *mem);
+DILL_EXPORT int dill_suffix_detach(
+ int s,
+ int64_t deadline);
+
+#if !defined DILL_DISABLE_RAW_NAMES
+#define suffix_storage dill_suffix_storage
+#define suffix_attach dill_suffix_attach
+#define suffix_attach_mem dill_suffix_attach_mem
+#define suffix_detach dill_suffix_detach
+#endif
+
+/******************************************************************************/
+/* UDP protocol. */
+/* Each UDP packet is treated as a separate message. */
+/******************************************************************************/
+
+struct dill_udp_storage {char _[72];};
+
+DILL_EXPORT int dill_udp_open(
+ struct dill_ipaddr *local,
+ const struct dill_ipaddr *remote);
+DILL_EXPORT int dill_udp_open_mem(
+ struct dill_ipaddr *local,
+ const struct dill_ipaddr *remote,
+ struct dill_udp_storage *mem);
+DILL_EXPORT int dill_udp_send(
+ int s,
+ const struct dill_ipaddr *addr,
+ const void *buf,
+ size_t len);
+DILL_EXPORT ssize_t dill_udp_recv(
+ int s,
+ struct dill_ipaddr *addr,
+ void *buf,
+ size_t len,
+ int64_t deadline);
+DILL_EXPORT int dill_udp_sendl(
+ int s,
+ const struct dill_ipaddr *addr,
+ struct dill_iolist *first,
+ struct dill_iolist *last);
+DILL_EXPORT ssize_t dill_udp_recvl(
+ int s,
+ struct dill_ipaddr *addr,
+ struct dill_iolist *first,
+ struct dill_iolist *last,
+ int64_t deadline);
+
+#if !defined DILL_DISABLE_RAW_NAMES
+#define udp_storage dill_udp_storage
+#define udp_open dill_udp_open
+#define udp_open_mem dill_udp_open_mem
+#define udp_send dill_udp_send
+#define udp_recv dill_udp_recv
+#define udp_sendl dill_udp_sendl
+#define udp_recvl dill_udp_recvl
+#endif
+
+/******************************************************************************/
+/* HTTP */
+/******************************************************************************/
+
+struct dill_http_storage {char _[1296];};
+
+DILL_EXPORT int dill_http_attach(
+ int s);
+DILL_EXPORT int dill_http_attach_mem(
+ int s,
+ struct dill_http_storage *mem);
+DILL_EXPORT int dill_http_done(
+ int s,
+ int64_t deadline);
+DILL_EXPORT int dill_http_detach(
+ int s,
+ int64_t deadline);
+DILL_EXPORT int dill_http_sendrequest(
+ int s,
+ const char *command,
+ const char *resource,
+ int64_t deadline);
+DILL_EXPORT int dill_http_recvrequest(
+ int s,
+ char *command,
+ size_t commandlen,
+ char *resource,
+ size_t resourcelen,
+ int64_t deadline);
+DILL_EXPORT int dill_http_sendstatus(
+ int s,
+ int status,
+ const char *reason,
+ int64_t deadline);
+DILL_EXPORT int dill_http_recvstatus(
+ int s,
+ char *reason,
+ size_t reasonlen,
+ int64_t deadline);
+DILL_EXPORT int dill_http_sendfield(
+ int s,
+ const char *name,
+ const char *value,
+ int64_t deadline);
+DILL_EXPORT int dill_http_recvfield(
+ int s,
+ char *name,
+ size_t namelen,
+ char *value,
+ size_t valuelen,
+ int64_t deadline);
+
+#if !defined DILL_DISABLE_RAW_NAMES
+#define http_storage dill_http_storage
+#define http_attach dill_http_attach
+#define http_attach_mem dill_http_attach_mem
+#define http_done dill_http_done
+#define http_detach dill_http_detach
+#define http_sendrequest dill_http_sendrequest
+#define http_recvrequest dill_http_recvrequest
+#define http_sendstatus dill_http_sendstatus
+#define http_recvstatus dill_http_recvstatus
+#define http_sendfield dill_http_sendfield
+#define http_recvfield dill_http_recvfield
+#endif
+
+#if !defined DILL_DISABLE_TLS
+
+/******************************************************************************/
+/* TLS protocol. */
+/******************************************************************************/
+
+struct dill_tls_storage {char _[72];};
+
+DILL_EXPORT int dill_tls_attach_server(
+ int s,
+ const char *cert,
+ const char *pkey,
+ int64_t deadline);
+DILL_EXPORT int dill_tls_attach_server_mem(
+ int s,
+ const char *cert,
+ const char *pkey,
+ struct dill_tls_storage *mem,
+ int64_t deadline);
+DILL_EXPORT int dill_tls_attach_client(
+ int s,
+ int64_t deadline);
+DILL_EXPORT int dill_tls_attach_client_mem(
+ int s,
+ struct dill_tls_storage *mem,
+ int64_t deadline);
+DILL_EXPORT int dill_tls_done(
+ int s,
+ int64_t deadline);
+DILL_EXPORT int dill_tls_detach(
+ int s,
+ int64_t deadline);
+
+#if !defined DILL_DISABLE_RAW_NAMES
+#define tls_storage dill_tls_storage
+#define tls_attach_server dill_tls_attach_server
+#define tls_attach_server_mem dill_tls_attach_server_mem
+#define tls_attach_client dill_tls_attach_client
+#define tls_attach_client_mem dill_tls_attach_client_mem
+#define tls_done dill_tls_done
+#define tls_detach dill_tls_detach
+#endif
+
+/******************************************************************************/
+/* DTLS protocol. */
+/******************************************************************************/
+
+struct dill_dtls_storage {char _[88];};
+
+DILL_EXPORT int dill_dtls_attach_server(
+ int s,
+ const char *cert,
+ const char *pkey,
+ int64_t deadline);
+DILL_EXPORT int dill_dtls_attach_server_mem(
+ int s,
+ const char *cert,
+ const char *pkey,
+ struct dill_dtls_storage *mem,
+ int64_t deadline);
+DILL_EXPORT int dill_dtls_attach_client(
+ int s,
+ int64_t deadline);
+DILL_EXPORT int dill_dtls_attach_client_mem(
+ int s,
+ struct dill_dtls_storage *mem,
+ int64_t deadline);
+DILL_EXPORT int dill_dtls_done(
+ int s,
+ int64_t deadline);
+DILL_EXPORT int dill_dtls_detach(
+ int s,
+ int64_t deadline);
+
+#if !defined DILL_DISABLE_RAW_NAMES
+#define dtls_storage dill_dtls_storage
+#define dtls_attach_server dill_dtls_attach_server
+#define dtls_attach_server_mem dill_dtls_attach_server_mem
+#define dtls_attach_client dill_dtls_attach_client
+#define dtls_attach_client_mem dill_dtls_attach_client_mem
+#define dtls_done dill_dtls_done
+#define dtls_detach dill_dtls_detach
+#endif
+
+#endif
+
+/******************************************************************************/
+/* WebSockets protocol. */
+/******************************************************************************/
+
+struct dill_ws_storage {char _[176];};
+
+#define DILL_WS_BINARY 0
+#define DILL_WS_TEXT 1
+#define DILL_WS_NOHTTP 2
+
+DILL_EXPORT int dill_ws_attach_client(
+ int s,
+ int flags,
+ const char *resource,
+ const char *host,
+ int64_t deadline);
+DILL_EXPORT int dill_ws_attach_client_mem(
+ int s,
+ int flags,
+ const char *resource,
+ const char *host,
+ struct dill_ws_storage *mem,
+ int64_t deadline);
+DILL_EXPORT int dill_ws_attach_server(
+ int s,
+ int flags,
+ char *resource,
+ size_t resourcelen,
+ char *host,
+ size_t hostlen,
+ int64_t deadline);
+DILL_EXPORT int dill_ws_attach_server_mem(
+ int s,
+ int flags,
+ char *resource,
+ size_t resourcelen,
+ char *host,
+ size_t hostlen,
+ struct dill_ws_storage *mem,
+ int64_t deadline);
+DILL_EXPORT int dill_ws_send(
+ int s,
+ int flags,
+ const void *buf,
+ size_t len,
+ int64_t deadline);
+DILL_EXPORT ssize_t dill_ws_recv(
+ int s,
+ int *flags,
+ void *buf,
+ size_t len,
+ int64_t deadline);
+DILL_EXPORT int dill_ws_sendl(
+ int s,
+ int flags,
+ struct dill_iolist *first,
+ struct dill_iolist *last,
+ int64_t deadline);
+DILL_EXPORT ssize_t dill_ws_recvl(
+ int s,
+ int *flags,
+ struct dill_iolist *first,
+ struct dill_iolist *last,
+ int64_t deadline);
+DILL_EXPORT int dill_ws_done(
+ int s,
+ int status,
+ const void *buf,
+ size_t len,
+ int64_t deadline);
+DILL_EXPORT int dill_ws_detach(
+ int s,
+ int status,
+ const void *buf,
+ size_t len,
+ int64_t deadline);
+DILL_EXPORT ssize_t dill_ws_status(
+ int s,
+ int *status,
+ void *buf,
+ size_t len);
+
+/* Helper functions for those who want to implement HTTP exchange by hand. */
+
+#define WS_KEY_SIZE 32
+
+DILL_EXPORT int dill_ws_request_key(
+ char *request_key);
+DILL_EXPORT int dill_ws_response_key(
+ const char *request_key,
+ char *response_key);
+
+#if !defined DILL_DISABLE_RAW_NAMES
+#define WS_BINARY DILL_WS_BINARY
+#define WS_TEXT DILL_WS_TEXT
+#define WS_NOHTTP DILL_WS_NOHTTP
+#define ws_storage dill_ws_storage
+#define ws_attach_server dill_ws_attach_server
+#define ws_attach_server_mem dill_ws_attach_server_mem
+#define ws_attach_client dill_ws_attach_client
+#define ws_attach_client_mem dill_ws_attach_client_mem
+#define ws_send dill_ws_send
+#define ws_recv dill_ws_recv
+#define ws_sendl dill_ws_sendl
+#define ws_recvl dill_ws_recvl
+#define ws_done dill_ws_done
+#define ws_detach dill_ws_detach
+#define ws_status dill_ws_status
+#define ws_request_key dill_ws_request_key
+#define ws_response_key dill_ws_response_key
+#endif
+
+/******************************************************************************/
+/* TERM protocol. */
+/* Implementes terminal handshake on the top of any message-based protocol. */
+/******************************************************************************/
+
+struct dill_term_storage {char _[88];};
+
+DILL_EXPORT int dill_term_attach(
+ int s,
+ const void *buf,
+ size_t len);
+DILL_EXPORT int dill_term_attach_mem(
+ int s,
+ const void *buf,
+ size_t len,
+ struct dill_term_storage *mem);
+DILL_EXPORT int dill_term_done(
+ int s,
+ int64_t deadline);
+DILL_EXPORT int dill_term_detach(
+ int s,
+ int64_t deadline);
+
+#if !defined DILL_DISABLE_RAW_NAMES
+#define term_storage dill_term_storage
+#define term_attach dill_term_attach
+#define term_attach_mem dill_term_attach_mem
+#define term_done dill_term_done
+#define term_detach dill_term_detach
+#endif
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/vendor/sustrik/libdill/libdillimpl.h b/vendor/sustrik/libdill/libdillimpl.h
new file mode 100644
index 0000000..75f8605
--- /dev/null
+++ b/vendor/sustrik/libdill/libdillimpl.h
@@ -0,0 +1,89 @@
+/*
+
+ Copyright (c) 2017 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#ifndef LIBDILLIMPL_H_INCLUDED
+#define LIBDILLIMPL_H_INCLUDED
+
+#include "libdill.h"
+
+/******************************************************************************/
+/* Handles */
+/******************************************************************************/
+
+struct dill_hvfs {
+ void *(*query)(struct dill_hvfs *vfs, const void *type);
+ void (*close)(struct dill_hvfs *vfs);
+};
+
+DILL_EXPORT int dill_hmake(struct dill_hvfs *vfs);
+DILL_EXPORT void *dill_hquery(int h, const void *type);
+
+#if !defined DILL_DISABLE_RAW_NAMES
+#define hvfs dill_hvfs
+#define hmake dill_hmake
+#define hquery dill_hquery
+#endif
+
+#if !defined DILL_DISABLE_SOCKETS
+
+/******************************************************************************/
+/* Bytestream sockets. */
+/******************************************************************************/
+
+DILL_EXPORT extern const void *dill_bsock_type;
+
+struct dill_bsock_vfs {
+ int (*bsendl)(struct dill_bsock_vfs *vfs,
+ struct dill_iolist *first, struct dill_iolist *last, int64_t deadline);
+ int (*brecvl)(struct dill_bsock_vfs *vfs,
+ struct dill_iolist *first, struct dill_iolist *last, int64_t deadline);
+};
+
+#if !defined DILL_DISABLE_RAW_NAMES
+#define bsock_vfs dill_bsock_vfs
+#define bsock_type dill_bsock_type
+#endif
+
+/******************************************************************************/
+/* Message sockets. */
+/******************************************************************************/
+
+DILL_EXPORT extern const void *dill_msock_type;
+
+struct dill_msock_vfs {
+ int (*msendl)(struct dill_msock_vfs *vfs,
+ struct dill_iolist *first, struct dill_iolist *last, int64_t deadline);
+ ssize_t (*mrecvl)(struct dill_msock_vfs *vfs,
+ struct dill_iolist *first, struct dill_iolist *last, int64_t deadline);
+};
+
+#if !defined DILL_DISABLE_RAW_NAMES
+#define msock_vfs dill_msock_vfs
+#define msock_type dill_msock_type
+#endif
+
+#endif
+
+#endif
+
diff --git a/vendor/sustrik/libdill/list.h b/vendor/sustrik/libdill/list.h
new file mode 100644
index 0000000..5efccc2
--- /dev/null
+++ b/vendor/sustrik/libdill/list.h
@@ -0,0 +1,74 @@
+/*
+
+ Copyright (c) 2016 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#ifndef DILL_LIST_INCLUDED
+#define DILL_LIST_INCLUDED
+
+#include "utils.h"
+
+/* Doubly-linked list. */
+struct dill_list {
+ struct dill_list *next;
+ struct dill_list *prev;
+};
+
+/* Initialize the list. */
+static inline void dill_list_init(struct dill_list *self) {
+ self->next = self;
+ self->prev = self;
+}
+
+/* True if the list has no items except for the head. */
+static inline int dill_list_empty(struct dill_list *self) {
+ return self->next == self;
+}
+
+/* True if the list has only one item in addition to the head. */
+static inline int dill_list_oneitem(struct dill_list *self) {
+ return self->next != self && self->next == self->prev;
+}
+
+/* Returns an iterator to one past the item pointed to by 'it'. If 'it' is the
+ list itself it returns the first item of the list. At the end of
+ the list, it returns the list itself. */
+#define dill_list_next(it) ((it)->next)
+
+/* Adds the item to the list before the item pointed to by 'before'. If 'before'
+ is the list itself the item is inserted to the end of the list. */
+static inline void dill_list_insert(struct dill_list *item,
+ struct dill_list *before) {
+ item->next = before;
+ item->prev = before->prev;
+ before->prev->next = item;
+ before->prev = item;
+}
+
+/* Removes the item from the list. */
+static void dill_list_erase(struct dill_list *item) {
+ item->prev->next = item->next;
+ item->next->prev = item->prev;
+}
+
+#endif
+
diff --git a/vendor/sustrik/libdill/m4/.gitignore b/vendor/sustrik/libdill/m4/.gitignore
new file mode 100644
index 0000000..e69de29
diff --git a/vendor/sustrik/libdill/now.c b/vendor/sustrik/libdill/now.c
new file mode 100644
index 0000000..08c2ec3
--- /dev/null
+++ b/vendor/sustrik/libdill/now.c
@@ -0,0 +1,121 @@
+/*
+
+ Copyright (c) 2017 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#include
+#include
+#include
+
+#if defined(__x86_64__) || defined(__i386__)
+#include
+#endif
+
+#include "ctx.h"
+
+int64_t dill_mnow(void) {
+
+/* Implementation using Mach timers. */
+#if defined __APPLE__
+ static mach_timebase_info_data_t dill_mtid = {0};
+ if (dill_slow(!dill_mtid.denom))
+ mach_timebase_info(&dill_mtid);
+ uint64_t ticks = mach_absolute_time();
+ return (int64_t)(ticks * dill_mtid.numer / dill_mtid.denom / 1000000);
+#else
+
+/* Implementation using clock_gettime(). */
+#if defined CLOCK_MONOTONIC_COARSE
+ clock_t id = CLOCK_MONOTONIC_COARSE;
+#elif defined CLOCK_MONOTONIC_FAST
+ clock_t id = CLOCK_MONOTONIC_FAST;
+#elif defined CLOCK_MONOTONIC
+ clock_t id = CLOCK_MONOTONIC;
+#else
+#define DILL_dill_now_FALLBACK
+#endif
+#if !defined DILL_dill_now_FALLBACK
+ struct timespec ts;
+ int rc = clock_gettime(id, &ts);
+ dill_assert (rc == 0);
+ return ((int64_t)ts.tv_sec) * 1000 + (((int64_t)ts.tv_nsec) / 1000000);
+
+/* Implementation using gettimeofday(). This is slow and error-prone
+ (the time can jump backwards!), but it's just a last resort option. */
+#else
+ struct timeval tv;
+ int rc = gettimeofday(&tv, NULL);
+ dill_assert (rc == 0);
+ return ((int64_t)tv.tv_sec) * 1000 + (((int64_t)tv.tv_usec) / 1000);
+#endif
+
+#endif
+}
+
+/* Like now(), this function can be called only after context is initialized
+ but unlike now() it doesn't do time caching. */
+static int64_t dill_now_(void) {
+#if defined __APPLE__
+ struct dill_ctx_now *ctx = &dill_getctx->now;
+ uint64_t ticks = mach_absolute_time();
+ return (int64_t)(ticks * ctx->mtid.numer / ctx->mtid.denom / 1000000);
+#else
+ return dill_mnow();
+#endif
+}
+
+int64_t dill_now(void) {
+#if defined(__x86_64__) || defined(__i386__)
+ /* On x86 platforms, rdtsc instruction can be used to quickly check time
+ in form of CPU cycles. If less than 1M cycles have elapsed since the
+ last dill_now_() call we assume it's still the same millisecond and return
+ cached time. This optimization can give a huge speedup with old systems.
+ 1M number is chosen is such a way that it results in getting time every
+ millisecond on 1GHz processors. On faster processors we'll query time
+ somewhat more often but the number of queries should still be
+ statistically insignificant. On slower processors we'll start losing
+ precision, e.g. on 500MHz processor we can diverge by 1ms. */
+ struct dill_ctx_now *ctx = &dill_getctx->now;
+ uint64_t tsc = __rdtsc();
+ int64_t diff = tsc - ctx->last_tsc;
+ if(diff < 0) diff = -diff;
+ if(dill_fast(diff < 1000000ULL)) return ctx->last_time;
+ ctx->last_tsc = tsc;
+ ctx->last_time = dill_now_();
+ return ctx->last_time;
+#else
+ return dill_now_();
+#endif
+}
+
+int dill_ctx_now_init(struct dill_ctx_now *ctx) {
+#if defined __APPLE__
+ mach_timebase_info(&ctx->mtid);
+#endif
+#if defined(__x86_64__) || defined(__i386__)
+ ctx->last_time = dill_mnow();
+ ctx->last_tsc = __rdtsc();
+#endif
+ return 0;
+}
+
+void dill_ctx_now_term(struct dill_ctx_now *ctx) {
+}
+
diff --git a/vendor/sustrik/libdill/now.h b/vendor/sustrik/libdill/now.h
new file mode 100644
index 0000000..345da2d
--- /dev/null
+++ b/vendor/sustrik/libdill/now.h
@@ -0,0 +1,52 @@
+/*
+
+ Copyright (c) 2017 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#ifndef DILL_NOW_INCLUDED
+#define DILL_NOW_INCLUDED
+
+#include
+
+#if defined __APPLE__
+#include
+#endif
+
+struct dill_ctx_now {
+#if defined __APPLE__
+ mach_timebase_info_data_t mtid;
+#endif
+#if defined(__x86_64__) || defined(__i386__)
+ int64_t last_time;
+ uint64_t last_tsc;
+#endif
+};
+
+int dill_ctx_now_init(struct dill_ctx_now *ctx);
+void dill_ctx_now_term(struct dill_ctx_now *ctx);
+
+/* Same as dill_now() except that it doesn't use the context.
+ I.e. it can be called before calling dill_ctx_now_init(). */
+int64_t dill_mnow(void);
+
+#endif
+
diff --git a/vendor/sustrik/libdill/package_version.sh b/vendor/sustrik/libdill/package_version.sh
new file mode 100755
index 0000000..c61e140
--- /dev/null
+++ b/vendor/sustrik/libdill/package_version.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+# Copyright (c) 2013 Luca Barbato
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom
+# the Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+if [ -d .git ]; then
+ # Retrieve the version from the last git tag.
+ VER=`git describe --always | sed -e "s:v::"`
+ if [ x"`git diff-index --name-only HEAD`" != x ]; then
+ # If the sources have been changed locally, add -dirty to the version.
+ VER="${VER}-dirty"
+ fi
+elif [ -f .version ]; then
+ # If git is not available (e.g. when building from source package)
+ # we can extract the package version from .version file.
+ VER=`< .version`
+else
+ # The package version cannot be retrieved.
+ VER="Unknown"
+fi
+
+printf '%s' "$VER"
+
diff --git a/vendor/sustrik/libdill/poll.c.inc b/vendor/sustrik/libdill/poll.c.inc
new file mode 100644
index 0000000..a575799
--- /dev/null
+++ b/vendor/sustrik/libdill/poll.c.inc
@@ -0,0 +1,227 @@
+/*
+
+ Copyright (c) 2016 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#include
+#include
+#include
+#include
+#include
+
+#include "cr.h"
+#include "list.h"
+#include "pollset.h"
+#include "utils.h"
+#include "ctx.h"
+
+/*
+
+ ctx->pollset_size
+ |
+ ctx->pollset V
+ +-------+-------+-------+-----+-------+--------------------------------+
+ | pfd 0 | pfd 1 | pfd 2 | ... | pfd N | empty |
+ +-------+-------+-------+-----+-------+--------------------------------+
+ ^ ^ ^
+ | | |
+ idx +------idx------+ |
+ | | |
+ +------+------+------+----------------------------------------+--------+
+ | fd=0 | fd=1 | fd=2 | ... | fd=max |
+ +------+------+------+----------------------------------------+--------+
+ ctx->fdinfos ^
+ |
+ ctx->nfdinfos
+
+*/
+
+/* Additional info about file descriptor. */
+struct dill_fdinfo {
+ /* Index of the file descriptor in the pollset.
+ -1 means the fd is not in the pollset. */
+ int idx;
+ /* Clause waiting for in. NULL if none. */
+ struct dill_fdclause *in;
+ /* Clause waiting for out. NULL if none. */
+ struct dill_fdclause *out;
+ /* 1 is the file descriptor was used before, 0 otherwise. */
+ unsigned int cached : 1;
+};
+
+int dill_ctx_pollset_init(struct dill_ctx_pollset *ctx) {
+ int err;
+ ctx->nfdinfos = dill_maxfds();
+ /* Allocate largest possible pollset. */
+ ctx->pollset_size = 0;
+ ctx->pollset = malloc(sizeof(struct pollfd) * ctx->nfdinfos);
+ if(dill_slow(!ctx->pollset)) {err = ENOMEM; goto error1;}
+ ctx->fdinfos = malloc(sizeof(struct dill_fdinfo) * ctx->nfdinfos);
+ if(dill_slow(!ctx->fdinfos)) {err = ENOMEM; goto error2;}
+ /* Intialise fd infos. There's no fd in the pollset,
+ so set all indices to -1. */
+ int i;
+ for(i = 0; i != ctx->nfdinfos; ++i) {
+ ctx->fdinfos[i].idx = -1;
+ ctx->fdinfos[i].in = NULL;
+ ctx->fdinfos[i].out = NULL;
+ ctx->fdinfos[i].cached = 0;
+ }
+ return 0;
+error2:
+ free(ctx->pollset);
+ ctx->pollset = NULL;
+error1:
+ errno = err;
+ return -1;
+}
+
+void dill_ctx_pollset_term(struct dill_ctx_pollset *ctx) {
+ free(ctx->pollset);
+ free(ctx->fdinfos);
+}
+
+static void dill_fdcancelin(struct dill_clause *cl) {
+ struct dill_ctx_pollset *ctx = &dill_getctx->pollset;
+ struct dill_fdinfo *fdi = dill_cont(cl, struct dill_fdclause, cl)->fdinfo;
+ fdi->in = NULL;
+ ctx->pollset[fdi->idx].events &= ~POLLIN;
+ /* fd is left in the pollset. It will be purged once the event loop
+ iterates once more. */
+}
+
+static void dill_fdcancelout(struct dill_clause *cl) {
+ struct dill_ctx_pollset *ctx = &dill_getctx->pollset;
+ struct dill_fdinfo *fdi = dill_cont(cl, struct dill_fdclause, cl)->fdinfo;
+ fdi->out = NULL;
+ ctx->pollset[fdi->idx].events &= ~POLLOUT;
+ /* fd is left in the pollset. It will be purged once the event loop
+ iterates once more. */
+}
+
+int dill_pollset_in(struct dill_fdclause *fdcl, int id, int fd) {
+ struct dill_ctx_pollset *ctx = &dill_getctx->pollset;
+ if(dill_slow(fd < 0 || fd >= ctx->nfdinfos)) {errno = EBADF; return -1;}
+ struct dill_fdinfo *fdi = &ctx->fdinfos[fd];
+ if(dill_slow(!fdi->cached)) {
+ int flags = fcntl(fd, F_GETFD);
+ if(flags < 0 && errno == EBADF) return -1;
+ dill_assert(flags >= 0);
+ fdi->cached = 1;
+ }
+ if(fdi->idx < 0) {
+ fdi->idx = ctx->pollset_size;
+ ++ctx->pollset_size;
+ ctx->pollset[fdi->idx].fd = fd;
+ }
+ if(dill_slow(fdi->in)) {errno = EBUSY; return -1;}
+ ctx->pollset[fdi->idx].events |= POLLIN;
+ fdcl->fdinfo = fdi;
+ fdi->in = fdcl;
+ dill_waitfor(&fdcl->cl, id, dill_fdcancelin);
+ return 0;
+}
+
+int dill_pollset_out(struct dill_fdclause *fdcl, int id, int fd) {
+ struct dill_ctx_pollset *ctx = &dill_getctx->pollset;
+ if(dill_slow(fd < 0 || fd >= ctx->nfdinfos)) {errno = EBADF; return -1;}
+ struct dill_fdinfo *fdi = &ctx->fdinfos[fd];
+ if(dill_slow(!fdi->cached)) {
+ int flags = fcntl(fd, F_GETFD);
+ if(flags < 0 && errno == EBADF) return -1;
+ dill_assert(flags >= 0);
+ fdi->cached = 1;
+ }
+ if(fdi->idx < 0) {
+ fdi->idx = ctx->pollset_size;
+ ++ctx->pollset_size;
+ ctx->pollset[fdi->idx].fd = fd;
+ }
+ if(dill_slow(fdi->out)) {errno = EBUSY; return -1;}
+ ctx->pollset[fdi->idx].events |= POLLOUT;
+ fdcl->fdinfo = fdi;
+ fdi->out = fdcl;
+ dill_waitfor(&fdcl->cl, id, dill_fdcancelout);
+ return 0;
+}
+
+int dill_pollset_clean(int fd) {
+ struct dill_ctx_pollset *ctx = &dill_getctx->pollset;
+ struct dill_fdinfo *fdi = &ctx->fdinfos[fd];
+ if(!fdi->cached) return 0;
+ if(dill_slow(fdi->in || fdi->out)) {errno = EBUSY; return -1;}
+ /* If the fd happens to still be in the pollset remove it. */
+ if(fdi->idx >= 0) {
+ --ctx->pollset_size;
+ if(fdi->idx != ctx->pollset_size) {
+ struct pollfd *pfd = &ctx->pollset[fdi->idx];
+ struct pollfd *lastpfd = &ctx->pollset[ctx->pollset_size];
+ *pfd = *lastpfd;
+ ctx->fdinfos[pfd->fd].idx = fdi->idx;
+ }
+ fdi->idx = -1;
+ }
+ fdi->cached = 0;
+ return 0;
+}
+
+int dill_pollset_poll(int timeout) {
+ struct dill_ctx_pollset *ctx = &dill_getctx->pollset;
+ /* Wait for events. */
+ int numevs = poll(ctx->pollset, ctx->pollset_size, timeout);
+ if(numevs < 0 && errno == EINTR) return -1;
+ dill_assert(numevs >= 0);
+ /* Fire file descriptor events as needed. */
+ int i;
+ for(i = 0; i != ctx->pollset_size; ++i) {
+ struct pollfd *pfd = &ctx->pollset[i];
+ struct dill_fdinfo *fdi = &ctx->fdinfos[pfd->fd];
+ /* Resume the blocked coroutines. */
+ if(fdi->in &&
+ pfd->revents & (POLLIN | POLLERR | POLLHUP | POLLNVAL)) {
+ pfd->events &= ~POLLIN;
+ dill_trigger(&fdi->in->cl, 0);
+ }
+ if(fdi->out &&
+ pfd->revents & (POLLOUT | POLLERR | POLLHUP | POLLNVAL)) {
+ pfd->events &= ~POLLOUT;
+ dill_trigger(&fdi->out->cl, 0);
+ }
+ /* If nobody is polling for the fd remove it from the pollset. */
+ if(!pfd->events) {
+ fdi->idx = -1;
+ dill_assert(!fdi->in && !fdi->out);
+ --ctx->pollset_size;
+ /* Pollset has to be compact. Thus, unless we are removing the
+ last item from the pollset we want to move the last item
+ to the vacant slot left by the removed fd. */
+ if(i != ctx->pollset_size) {
+ struct pollfd *lastpfd = &ctx->pollset[ctx->pollset_size];
+ *pfd = *lastpfd;
+ ctx->fdinfos[pfd->fd].idx = i;
+ }
+ --i;
+ }
+ }
+ return numevs > 0;
+}
+
diff --git a/vendor/sustrik/libdill/poll.h.inc b/vendor/sustrik/libdill/poll.h.inc
new file mode 100644
index 0000000..5a26b70
--- /dev/null
+++ b/vendor/sustrik/libdill/poll.h.inc
@@ -0,0 +1,48 @@
+/*
+
+ Copyright (c) 2016 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#ifndef DILL_POLL_INCLUDED
+#define DILL_POLL_INCLUDED
+
+#include
+
+#include "cr.h"
+#include "list.h"
+
+struct dill_fdinfo;
+
+struct dill_fdclause {
+ struct dill_clause cl;
+ struct dill_fdinfo *fdinfo;
+};
+
+struct dill_ctx_pollset {
+ /* Pollset, as used by poll(2). */
+ int pollset_size;
+ struct pollfd *pollset;
+ /* Info about all file descriptors.
+ File descriptors are used as indices in this array. */
+ int nfdinfos;
+ struct dill_fdinfo *fdinfos;
+};
+
+#endif
diff --git a/vendor/sustrik/libdill/pollset.c b/vendor/sustrik/libdill/pollset.c
new file mode 100644
index 0000000..da306d9
--- /dev/null
+++ b/vendor/sustrik/libdill/pollset.c
@@ -0,0 +1,41 @@
+/*
+
+ Copyright (c) 2016 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+/* Include the poll-mechanism-specific stuff. */
+
+/* User overloads. */
+#if defined DILL_EPOLL
+#include "epoll.c.inc"
+#elif defined DILL_KQUEUE
+#include "kqueue.c.inc"
+#elif defined DILL_POLL
+#include "poll.c.inc"
+/* Defaults. */
+#elif defined HAVE_EPOLL
+#include "epoll.c.inc"
+#elif defined HAVE_KQUEUE
+#include "kqueue.c.inc"
+#else
+#include "poll.c.inc"
+#endif
diff --git a/vendor/sustrik/libdill/pollset.h b/vendor/sustrik/libdill/pollset.h
new file mode 100644
index 0000000..0a8d6f8
--- /dev/null
+++ b/vendor/sustrik/libdill/pollset.h
@@ -0,0 +1,61 @@
+/*
+
+ Copyright (c) 2016 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#ifndef DILL_POLLSET_INCLUDED
+#define DILL_POLLSET_INCLUDED
+
+/* User overloads. */
+#if defined DILL_EPOLL
+#include "epoll.h.inc"
+#elif defined DILL_KQUEUE
+#include "kqueue.h.inc"
+#elif defined DILL_POLL
+#include "poll.h.inc"
+/* Defaults. */
+#elif defined HAVE_EPOLL
+#include "epoll.h.inc"
+#elif defined HAVE_KQUEUE
+#include "kqueue.h.inc"
+#else
+#include "poll.h.inc"
+#endif
+
+int dill_ctx_pollset_init(struct dill_ctx_pollset *ctx);
+void dill_ctx_pollset_term(struct dill_ctx_pollset *ctx);
+
+/* Add waiting for an in event on the fd to the list of current clauses. */
+int dill_pollset_in(struct dill_fdclause *fdcl, int id, int fd);
+
+/* Add waiting for an out event on the fd to the list of current clauses. */
+int dill_pollset_out(struct dill_fdclause *fdcl, int id, int fd);
+
+/* Drop any cached info about the file descriptor. */
+int dill_pollset_clean(int fd);
+
+/* Wait for events. 'timeout' is in milliseconds. Return 0 if the timeout expired or
+ 1 if at least one clause was triggered. */
+int dill_pollset_poll(int timeout);
+
+#endif
+
diff --git a/vendor/sustrik/libdill/qlist.h b/vendor/sustrik/libdill/qlist.h
new file mode 100644
index 0000000..4192175
--- /dev/null
+++ b/vendor/sustrik/libdill/qlist.h
@@ -0,0 +1,67 @@
+/*
+
+ Copyright (c) 2016 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#ifndef DILL_QLIST_INCLUDED
+#define DILL_QLIST_INCLUDED
+
+#include "slist.h"
+#include "utils.h"
+
+/* Singly-linked list that's first-in-first-out, so it's actually a queue.
+ To iterate over the items, use the underlying slist. */
+
+struct dill_qlist {
+ struct dill_slist slist;
+ struct dill_slist *last;
+};
+
+/* Initialize the list. */
+static inline void dill_qlist_init(struct dill_qlist *self) {
+ dill_slist_init(&self->slist);
+ self->last = &self->slist;
+}
+
+/* True if the list has no items. */
+static inline int dill_qlist_empty(struct dill_qlist *self) {
+ return self->slist.next == &self->slist;
+}
+
+/* Push an item to the end of the list. */
+static inline void dill_qlist_push(struct dill_qlist *self,
+ struct dill_slist *item) {
+ item->next = &self->slist;
+ self->last->next = item;
+ self->last = item;
+}
+
+/* Pop an item from the beginning of the list. */
+static inline struct dill_slist *dill_qlist_pop(struct dill_qlist *self) {
+ struct dill_slist *item = self->slist.next;
+ self->slist.next = item->next;
+ if(item == self->last) self->last = &self->slist;
+ return item;
+}
+
+#endif
+
diff --git a/vendor/sustrik/libdill/rbtree.c b/vendor/sustrik/libdill/rbtree.c
new file mode 100644
index 0000000..efdfe74
--- /dev/null
+++ b/vendor/sustrik/libdill/rbtree.c
@@ -0,0 +1,279 @@
+/*
+
+ Copyright (c) 2016 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+
+/* This implementation is based on Emin Martinian's implementation of the algorithm:
+ http://web.mit.edu/~emin/Desktop/ref_to_emin/www.old/source_code/red_black_tree/index.html */
+
+#include
+
+#include "rbtree.h"
+
+void dill_rbtree_init(struct dill_rbtree *self) {
+ struct dill_rbtree_item *temp;
+ temp = &self->nil;
+ temp->up = temp->left = temp->right=temp;
+ temp->red = 0;
+ temp->val = 0;
+ temp = &self->root;
+ temp->up = temp->left=temp->right = &self->nil;
+ temp->val = 0;
+ temp->red = 0;
+}
+
+static void dill_rbtree_lrotate(struct dill_rbtree* self,
+ struct dill_rbtree_item* x) {
+ struct dill_rbtree_item *y;
+ struct dill_rbtree_item *nil = &self->nil;
+ y = x->right;
+ x->right = y->left;
+ if(y->left != nil) y->left->up=x;
+ y->up = x->up;
+ if(x == x->up->left) {
+ x->up->left = y;
+ } else {
+ x->up->right = y;
+ }
+ y->left = x;
+ x->up = y;
+}
+
+static void dill_rbtree_rrotate(struct dill_rbtree *self,
+ struct dill_rbtree_item *y) {
+ struct dill_rbtree_item *x;
+ struct dill_rbtree_item *nil = &self->nil;
+ x = y->left;
+ y->left = x->right;
+ if(nil != x->right) x->right->up = y;
+ x->up = y->up;
+ if(y == y->up->left) {
+ y->up->left = x;
+ } else {
+ y->up->right = x;
+ }
+ x->right = y;
+ y->up = x;
+}
+
+static void dill_rbtree_insert_help(struct dill_rbtree *self,
+ struct dill_rbtree_item *z) {
+ struct dill_rbtree_item *x;
+ struct dill_rbtree_item *y;
+ struct dill_rbtree_item *nil = &self->nil;
+ z->left = z->right = nil;
+ y = &self->root;
+ x = self->root.left;
+ while(x != nil) {
+ y=x;
+ if(x->val > z->val) {
+ x = x->left;
+ } else {
+ x = x->right;
+ }
+ }
+ z->up = y;
+ if((y == &self->root) || (y->val > z->val)) {
+ y->left = z;
+ } else {
+ y->right = z;
+ }
+}
+
+void dill_rbtree_insert(struct dill_rbtree *tree, int64_t val,
+ struct dill_rbtree_item *item) {
+ struct dill_rbtree_item *y;
+ struct dill_rbtree_item *x;
+ x = item;
+ x->val = val;
+ dill_rbtree_insert_help(tree, x);
+ x->red = 1;
+ while(x->up->red) {
+ if(x->up == x->up->up->left) {
+ y = x->up->up->right;
+ if(y->red) {
+ x->up->red = 0;
+ y->red = 0;
+ x->up->up->red = 1;
+ x = x->up->up;
+ } else {
+ if(x == x->up->right) {
+ x = x->up;
+ dill_rbtree_lrotate(tree, x);
+ }
+ x->up->red = 0;
+ x->up->up->red = 1;
+ dill_rbtree_rrotate(tree, x->up->up);
+ }
+ } else {
+ y = x->up->up->left;
+ if(y->red) {
+ x->up->red = 0;
+ y->red = 0;
+ x->up->up->red = 1;
+ x = x->up->up;
+ } else {
+ if(x == x->up->left) {
+ x = x->up;
+ dill_rbtree_rrotate(tree, x);
+ }
+ x->up->red = 0;
+ x->up->up->red = 1;
+ dill_rbtree_lrotate(tree, x->up->up);
+ }
+ }
+ }
+ tree->root.left->red = 0;
+}
+
+int dill_rbtree_empty(struct dill_rbtree *self) {
+ struct dill_rbtree_item* nil = &self->nil;
+ return self->root.left == nil;
+}
+
+struct dill_rbtree_item *dill_rbtree_first(struct dill_rbtree *self) {
+ struct dill_rbtree_item* nil = &self->nil;
+ struct dill_rbtree_item *x = self->root.left;
+ if(x == nil) return NULL;
+ while(x->left != nil) x = x->left;
+ return x;
+}
+
+static struct dill_rbtree_item *dill_rbtree_next_help(struct dill_rbtree *tree,
+ struct dill_rbtree_item *x) {
+ struct dill_rbtree_item *y;
+ struct dill_rbtree_item *nil = &tree->nil;
+ struct dill_rbtree_item *root = &tree->root;
+ if(nil != (y = x->right)) {
+ while(y->left != nil) {
+ y = y->left;
+ }
+ return y;
+ } else {
+ y = x->up;
+ while(x == y->right) {
+ x = y;
+ y = y->up;
+ }
+ if(y == root) return nil;
+ return y;
+ }
+}
+
+struct dill_rbtree_item *dill_rbtree_next(struct dill_rbtree *tree,
+ struct dill_rbtree_item *x) {
+ struct dill_rbtree_item *it = dill_rbtree_next_help(tree, x);
+ if(it == &tree->nil) return NULL;
+ return it;
+}
+
+static void dill_rbtree_fixup(struct dill_rbtree *tree,
+ struct dill_rbtree_item *x) {
+ struct dill_rbtree_item *root = tree->root.left;
+ struct dill_rbtree_item *w;
+ while((!x->red) && (root != x)) {
+ if (x == x->up->left) {
+ w = x->up->right;
+ if(w->red) {
+ w->red = 0;
+ x->up->red = 1;
+ dill_rbtree_lrotate(tree, x->up);
+ w = x->up->right;
+ }
+ if((!w->right->red) && (!w->left->red)) {
+ w->red = 1;
+ x = x->up;
+ } else {
+ if(!w->right->red) {
+ w->left->red = 0;
+ w->red = 1;
+ dill_rbtree_rrotate(tree, w);
+ w = x->up->right;
+ }
+ w->red = x->up->red;
+ x->up->red = 0;
+ w->right->red = 0;
+ dill_rbtree_lrotate(tree, x->up);
+ x = root;
+ }
+ } else {
+ w = x->up->left;
+ if(w->red) {
+ w->red = 0;
+ x->up->red = 1;
+ dill_rbtree_rrotate(tree, x->up);
+ w = x->up->left;
+ }
+ if((!w->right->red) && (!w->left->red)) {
+ w->red = 1;
+ x = x->up;
+ } else {
+ if(!w->left->red) {
+ w->right->red = 0;
+ w->red = 1;
+ dill_rbtree_lrotate(tree, w);
+ w = x->up->left;
+ }
+ w->red = x->up->red;
+ x->up->red = 0;
+ w->left->red = 0;
+ dill_rbtree_rrotate(tree, x->up);
+ x = root;
+ }
+ }
+ }
+ x->red = 0;
+}
+
+void dill_rbtree_erase(struct dill_rbtree *tree, struct dill_rbtree_item *z) {
+ struct dill_rbtree_item *y;
+ struct dill_rbtree_item *x;
+ struct dill_rbtree_item *nil=&tree->nil;
+ struct dill_rbtree_item *root=&tree->root;
+ y = ((z->left == nil) || (z->right == nil)) ? z :
+ dill_rbtree_next_help(tree, z);
+ x = (y->left == nil) ? y->right : y->left;
+ if(root == (x->up = y->up)) {
+ root->left = x;
+ } else {
+ if(y == y->up->left) {
+ y->up->left = x;
+ } else {
+ y->up->right = x;
+ }
+ }
+ if(y != z) {
+ if(!(y->red)) dill_rbtree_fixup(tree, x);
+ y->left = z->left;
+ y->right = z->right;
+ y->up = z->up;
+ y->red = z->red;
+ z->left->up = z->right->up=y;
+ if(z == z->up->left) {
+ z->up->left=y;
+ } else {
+ z->up->right=y;
+ }
+ } else {
+ if(!(y->red)) dill_rbtree_fixup(tree, x);
+ }
+}
+
diff --git a/vendor/sustrik/libdill/rbtree.h b/vendor/sustrik/libdill/rbtree.h
new file mode 100644
index 0000000..42f9b5f
--- /dev/null
+++ b/vendor/sustrik/libdill/rbtree.h
@@ -0,0 +1,66 @@
+/*
+
+ Copyright (c) 2016 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#ifndef DILL_RBTREE_INCLUDED
+#define DILL_RBTREE_INCLUDED
+
+#include
+#include
+
+struct dill_rbtree_item {
+ int red;
+ struct dill_rbtree_item *left;
+ struct dill_rbtree_item *right;
+ struct dill_rbtree_item *up;
+ int64_t val;
+};
+
+struct dill_rbtree {
+ struct dill_rbtree_item root;
+ struct dill_rbtree_item nil;
+};
+
+/* Initialize the tree. */
+void dill_rbtree_init(struct dill_rbtree *self);
+
+/* Returns 1 if there are no items in the tree. 0 otherwise. */
+int dill_rbtree_empty(struct dill_rbtree *self);
+
+/* Insert an item into the tree & set its value to 'val' */
+void dill_rbtree_insert(struct dill_rbtree *self, int64_t val,
+ struct dill_rbtree_item *item);
+
+/* Remove an item from a tree. */
+void dill_rbtree_erase(struct dill_rbtree *self, struct dill_rbtree_item *item);
+
+/* Return an item with the lowest value. If there are no items in the tree, NULL
+ is returned. */
+struct dill_rbtree_item *dill_rbtree_first(struct dill_rbtree *self);
+
+/* Iterate through the tree. Items are returned starting with those with
+ the lowest values and ending with those with the highest values. Items with
+ equal values are returned in no particular order. If 'it' points to the
+ last item, NULL is returned. */
+struct dill_rbtree_item *dill_rbtree_next(struct dill_rbtree *self,
+ struct dill_rbtree_item *it);
+
+#endif
diff --git a/vendor/sustrik/libdill/slist.h b/vendor/sustrik/libdill/slist.h
new file mode 100644
index 0000000..8408ef8
--- /dev/null
+++ b/vendor/sustrik/libdill/slist.h
@@ -0,0 +1,67 @@
+/*
+
+ Copyright (c) 2016 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#ifndef DILL_SLIST_INCLUDED
+#define DILL_SLIST_INCLUDED
+
+#include "utils.h"
+
+/* A Singly-linked list that's last-in-first-out, so it's actually a stack.
+ To prevent confusion with C's call stack, we'll call it slist. */
+
+struct dill_slist {
+ struct dill_slist *next;
+};
+
+/* Initialize the list. */
+static inline void dill_slist_init(struct dill_slist *self) {
+ self->next = self;
+}
+
+/* True if the list has no items. */
+static inline int dill_slist_empty(struct dill_slist *self) {
+ return self->next == self;
+}
+
+/* Returns the next item in the list. If 'it' is the list itself, it returns the
+ first element in the list. If there are no more elements in the list,
+ returns a pointer to the list itself. */
+#define dill_slist_next(it) ((it)->next)
+
+/* Push the item to the beginning of the list. */
+static inline void dill_slist_push(struct dill_slist *self,
+ struct dill_slist *item) {
+ item->next = self->next;
+ self->next = item;
+}
+
+/* Pop an item from the beginning of the list. */
+static inline struct dill_slist *dill_slist_pop(struct dill_slist *self) {
+ struct dill_slist *item = self->next;
+ self->next = item->next;
+ return item;
+}
+
+#endif
+
diff --git a/vendor/sustrik/libdill/stack.c b/vendor/sustrik/libdill/stack.c
new file mode 100644
index 0000000..b13453e
--- /dev/null
+++ b/vendor/sustrik/libdill/stack.c
@@ -0,0 +1,150 @@
+/*
+
+ Copyright (c) 2016 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "stack.h"
+#include "utils.h"
+#include "ctx.h"
+
+/* The stacks are cached. The advantage of this is twofold. First, caching is
+ faster than malloc(). Second, it results in fewer calls to
+ mprotect(). */
+
+/* Stack size in bytes. */
+static size_t dill_stack_size = 256 * 1024;
+/* Maximum number of unused cached stacks. Must be at least 1. */
+static int dill_max_cached_stacks = 64;
+
+/* Returns the smallest value that's greater than val and is a multiple of unit. */
+static size_t dill_align(size_t val, size_t unit) {
+ return val % unit ? val + unit - val % unit : val;
+}
+
+/* Get memory page size. The query is done once. The value is cached. */
+static size_t dill_page_size(void) {
+ static long pgsz = 0;
+ if(dill_fast(pgsz))
+ return (size_t)pgsz;
+ pgsz = sysconf(_SC_PAGE_SIZE);
+ dill_assert(pgsz > 0);
+ return (size_t)pgsz;
+}
+
+int dill_ctx_stack_init(struct dill_ctx_stack *ctx) {
+ ctx->count = 0;
+ dill_slist_init(&ctx->cache);
+ return 0;
+}
+
+void dill_ctx_stack_term(struct dill_ctx_stack *ctx) {
+ /* Deallocate leftover coroutines. */
+ struct dill_slist *it;
+ while((it = dill_slist_pop(&ctx->cache)) != &ctx->cache) {
+#if (HAVE_POSIX_MEMALIGN && HAVE_MPROTECT) & !defined DILL_NOGUARD
+ void *ptr = ((uint8_t*)(it + 1)) - dill_stack_size - dill_page_size();
+ int rc = mprotect(ptr, dill_page_size(), PROT_READ|PROT_WRITE);
+ dill_assert(rc == 0);
+ free(ptr);
+#else
+ void *ptr = ((uint8_t*)(it + 1)) - dill_stack_size;
+ free(ptr);
+#endif
+ }
+}
+
+void *dill_allocstack(size_t *stack_size) {
+ struct dill_ctx_stack *ctx = &dill_getctx->stack;
+ if(stack_size)
+ *stack_size = dill_stack_size;
+ /* If there's a cached stack, use it. */
+ if(!dill_slist_empty(&ctx->cache)) {
+ --ctx->count;
+ return (void*)(dill_slist_pop(&ctx->cache) + 1);
+ }
+ /* Allocate a new stack. */
+ uint8_t *top;
+#if (HAVE_POSIX_MEMALIGN && HAVE_MPROTECT) & !defined DILL_NOGUARD
+ /* Allocate the stack so that it's memory-page-aligned.
+ Add one page as a stack overflow guard. */
+ size_t sz = dill_align(dill_stack_size, dill_page_size()) +
+ dill_page_size();
+ uint8_t *ptr;
+ int rc = posix_memalign((void**)&ptr, dill_page_size(), sz);
+ if(dill_slow(rc != 0)) {
+ errno = rc;
+ return NULL;
+ }
+ /* The bottom page is used as a stack guard. This way a stack overflow will
+ cause a segfault instead of randomly overwriting the heap. */
+ rc = mprotect(ptr, dill_page_size(), PROT_NONE);
+ if(dill_slow(rc != 0)) {
+ int err = errno;
+ free(ptr);
+ errno = err;
+ return NULL;
+ }
+ top = ptr + dill_page_size() + dill_stack_size;
+#else
+ /* Simple allocation without a guard page. */
+ uint8_t *ptr = malloc(dill_stack_size);
+ if(dill_slow(!ptr)) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ top = ptr + dill_stack_size;
+#endif
+ return top;
+}
+
+void dill_freestack(void *stack) {
+ struct dill_ctx_stack *ctx = &dill_getctx->stack;
+ struct dill_slist *item = ((struct dill_slist*)stack) - 1;
+ /* If the cache is full we will deallocate one stack from the cache.
+ We can't deallocate the stack passed to this function directly because
+ this very function can be still executing on that stack. */
+ if(ctx->count >= dill_max_cached_stacks) {
+ struct dill_slist *old = dill_slist_pop(&ctx->cache);
+ --ctx->count;
+#if (HAVE_POSIX_MEMALIGN && HAVE_MPROTECT) & !defined DILL_NOGUARD
+ void *ptr = ((uint8_t*)(old + 1)) - dill_stack_size - dill_page_size();
+ int rc = mprotect(ptr, dill_page_size(), PROT_READ|PROT_WRITE);
+ dill_assert(rc == 0);
+ free(ptr);
+#else
+ void *ptr = ((uint8_t*)(old + 1)) - dill_stack_size;
+ free(ptr);
+#endif
+ }
+ /* Put the stack into the cache. */
+ dill_slist_push(&ctx->cache, item);
+ ++ctx->count;
+}
+
diff --git a/vendor/sustrik/libdill/stack.h b/vendor/sustrik/libdill/stack.h
new file mode 100644
index 0000000..d0010a4
--- /dev/null
+++ b/vendor/sustrik/libdill/stack.h
@@ -0,0 +1,51 @@
+/*
+
+ Copyright (c) 2016 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#ifndef DILL_STACK_INCLUDED
+#define DILL_STACK_INCLUDED
+
+#include
+
+#include "slist.h"
+
+/* A stack of unused coroutine stacks. This allows for extra-fast allocation
+ of a new stack. The LIFO nature of this structure minimises cache misses.
+ When the stack is cached its dill_qlist_item is placed on its top rather
+ then on the bottom. That way we minimise page misses. */
+struct dill_ctx_stack {
+ int count;
+ struct dill_slist cache;
+};
+
+int dill_ctx_stack_init(struct dill_ctx_stack *ctx);
+void dill_ctx_stack_term(struct dill_ctx_stack *ctx);
+
+/* Allocates new stack. Returns pointer to the *top* of the stack.
+ For now we assume that the stack grows downwards. */
+void *dill_allocstack(size_t *stack_size);
+
+/* Deallocates a stack. The argument is pointer to the top of the stack. */
+void dill_freestack(void *stack);
+
+#endif
diff --git a/vendor/sustrik/libdill/utils.c b/vendor/sustrik/libdill/utils.c
new file mode 100644
index 0000000..eb3582d
--- /dev/null
+++ b/vendor/sustrik/libdill/utils.c
@@ -0,0 +1,338 @@
+/*
+
+ Copyright (c) 2017 Martin Sustrik
+ Copyright (c) 2014 Wirebird Labs LLC. All rights reserved.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "utils.h"
+
+void dill_print_size(char x) {
+}
+
+int dill_maxfds(void) {
+ /* Get the maximum number of file descriptors. */
+ struct rlimit rlim;
+ int rc = getrlimit(RLIMIT_NOFILE, &rlim);
+ dill_assert(rc == 0);
+ int maxfds = rlim.rlim_max;
+#if defined BSD
+ /* On newer versions of OSX, the above behaves weirdly and returns -1,
+ so use OPEN_MAX instead. */
+ if(maxfds < 0) return OPEN_MAX;
+#endif
+ return maxfds;
+}
+
+uint16_t dill_gets(const uint8_t *buf) {
+ return (((uint16_t)buf[0]) << 8) |
+ ((uint16_t)buf[1]);
+}
+
+void dill_puts(uint8_t *buf, uint16_t val) {
+ buf[0] = (uint8_t)(((val) >> 8) & 0xff);
+ buf[1] = (uint8_t)(val & 0xff);
+}
+
+uint32_t dill_getl(const uint8_t *buf) {
+ return (((uint32_t)buf[0]) << 24) |
+ (((uint32_t)buf[1]) << 16) |
+ (((uint32_t)buf[2]) << 8) |
+ ((uint32_t)buf[3]);
+}
+
+void dill_putl(uint8_t *buf, uint32_t val) {
+ buf[0] = (uint8_t)(((val) >> 24) & 0xff);
+ buf[1] = (uint8_t)(((val) >> 16) & 0xff);
+ buf[2] = (uint8_t)(((val) >> 8) & 0xff);
+ buf[3] = (uint8_t)(val & 0xff);
+}
+
+uint64_t dill_getll(const uint8_t *buf) {
+ return (((uint64_t)buf[0]) << 56) |
+ (((uint64_t)buf[1]) << 48) |
+ (((uint64_t)buf[2]) << 40) |
+ (((uint64_t)buf[3]) << 32) |
+ (((uint64_t)buf[4]) << 24) |
+ (((uint64_t)buf[5]) << 16) |
+ (((uint64_t)buf[6]) << 8) |
+ (((uint64_t)buf[7] << 0));
+}
+
+void dill_putll(uint8_t *buf, uint64_t val) {
+ buf[0] = (uint8_t)((val >> 56) & 0xff);
+ buf[1] = (uint8_t)((val >> 48) & 0xff);
+ buf[2] = (uint8_t)((val >> 40) & 0xff);
+ buf[3] = (uint8_t)((val >> 32) & 0xff);
+ buf[4] = (uint8_t)((val >> 24) & 0xff);
+ buf[5] = (uint8_t)((val >> 16) & 0xff);
+ buf[6] = (uint8_t)((val >> 8) & 0xff);
+ buf[7] = (uint8_t)(val & 0xff);
+}
+
+const char *dill_lstrip(const char *string, char delim) {
+ const char *pos = string;
+ while(*pos && *pos == delim) ++pos;
+ return pos;
+}
+
+const char *dill_rstrip(const char *string, char delim) {
+ const char *end = string + strlen(string) - 1;
+ while(end > string && *end == delim) --end;
+ return ++end;
+}
+
+int dill_random(uint8_t *buf, size_t len) {
+ static int fd = -1;
+ if(dill_slow(fd < 0)) {
+ fd = open("/dev/urandom", O_RDONLY);
+ dill_assert(fd >= 0);
+ }
+ ssize_t sz = read(fd, buf, len);
+ if(dill_slow(sz < 0)) return -1;
+ dill_assert(sz == len);
+ return 0;
+}
+
+int dill_base64_decode(const char *in, size_t in_len, uint8_t *out,
+ size_t out_len) {
+ unsigned ii;
+ unsigned io;
+ unsigned rem;
+ uint32_t v;
+ uint8_t ch;
+
+ /* Unrolled lookup of ASCII code points.
+ 0xFF represents a non-base64 valid character. */
+ const uint8_t DECODEMAP[256] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xFF, 0xFF, 0x3F,
+ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B,
+ 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xFF,
+ 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
+ 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+ 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+ 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
+ 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+ for(io = 0, ii = 0, v = 0, rem = 0; ii < in_len; ii++) {
+ if(isspace(in[ii]))
+ continue;
+ if(in[ii] == '=')
+ break;
+ ch = DECODEMAP[(int)(in[ii])];
+ /* Discard invalid characters as per RFC 2045. */
+ if(ch == 0xFF)
+ break;
+ v = (v << 6) | ch;
+ rem += 6;
+ if (rem >= 8) {
+ rem -= 8;
+ if(io >= out_len)
+ return -1;
+ out[io++] = (v >> rem) & 255;
+ }
+ }
+ if(rem >= 8) {
+ rem -= 8;
+ if(io >= out_len)
+ return -1;
+ out[io++] = (v >> rem) & 255;
+ }
+ return io;
+}
+
+int dill_base64_encode(const uint8_t *in, size_t in_len, char *out,
+ size_t out_len) {
+ unsigned ii;
+ unsigned io;
+ unsigned rem;
+ uint32_t v;
+ uint8_t ch;
+
+ const uint8_t ENCODEMAP[64] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
+
+ for(io = 0, ii = 0, v = 0, rem = 0; ii < in_len; ii++) {
+ ch = in[ii];
+ v = (v << 8) | ch;
+ rem += 8;
+ while(rem >= 6) {
+ rem -= 6;
+ if(io >= out_len)
+ return -1;
+ out[io++] = ENCODEMAP[(v >> rem) & 63];
+ }
+ }
+ if(rem) {
+ v <<= (6 - rem);
+ if(io >= out_len)
+ return -1;
+ out[io++] = ENCODEMAP[v & 63];
+ }
+ /* Pad to a multiple of 3. */
+ while(io & 3) {
+ if(io >= out_len)
+ return -1;
+ out[io++] = '=';
+ }
+ if(io >= out_len)
+ return -1;
+ out[io] = '\0';
+ return io;
+}
+
+#define dill_sha1_rol32(num,bits) ((num << bits) | (num >> (32 - bits)))
+
+void dill_sha1_init(struct dill_sha1 *self) {
+ /* Detect endianness. */
+ union {
+ uint32_t i;
+ char c[4];
+ } test = {0x00000001};
+ self->is_little_endian = test.c[0];
+ /* Initial state of the hash. */
+ self->state [0] = 0x67452301;
+ self->state [1] = 0xefcdab89;
+ self->state [2] = 0x98badcfe;
+ self->state [3] = 0x10325476;
+ self->state [4] = 0xc3d2e1f0;
+ self->bytes_hashed = 0;
+ self->buffer_offset = 0;
+}
+
+static void dill_sha1_add(struct dill_sha1 *self, uint8_t data) {
+ uint8_t i;
+ uint32_t a, b, c, d, e, t;
+ uint8_t *const buf = (uint8_t*)self->buffer;
+ if(self->is_little_endian)
+ buf[self->buffer_offset ^ 3] = data;
+ else
+ buf[self->buffer_offset] = data;
+ self->buffer_offset++;
+ if(self->buffer_offset == DILL_SHA1_BLOCK_LEN) {
+ a = self->state[0];
+ b = self->state[1];
+ c = self->state[2];
+ d = self->state[3];
+ e = self->state[4];
+ for(i = 0; i < 80; i++) {
+ if(i >= 16) {
+ t = self->buffer[(i + 13) & 15] ^
+ self->buffer[(i + 8) & 15] ^
+ self->buffer[(i + 2) & 15] ^
+ self->buffer[i & 15];
+ self->buffer[i & 15] = dill_sha1_rol32(t, 1);
+ }
+ if (i < 20)
+ t = (d ^ (b & (c ^ d))) + 0x5A827999;
+ else if(i < 40)
+ t = (b ^ c ^ d) + 0x6ED9EBA1;
+ else if(i < 60)
+ t = ((b & c) | (d & (b | c))) + 0x8F1BBCDC;
+ else
+ t = (b ^ c ^ d) + 0xCA62C1D6;
+ t += dill_sha1_rol32(a, 5) + e + self->buffer[i & 15];
+ e = d;
+ d = c;
+ c = dill_sha1_rol32(b, 30);
+ b = a;
+ a = t;
+ }
+ self->state[0] += a;
+ self->state[1] += b;
+ self->state[2] += c;
+ self->state[3] += d;
+ self->state[4] += e;
+ self->buffer_offset = 0;
+ }
+}
+
+void dill_sha1_hashbyte(struct dill_sha1 *self, uint8_t data) {
+ ++self->bytes_hashed;
+ dill_sha1_add(self, data);
+}
+
+uint8_t *dill_sha1_result(struct dill_sha1 *self) {
+ int i;
+ /* Pad to complete the last block. */
+ dill_sha1_add(self, 0x80);
+ while(self->buffer_offset != 56)
+ dill_sha1_add (self, 0x00);
+ /* Append length in the last 8 bytes. SHA-1 supports 64-bit hashes, so
+ zero-pad the top bits. Shifting to multiply by 8 as SHA-1 supports
+ bit- as well as byte-streams. */
+ dill_sha1_add(self, 0);
+ dill_sha1_add(self, 0);
+ dill_sha1_add(self, 0);
+ dill_sha1_add(self, self->bytes_hashed >> 29);
+ dill_sha1_add(self, self->bytes_hashed >> 21);
+ dill_sha1_add(self, self->bytes_hashed >> 13);
+ dill_sha1_add(self, self->bytes_hashed >> 5);
+ dill_sha1_add(self, self->bytes_hashed << 3);
+ /* Correct byte order for little-endian systems. */
+ if(self->is_little_endian) {
+ for(i = 0; i < 5; i++) {
+ self->state[i] =
+ (((self->state[i]) << 24) & 0xFF000000) |
+ (((self->state[i]) << 8) & 0x00FF0000) |
+ (((self->state[i]) >> 8) & 0x0000FF00) |
+ (((self->state[i]) >> 24) & 0x000000FF);
+ }
+ }
+ /* 20-octet pointer to hash. */
+ return (uint8_t*)self->state;
+}
+
diff --git a/vendor/sustrik/libdill/utils.h b/vendor/sustrik/libdill/utils.h
new file mode 100644
index 0000000..6c43989
--- /dev/null
+++ b/vendor/sustrik/libdill/utils.h
@@ -0,0 +1,171 @@
+/*
+
+ Copyright (c) 2017 Martin Sustrik
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+*/
+
+#ifndef DILL_UTILS_H_INCLUDED
+#define DILL_UTILS_H_INCLUDED
+
+#include
+#include
+#include
+#include
+
+#define dill_concat(x,y) x##y
+
+/* Defines a unique identifier of type const void*. */
+#define dill_unique_id(name) \
+ static const int dill_concat(name, ___) = 0;\
+ const void *name = & dill_concat(name, ___);
+
+/* Takes a pointer to a member variable and computes pointer to the structure
+ that contains it. 'type' is type of the structure, not the member. */
+#define dill_cont(ptr, type, member) \
+ (ptr ? ((type*) (((char*) ptr) - offsetof(type, member))) : NULL)
+
+/* Compile-time assert. */
+#define DILL_CT_ASSERT_HELPER2(prefix, line) \
+ prefix##line
+#define DILL_CT_ASSERT_HELPER1(prefix, line) \
+ DILL_CT_ASSERT_HELPER2(prefix, line)
+#define DILL_CT_ASSERT(x) \
+ typedef int DILL_CT_ASSERT_HELPER1(ct_assert_,__COUNTER__) [(x) ? 1 : -1];
+
+/* DILL_CHECK_STORAGE checks whether struct 'type' fits into struct 'storage'.
+ If DILL_PRINT_SIZES macro is defined it will also print the size
+ of 'type'. */
+#if defined DILL_PRINT_SIZES
+#define DILL_CHECK_STORAGE(type, storage) \
+ static void dill_print_size_2_##type(void) { \
+ char x[sizeof(struct type)]; \
+ dill_print_size(&x); \
+ } \
+ DILL_CT_ASSERT(sizeof(struct type) <= sizeof(struct storage))
+#else
+#define DILL_CHECK_STORAGE(type, storage) \
+ DILL_CT_ASSERT(sizeof(struct type) <= sizeof(struct storage))
+#endif
+
+void dill_print_size(char x);
+
+/* Optimisation hints. */
+#if defined __GNUC__ || defined __llvm__
+#define dill_fast(x) __builtin_expect(!!(x), 1)
+#define dill_slow(x) __builtin_expect(!!(x), 0)
+#else
+#define dill_fast(x) (x)
+#define dill_slow(x) (x)
+#endif
+
+/* Define our own assert. This way we are sure that it stays in place even
+ if the standard C assert would be thrown away by the compiler. It also
+ allows us to overload it as needed. */
+#define dill_assert(x) \
+ do {\
+ if (dill_slow(!(x))) {\
+ fprintf(stderr, "Assert failed: " #x " (%s:%d)\n",\
+ __FILE__, __LINE__);\
+ fflush(stderr);\
+ abort();\
+ }\
+ } while (0)
+
+/* Workaround missing __rdtsc in Clang < 3.5 (or Clang < 6.0 on Xcode) */
+#if defined(__x86_64__) || defined(__i386__)
+#if defined __clang__
+#if (!defined(__apple_build_version__) && \
+ ((__clang_major__ < 3) || \
+ ((__clang_major__ == 3) && (__clang_minor__ < 5)))) || \
+ (defined(__apple_build_version__) && (__clang_major__ < 6))
+static inline uint64_t __rdtsc() {
+#if defined __i386__
+ uint64_t x;
+ asm volatile ("rdtsc" : "=A" (x));
+ return x;
+#else
+ uint64_t a, d;
+ asm volatile ("rdtsc" : "=a" (a), "=d" (d));
+ return (d << 32) | a;
+#endif
+}
+#endif
+#endif
+#endif
+
+/* Returns the maximum possible file descriptor number */
+int dill_maxfds(void);
+
+/* Encoding and decoding integers from network byte order. */
+uint16_t dill_gets(const uint8_t *buf);
+void dill_puts(uint8_t *buf, uint16_t val);
+uint32_t dill_getl(const uint8_t *buf);
+void dill_putl(uint8_t *buf, uint32_t val);
+uint64_t dill_getll(const uint8_t *buf);
+void dill_putll(uint8_t *buf, uint64_t val);
+
+/* Returns a pointer to the first character in string that is not delim. */
+const char *dill_lstrip(const char *string, char delim);
+/* Returns a pointer after the last character in string that is not delim. */
+const char *dill_rstrip(const char *string, char delim);
+
+/* Cryptographically random bytes. */
+int dill_random(uint8_t *buf, size_t len);
+
+/******************************************************************************/
+/* Base64 functions are based on base64.c (Public Domain) by Jon Mayo. */
+/******************************************************************************/
+
+int dill_base64_encode(const uint8_t *in, size_t in_len,
+ char *out, size_t out_len);
+int dill_base64_decode(const char *in, size_t in_len,
+ uint8_t *out, size_t out_len);
+
+/******************************************************************************/
+/* SHA-1 SECURITY NOTICE: */
+/* The algorithm as designed below is not intended for general purpose use. */
+/* As-designed, it is a single-purpose function for this WebSocket */
+/* Opening Handshake. As per RFC 6455 10.8, SHA-1 usage "doesn't depend on */
+/* any security properties of SHA-1, such as collision resistance or */
+/* resistance to the second pre-image attack (as described in [RFC4270])". */
+/* Caveat emptor for uses of this function elsewhere. */
+/* */
+/* Based on sha1.c (Public Domain) by Steve Reid, these functions calculate */
+/* the SHA1 hash of arbitrary byte locations byte-by-byte. */
+/******************************************************************************/
+
+#define DILL_SHA1_HASH_LEN 20
+#define DILL_SHA1_BLOCK_LEN 64
+
+struct dill_sha1 {
+ uint32_t buffer[DILL_SHA1_BLOCK_LEN / sizeof (uint32_t)];
+ uint32_t state[DILL_SHA1_HASH_LEN / sizeof (uint32_t)];
+ uint32_t bytes_hashed;
+ uint8_t buffer_offset;
+ uint8_t is_little_endian;
+};
+
+void dill_sha1_init(struct dill_sha1 *self);
+void dill_sha1_hashbyte(struct dill_sha1 *self, uint8_t data);
+uint8_t *dill_sha1_result(struct dill_sha1 *self);
+
+#endif
+