From 004a83f51cc730d0c753eea62bc16019434848fc Mon Sep 17 00:00:00 2001 From: Jean-Francois Smigielski Date: Tue, 11 Sep 2018 18:19:05 +0200 Subject: [PATCH 1/9] Use libdill instead of libevent --- CMakeLists.txt | 35 +- README.md | 2 +- main/CMakeLists.txt | 25 - main/children.c | 11 - main/cnx.c | 71 --- main/common.c | 40 -- main/gridinit-utils.h | 27 +- main/gridinit.c | 1002 +++++++++++-------------------------- main/gridinit_cmd.c | 40 +- main/gridinit_internals.h | 4 - main/uid.c | 8 +- 11 files changed, 375 insertions(+), 890 deletions(-) delete mode 100644 main/CMakeLists.txt delete mode 100644 main/cnx.c delete mode 100644 main/common.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c868c7..e740823 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,27 +77,38 @@ 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) +pkg_check_modules(DILL REQUIRED libdill) -### 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) set(CMAKE_LIBRARY_PATH "") set(CMAKE_INCLUDE_PATH "") -include_directories(AFTER ${GLIB2_INCLUDE_DIRS}) +include_directories(AFTER + ${DILL_INCLUDE_DIRS} + ${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} ${DILL_STATIC_LIBRARIES}) + +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..2ab745c 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Gridinit is a tool used to manage non-daemon processes. ### Dependencies * cmake, make, gcc -* libevent, libevent-devel +* libdill * glib, glib-devel ### 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..db17da2 100644 --- a/main/gridinit.c +++ b/main/gridinit.c @@ -41,40 +41,25 @@ along with this program. If not, see . #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 +133,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 +167,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 +232,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 +376,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 +745,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 +754,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 +812,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 +836,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 +1073,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 +1115,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 +1136,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 +1338,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 +1363,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 +1437,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 +1536,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..7d9dded 100644 --- a/main/gridinit_cmd.c +++ b/main/gridinit_cmd.c @@ -27,9 +27,11 @@ along with this program. If not, see . #include #include #include -#include "./format_output.h" +#include + #include +#include "./format_output.h" #include "./gridinit_internals.h" #define MINI 0 @@ -149,6 +151,42 @@ static const gchar description[] = "group\n"; +static 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; +} + static gint compare_child_info(gconstpointer p1, gconstpointer p2) { 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; } From 65f6e0f472a73ad25beeda5abcaa80a1f616c42d Mon Sep 17 00:00:00 2001 From: Jean-Francois Smigielski Date: Wed, 19 Sep 2018 10:23:34 +0200 Subject: [PATCH 2/9] gridinit_cmd: Reduce the line buffer size No need of 64kiB, 8kiB is enough --- main/gridinit_cmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/gridinit_cmd.c b/main/gridinit_cmd.c index 7d9dded..62413ce 100644 --- a/main/gridinit_cmd.c +++ b/main/gridinit_cmd.c @@ -39,7 +39,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; From 9e98e5db228089d0359a8195958246a58a446332 Mon Sep 17 00:00:00 2001 From: Jean-Francois Smigielski Date: Wed, 19 Sep 2018 10:24:08 +0200 Subject: [PATCH 3/9] Remove useless #include --- main/gridinit.c | 12 +----------- main/gridinit_cmd.c | 4 +--- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/main/gridinit.c b/main/gridinit.c index db17da2..8c95bba 100644 --- a/main/gridinit.c +++ b/main/gridinit.c @@ -18,28 +18,18 @@ 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 diff --git a/main/gridinit_cmd.c b/main/gridinit_cmd.c index 62413ce..9b2e960 100644 --- a/main/gridinit_cmd.c +++ b/main/gridinit_cmd.c @@ -18,15 +18,13 @@ along with this program. If not, see . */ #include -#include +#include #include #include -#include #include #include #include #include -#include #include #include From f06d4083760d79a428fa8603d0c6971f032db752 Mon Sep 17 00:00:00 2001 From: Jean-Francois Smigielski Date: Wed, 19 Sep 2018 22:51:16 +0200 Subject: [PATCH 4/9] Depends on a local libdill copy We temporarily rley on a copy of libdill, due to a too rare availability in traditionnal distros --- CMakeLists.txt | 20 +- vendor/sustrik/libdill/AUTHORS | 29 + vendor/sustrik/libdill/COPYING | 19 + vendor/sustrik/libdill/Makefile.am | 124 +++ vendor/sustrik/libdill/README.md | 13 + vendor/sustrik/libdill/abi_version.sh | 49 + vendor/sustrik/libdill/autogen.sh | 3 + vendor/sustrik/libdill/bsock.c | 65 ++ vendor/sustrik/libdill/chan.c | 348 +++++++ vendor/sustrik/libdill/configure.ac | 177 ++++ vendor/sustrik/libdill/cr.c | 535 +++++++++++ vendor/sustrik/libdill/cr.h | 170 ++++ vendor/sustrik/libdill/ctx.c | 203 +++++ vendor/sustrik/libdill/ctx.h | 67 ++ vendor/sustrik/libdill/epoll.c.inc | 270 ++++++ vendor/sustrik/libdill/epoll.h.inc | 46 + vendor/sustrik/libdill/fd.c | 392 ++++++++ vendor/sustrik/libdill/fd.h | 89 ++ vendor/sustrik/libdill/handle.c | 168 ++++ vendor/sustrik/libdill/handle.h | 43 + vendor/sustrik/libdill/iol.c | 108 +++ vendor/sustrik/libdill/iol.h | 58 ++ vendor/sustrik/libdill/ipc.c | 504 +++++++++++ vendor/sustrik/libdill/kqueue.c.inc | 307 +++++++ vendor/sustrik/libdill/kqueue.h.inc | 43 + vendor/sustrik/libdill/libdill.c | 83 ++ vendor/sustrik/libdill/libdill.h | 1008 +++++++++++++++++++++ vendor/sustrik/libdill/libdillimpl.h | 89 ++ vendor/sustrik/libdill/list.h | 74 ++ vendor/sustrik/libdill/m4/.gitignore | 0 vendor/sustrik/libdill/now.c | 121 +++ vendor/sustrik/libdill/now.h | 52 ++ vendor/sustrik/libdill/package_version.sh | 40 + vendor/sustrik/libdill/poll.c.inc | 227 +++++ vendor/sustrik/libdill/poll.h.inc | 48 + vendor/sustrik/libdill/pollset.c | 41 + vendor/sustrik/libdill/pollset.h | 61 ++ vendor/sustrik/libdill/qlist.h | 67 ++ vendor/sustrik/libdill/rbtree.c | 279 ++++++ vendor/sustrik/libdill/rbtree.h | 66 ++ vendor/sustrik/libdill/slist.h | 67 ++ vendor/sustrik/libdill/stack.c | 150 +++ vendor/sustrik/libdill/stack.h | 51 ++ vendor/sustrik/libdill/utils.c | 338 +++++++ vendor/sustrik/libdill/utils.h | 171 ++++ 45 files changed, 6880 insertions(+), 3 deletions(-) create mode 100644 vendor/sustrik/libdill/AUTHORS create mode 100644 vendor/sustrik/libdill/COPYING create mode 100644 vendor/sustrik/libdill/Makefile.am create mode 100644 vendor/sustrik/libdill/README.md create mode 100755 vendor/sustrik/libdill/abi_version.sh create mode 100755 vendor/sustrik/libdill/autogen.sh create mode 100644 vendor/sustrik/libdill/bsock.c create mode 100644 vendor/sustrik/libdill/chan.c create mode 100644 vendor/sustrik/libdill/configure.ac create mode 100644 vendor/sustrik/libdill/cr.c create mode 100644 vendor/sustrik/libdill/cr.h create mode 100644 vendor/sustrik/libdill/ctx.c create mode 100644 vendor/sustrik/libdill/ctx.h create mode 100644 vendor/sustrik/libdill/epoll.c.inc create mode 100644 vendor/sustrik/libdill/epoll.h.inc create mode 100644 vendor/sustrik/libdill/fd.c create mode 100644 vendor/sustrik/libdill/fd.h create mode 100644 vendor/sustrik/libdill/handle.c create mode 100644 vendor/sustrik/libdill/handle.h create mode 100644 vendor/sustrik/libdill/iol.c create mode 100644 vendor/sustrik/libdill/iol.h create mode 100644 vendor/sustrik/libdill/ipc.c create mode 100644 vendor/sustrik/libdill/kqueue.c.inc create mode 100644 vendor/sustrik/libdill/kqueue.h.inc create mode 100644 vendor/sustrik/libdill/libdill.c create mode 100644 vendor/sustrik/libdill/libdill.h create mode 100644 vendor/sustrik/libdill/libdillimpl.h create mode 100644 vendor/sustrik/libdill/list.h create mode 100644 vendor/sustrik/libdill/m4/.gitignore create mode 100644 vendor/sustrik/libdill/now.c create mode 100644 vendor/sustrik/libdill/now.h create mode 100755 vendor/sustrik/libdill/package_version.sh create mode 100644 vendor/sustrik/libdill/poll.c.inc create mode 100644 vendor/sustrik/libdill/poll.h.inc create mode 100644 vendor/sustrik/libdill/pollset.c create mode 100644 vendor/sustrik/libdill/pollset.h create mode 100644 vendor/sustrik/libdill/qlist.h create mode 100644 vendor/sustrik/libdill/rbtree.c create mode 100644 vendor/sustrik/libdill/rbtree.h create mode 100644 vendor/sustrik/libdill/slist.h create mode 100644 vendor/sustrik/libdill/stack.c create mode 100644 vendor/sustrik/libdill/stack.h create mode 100644 vendor/sustrik/libdill/utils.c create mode 100644 vendor/sustrik/libdill/utils.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e740823..de47e48 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,13 +82,22 @@ endif() find_package(PkgConfig) pkg_check_modules(GLIB2 REQUIRED glib-2.0 gthread-2.0 gmodule-2.0) -pkg_check_modules(DILL REQUIRED libdill) +add_custom_target( + libdill_s + ALL + COMMAND mkdir -p ${CMAKE_BINARY_DIR}/vendor/sustrik/libdill + COMMAND lndir ${CMAKE_SOURCE_DIR}/vendor/sustrik/libdill + COMMAND ./autogen.sh + COMMAND ./configure --enable-static --disable-shared + COMMAND make + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/vendor/sustrik/libdill + COMMENT "Building libdill") set(CMAKE_LIBRARY_PATH "") set(CMAKE_INCLUDE_PATH "") include_directories(AFTER - ${DILL_INCLUDE_DIRS} + ${CMAKE_SOURCE_DIR}/vendor/sustrik/libdill ${GLIB2_INCLUDE_DIRS}) add_definitions(-DLOG_DOMAIN="gridinit") @@ -102,7 +111,12 @@ add_executable(gridinit main/uid.c main/utils.c) target_link_libraries(gridinit - ${GLIB2_LIBRARIES} ${DILL_STATIC_LIBRARIES}) + ${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 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 + From 9749f27e364a56039e15bde91397260d72f58cad Mon Sep 17 00:00:00 2001 From: Jean-Francois Smigielski Date: Wed, 19 Sep 2018 23:18:42 +0200 Subject: [PATCH 5/9] Fix README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2ab745c..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 -* libdill * glib, glib-devel +* libdill (currently embedded) ### Compile-time configuration From a6fb2aaecf6be839776c84b8360d3e36cc286683 Mon Sep 17 00:00:00 2001 From: Jean-Francois Smigielski Date: Wed, 19 Sep 2018 23:45:58 +0200 Subject: [PATCH 6/9] gridinit_cmd: Minor simplification --- main/gridinit_cmd.c | 78 +++++++++++++-------------------------------- 1 file changed, 22 insertions(+), 56 deletions(-) diff --git a/main/gridinit_cmd.c b/main/gridinit_cmd.c index 9b2e960..72f4db7 100644 --- a/main/gridinit_cmd.c +++ b/main/gridinit_cmd.c @@ -152,37 +152,28 @@ static const gchar description[] = static int __open_unix_client(const char *path) { - int sock; - struct sockaddr_un local = {}; + 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)); - /* Create ressources to monitor */ - sock = socket(PF_UNIX, SOCK_STREAM, 0); + int 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; + if (-1 == connect(sock, (struct sockaddr *)&local, sizeof(local))) { + int errsav = errno; close(sock); errno = errsav; + return -1; } - return -1; + + errno = 0; + return sock; } static gint @@ -223,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) @@ -233,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* @@ -272,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; @@ -309,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); @@ -324,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) From a5cf706385ca281893e40278c6297e8e3fe1c0bb Mon Sep 17 00:00:00 2001 From: Jean-Francois Smigielski Date: Wed, 19 Sep 2018 23:20:48 +0200 Subject: [PATCH 7/9] ci: Fix the dependencies on Travis-CI --- .travis.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) 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 From b1a313a5a293ba8289c511fe7bf742623b73ba5a Mon Sep 17 00:00:00 2001 From: Jean-Francois Smigielski Date: Thu, 20 Sep 2018 00:25:28 +0200 Subject: [PATCH 8/9] cmake: Ignore well-known warnings The warnings are erroneously raised because of a partial implementation of C99. --- CMakeLists.txt | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index de47e48..6d94863 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") @@ -85,15 +86,22 @@ pkg_check_modules(GLIB2 REQUIRED glib-2.0 gthread-2.0 gmodule-2.0) add_custom_target( libdill_s - ALL - COMMAND mkdir -p ${CMAKE_BINARY_DIR}/vendor/sustrik/libdill - COMMAND lndir ${CMAKE_SOURCE_DIR}/vendor/sustrik/libdill 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 + COMMAND mkdir -p ${CMAKE_BINARY_DIR}/vendor/sustrik/libdill + COMMAND lndir ${CMAKE_SOURCE_DIR}/vendor/sustrik/libdill + WORKING_DIRECTORY ${CMAKE_BINARY_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 From 559c36733f69156a407eda7a0bc9810df5607b30 Mon Sep 17 00:00:00 2001 From: Jean-Francois Smigielski Date: Thu, 20 Sep 2018 11:19:31 +0200 Subject: [PATCH 9/9] cmake: Fix the build when CMAKE_SOURCE_DIR != CMAKE_BINARY_DIR --- CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d94863..faaed13 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,6 +86,7 @@ pkg_check_modules(GLIB2 REQUIRED glib-2.0 gthread-2.0 gmodule-2.0) add_custom_target( libdill_s + ALL COMMAND ./autogen.sh COMMAND ./configure --enable-static --disable-shared COMMAND make @@ -95,9 +96,9 @@ add_custom_target( 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 lndir ${CMAKE_SOURCE_DIR}/vendor/sustrik/libdill - WORKING_DIRECTORY ${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()