This commit is contained in:
@@ -0,0 +1,269 @@
|
||||
|
||||
SHELL = /bin/sh
|
||||
|
||||
# V=0 quiet, V=1 verbose. other values don't work.
|
||||
V = 0
|
||||
V0 = $(V:0=)
|
||||
Q1 = $(V:1=)
|
||||
Q = $(Q1:0=@)
|
||||
ECHO1 = $(V:1=@ :)
|
||||
ECHO = $(ECHO1:0=@ echo)
|
||||
NULLCMD = :
|
||||
|
||||
#### Start of system configuration section. ####
|
||||
|
||||
srcdir = .
|
||||
topdir = /usr/include/ruby-3.2.0
|
||||
hdrdir = $(topdir)
|
||||
arch_hdrdir = /usr/include/x86_64-linux-gnu/ruby-3.2.0
|
||||
PATH_SEPARATOR = :
|
||||
VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby
|
||||
prefix = $(DESTDIR)/usr
|
||||
rubysitearchprefix = $(sitearchlibdir)/$(RUBY_BASE_NAME)
|
||||
rubyarchprefix = $(archlibdir)/$(RUBY_BASE_NAME)
|
||||
rubylibprefix = $(libdir)/$(RUBY_BASE_NAME)
|
||||
exec_prefix = $(prefix)
|
||||
vendorarchhdrdir = $(sitearchincludedir)/$(RUBY_VERSION_NAME)/vendor_ruby
|
||||
sitearchhdrdir = $(sitearchincludedir)/$(RUBY_VERSION_NAME)/site_ruby
|
||||
rubyarchhdrdir = $(archincludedir)/$(RUBY_VERSION_NAME)
|
||||
vendorhdrdir = $(rubyhdrdir)/vendor_ruby
|
||||
sitehdrdir = $(rubyhdrdir)/site_ruby
|
||||
rubyhdrdir = $(includedir)/$(RUBY_VERSION_NAME)
|
||||
vendorarchdir = $(rubysitearchprefix)/vendor_ruby/$(ruby_version)
|
||||
vendorlibdir = $(vendordir)/$(ruby_version)
|
||||
vendordir = $(rubylibprefix)/vendor_ruby
|
||||
sitearchdir = $(DESTDIR)/usr/local/lib/x86_64-linux-gnu/site_ruby
|
||||
sitelibdir = $(sitedir)/$(ruby_version)
|
||||
sitedir = $(DESTDIR)/usr/local/lib/site_ruby
|
||||
rubyarchdir = $(rubyarchprefix)/$(ruby_version)
|
||||
rubylibdir = $(rubylibprefix)/$(ruby_version)
|
||||
sitearchincludedir = $(includedir)/$(sitearch)
|
||||
archincludedir = $(includedir)/$(arch)
|
||||
sitearchlibdir = $(libdir)/$(sitearch)
|
||||
archlibdir = $(libdir)/$(arch)
|
||||
ridir = $(datarootdir)/$(RI_BASE_NAME)
|
||||
mandir = $(datarootdir)/man
|
||||
localedir = $(datarootdir)/locale
|
||||
libdir = $(exec_prefix)/lib
|
||||
psdir = $(docdir)
|
||||
pdfdir = $(docdir)
|
||||
dvidir = $(docdir)
|
||||
htmldir = $(docdir)
|
||||
infodir = $(datarootdir)/info
|
||||
docdir = $(datarootdir)/doc/$(PACKAGE)
|
||||
oldincludedir = $(DESTDIR)/usr/include
|
||||
includedir = $(prefix)/include
|
||||
runstatedir = $(DESTDIR)/var/run
|
||||
localstatedir = $(DESTDIR)/var
|
||||
sharedstatedir = $(prefix)/com
|
||||
sysconfdir = $(DESTDIR)/etc
|
||||
datadir = $(datarootdir)
|
||||
datarootdir = $(prefix)/share
|
||||
libexecdir = $(exec_prefix)/libexec
|
||||
sbindir = $(exec_prefix)/sbin
|
||||
bindir = $(exec_prefix)/bin
|
||||
archdir = $(rubyarchdir)
|
||||
|
||||
|
||||
CC_WRAPPER =
|
||||
CC = x86_64-linux-gnu-gcc
|
||||
CXX = x86_64-linux-gnu-g++
|
||||
LIBRUBY = $(LIBRUBY_SO)
|
||||
LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
|
||||
LIBRUBYARG_SHARED = -l$(RUBY_SO_NAME)
|
||||
LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static $(MAINLIBS)
|
||||
empty =
|
||||
OUTFLAG = -o $(empty)
|
||||
COUTFLAG = -o $(empty)
|
||||
CSRCFLAG = $(empty)
|
||||
|
||||
RUBY_EXTCONF_H =
|
||||
cflags = $(optflags) $(debugflags) $(warnflags)
|
||||
cxxflags =
|
||||
optflags = -O3 -fno-fast-math
|
||||
debugflags = -ggdb3
|
||||
warnflags = -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wmisleading-indentation -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wundef
|
||||
cppflags =
|
||||
CCDLFLAGS = -fPIC
|
||||
CFLAGS = $(CCDLFLAGS) -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=BUILDDIR=. -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection -fdebug-prefix-map=BUILDDIR=/usr/src/ruby3.2-3.2.3-1ubuntu0.24.04.8 -fPIC $(ARCH_FLAG)
|
||||
INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir)
|
||||
DEFS =
|
||||
CPPFLAGS = -DHAVE_OPENSSL_SSL_H -DHAVE_OPENSSL_ERR_H -DWITH_SSL -DBUILD_FOR_RUBY -DHAVE_RB_THREAD_CALL_WITHOUT_GVL -DHAVE_RB_THREAD_FD_SELECT -DHAVE_TYPE_RB_FDSET_T -DHAVE_RB_WAIT_FOR_SINGLE_FD -DHAVE_RB_TIME_NEW -DHAVE_INOTIFY_INIT -DHAVE_INOTIFY -DHAVE_WRITEV -DHAVE_PIPE2 -DHAVE_ACCEPT4 -DHAVE_CONST_SOCK_CLOEXEC -DOS_UNIX -DHAVE_EPOLL_CREATE -DHAVE_EPOLL -DHAVE_CLOCK_GETTIME -DHAVE_CONST_CLOCK_MONOTONIC_RAW -DHAVE_CONST_CLOCK_MONOTONIC -DHAVE_MAKE_PAIR -Wdate-time -D_FORTIFY_SOURCE=3 $(DEFS) $(cppflags)
|
||||
CXXFLAGS = $(CCDLFLAGS) -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=BUILDDIR=. -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection -fdebug-prefix-map=BUILDDIR=/usr/src/ruby3.2-3.2.3-1ubuntu0.24.04.8 $(ARCH_FLAG)
|
||||
ldflags = -L. -Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed
|
||||
dldflags = -Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now
|
||||
ARCH_FLAG =
|
||||
DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG)
|
||||
LDSHARED = $(CXX) -shared
|
||||
LDSHAREDXX = $(CXX) -shared
|
||||
AR = x86_64-linux-gnu-gcc-ar
|
||||
EXEEXT =
|
||||
|
||||
RUBY_INSTALL_NAME = $(RUBY_BASE_NAME)3.2
|
||||
RUBY_SO_NAME = ruby-3.2
|
||||
RUBYW_INSTALL_NAME =
|
||||
RUBY_VERSION_NAME = $(RUBY_BASE_NAME)-$(ruby_version)
|
||||
RUBYW_BASE_NAME = rubyw
|
||||
RUBY_BASE_NAME = ruby
|
||||
|
||||
arch = x86_64-linux-gnu
|
||||
sitearch = $(arch)
|
||||
ruby_version = 3.2.0
|
||||
ruby = $(bindir)/$(RUBY_BASE_NAME)3.2
|
||||
RUBY = $(ruby)
|
||||
BUILTRUBY = $(bindir)/$(RUBY_BASE_NAME)3.2
|
||||
ruby_headers = $(hdrdir)/ruby.h $(hdrdir)/ruby/backward.h $(hdrdir)/ruby/ruby.h $(hdrdir)/ruby/defines.h $(hdrdir)/ruby/missing.h $(hdrdir)/ruby/intern.h $(hdrdir)/ruby/st.h $(hdrdir)/ruby/subst.h $(arch_hdrdir)/ruby/config.h
|
||||
|
||||
RM = rm -f
|
||||
RM_RF = rm -fr
|
||||
RMDIRS = rmdir --ignore-fail-on-non-empty -p
|
||||
MAKEDIRS = /bin/mkdir -p
|
||||
INSTALL = /usr/bin/install -c
|
||||
INSTALL_PROG = $(INSTALL) -m 0755
|
||||
INSTALL_DATA = $(INSTALL) -m 644
|
||||
COPY = cp
|
||||
TOUCH = exit >
|
||||
|
||||
#### End of system configuration section. ####
|
||||
|
||||
preload =
|
||||
libpath = . $(archlibdir)
|
||||
LIBPATH = -L. -L$(archlibdir)
|
||||
DEFFILE =
|
||||
|
||||
CLEANFILES = mkmf.log
|
||||
DISTCLEANFILES =
|
||||
DISTCLEANDIRS =
|
||||
|
||||
extout =
|
||||
extout_prefix =
|
||||
target_prefix =
|
||||
LOCAL_LIBS =
|
||||
LIBS = $(LIBRUBYARG_SHARED) -lssl -lcrypto -lcrypto -lssl -lm -lpthread -lc
|
||||
ORIG_SRCS = binder.cpp cmain.cpp ed.cpp em.cpp kb.cpp page.cpp pipe.cpp rubymain.cpp ssl.cpp
|
||||
SRCS = $(ORIG_SRCS)
|
||||
OBJS = binder.o cmain.o ed.o em.o kb.o page.o pipe.o rubymain.o ssl.o
|
||||
HDRS = $(srcdir)/binder.h $(srcdir)/ed.h $(srcdir)/em.h $(srcdir)/eventmachine.h $(srcdir)/page.h $(srcdir)/project.h $(srcdir)/ssl.h
|
||||
LOCAL_HDRS =
|
||||
TARGET = rubyeventmachine
|
||||
TARGET_NAME = rubyeventmachine
|
||||
TARGET_ENTRY = Init_$(TARGET_NAME)
|
||||
DLLIB = $(TARGET).so
|
||||
EXTSTATIC =
|
||||
STATIC_LIB =
|
||||
|
||||
TIMESTAMP_DIR = .
|
||||
BINDIR = $(bindir)
|
||||
RUBYCOMMONDIR = $(sitedir)$(target_prefix)
|
||||
RUBYLIBDIR = $(sitelibdir)$(target_prefix)
|
||||
RUBYARCHDIR = $(sitearchdir)$(target_prefix)
|
||||
HDRDIR = $(sitehdrdir)$(target_prefix)
|
||||
ARCHHDRDIR = $(sitearchhdrdir)$(target_prefix)
|
||||
TARGET_SO_DIR =
|
||||
TARGET_SO = $(TARGET_SO_DIR)$(DLLIB)
|
||||
CLEANLIBS = $(TARGET_SO) false
|
||||
CLEANOBJS = $(OBJS) *.bak
|
||||
TARGET_SO_DIR_TIMESTAMP = $(TIMESTAMP_DIR)/.sitearchdir.time
|
||||
|
||||
all: $(DLLIB)
|
||||
static: $(STATIC_LIB)
|
||||
.PHONY: all install static install-so install-rb
|
||||
.PHONY: clean clean-so clean-static clean-rb
|
||||
|
||||
clean-static::
|
||||
clean-rb-default::
|
||||
clean-rb::
|
||||
clean-so::
|
||||
clean: clean-so clean-static clean-rb-default clean-rb
|
||||
-$(Q)$(RM_RF) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) .*.time
|
||||
|
||||
distclean-rb-default::
|
||||
distclean-rb::
|
||||
distclean-so::
|
||||
distclean-static::
|
||||
distclean: clean distclean-so distclean-static distclean-rb-default distclean-rb
|
||||
-$(Q)$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
|
||||
-$(Q)$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
|
||||
-$(Q)$(RMDIRS) $(DISTCLEANDIRS) 2> /dev/null || true
|
||||
|
||||
realclean: distclean
|
||||
install: install-so install-rb
|
||||
|
||||
install-so: $(DLLIB) $(TARGET_SO_DIR_TIMESTAMP)
|
||||
$(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
|
||||
clean-static::
|
||||
-$(Q)$(RM) $(STATIC_LIB)
|
||||
install-rb: pre-install-rb do-install-rb install-rb-default
|
||||
install-rb-default: pre-install-rb-default do-install-rb-default
|
||||
pre-install-rb: Makefile
|
||||
pre-install-rb-default: Makefile
|
||||
do-install-rb:
|
||||
do-install-rb-default:
|
||||
pre-install-rb-default:
|
||||
@$(NULLCMD)
|
||||
$(TARGET_SO_DIR_TIMESTAMP):
|
||||
$(Q) $(MAKEDIRS) $(@D) $(RUBYARCHDIR)
|
||||
$(Q) $(TOUCH) $@
|
||||
|
||||
site-install: site-install-so site-install-rb
|
||||
site-install-so: install-so
|
||||
site-install-rb: install-rb
|
||||
|
||||
.SUFFIXES: .c .m .cc .mm .cxx .cpp .o .S
|
||||
|
||||
.cc.o:
|
||||
$(ECHO) compiling $(<)
|
||||
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
||||
|
||||
.cc.S:
|
||||
$(ECHO) translating $(<)
|
||||
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
||||
|
||||
.mm.o:
|
||||
$(ECHO) compiling $(<)
|
||||
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
||||
|
||||
.mm.S:
|
||||
$(ECHO) translating $(<)
|
||||
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
||||
|
||||
.cxx.o:
|
||||
$(ECHO) compiling $(<)
|
||||
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
||||
|
||||
.cxx.S:
|
||||
$(ECHO) translating $(<)
|
||||
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
||||
|
||||
.cpp.o:
|
||||
$(ECHO) compiling $(<)
|
||||
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
||||
|
||||
.cpp.S:
|
||||
$(ECHO) translating $(<)
|
||||
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
||||
|
||||
.c.o:
|
||||
$(ECHO) compiling $(<)
|
||||
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
||||
|
||||
.c.S:
|
||||
$(ECHO) translating $(<)
|
||||
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
||||
|
||||
.m.o:
|
||||
$(ECHO) compiling $(<)
|
||||
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
||||
|
||||
.m.S:
|
||||
$(ECHO) translating $(<)
|
||||
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
||||
|
||||
$(TARGET_SO): $(OBJS) Makefile
|
||||
$(ECHO) linking shared-object $(DLLIB)
|
||||
-$(Q)$(RM) $(@)
|
||||
$(Q) $(LDSHAREDXX) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
|
||||
|
||||
|
||||
|
||||
$(OBJS): $(HDRS) $(ruby_headers)
|
||||
@@ -0,0 +1,124 @@
|
||||
/*****************************************************************************
|
||||
|
||||
$Id$
|
||||
|
||||
File: binder.cpp
|
||||
Date: 07Apr06
|
||||
|
||||
Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
|
||||
Gmail: blackhedd
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of either: 1) the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version; or 2) Ruby's License.
|
||||
|
||||
See the file COPYING for complete licensing information.
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#include "project.h"
|
||||
|
||||
#define DEV_URANDOM "/dev/urandom"
|
||||
|
||||
|
||||
std::map<uintptr_t, Bindable_t*> Bindable_t::BindingBag;
|
||||
|
||||
|
||||
/********************************
|
||||
STATIC Bindable_t::CreateBinding
|
||||
********************************/
|
||||
|
||||
uintptr_t Bindable_t::CreateBinding()
|
||||
{
|
||||
static uintptr_t num = 0;
|
||||
while(BindingBag[++num]) {}
|
||||
return num;
|
||||
}
|
||||
|
||||
#if 0
|
||||
string Bindable_t::CreateBinding()
|
||||
{
|
||||
static int index = 0;
|
||||
static string seed;
|
||||
|
||||
if ((index >= 1000000) || (seed.length() == 0)) {
|
||||
#ifdef OS_UNIX
|
||||
int fd = open (DEV_URANDOM, O_RDONLY);
|
||||
if (fd < 0)
|
||||
throw std::runtime_error ("No entropy device");
|
||||
|
||||
unsigned char u[16];
|
||||
size_t r = read (fd, u, sizeof(u));
|
||||
if (r < sizeof(u))
|
||||
throw std::runtime_error ("Unable to read entropy device");
|
||||
|
||||
unsigned char *u1 = (unsigned char*)u;
|
||||
char u2 [sizeof(u) * 2 + 1];
|
||||
|
||||
for (size_t i=0; i < sizeof(u); i++)
|
||||
sprintf (u2 + (i * 2), "%02x", u1[i]);
|
||||
|
||||
seed = string (u2);
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef OS_WIN32
|
||||
UUID uuid;
|
||||
UuidCreate (&uuid);
|
||||
unsigned char *uuidstring = NULL;
|
||||
UuidToString (&uuid, &uuidstring);
|
||||
if (!uuidstring)
|
||||
throw std::runtime_error ("Unable to read uuid");
|
||||
seed = string ((const char*)uuidstring);
|
||||
|
||||
RpcStringFree (&uuidstring);
|
||||
#endif
|
||||
|
||||
index = 0;
|
||||
|
||||
|
||||
}
|
||||
|
||||
stringstream ss;
|
||||
ss << seed << (++index);
|
||||
return ss.str();
|
||||
}
|
||||
#endif
|
||||
|
||||
/*****************************
|
||||
STATIC: Bindable_t::GetObject
|
||||
*****************************/
|
||||
|
||||
Bindable_t *Bindable_t::GetObject (const uintptr_t binding)
|
||||
{
|
||||
std::map<uintptr_t, Bindable_t*>::const_iterator i = BindingBag.find (binding);
|
||||
if (i != BindingBag.end())
|
||||
return i->second;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**********************
|
||||
Bindable_t::Bindable_t
|
||||
**********************/
|
||||
|
||||
Bindable_t::Bindable_t()
|
||||
{
|
||||
Binding = Bindable_t::CreateBinding();
|
||||
BindingBag [Binding] = this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***********************
|
||||
Bindable_t::~Bindable_t
|
||||
***********************/
|
||||
|
||||
Bindable_t::~Bindable_t() NO_EXCEPT_FALSE
|
||||
{
|
||||
BindingBag.erase (Binding);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*****************************************************************************
|
||||
|
||||
$Id$
|
||||
|
||||
File: binder.h
|
||||
Date: 07Apr06
|
||||
|
||||
Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
|
||||
Gmail: blackhedd
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of either: 1) the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version; or 2) Ruby's License.
|
||||
|
||||
See the file COPYING for complete licensing information.
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef __ObjectBindings__H_
|
||||
#define __ObjectBindings__H_
|
||||
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
#define NO_EXCEPT_FALSE noexcept(false)
|
||||
#else
|
||||
#define NO_EXCEPT_FALSE
|
||||
#endif
|
||||
|
||||
class Bindable_t
|
||||
{
|
||||
public:
|
||||
static uintptr_t CreateBinding();
|
||||
static Bindable_t *GetObject (const uintptr_t);
|
||||
static std::map<uintptr_t, Bindable_t*> BindingBag;
|
||||
|
||||
public:
|
||||
Bindable_t();
|
||||
virtual ~Bindable_t() NO_EXCEPT_FALSE;
|
||||
|
||||
const uintptr_t GetBinding() {return Binding;}
|
||||
|
||||
private:
|
||||
uintptr_t Binding;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // __ObjectBindings__H_
|
||||
|
||||
@@ -0,0 +1,988 @@
|
||||
/*****************************************************************************
|
||||
|
||||
$Id$
|
||||
|
||||
File: cmain.cpp
|
||||
Date: 06Apr06
|
||||
|
||||
Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
|
||||
Gmail: blackhedd
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of either: 1) the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version; or 2) Ruby's License.
|
||||
|
||||
See the file COPYING for complete licensing information.
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#include "project.h"
|
||||
|
||||
/* 21Sep09: ruby 1.9 defines macros for common i/o functions that point to rb_w32_* implementations.
|
||||
We need to undef the stat to fix a build failure in evma_send_file_data_to_connection.
|
||||
See http://groups.google.com/group/eventmachine/browse_thread/thread/fc60d9bb738ffc71
|
||||
*/
|
||||
#if defined(BUILD_FOR_RUBY) && defined(OS_WIN32)
|
||||
#undef stat
|
||||
#undef fstat
|
||||
#endif
|
||||
|
||||
static EventMachine_t *EventMachine;
|
||||
static Poller_t Poller = Poller_Default;
|
||||
|
||||
extern "C" void ensure_eventmachine (const char *caller = "unknown caller")
|
||||
{
|
||||
if (!EventMachine) {
|
||||
const int err_size = 128;
|
||||
char err_string[err_size];
|
||||
snprintf (err_string, err_size, "eventmachine not initialized: %s", caller);
|
||||
#ifdef BUILD_FOR_RUBY
|
||||
rb_raise(rb_eRuntimeError, "%s", err_string);
|
||||
#else
|
||||
throw std::runtime_error (err_string);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/***********************
|
||||
evma_initialize_library
|
||||
***********************/
|
||||
|
||||
extern "C" void evma_initialize_library (EMCallback cb)
|
||||
{
|
||||
if (EventMachine)
|
||||
#ifdef BUILD_FOR_RUBY
|
||||
rb_raise(rb_eRuntimeError, "eventmachine already initialized: evma_initialize_library");
|
||||
#else
|
||||
throw std::runtime_error ("eventmachine already initialized: evma_initialize_library");
|
||||
#endif
|
||||
|
||||
EventMachine = new EventMachine_t (cb, Poller);
|
||||
}
|
||||
|
||||
|
||||
/********************
|
||||
evma_release_library
|
||||
********************/
|
||||
|
||||
extern "C" void evma_release_library()
|
||||
{
|
||||
ensure_eventmachine("evma_release_library");
|
||||
delete EventMachine;
|
||||
EventMachine = NULL;
|
||||
}
|
||||
|
||||
|
||||
/*********************
|
||||
evma_run_machine_once
|
||||
*********************/
|
||||
|
||||
extern "C" bool evma_run_machine_once()
|
||||
{
|
||||
ensure_eventmachine("evma_run_machine_once");
|
||||
return EventMachine->RunOnce();
|
||||
}
|
||||
|
||||
|
||||
/****************
|
||||
evma_run_machine
|
||||
****************/
|
||||
|
||||
extern "C" void evma_run_machine()
|
||||
{
|
||||
ensure_eventmachine("evma_run_machine");
|
||||
EventMachine->Run();
|
||||
}
|
||||
|
||||
|
||||
/**************************
|
||||
evma_install_oneshot_timer
|
||||
**************************/
|
||||
|
||||
extern "C" const uintptr_t evma_install_oneshot_timer (uint64_t milliseconds)
|
||||
{
|
||||
ensure_eventmachine("evma_install_oneshot_timer");
|
||||
return EventMachine->InstallOneshotTimer (milliseconds);
|
||||
}
|
||||
|
||||
|
||||
/**********************
|
||||
evma_connect_to_server
|
||||
**********************/
|
||||
|
||||
extern "C" const uintptr_t evma_connect_to_server (const char *bind_addr, int bind_port, const char *server, int port)
|
||||
{
|
||||
ensure_eventmachine("evma_connect_to_server");
|
||||
return EventMachine->ConnectToServer (bind_addr, bind_port, server, port);
|
||||
}
|
||||
|
||||
/***************************
|
||||
evma_connect_to_unix_server
|
||||
***************************/
|
||||
|
||||
extern "C" const uintptr_t evma_connect_to_unix_server (const char *server)
|
||||
{
|
||||
ensure_eventmachine("evma_connect_to_unix_server");
|
||||
return EventMachine->ConnectToUnixServer (server);
|
||||
}
|
||||
|
||||
/**************
|
||||
evma_attach_fd
|
||||
**************/
|
||||
|
||||
extern "C" const uintptr_t evma_attach_fd (int file_descriptor, int watch_mode)
|
||||
{
|
||||
ensure_eventmachine("evma_attach_fd");
|
||||
return EventMachine->AttachFD (file_descriptor, watch_mode ? true : false);
|
||||
}
|
||||
|
||||
/**************
|
||||
evma_detach_fd
|
||||
**************/
|
||||
|
||||
extern "C" int evma_detach_fd (const uintptr_t binding)
|
||||
{
|
||||
ensure_eventmachine("evma_detach_fd");
|
||||
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
|
||||
if (ed)
|
||||
return EventMachine->DetachFD (ed);
|
||||
else
|
||||
#ifdef BUILD_FOR_RUBY
|
||||
rb_raise(rb_eRuntimeError, "invalid binding to detach");
|
||||
#else
|
||||
throw std::runtime_error ("invalid binding to detach");
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
/************************
|
||||
evma_get_file_descriptor
|
||||
************************/
|
||||
|
||||
extern "C" int evma_get_file_descriptor (const uintptr_t binding)
|
||||
{
|
||||
ensure_eventmachine("evma_get_file_descriptor");
|
||||
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
|
||||
if (ed)
|
||||
return ed->GetSocket();
|
||||
else
|
||||
#ifdef BUILD_FOR_RUBY
|
||||
rb_raise(rb_eRuntimeError, "invalid binding to get_fd");
|
||||
#else
|
||||
throw std::runtime_error ("invalid binding to get_fd");
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
/***********************
|
||||
evma_is_notify_readable
|
||||
***********************/
|
||||
|
||||
extern "C" int evma_is_notify_readable (const uintptr_t binding)
|
||||
{
|
||||
ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
|
||||
if (cd)
|
||||
return cd->IsNotifyReadable() ? 1 : 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/************************
|
||||
evma_set_notify_readable
|
||||
************************/
|
||||
|
||||
extern "C" void evma_set_notify_readable (const uintptr_t binding, int mode)
|
||||
{
|
||||
ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
|
||||
if (cd)
|
||||
cd->SetNotifyReadable (mode ? true : false);
|
||||
}
|
||||
|
||||
/***********************
|
||||
evma_is_notify_writable
|
||||
***********************/
|
||||
|
||||
extern "C" int evma_is_notify_writable (const uintptr_t binding)
|
||||
{
|
||||
ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
|
||||
if (cd)
|
||||
return cd->IsNotifyWritable() ? 1 : 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/************************
|
||||
evma_set_notify_writable
|
||||
************************/
|
||||
|
||||
extern "C" void evma_set_notify_writable (const uintptr_t binding, int mode)
|
||||
{
|
||||
ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
|
||||
if (cd)
|
||||
cd->SetNotifyWritable (mode ? true : false);
|
||||
}
|
||||
|
||||
/**********
|
||||
evma_pause
|
||||
**********/
|
||||
|
||||
extern "C" int evma_pause (const uintptr_t binding)
|
||||
{
|
||||
EventableDescriptor *cd = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
|
||||
if (cd)
|
||||
return cd->Pause() ? 1 : 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***********
|
||||
evma_resume
|
||||
***********/
|
||||
|
||||
extern "C" int evma_resume (const uintptr_t binding)
|
||||
{
|
||||
EventableDescriptor *cd = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
|
||||
if (cd)
|
||||
return cd->Resume() ? 1 : 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**************
|
||||
evma_is_paused
|
||||
**************/
|
||||
|
||||
extern "C" int evma_is_paused (const uintptr_t binding)
|
||||
{
|
||||
EventableDescriptor *cd = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
|
||||
if (cd)
|
||||
return cd->IsPaused() ? 1 : 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/************************
|
||||
evma_num_close_scheduled
|
||||
************************/
|
||||
|
||||
extern "C" int evma_num_close_scheduled ()
|
||||
{
|
||||
ensure_eventmachine("evma_num_close_scheduled");
|
||||
return EventMachine->NumCloseScheduled;
|
||||
}
|
||||
|
||||
/**********************
|
||||
evma_create_tcp_server
|
||||
**********************/
|
||||
|
||||
extern "C" const uintptr_t evma_create_tcp_server (const char *address, int port)
|
||||
{
|
||||
ensure_eventmachine("evma_create_tcp_server");
|
||||
return EventMachine->CreateTcpServer (address, port);
|
||||
}
|
||||
|
||||
/******************************
|
||||
evma_create_unix_domain_server
|
||||
******************************/
|
||||
|
||||
extern "C" const uintptr_t evma_create_unix_domain_server (const char *filename)
|
||||
{
|
||||
ensure_eventmachine("evma_create_unix_domain_server");
|
||||
return EventMachine->CreateUnixDomainServer (filename);
|
||||
}
|
||||
|
||||
/***********************
|
||||
evma_attach_sd
|
||||
************************/
|
||||
|
||||
extern "C" const uintptr_t evma_attach_sd (int sd)
|
||||
{
|
||||
ensure_eventmachine("evma_attach_sd");
|
||||
return EventMachine->AttachSD (sd);
|
||||
}
|
||||
|
||||
/*************************
|
||||
evma_open_datagram_socket
|
||||
*************************/
|
||||
|
||||
extern "C" const uintptr_t evma_open_datagram_socket (const char *address, int port)
|
||||
{
|
||||
ensure_eventmachine("evma_open_datagram_socket");
|
||||
return EventMachine->OpenDatagramSocket (address, port);
|
||||
}
|
||||
|
||||
/******************
|
||||
evma_open_keyboard
|
||||
******************/
|
||||
|
||||
extern "C" const uintptr_t evma_open_keyboard()
|
||||
{
|
||||
ensure_eventmachine("evma_open_keyboard");
|
||||
return EventMachine->OpenKeyboard();
|
||||
}
|
||||
|
||||
/*******************
|
||||
evma_watch_filename
|
||||
*******************/
|
||||
|
||||
extern "C" const uintptr_t evma_watch_filename (const char *fname)
|
||||
{
|
||||
ensure_eventmachine("evma_watch_filename");
|
||||
return EventMachine->WatchFile(fname);
|
||||
}
|
||||
|
||||
/*********************
|
||||
evma_unwatch_filename
|
||||
*********************/
|
||||
|
||||
extern "C" void evma_unwatch_filename (const uintptr_t sig)
|
||||
{
|
||||
ensure_eventmachine("evma_unwatch_file");
|
||||
EventMachine->UnwatchFile(sig);
|
||||
}
|
||||
|
||||
/**************
|
||||
evma_watch_pid
|
||||
**************/
|
||||
|
||||
extern "C" const uintptr_t evma_watch_pid (int pid)
|
||||
{
|
||||
ensure_eventmachine("evma_watch_pid");
|
||||
return EventMachine->WatchPid(pid);
|
||||
}
|
||||
|
||||
/****************
|
||||
evma_unwatch_pid
|
||||
****************/
|
||||
|
||||
extern "C" void evma_unwatch_pid (const uintptr_t sig)
|
||||
{
|
||||
ensure_eventmachine("evma_unwatch_pid");
|
||||
EventMachine->UnwatchPid(sig);
|
||||
}
|
||||
|
||||
/****************************
|
||||
evma_send_data_to_connection
|
||||
****************************/
|
||||
|
||||
extern "C" int evma_send_data_to_connection (const uintptr_t binding, const char *data, int data_length)
|
||||
{
|
||||
ensure_eventmachine("evma_send_data_to_connection");
|
||||
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
|
||||
if (ed)
|
||||
return ed->SendOutboundData(data, data_length);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/******************
|
||||
evma_send_datagram
|
||||
******************/
|
||||
|
||||
extern "C" int evma_send_datagram (const uintptr_t binding, const char *data, int data_length, const char *address, int port)
|
||||
{
|
||||
ensure_eventmachine("evma_send_datagram");
|
||||
DatagramDescriptor *dd = dynamic_cast <DatagramDescriptor*> (Bindable_t::GetObject (binding));
|
||||
if (dd)
|
||||
return dd->SendOutboundDatagram(data, data_length, address, port);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/*********************
|
||||
evma_close_connection
|
||||
*********************/
|
||||
|
||||
extern "C" void evma_close_connection (const uintptr_t binding, int after_writing)
|
||||
{
|
||||
ensure_eventmachine("evma_close_connection");
|
||||
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
|
||||
if (ed)
|
||||
ed->ScheduleClose (after_writing ? true : false);
|
||||
}
|
||||
|
||||
/***********************************
|
||||
evma_report_connection_error_status
|
||||
***********************************/
|
||||
|
||||
extern "C" int evma_report_connection_error_status (const uintptr_t binding)
|
||||
{
|
||||
ensure_eventmachine("evma_report_connection_error_status");
|
||||
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
|
||||
if (ed)
|
||||
return ed->ReportErrorStatus();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/********************
|
||||
evma_stop_tcp_server
|
||||
********************/
|
||||
|
||||
extern "C" void evma_stop_tcp_server (const uintptr_t binding)
|
||||
{
|
||||
ensure_eventmachine("evma_stop_tcp_server");
|
||||
AcceptorDescriptor::StopAcceptor (binding);
|
||||
}
|
||||
|
||||
|
||||
/*****************
|
||||
evma_stop_machine
|
||||
*****************/
|
||||
|
||||
extern "C" void evma_stop_machine()
|
||||
{
|
||||
ensure_eventmachine("evma_stop_machine");
|
||||
EventMachine->ScheduleHalt();
|
||||
}
|
||||
|
||||
/*****************
|
||||
evma_stopping
|
||||
*****************/
|
||||
|
||||
extern "C" bool evma_stopping()
|
||||
{
|
||||
ensure_eventmachine("evma_stopping");
|
||||
return EventMachine->Stopping();
|
||||
}
|
||||
|
||||
/**************
|
||||
evma_start_tls
|
||||
**************/
|
||||
|
||||
extern "C" void evma_start_tls (const uintptr_t binding)
|
||||
{
|
||||
ensure_eventmachine("evma_start_tls");
|
||||
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
|
||||
if (ed)
|
||||
ed->StartTls();
|
||||
}
|
||||
|
||||
/******************
|
||||
evma_set_tls_parms
|
||||
******************/
|
||||
|
||||
extern "C" void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filename, int verify_peer, int fail_if_no_peer_cert, const char *sni_hostname, const char *cipherlist, const char *ecdh_curve, const char *dhparam, int ssl_version)
|
||||
{
|
||||
ensure_eventmachine("evma_set_tls_parms");
|
||||
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
|
||||
if (ed)
|
||||
ed->SetTlsParms (privatekey_filename, certchain_filename, (verify_peer == 1 ? true : false), (fail_if_no_peer_cert == 1 ? true : false), sni_hostname, cipherlist, ecdh_curve, dhparam, ssl_version);
|
||||
}
|
||||
|
||||
/******************
|
||||
evma_get_peer_cert
|
||||
******************/
|
||||
|
||||
#ifdef WITH_SSL
|
||||
extern "C" X509 *evma_get_peer_cert (const uintptr_t binding)
|
||||
{
|
||||
ensure_eventmachine("evma_get_peer_cert");
|
||||
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
|
||||
if (ed)
|
||||
return ed->GetPeerCert();
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/******************
|
||||
evma_get_cipher_bits
|
||||
******************/
|
||||
|
||||
#ifdef WITH_SSL
|
||||
extern "C" int evma_get_cipher_bits (const uintptr_t binding)
|
||||
{
|
||||
ensure_eventmachine("evma_get_cipher_bits");
|
||||
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
|
||||
if (ed)
|
||||
return ed->GetCipherBits();
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/******************
|
||||
evma_get_cipher_name
|
||||
******************/
|
||||
|
||||
#ifdef WITH_SSL
|
||||
extern "C" const char *evma_get_cipher_name (const uintptr_t binding)
|
||||
{
|
||||
ensure_eventmachine("evma_get_cipher_name");
|
||||
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
|
||||
if (ed)
|
||||
return ed->GetCipherName();
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/******************
|
||||
evma_get_cipher_protocol
|
||||
******************/
|
||||
|
||||
#ifdef WITH_SSL
|
||||
extern "C" const char *evma_get_cipher_protocol (const uintptr_t binding)
|
||||
{
|
||||
ensure_eventmachine("evma_get_cipher_protocol");
|
||||
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
|
||||
if (ed)
|
||||
return ed->GetCipherProtocol();
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/******************
|
||||
evma_get_sni_hostname
|
||||
******************/
|
||||
|
||||
#ifdef WITH_SSL
|
||||
extern "C" const char *evma_get_sni_hostname (const uintptr_t binding)
|
||||
{
|
||||
ensure_eventmachine("evma_get_sni_hostname");
|
||||
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
|
||||
if (ed)
|
||||
return ed->GetSNIHostname();
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/********************
|
||||
evma_accept_ssl_peer
|
||||
********************/
|
||||
|
||||
#ifdef WITH_SSL
|
||||
extern "C" void evma_accept_ssl_peer (const uintptr_t binding)
|
||||
{
|
||||
ensure_eventmachine("evma_accept_ssl_peer");
|
||||
ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
|
||||
if (cd)
|
||||
cd->AcceptSslPeer();
|
||||
}
|
||||
#endif
|
||||
|
||||
/*****************
|
||||
evma_get_peername
|
||||
*****************/
|
||||
|
||||
extern "C" int evma_get_peername (const uintptr_t binding, struct sockaddr *sa, socklen_t *len)
|
||||
{
|
||||
ensure_eventmachine("evma_get_peername");
|
||||
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
|
||||
if (ed) {
|
||||
return ed->GetPeername (sa, len) ? 1 : 0;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************
|
||||
evma_get_sockname
|
||||
*****************/
|
||||
|
||||
extern "C" int evma_get_sockname (const uintptr_t binding, struct sockaddr *sa, socklen_t *len)
|
||||
{
|
||||
ensure_eventmachine("evma_get_sockname");
|
||||
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
|
||||
if (ed) {
|
||||
return ed->GetSockname (sa, len) ? 1 : 0;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***********************
|
||||
evma_get_subprocess_pid
|
||||
***********************/
|
||||
|
||||
#ifdef OS_UNIX
|
||||
extern "C" int evma_get_subprocess_pid (const uintptr_t binding, pid_t *pid)
|
||||
{
|
||||
ensure_eventmachine("evma_get_subprocess_pid");
|
||||
PipeDescriptor *pd = dynamic_cast <PipeDescriptor*> (Bindable_t::GetObject (binding));
|
||||
if (pd) {
|
||||
return pd->GetSubprocessPid (pid) ? 1 : 0;
|
||||
}
|
||||
else if (pid && EventMachine->SubprocessPid) {
|
||||
*pid = EventMachine->SubprocessPid;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
extern "C" int evma_get_subprocess_pid (const uintptr_t binding UNUSED, pid_t *pid UNUSED)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**************************
|
||||
evma_get_subprocess_status
|
||||
**************************/
|
||||
|
||||
extern "C" int evma_get_subprocess_status (const uintptr_t binding UNUSED, int *status)
|
||||
{
|
||||
ensure_eventmachine("evma_get_subprocess_status");
|
||||
if (status) {
|
||||
*status = EventMachine->SubprocessExitStatus;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*************************
|
||||
evma_get_connection_count
|
||||
*************************/
|
||||
|
||||
extern "C" int evma_get_connection_count()
|
||||
{
|
||||
ensure_eventmachine("evma_get_connection_count");
|
||||
return EventMachine->GetConnectionCount();
|
||||
}
|
||||
|
||||
/*********************
|
||||
evma_signal_loopbreak
|
||||
*********************/
|
||||
|
||||
extern "C" void evma_signal_loopbreak()
|
||||
{
|
||||
ensure_eventmachine("evma_signal_loopbreak");
|
||||
EventMachine->SignalLoopBreaker();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************
|
||||
evma_get_comm_inactivity_timeout
|
||||
********************************/
|
||||
|
||||
extern "C" float evma_get_comm_inactivity_timeout (const uintptr_t binding)
|
||||
{
|
||||
ensure_eventmachine("evma_get_comm_inactivity_timeout");
|
||||
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
|
||||
if (ed) {
|
||||
return ((float)ed->GetCommInactivityTimeout() / 1000);
|
||||
}
|
||||
else
|
||||
return 0.0; //Perhaps this should be an exception. Access to an unknown binding.
|
||||
}
|
||||
|
||||
/********************************
|
||||
evma_set_comm_inactivity_timeout
|
||||
********************************/
|
||||
|
||||
extern "C" int evma_set_comm_inactivity_timeout (const uintptr_t binding, float value)
|
||||
{
|
||||
ensure_eventmachine("evma_set_comm_inactivity_timeout");
|
||||
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
|
||||
if (ed) {
|
||||
return ed->SetCommInactivityTimeout ((uint64_t)(value * 1000));
|
||||
}
|
||||
else
|
||||
return 0; //Perhaps this should be an exception. Access to an unknown binding.
|
||||
}
|
||||
|
||||
|
||||
/********************************
|
||||
evma_get_pending_connect_timeout
|
||||
********************************/
|
||||
|
||||
extern "C" float evma_get_pending_connect_timeout (const uintptr_t binding)
|
||||
{
|
||||
ensure_eventmachine("evma_get_pending_connect_timeout");
|
||||
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
|
||||
if (ed) {
|
||||
return ((float)ed->GetPendingConnectTimeout() / 1000);
|
||||
}
|
||||
else
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
|
||||
/********************************
|
||||
evma_set_pending_connect_timeout
|
||||
********************************/
|
||||
|
||||
extern "C" int evma_set_pending_connect_timeout (const uintptr_t binding, float value)
|
||||
{
|
||||
ensure_eventmachine("evma_set_pending_connect_timeout");
|
||||
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
|
||||
if (ed) {
|
||||
return ed->SetPendingConnectTimeout ((uint64_t)(value * 1000));
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**********************
|
||||
evma_set_timer_quantum
|
||||
**********************/
|
||||
|
||||
extern "C" void evma_set_timer_quantum (int interval)
|
||||
{
|
||||
ensure_eventmachine("evma_set_timer_quantum");
|
||||
EventMachine->SetTimerQuantum (interval);
|
||||
}
|
||||
|
||||
|
||||
/************************
|
||||
evma_get_max_timer_count
|
||||
************************/
|
||||
|
||||
extern "C" int evma_get_max_timer_count()
|
||||
{
|
||||
return EventMachine_t::GetMaxTimerCount();
|
||||
}
|
||||
|
||||
/************************
|
||||
evma_set_max_timer_count
|
||||
************************/
|
||||
|
||||
extern "C" void evma_set_max_timer_count (int ct)
|
||||
{
|
||||
// This may only be called if the reactor is not running.
|
||||
|
||||
if (EventMachine)
|
||||
#ifdef BUILD_FOR_RUBY
|
||||
rb_raise(rb_eRuntimeError, "eventmachine already initialized: evma_set_max_timer_count");
|
||||
#else
|
||||
throw std::runtime_error ("eventmachine already initialized: evma_set_max_timer_count");
|
||||
#endif
|
||||
EventMachine_t::SetMaxTimerCount (ct);
|
||||
}
|
||||
|
||||
/******************
|
||||
evma_get/set_simultaneous_accept_count
|
||||
******************/
|
||||
|
||||
extern "C" void evma_set_simultaneous_accept_count (int count)
|
||||
{
|
||||
EventMachine_t::SetSimultaneousAcceptCount(count);
|
||||
}
|
||||
|
||||
extern "C" int evma_get_simultaneous_accept_count()
|
||||
{
|
||||
return EventMachine_t::GetSimultaneousAcceptCount();
|
||||
}
|
||||
|
||||
|
||||
/******************
|
||||
evma_setuid_string
|
||||
******************/
|
||||
|
||||
extern "C" void evma_setuid_string (const char *username)
|
||||
{
|
||||
// We do NOT need to be running an EM instance because this method is static.
|
||||
EventMachine_t::SetuidString (username);
|
||||
}
|
||||
|
||||
|
||||
/**********
|
||||
evma_popen
|
||||
**********/
|
||||
|
||||
extern "C" const uintptr_t evma_popen (char * const*cmd_strings)
|
||||
{
|
||||
ensure_eventmachine("evma_popen");
|
||||
return EventMachine->Socketpair (cmd_strings);
|
||||
}
|
||||
|
||||
|
||||
/***************************
|
||||
evma_get_outbound_data_size
|
||||
***************************/
|
||||
|
||||
extern "C" int evma_get_outbound_data_size (const uintptr_t binding)
|
||||
{
|
||||
ensure_eventmachine("evma_get_outbound_data_size");
|
||||
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
|
||||
return ed ? ed->GetOutboundDataSize() : 0;
|
||||
}
|
||||
|
||||
|
||||
/**************
|
||||
evma_set_epoll
|
||||
**************/
|
||||
|
||||
extern "C" void evma_set_epoll (int use)
|
||||
{
|
||||
if (use)
|
||||
Poller = Poller_Epoll;
|
||||
else
|
||||
Poller = Poller_Default;
|
||||
}
|
||||
|
||||
/***************
|
||||
evma_set_kqueue
|
||||
***************/
|
||||
|
||||
extern "C" void evma_set_kqueue (int use)
|
||||
{
|
||||
if (use)
|
||||
Poller = Poller_Kqueue;
|
||||
else
|
||||
Poller = Poller_Default;
|
||||
}
|
||||
|
||||
|
||||
/**********************
|
||||
evma_set_rlimit_nofile
|
||||
**********************/
|
||||
|
||||
extern "C" int evma_set_rlimit_nofile (int nofiles)
|
||||
{
|
||||
return EventMachine_t::SetRlimitNofile (nofiles);
|
||||
}
|
||||
|
||||
|
||||
/*********************************
|
||||
evma_send_file_data_to_connection
|
||||
*********************************/
|
||||
|
||||
extern "C" int evma_send_file_data_to_connection (const uintptr_t binding, const char *filename)
|
||||
{
|
||||
/* This is a sugaring over send_data_to_connection that reads a file into a
|
||||
* locally-allocated buffer, and sends the file data to the remote peer.
|
||||
* Return the number of bytes written to the caller.
|
||||
* TODO, needs to impose a limit on the file size. This is intended only for
|
||||
* small files. (I don't know, maybe 8K or less.) For larger files, use interleaved
|
||||
* I/O to avoid slowing the rest of the system down.
|
||||
* TODO: we should return a code rather than barf, in case of file-not-found.
|
||||
* TODO, does this compile on Windows?
|
||||
* TODO, given that we want this to work only with small files, how about allocating
|
||||
* the buffer on the stack rather than the heap?
|
||||
*
|
||||
* Modified 25Jul07. This now returns -1 on file-too-large; 0 for success, and a positive
|
||||
* errno in case of other errors.
|
||||
*
|
||||
* Contributed by Kirk Haines.
|
||||
*/
|
||||
|
||||
char data[32*1024];
|
||||
int r;
|
||||
|
||||
ensure_eventmachine("evma_send_file_data_to_connection");
|
||||
|
||||
#if defined(OS_WIN32)
|
||||
int Fd = open (filename, O_RDONLY|O_BINARY);
|
||||
#else
|
||||
int Fd = open (filename, O_RDONLY);
|
||||
#endif
|
||||
if (Fd < 0)
|
||||
return errno;
|
||||
// From here on, all early returns MUST close Fd.
|
||||
|
||||
struct stat st;
|
||||
if (fstat (Fd, &st)) {
|
||||
int e = errno;
|
||||
close (Fd);
|
||||
return e;
|
||||
}
|
||||
|
||||
off_t filesize = st.st_size;
|
||||
if (filesize <= 0) {
|
||||
close (Fd);
|
||||
return 0;
|
||||
}
|
||||
else if (filesize > (off_t) sizeof(data)) {
|
||||
close (Fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
r = read (Fd, data, filesize);
|
||||
if (r != filesize) {
|
||||
int e = errno;
|
||||
close (Fd);
|
||||
return e;
|
||||
}
|
||||
evma_send_data_to_connection (binding, data, r);
|
||||
close (Fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/****************
|
||||
evma_start_proxy
|
||||
*****************/
|
||||
|
||||
extern "C" void evma_start_proxy (const uintptr_t from, const uintptr_t to, const unsigned long bufsize, const unsigned long length)
|
||||
{
|
||||
ensure_eventmachine("evma_start_proxy");
|
||||
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (from));
|
||||
if (ed)
|
||||
ed->StartProxy(to, bufsize, length);
|
||||
}
|
||||
|
||||
|
||||
/***************
|
||||
evma_stop_proxy
|
||||
****************/
|
||||
|
||||
extern "C" void evma_stop_proxy (const uintptr_t from)
|
||||
{
|
||||
ensure_eventmachine("evma_stop_proxy");
|
||||
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (from));
|
||||
if (ed)
|
||||
ed->StopProxy();
|
||||
}
|
||||
|
||||
/******************
|
||||
evma_proxied_bytes
|
||||
*******************/
|
||||
|
||||
extern "C" unsigned long evma_proxied_bytes (const uintptr_t from)
|
||||
{
|
||||
ensure_eventmachine("evma_proxied_bytes");
|
||||
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (from));
|
||||
if (ed)
|
||||
return ed->GetProxiedBytes();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/***************************
|
||||
evma_get_last_activity_time
|
||||
****************************/
|
||||
|
||||
extern "C" uint64_t evma_get_last_activity_time(const uintptr_t from)
|
||||
{
|
||||
ensure_eventmachine("evma_get_last_activity_time");
|
||||
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (from));
|
||||
if (ed)
|
||||
return ed->GetLastActivity();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/***************************
|
||||
evma_get_heartbeat_interval
|
||||
****************************/
|
||||
|
||||
extern "C" float evma_get_heartbeat_interval()
|
||||
{
|
||||
ensure_eventmachine("evma_get_heartbeat_interval");
|
||||
return EventMachine->GetHeartbeatInterval();
|
||||
}
|
||||
|
||||
|
||||
/***************************
|
||||
evma_set_heartbeat_interval
|
||||
****************************/
|
||||
|
||||
extern "C" int evma_set_heartbeat_interval(float interval)
|
||||
{
|
||||
ensure_eventmachine("evma_set_heartbeat_interval");
|
||||
return EventMachine->SetHeartbeatInterval(interval);
|
||||
}
|
||||
|
||||
|
||||
/**************************
|
||||
evma_get_current_loop_time
|
||||
***************************/
|
||||
|
||||
extern "C" uint64_t evma_get_current_loop_time()
|
||||
{
|
||||
ensure_eventmachine("evma_get_current_loop_time");
|
||||
return EventMachine->GetCurrentLoopTime();
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,457 @@
|
||||
/*****************************************************************************
|
||||
|
||||
$Id$
|
||||
|
||||
File: ed.h
|
||||
Date: 06Apr06
|
||||
|
||||
Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
|
||||
Gmail: blackhedd
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of either: 1) the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version; or 2) Ruby's License.
|
||||
|
||||
See the file COPYING for complete licensing information.
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef __EventableDescriptor__H_
|
||||
#define __EventableDescriptor__H_
|
||||
|
||||
|
||||
class EventMachine_t; // forward reference
|
||||
#ifdef WITH_SSL
|
||||
class SslBox_t; // forward reference
|
||||
#endif
|
||||
|
||||
bool SetSocketNonblocking (SOCKET);
|
||||
bool SetFdCloexec (int);
|
||||
|
||||
/*************************
|
||||
class EventableDescriptor
|
||||
*************************/
|
||||
|
||||
class EventableDescriptor: public Bindable_t
|
||||
{
|
||||
public:
|
||||
EventableDescriptor (SOCKET, EventMachine_t*);
|
||||
virtual ~EventableDescriptor() NO_EXCEPT_FALSE;
|
||||
|
||||
SOCKET GetSocket() {return MySocket;}
|
||||
void SetSocketInvalid() { MySocket = INVALID_SOCKET; }
|
||||
void Close();
|
||||
|
||||
virtual void Read() = 0;
|
||||
virtual void Write() = 0;
|
||||
virtual void Heartbeat() = 0;
|
||||
|
||||
// These methods tell us whether the descriptor
|
||||
// should be selected or polled for read/write.
|
||||
virtual bool SelectForRead() = 0;
|
||||
virtual bool SelectForWrite() = 0;
|
||||
|
||||
// are we scheduled for a close, or in an error state, or already closed?
|
||||
bool ShouldDelete();
|
||||
// Do we have any data to write? This is used by ShouldDelete.
|
||||
virtual int GetOutboundDataSize() {return 0;}
|
||||
virtual bool IsWatchOnly(){ return bWatchOnly; }
|
||||
|
||||
virtual void ScheduleClose (bool after_writing);
|
||||
bool IsCloseScheduled();
|
||||
virtual void HandleError(){ ScheduleClose (false); }
|
||||
|
||||
void SetEventCallback (EMCallback);
|
||||
|
||||
virtual bool GetPeername (struct sockaddr*, socklen_t*) = 0;
|
||||
virtual bool GetSockname (struct sockaddr*, socklen_t*) = 0;
|
||||
virtual bool GetSubprocessPid (pid_t*) {return false;}
|
||||
|
||||
virtual void StartTls() {}
|
||||
virtual void SetTlsParms (const char *, const char *, bool, bool, const char *, const char *, const char *, const char *, int) {}
|
||||
|
||||
#ifdef WITH_SSL
|
||||
virtual X509 *GetPeerCert() {return NULL;}
|
||||
virtual int GetCipherBits() {return -1;}
|
||||
virtual const char *GetCipherName() {return NULL;}
|
||||
virtual const char *GetCipherProtocol() {return NULL;}
|
||||
virtual const char *GetSNIHostname() {return NULL;}
|
||||
#endif
|
||||
|
||||
virtual uint64_t GetCommInactivityTimeout() {return 0;}
|
||||
virtual int SetCommInactivityTimeout (uint64_t) {return 0;}
|
||||
uint64_t GetPendingConnectTimeout();
|
||||
int SetPendingConnectTimeout (uint64_t value);
|
||||
uint64_t GetLastActivity() { return LastActivity; }
|
||||
|
||||
#ifdef HAVE_EPOLL
|
||||
struct epoll_event *GetEpollEvent() { return &EpollEvent; }
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_KQUEUE
|
||||
bool GetKqueueArmWrite() { return bKqueueArmWrite; }
|
||||
#endif
|
||||
|
||||
virtual void StartProxy(const uintptr_t, const unsigned long, const unsigned long);
|
||||
virtual void StopProxy();
|
||||
virtual unsigned long GetProxiedBytes(){ return ProxiedBytes; };
|
||||
virtual void SetProxiedFrom(EventableDescriptor*, const unsigned long);
|
||||
virtual int SendOutboundData(const char*,unsigned long){ return -1; }
|
||||
virtual bool IsPaused(){ return bPaused; }
|
||||
virtual bool Pause(){ bPaused = true; return bPaused; }
|
||||
virtual bool Resume(){ bPaused = false; return bPaused; }
|
||||
|
||||
void SetUnbindReasonCode(int code){ UnbindReasonCode = code; }
|
||||
virtual int ReportErrorStatus(){ return 0; }
|
||||
virtual bool IsConnectPending(){ return false; }
|
||||
virtual uint64_t GetNextHeartbeat();
|
||||
|
||||
private:
|
||||
bool bCloseNow;
|
||||
bool bCloseAfterWriting;
|
||||
|
||||
protected:
|
||||
SOCKET MySocket;
|
||||
bool bAttached;
|
||||
bool bWatchOnly;
|
||||
|
||||
EMCallback EventCallback;
|
||||
void _GenericInboundDispatch (const char *buffer, unsigned long size);
|
||||
bool _GenericGetPeername (struct sockaddr*, socklen_t*);
|
||||
bool _GenericGetSockname (struct sockaddr*, socklen_t*);
|
||||
|
||||
uint64_t CreatedAt;
|
||||
bool bCallbackUnbind;
|
||||
int UnbindReasonCode;
|
||||
|
||||
unsigned long BytesToProxy;
|
||||
EventableDescriptor *ProxyTarget;
|
||||
EventableDescriptor *ProxiedFrom;
|
||||
unsigned long ProxiedBytes;
|
||||
|
||||
unsigned long MaxOutboundBufSize;
|
||||
|
||||
#ifdef HAVE_EPOLL
|
||||
struct epoll_event EpollEvent;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_KQUEUE
|
||||
bool bKqueueArmWrite;
|
||||
#endif
|
||||
|
||||
EventMachine_t *MyEventMachine;
|
||||
uint64_t PendingConnectTimeout;
|
||||
uint64_t InactivityTimeout;
|
||||
uint64_t LastActivity;
|
||||
uint64_t NextHeartbeat;
|
||||
bool bPaused;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*************************
|
||||
class LoopbreakDescriptor
|
||||
*************************/
|
||||
|
||||
class LoopbreakDescriptor: public EventableDescriptor
|
||||
{
|
||||
public:
|
||||
LoopbreakDescriptor (SOCKET, EventMachine_t*);
|
||||
virtual ~LoopbreakDescriptor() {}
|
||||
|
||||
virtual void Read();
|
||||
virtual void Write();
|
||||
virtual void Heartbeat() {}
|
||||
|
||||
virtual bool SelectForRead() {return true;}
|
||||
virtual bool SelectForWrite() {return false;}
|
||||
|
||||
virtual bool GetPeername (struct sockaddr* s, socklen_t* len) { return _GenericGetPeername (s, len); }
|
||||
virtual bool GetSockname (struct sockaddr* s, socklen_t* len) { return _GenericGetSockname (s, len); }
|
||||
};
|
||||
|
||||
|
||||
/**************************
|
||||
class ConnectionDescriptor
|
||||
**************************/
|
||||
|
||||
class ConnectionDescriptor: public EventableDescriptor
|
||||
{
|
||||
public:
|
||||
ConnectionDescriptor (SOCKET, EventMachine_t*);
|
||||
virtual ~ConnectionDescriptor();
|
||||
|
||||
int SendOutboundData (const char*, unsigned long);
|
||||
|
||||
void SetConnectPending (bool f);
|
||||
virtual void ScheduleClose (bool after_writing);
|
||||
virtual void HandleError();
|
||||
|
||||
void SetNotifyReadable (bool);
|
||||
void SetNotifyWritable (bool);
|
||||
void SetAttached (bool);
|
||||
void SetWatchOnly (bool);
|
||||
|
||||
bool Pause();
|
||||
bool Resume();
|
||||
|
||||
bool IsNotifyReadable(){ return bNotifyReadable; }
|
||||
bool IsNotifyWritable(){ return bNotifyWritable; }
|
||||
|
||||
virtual void Read();
|
||||
virtual void Write();
|
||||
virtual void Heartbeat();
|
||||
|
||||
virtual bool SelectForRead();
|
||||
virtual bool SelectForWrite();
|
||||
|
||||
// Do we have any data to write? This is used by ShouldDelete.
|
||||
virtual int GetOutboundDataSize() {return OutboundDataSize;}
|
||||
|
||||
virtual void StartTls();
|
||||
virtual void SetTlsParms (const char *, const char *, bool, bool, const char *, const char *, const char *, const char *, int);
|
||||
|
||||
#ifdef WITH_SSL
|
||||
virtual X509 *GetPeerCert();
|
||||
virtual int GetCipherBits();
|
||||
virtual const char *GetCipherName();
|
||||
virtual const char *GetCipherProtocol();
|
||||
virtual const char *GetSNIHostname();
|
||||
virtual bool VerifySslPeer(const char*);
|
||||
virtual void AcceptSslPeer();
|
||||
#endif
|
||||
|
||||
void SetServerMode() {bIsServer = true;}
|
||||
|
||||
virtual bool GetPeername (struct sockaddr* s, socklen_t* len) { return _GenericGetPeername (s, len); }
|
||||
virtual bool GetSockname (struct sockaddr* s, socklen_t* len) { return _GenericGetSockname (s, len); }
|
||||
|
||||
virtual uint64_t GetCommInactivityTimeout();
|
||||
virtual int SetCommInactivityTimeout (uint64_t value);
|
||||
|
||||
virtual int ReportErrorStatus();
|
||||
virtual bool IsConnectPending(){ return bConnectPending; }
|
||||
|
||||
protected:
|
||||
struct OutboundPage {
|
||||
OutboundPage (const char *b, int l, int o=0): Buffer(b), Length(l), Offset(o) {}
|
||||
void Free() {if (Buffer) free (const_cast<char*>(Buffer)); }
|
||||
const char *Buffer;
|
||||
int Length;
|
||||
int Offset;
|
||||
};
|
||||
|
||||
protected:
|
||||
bool bConnectPending;
|
||||
|
||||
bool bNotifyReadable;
|
||||
bool bNotifyWritable;
|
||||
|
||||
bool bReadAttemptedAfterClose;
|
||||
bool bWriteAttemptedAfterClose;
|
||||
|
||||
std::deque<OutboundPage> OutboundPages;
|
||||
int OutboundDataSize;
|
||||
|
||||
#ifdef WITH_SSL
|
||||
SslBox_t *SslBox;
|
||||
std::string CertChainFilename;
|
||||
std::string PrivateKeyFilename;
|
||||
std::string CipherList;
|
||||
std::string EcdhCurve;
|
||||
std::string DhParam;
|
||||
int Protocols;
|
||||
bool bHandshakeSignaled;
|
||||
bool bSslVerifyPeer;
|
||||
bool bSslFailIfNoPeerCert;
|
||||
std::string SniHostName;
|
||||
bool bSslPeerAccepted;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_KQUEUE
|
||||
bool bGotExtraKqueueEvent;
|
||||
#endif
|
||||
|
||||
bool bIsServer;
|
||||
|
||||
private:
|
||||
void _UpdateEvents();
|
||||
void _UpdateEvents(bool, bool);
|
||||
void _WriteOutboundData();
|
||||
void _DispatchInboundData (const char *buffer, unsigned long size);
|
||||
void _DispatchCiphertext();
|
||||
int _SendRawOutboundData (const char *buffer, unsigned long size);
|
||||
void _CheckHandshakeStatus();
|
||||
|
||||
};
|
||||
|
||||
|
||||
/************************
|
||||
class DatagramDescriptor
|
||||
************************/
|
||||
|
||||
class DatagramDescriptor: public EventableDescriptor
|
||||
{
|
||||
public:
|
||||
DatagramDescriptor (SOCKET, EventMachine_t*);
|
||||
virtual ~DatagramDescriptor();
|
||||
|
||||
virtual void Read();
|
||||
virtual void Write();
|
||||
virtual void Heartbeat();
|
||||
|
||||
virtual bool SelectForRead() {return true;}
|
||||
virtual bool SelectForWrite();
|
||||
|
||||
int SendOutboundData (const char*, unsigned long);
|
||||
int SendOutboundDatagram (const char*, unsigned long, const char*, int);
|
||||
|
||||
// Do we have any data to write? This is used by ShouldDelete.
|
||||
virtual int GetOutboundDataSize() {return OutboundDataSize;}
|
||||
|
||||
virtual bool GetPeername (struct sockaddr* s, socklen_t* len);
|
||||
virtual bool GetSockname (struct sockaddr* s, socklen_t* len) { return _GenericGetSockname (s, len); };
|
||||
|
||||
virtual uint64_t GetCommInactivityTimeout();
|
||||
virtual int SetCommInactivityTimeout (uint64_t value);
|
||||
|
||||
protected:
|
||||
struct OutboundPage {
|
||||
OutboundPage (const char *b, int l, struct sockaddr_in6 f, int o=0): Buffer(b), Length(l), Offset(o), From(f) {}
|
||||
void Free() {if (Buffer) free (const_cast<char*>(Buffer)); }
|
||||
const char *Buffer;
|
||||
int Length;
|
||||
int Offset;
|
||||
struct sockaddr_in6 From;
|
||||
};
|
||||
|
||||
std::deque<OutboundPage> OutboundPages;
|
||||
int OutboundDataSize;
|
||||
|
||||
struct sockaddr_in6 ReturnAddress;
|
||||
};
|
||||
|
||||
|
||||
/************************
|
||||
class AcceptorDescriptor
|
||||
************************/
|
||||
|
||||
class AcceptorDescriptor: public EventableDescriptor
|
||||
{
|
||||
public:
|
||||
AcceptorDescriptor (SOCKET, EventMachine_t*);
|
||||
virtual ~AcceptorDescriptor();
|
||||
|
||||
virtual void Read();
|
||||
virtual void Write();
|
||||
virtual void Heartbeat();
|
||||
|
||||
virtual bool SelectForRead() {return true;}
|
||||
virtual bool SelectForWrite() {return false;}
|
||||
|
||||
virtual bool GetPeername (struct sockaddr* s, socklen_t* len) { return _GenericGetPeername (s, len); }
|
||||
virtual bool GetSockname (struct sockaddr* s, socklen_t* len) { return _GenericGetSockname (s, len); };
|
||||
|
||||
static void StopAcceptor (const uintptr_t binding);
|
||||
};
|
||||
|
||||
/********************
|
||||
class PipeDescriptor
|
||||
********************/
|
||||
|
||||
#ifdef OS_UNIX
|
||||
class PipeDescriptor: public EventableDescriptor
|
||||
{
|
||||
public:
|
||||
PipeDescriptor (SOCKET, pid_t, EventMachine_t*);
|
||||
virtual ~PipeDescriptor() NO_EXCEPT_FALSE;
|
||||
|
||||
virtual void Read();
|
||||
virtual void Write();
|
||||
virtual void Heartbeat();
|
||||
|
||||
virtual bool SelectForRead();
|
||||
virtual bool SelectForWrite();
|
||||
|
||||
int SendOutboundData (const char*, unsigned long);
|
||||
virtual int GetOutboundDataSize() {return OutboundDataSize;}
|
||||
|
||||
virtual bool GetPeername (struct sockaddr* s, socklen_t* len) { return _GenericGetPeername (s, len); }
|
||||
virtual bool GetSockname (struct sockaddr* s, socklen_t* len) { return _GenericGetSockname (s, len); }
|
||||
|
||||
virtual bool GetSubprocessPid (pid_t*);
|
||||
|
||||
protected:
|
||||
struct OutboundPage {
|
||||
OutboundPage (const char *b, int l, int o=0): Buffer(b), Length(l), Offset(o) {}
|
||||
void Free() {if (Buffer) free (const_cast<char*>(Buffer)); }
|
||||
const char *Buffer;
|
||||
int Length;
|
||||
int Offset;
|
||||
};
|
||||
|
||||
protected:
|
||||
bool bReadAttemptedAfterClose;
|
||||
|
||||
std::deque<OutboundPage> OutboundPages;
|
||||
int OutboundDataSize;
|
||||
|
||||
pid_t SubprocessPid;
|
||||
|
||||
private:
|
||||
void _DispatchInboundData (const char *buffer, int size);
|
||||
};
|
||||
#endif // OS_UNIX
|
||||
|
||||
|
||||
/************************
|
||||
class KeyboardDescriptor
|
||||
************************/
|
||||
|
||||
class KeyboardDescriptor: public EventableDescriptor
|
||||
{
|
||||
public:
|
||||
KeyboardDescriptor (EventMachine_t*);
|
||||
virtual ~KeyboardDescriptor();
|
||||
|
||||
virtual void Read();
|
||||
virtual void Write();
|
||||
virtual void Heartbeat();
|
||||
|
||||
virtual bool SelectForRead() {return true;}
|
||||
virtual bool SelectForWrite() {return false;}
|
||||
|
||||
virtual bool GetPeername (struct sockaddr* s, socklen_t* len) { return _GenericGetPeername (s, len); }
|
||||
virtual bool GetSockname (struct sockaddr* s, socklen_t* len) { return _GenericGetSockname (s, len); }
|
||||
|
||||
protected:
|
||||
bool bReadAttemptedAfterClose;
|
||||
|
||||
private:
|
||||
void _DispatchInboundData (const char *buffer, int size);
|
||||
};
|
||||
|
||||
|
||||
/***********************
|
||||
class InotifyDescriptor
|
||||
************************/
|
||||
|
||||
class InotifyDescriptor: public EventableDescriptor
|
||||
{
|
||||
public:
|
||||
InotifyDescriptor (EventMachine_t*);
|
||||
virtual ~InotifyDescriptor();
|
||||
|
||||
void Read();
|
||||
void Write();
|
||||
|
||||
virtual void Heartbeat() {}
|
||||
virtual bool SelectForRead() {return true;}
|
||||
virtual bool SelectForWrite() {return false;}
|
||||
|
||||
virtual bool GetPeername (struct sockaddr* s, socklen_t* len) { return false; }
|
||||
virtual bool GetSockname (struct sockaddr* s, socklen_t* len) { return false; }
|
||||
};
|
||||
|
||||
#endif // __EventableDescriptor__H_
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,308 @@
|
||||
/*****************************************************************************
|
||||
|
||||
$Id$
|
||||
|
||||
File: em.h
|
||||
Date: 06Apr06
|
||||
|
||||
Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
|
||||
Gmail: blackhedd
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of either: 1) the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version; or 2) Ruby's License.
|
||||
|
||||
See the file COPYING for complete licensing information.
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef __EventMachine__H_
|
||||
#define __EventMachine__H_
|
||||
|
||||
#ifdef BUILD_FOR_RUBY
|
||||
#include <ruby.h>
|
||||
#ifdef HAVE_RB_THREAD_FD_SELECT
|
||||
#define EmSelect rb_thread_fd_select
|
||||
#else
|
||||
// ruby 1.9.1 and below
|
||||
#define EmSelect rb_thread_select
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
|
||||
#include <ruby/thread.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_RB_WAIT_FOR_SINGLE_FD
|
||||
#include <ruby/io.h>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_RB_TRAP_IMMEDIATE)
|
||||
#include <rubysig.h>
|
||||
#elif defined(HAVE_RB_ENABLE_INTERRUPT)
|
||||
extern "C" {
|
||||
void rb_enable_interrupt(void);
|
||||
void rb_disable_interrupt(void);
|
||||
}
|
||||
|
||||
#define TRAP_BEG rb_enable_interrupt()
|
||||
#define TRAP_END do { rb_disable_interrupt(); rb_thread_check_ints(); } while(0)
|
||||
#else
|
||||
#define TRAP_BEG
|
||||
#define TRAP_END
|
||||
#endif
|
||||
|
||||
// 1.9.0 compat
|
||||
#ifndef RUBY_UBF_IO
|
||||
#define RUBY_UBF_IO RB_UBF_DFL
|
||||
#endif
|
||||
#ifndef RSTRING_PTR
|
||||
#define RSTRING_PTR(str) RSTRING(str)->ptr
|
||||
#endif
|
||||
#ifndef RSTRING_LEN
|
||||
#define RSTRING_LEN(str) RSTRING(str)->len
|
||||
#endif
|
||||
#ifndef RSTRING_LENINT
|
||||
#define RSTRING_LENINT(str) RSTRING_LEN(str)
|
||||
#endif
|
||||
#else
|
||||
#define EmSelect select
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_TYPE_RB_FDSET_T)
|
||||
#define fd_check(n) (((n) < FD_SETSIZE) ? 1 : 0*fprintf(stderr, "fd %d too large for select\n", (n)))
|
||||
// These definitions are cribbed from include/ruby/intern.h in Ruby 1.9.3,
|
||||
// with this change: any macros that read or write the nth element of an
|
||||
// fdset first call fd_check to make sure n is in bounds.
|
||||
typedef fd_set rb_fdset_t;
|
||||
#define rb_fd_zero(f) FD_ZERO(f)
|
||||
#define rb_fd_set(n, f) do { if (fd_check(n)) FD_SET((n), (f)); } while(0)
|
||||
#define rb_fd_clr(n, f) do { if (fd_check(n)) FD_CLR((n), (f)); } while(0)
|
||||
#define rb_fd_isset(n, f) (fd_check(n) ? FD_ISSET((n), (f)) : 0)
|
||||
#define rb_fd_copy(d, s, n) (*(d) = *(s))
|
||||
#define rb_fd_dup(d, s) (*(d) = *(s))
|
||||
#define rb_fd_resize(n, f) ((void)(f))
|
||||
#define rb_fd_ptr(f) (f)
|
||||
#define rb_fd_init(f) FD_ZERO(f)
|
||||
#define rb_fd_init_copy(d, s) (*(d) = *(s))
|
||||
#define rb_fd_term(f) ((void)(f))
|
||||
#define rb_fd_max(f) FD_SETSIZE
|
||||
#define rb_fd_select(n, rfds, wfds, efds, timeout) \
|
||||
select(fd_check((n)-1) ? (n) : FD_SETSIZE, (rfds), (wfds), (efds), (timeout))
|
||||
#define rb_thread_fd_select(n, rfds, wfds, efds, timeout) \
|
||||
rb_thread_select(fd_check((n)-1) ? (n) : FD_SETSIZE, (rfds), (wfds), (efds), (timeout))
|
||||
#endif
|
||||
|
||||
|
||||
// This Solaris fix is adapted from eval_intern.h in Ruby 1.9.3:
|
||||
// Solaris sys/select.h switches select to select_large_fdset to support larger
|
||||
// file descriptors if FD_SETSIZE is larger than 1024 on 32bit environment.
|
||||
// But Ruby doesn't change FD_SETSIZE because fd_set is allocated dynamically.
|
||||
// So following definition is required to use select_large_fdset.
|
||||
#ifdef HAVE_SELECT_LARGE_FDSET
|
||||
#define select(n, r, w, e, t) select_large_fdset((n), (r), (w), (e), (t))
|
||||
extern "C" {
|
||||
int select_large_fdset(int, fd_set *, fd_set *, fd_set *, struct timeval *);
|
||||
}
|
||||
#endif
|
||||
|
||||
class EventableDescriptor;
|
||||
class InotifyDescriptor;
|
||||
struct SelectData_t;
|
||||
|
||||
/*************
|
||||
enum Poller_t
|
||||
*************/
|
||||
enum Poller_t {
|
||||
Poller_Default, // typically Select
|
||||
Poller_Epoll,
|
||||
Poller_Kqueue
|
||||
};
|
||||
|
||||
|
||||
/********************
|
||||
class EventMachine_t
|
||||
********************/
|
||||
|
||||
class EventMachine_t
|
||||
{
|
||||
public:
|
||||
static int GetMaxTimerCount();
|
||||
static void SetMaxTimerCount (int);
|
||||
|
||||
static int GetSimultaneousAcceptCount();
|
||||
static void SetSimultaneousAcceptCount (int);
|
||||
|
||||
public:
|
||||
EventMachine_t (EMCallback, Poller_t);
|
||||
virtual ~EventMachine_t();
|
||||
|
||||
bool RunOnce();
|
||||
void Run();
|
||||
void ScheduleHalt();
|
||||
bool Stopping();
|
||||
void SignalLoopBreaker();
|
||||
const uintptr_t InstallOneshotTimer (uint64_t);
|
||||
const uintptr_t ConnectToServer (const char *, int, const char *, int);
|
||||
const uintptr_t ConnectToUnixServer (const char *);
|
||||
|
||||
const uintptr_t CreateTcpServer (const char *, int);
|
||||
const uintptr_t OpenDatagramSocket (const char *, int);
|
||||
const uintptr_t CreateUnixDomainServer (const char*);
|
||||
const uintptr_t AttachSD (SOCKET);
|
||||
const uintptr_t OpenKeyboard();
|
||||
//const char *Popen (const char*, const char*);
|
||||
const uintptr_t Socketpair (char* const*);
|
||||
|
||||
void Add (EventableDescriptor*);
|
||||
void Modify (EventableDescriptor*);
|
||||
void Deregister (EventableDescriptor*);
|
||||
|
||||
const uintptr_t AttachFD (SOCKET, bool);
|
||||
int DetachFD (EventableDescriptor*);
|
||||
|
||||
void ArmKqueueWriter (EventableDescriptor*);
|
||||
void ArmKqueueReader (EventableDescriptor*);
|
||||
|
||||
void SetTimerQuantum (int);
|
||||
static void SetuidString (const char*);
|
||||
static int SetRlimitNofile (int);
|
||||
|
||||
pid_t SubprocessPid;
|
||||
int SubprocessExitStatus;
|
||||
|
||||
int GetConnectionCount();
|
||||
float GetHeartbeatInterval();
|
||||
int SetHeartbeatInterval(float);
|
||||
|
||||
const uintptr_t WatchFile (const char*);
|
||||
void UnwatchFile (int);
|
||||
void UnwatchFile (const uintptr_t);
|
||||
|
||||
#ifdef HAVE_KQUEUE
|
||||
void _HandleKqueueFileEvent (struct kevent*);
|
||||
void _RegisterKqueueFileEvent(int);
|
||||
#endif
|
||||
|
||||
const uintptr_t WatchPid (int);
|
||||
void UnwatchPid (int);
|
||||
void UnwatchPid (const uintptr_t);
|
||||
|
||||
#ifdef HAVE_KQUEUE
|
||||
void _HandleKqueuePidEvent (struct kevent*);
|
||||
#endif
|
||||
|
||||
uint64_t GetCurrentLoopTime() { return MyCurrentLoopTime; }
|
||||
|
||||
void QueueHeartbeat(EventableDescriptor*);
|
||||
void ClearHeartbeat(uint64_t, EventableDescriptor*);
|
||||
|
||||
uint64_t GetRealTime();
|
||||
|
||||
Poller_t GetPoller() { return Poller; }
|
||||
|
||||
static int name2address (const char *server, int port, int socktype, struct sockaddr *addr, size_t *addr_len);
|
||||
|
||||
private:
|
||||
void _RunTimers();
|
||||
void _UpdateTime();
|
||||
void _AddNewDescriptors();
|
||||
void _ModifyDescriptors();
|
||||
void _InitializeLoopBreaker();
|
||||
void _CleanupSockets();
|
||||
|
||||
void _RunSelectOnce();
|
||||
void _RunEpollOnce();
|
||||
void _RunKqueueOnce();
|
||||
|
||||
void _ModifyEpollEvent (EventableDescriptor*);
|
||||
void _DispatchHeartbeats();
|
||||
timeval _TimeTilNextEvent();
|
||||
void _CleanBadDescriptors();
|
||||
|
||||
public:
|
||||
void _ReadLoopBreaker();
|
||||
void _ReadInotifyEvents();
|
||||
int NumCloseScheduled;
|
||||
|
||||
private:
|
||||
enum {
|
||||
MaxEpollDescriptors = 64*1024,
|
||||
MaxEvents = 4096
|
||||
};
|
||||
int HeartbeatInterval;
|
||||
EMCallback EventCallback;
|
||||
|
||||
class Timer_t: public Bindable_t {
|
||||
};
|
||||
|
||||
std::multimap<uint64_t, Timer_t> Timers;
|
||||
std::multimap<uint64_t, EventableDescriptor*> Heartbeats;
|
||||
std::map<int, Bindable_t*> Files;
|
||||
std::map<int, Bindable_t*> Pids;
|
||||
std::vector<EventableDescriptor*> Descriptors;
|
||||
std::vector<EventableDescriptor*> NewDescriptors;
|
||||
std::set<EventableDescriptor*> ModifiedDescriptors;
|
||||
|
||||
SOCKET LoopBreakerReader;
|
||||
SOCKET LoopBreakerWriter;
|
||||
#ifdef OS_WIN32
|
||||
struct sockaddr_in LoopBreakerTarget;
|
||||
#endif
|
||||
|
||||
timeval Quantum;
|
||||
|
||||
uint64_t MyCurrentLoopTime;
|
||||
|
||||
#ifdef OS_WIN32
|
||||
unsigned TickCountTickover;
|
||||
unsigned LastTickCount;
|
||||
#endif
|
||||
|
||||
#ifdef OS_DARWIN
|
||||
mach_timebase_info_data_t mach_timebase;
|
||||
#endif
|
||||
|
||||
private:
|
||||
bool bTerminateSignalReceived;
|
||||
SelectData_t *SelectData;
|
||||
|
||||
Poller_t Poller;
|
||||
|
||||
int epfd; // Epoll file-descriptor
|
||||
#ifdef HAVE_EPOLL
|
||||
struct epoll_event epoll_events [MaxEvents];
|
||||
#endif
|
||||
|
||||
int kqfd; // Kqueue file-descriptor
|
||||
#ifdef HAVE_KQUEUE
|
||||
struct kevent Karray [MaxEvents];
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
InotifyDescriptor *inotify; // pollable descriptor for our inotify instance
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/*******************
|
||||
struct SelectData_t
|
||||
*******************/
|
||||
|
||||
struct SelectData_t
|
||||
{
|
||||
SelectData_t();
|
||||
~SelectData_t();
|
||||
|
||||
int _Select();
|
||||
void _Clear();
|
||||
|
||||
SOCKET maxsocket;
|
||||
rb_fdset_t fdreads;
|
||||
rb_fdset_t fdwrites;
|
||||
rb_fdset_t fderrors;
|
||||
timeval tv;
|
||||
int nSockets;
|
||||
};
|
||||
|
||||
#endif // __EventMachine__H_
|
||||
@@ -0,0 +1,143 @@
|
||||
/*****************************************************************************
|
||||
|
||||
$Id$
|
||||
|
||||
File: eventmachine.h
|
||||
Date: 15Apr06
|
||||
|
||||
Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
|
||||
Gmail: blackhedd
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of either: 1) the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version; or 2) Ruby's License.
|
||||
|
||||
See the file COPYING for complete licensing information.
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef __EVMA_EventMachine__H_
|
||||
#define __EVMA_EventMachine__H_
|
||||
|
||||
#if __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum { // Event names
|
||||
EM_TIMER_FIRED = 100,
|
||||
EM_CONNECTION_READ = 101,
|
||||
EM_CONNECTION_UNBOUND = 102,
|
||||
EM_CONNECTION_ACCEPTED = 103,
|
||||
EM_CONNECTION_COMPLETED = 104,
|
||||
EM_LOOPBREAK_SIGNAL = 105,
|
||||
EM_CONNECTION_NOTIFY_READABLE = 106,
|
||||
EM_CONNECTION_NOTIFY_WRITABLE = 107,
|
||||
EM_SSL_HANDSHAKE_COMPLETED = 108,
|
||||
EM_SSL_VERIFY = 109,
|
||||
EM_PROXY_TARGET_UNBOUND = 110,
|
||||
EM_PROXY_COMPLETED = 111
|
||||
};
|
||||
|
||||
enum { // SSL/TLS Protocols
|
||||
EM_PROTO_SSLv2 = 2,
|
||||
EM_PROTO_SSLv3 = 4,
|
||||
EM_PROTO_TLSv1 = 8,
|
||||
EM_PROTO_TLSv1_1 = 16,
|
||||
EM_PROTO_TLSv1_2 = 32
|
||||
};
|
||||
|
||||
void evma_initialize_library (EMCallback);
|
||||
bool evma_run_machine_once();
|
||||
void evma_run_machine();
|
||||
void evma_release_library();
|
||||
const uintptr_t evma_install_oneshot_timer (uint64_t milliseconds);
|
||||
const uintptr_t evma_connect_to_server (const char *bind_addr, int bind_port, const char *server, int port);
|
||||
const uintptr_t evma_connect_to_unix_server (const char *server);
|
||||
|
||||
const uintptr_t evma_attach_fd (int file_descriptor, int watch_mode);
|
||||
int evma_detach_fd (const uintptr_t binding);
|
||||
int evma_get_file_descriptor (const uintptr_t binding);
|
||||
int evma_is_notify_readable (const uintptr_t binding);
|
||||
void evma_set_notify_readable (const uintptr_t binding, int mode);
|
||||
int evma_is_notify_writable (const uintptr_t binding);
|
||||
void evma_set_notify_writable (const uintptr_t binding, int mode);
|
||||
|
||||
int evma_pause(const uintptr_t binding);
|
||||
int evma_is_paused(const uintptr_t binding);
|
||||
int evma_resume(const uintptr_t binding);
|
||||
|
||||
int evma_num_close_scheduled();
|
||||
|
||||
void evma_stop_tcp_server (const uintptr_t binding);
|
||||
const uintptr_t evma_create_tcp_server (const char *address, int port);
|
||||
const uintptr_t evma_create_unix_domain_server (const char *filename);
|
||||
const uintptr_t evma_attach_sd (int sd);
|
||||
const uintptr_t evma_open_datagram_socket (const char *server, int port);
|
||||
const uintptr_t evma_open_keyboard();
|
||||
void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filenane, int verify_peer, int fail_if_no_peer_cert, const char *sni_hostname, const char *cipherlist, const char *ecdh_curve, const char *dhparam, int protocols);
|
||||
void evma_start_tls (const uintptr_t binding);
|
||||
|
||||
#ifdef WITH_SSL
|
||||
X509 *evma_get_peer_cert (const uintptr_t binding);
|
||||
int evma_get_cipher_bits (const uintptr_t binding);
|
||||
const char *evma_get_cipher_name (const uintptr_t binding);
|
||||
const char *evma_get_cipher_protocol (const uintptr_t binding);
|
||||
const char *evma_get_sni_hostname (const uintptr_t binding);
|
||||
void evma_accept_ssl_peer (const uintptr_t binding);
|
||||
#endif
|
||||
|
||||
int evma_get_peername (const uintptr_t binding, struct sockaddr*, socklen_t*);
|
||||
int evma_get_sockname (const uintptr_t binding, struct sockaddr*, socklen_t*);
|
||||
int evma_get_subprocess_pid (const uintptr_t binding, pid_t*);
|
||||
int evma_get_subprocess_status (const uintptr_t binding, int*);
|
||||
int evma_get_connection_count();
|
||||
int evma_send_data_to_connection (const uintptr_t binding, const char *data, int data_length);
|
||||
int evma_send_datagram (const uintptr_t binding, const char *data, int data_length, const char *address, int port);
|
||||
float evma_get_comm_inactivity_timeout (const uintptr_t binding);
|
||||
int evma_set_comm_inactivity_timeout (const uintptr_t binding, float value);
|
||||
float evma_get_pending_connect_timeout (const uintptr_t binding);
|
||||
int evma_set_pending_connect_timeout (const uintptr_t binding, float value);
|
||||
int evma_get_outbound_data_size (const uintptr_t binding);
|
||||
uint64_t evma_get_last_activity_time (const uintptr_t binding);
|
||||
int evma_send_file_data_to_connection (const uintptr_t binding, const char *filename);
|
||||
|
||||
void evma_close_connection (const uintptr_t binding, int after_writing);
|
||||
int evma_report_connection_error_status (const uintptr_t binding);
|
||||
void evma_signal_loopbreak();
|
||||
void evma_set_timer_quantum (int);
|
||||
int evma_get_max_timer_count();
|
||||
void evma_set_max_timer_count (int);
|
||||
int evma_get_simultaneous_accept_count();
|
||||
void evma_set_simultaneous_accept_count (int);
|
||||
void evma_setuid_string (const char *username);
|
||||
void evma_stop_machine();
|
||||
bool evma_stopping();
|
||||
float evma_get_heartbeat_interval();
|
||||
int evma_set_heartbeat_interval(float);
|
||||
|
||||
const uintptr_t evma_popen (char * const*cmd_strings);
|
||||
|
||||
const uintptr_t evma_watch_filename (const char *fname);
|
||||
void evma_unwatch_filename (const uintptr_t binding);
|
||||
|
||||
const uintptr_t evma_watch_pid (int);
|
||||
void evma_unwatch_pid (const uintptr_t binding);
|
||||
|
||||
void evma_start_proxy(const uintptr_t from, const uintptr_t to, const unsigned long bufsize, const unsigned long length);
|
||||
void evma_stop_proxy(const uintptr_t from);
|
||||
unsigned long evma_proxied_bytes(const uintptr_t from);
|
||||
|
||||
int evma_set_rlimit_nofile (int n_files);
|
||||
|
||||
void evma_set_epoll (int use);
|
||||
void evma_set_kqueue (int use);
|
||||
|
||||
uint64_t evma_get_current_loop_time();
|
||||
#if __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif // __EventMachine__H_
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
require 'fileutils'
|
||||
require 'mkmf'
|
||||
|
||||
# Eager check devs tools
|
||||
have_devel? if respond_to?(:have_devel?)
|
||||
|
||||
def check_libs libs = [], fatal = false
|
||||
libs.all? { |lib| have_library(lib) || (abort("could not find library: #{lib}") if fatal) }
|
||||
end
|
||||
|
||||
def check_heads heads = [], fatal = false
|
||||
heads.all? { |head| have_header(head) || (abort("could not find header: #{head}") if fatal)}
|
||||
end
|
||||
|
||||
def add_define(name)
|
||||
$defs.push("-D#{name}")
|
||||
end
|
||||
|
||||
##
|
||||
# OpenSSL:
|
||||
|
||||
# override append_library, so it actually appends (instead of prepending)
|
||||
# this fixes issues with linking ssl, since libcrypto depends on symbols in libssl
|
||||
def append_library(libs, lib)
|
||||
libs + " " + format(LIBARG, lib)
|
||||
end
|
||||
|
||||
SSL_HEADS = %w(openssl/ssl.h openssl/err.h)
|
||||
SSL_LIBS = %w(crypto ssl)
|
||||
# OpenSSL 1.1.0 and above for Windows use the Unix library names
|
||||
# OpenSSL 0.9.8 and 1.0.x for Windows use the *eay32 library names
|
||||
SSL_LIBS_WIN = RUBY_PLATFORM =~ /mswin|mingw|bccwin/ ? %w(ssleay32 libeay32) : []
|
||||
|
||||
def dir_config_wrapper(pretty_name, name, idefault=nil, ldefault=nil)
|
||||
inc, lib = dir_config(name, idefault, ldefault)
|
||||
if inc && lib
|
||||
# TODO: Remove when 2.0.0 is the minimum supported version
|
||||
# Ruby versions not incorporating the mkmf fix at
|
||||
# https://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/39717
|
||||
# do not properly search for lib directories, and must be corrected
|
||||
unless lib && lib[-3, 3] == 'lib'
|
||||
@libdir_basename = 'lib'
|
||||
inc, lib = dir_config(name, idefault, ldefault)
|
||||
end
|
||||
unless idefault && ldefault
|
||||
abort "-----\nCannot find #{pretty_name} include path #{inc}\n-----" unless inc && inc.split(File::PATH_SEPARATOR).any? { |dir| File.directory?(dir) }
|
||||
abort "-----\nCannot find #{pretty_name} library path #{lib}\n-----" unless lib && lib.split(File::PATH_SEPARATOR).any? { |dir| File.directory?(dir) }
|
||||
warn "-----\nUsing #{pretty_name} in path #{File.dirname inc}\n-----"
|
||||
end
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def dir_config_search(pretty_name, name, paths, &b)
|
||||
paths.each do |p|
|
||||
if dir_config_wrapper('OpenSSL', 'ssl', p + '/include', p + '/lib') && yield
|
||||
warn "-----\nFound #{pretty_name} in path #{p}\n-----"
|
||||
return true
|
||||
end
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
def pkg_config_wrapper(pretty_name, name)
|
||||
cflags, ldflags, libs = pkg_config(name)
|
||||
unless [cflags, ldflags, libs].any?(&:nil?) || [cflags, ldflags, libs].any?(&:empty?)
|
||||
warn "-----\nUsing #{pretty_name} from pkg-config #{cflags} && #{ldflags} && #{libs}\n-----"
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
if ENV['CROSS_COMPILING']
|
||||
openssl_version = ENV.fetch("OPENSSL_VERSION", "1.0.2e")
|
||||
openssl_dir = File.expand_path("~/.rake-compiler/builds/openssl-#{openssl_version}/")
|
||||
if File.exist?(openssl_dir)
|
||||
FileUtils.mkdir_p Dir.pwd+"/openssl/"
|
||||
FileUtils.cp Dir[openssl_dir+"/include/openssl/*.h"], Dir.pwd+"/openssl/", :verbose => true
|
||||
FileUtils.cp Dir[openssl_dir+"/lib*.a"], Dir.pwd, :verbose => true
|
||||
$INCFLAGS << " -I#{Dir.pwd}" # for the openssl headers
|
||||
add_define "WITH_SSL"
|
||||
else
|
||||
STDERR.puts
|
||||
STDERR.puts "**************************************************************************************"
|
||||
STDERR.puts "**** Cross-compiled OpenSSL not found"
|
||||
STDERR.puts "**** Run: hg clone http://bitbucket.org/ged/ruby-pg && cd ruby-pg && rake openssl_libs"
|
||||
STDERR.puts "**************************************************************************************"
|
||||
STDERR.puts
|
||||
end
|
||||
elsif dir_config_wrapper('OpenSSL', 'ssl')
|
||||
# If the user has provided a --with-ssl-dir argument, we must respect it or fail.
|
||||
add_define 'WITH_SSL' if (check_libs(SSL_LIBS) || check_libs(SSL_LIBS_WIN)) && check_heads(SSL_HEADS)
|
||||
elsif pkg_config_wrapper('OpenSSL', 'openssl')
|
||||
# If we can detect OpenSSL by pkg-config, use it as the next-best option
|
||||
add_define 'WITH_SSL' if (check_libs(SSL_LIBS) || check_libs(SSL_LIBS_WIN)) && check_heads(SSL_HEADS)
|
||||
elsif (check_libs(SSL_LIBS) || check_libs(SSL_LIBS_WIN)) && check_heads(SSL_HEADS)
|
||||
# If we don't even need any options to find a usable OpenSSL, go with it
|
||||
add_define 'WITH_SSL'
|
||||
elsif dir_config_search('OpenSSL', 'ssl', ['/usr/local', '/opt/local', '/usr/local/opt/openssl']) do
|
||||
(check_libs(SSL_LIBS) || check_libs(SSL_LIBS_WIN)) && check_heads(SSL_HEADS)
|
||||
end
|
||||
# Finally, look for OpenSSL in alternate locations including MacPorts and HomeBrew
|
||||
add_define 'WITH_SSL'
|
||||
end
|
||||
|
||||
add_define 'BUILD_FOR_RUBY'
|
||||
|
||||
# Ruby features:
|
||||
|
||||
have_var('rb_trap_immediate', ['ruby.h', 'rubysig.h'])
|
||||
have_func('rb_thread_blocking_region')
|
||||
have_func('rb_thread_call_without_gvl', 'ruby/thread.h')
|
||||
have_func('rb_thread_fd_select')
|
||||
have_type('rb_fdset_t', 'ruby/intern.h')
|
||||
have_func('rb_wait_for_single_fd')
|
||||
have_func('rb_enable_interrupt')
|
||||
have_func('rb_time_new')
|
||||
|
||||
# System features:
|
||||
|
||||
add_define('HAVE_INOTIFY') if inotify = have_func('inotify_init', 'sys/inotify.h')
|
||||
add_define('HAVE_OLD_INOTIFY') if !inotify && have_macro('__NR_inotify_init', 'sys/syscall.h')
|
||||
have_func('writev', 'sys/uio.h')
|
||||
have_func('pipe2', 'unistd.h')
|
||||
have_func('accept4', 'sys/socket.h')
|
||||
have_const('SOCK_CLOEXEC', 'sys/socket.h')
|
||||
|
||||
# Minor platform details between *nix and Windows:
|
||||
|
||||
if RUBY_PLATFORM =~ /(mswin|mingw|bccwin)/
|
||||
GNU_CHAIN = ENV['CROSS_COMPILING'] || $1 == 'mingw'
|
||||
OS_WIN32 = true
|
||||
add_define "OS_WIN32"
|
||||
else
|
||||
GNU_CHAIN = true
|
||||
OS_UNIX = true
|
||||
add_define 'OS_UNIX'
|
||||
|
||||
add_define "HAVE_KQUEUE" if have_header("sys/event.h") && have_header("sys/queue.h")
|
||||
end
|
||||
|
||||
# Adjust number of file descriptors (FD) on Windows
|
||||
|
||||
if RbConfig::CONFIG["host_os"] =~ /mingw/
|
||||
found = RbConfig::CONFIG.values_at("CFLAGS", "CPPFLAGS").
|
||||
any? { |v| v.include?("FD_SETSIZE") }
|
||||
|
||||
add_define "FD_SETSIZE=32767" unless found
|
||||
end
|
||||
|
||||
# Main platform invariances:
|
||||
|
||||
case RUBY_PLATFORM
|
||||
when /mswin32/, /mingw32/, /bccwin32/
|
||||
check_heads(%w[windows.h winsock.h], true)
|
||||
check_libs(%w[kernel32 rpcrt4 gdi32], true)
|
||||
|
||||
if GNU_CHAIN
|
||||
CONFIG['LDSHAREDXX'] = "$(CXX) -shared -static-libgcc -static-libstdc++"
|
||||
else
|
||||
$defs.push "-EHs"
|
||||
$defs.push "-GR"
|
||||
end
|
||||
|
||||
# Newer versions of Ruby already define _WIN32_WINNT, which is needed
|
||||
# to get access to newer POSIX networking functions (e.g. getaddrinfo)
|
||||
add_define '_WIN32_WINNT=0x0501' unless have_func('getaddrinfo')
|
||||
|
||||
when /solaris/
|
||||
add_define 'OS_SOLARIS8'
|
||||
check_libs(%w[nsl socket], true)
|
||||
|
||||
# If Ruby was compiled for 32-bits, then select() can only handle 1024 fds
|
||||
# There is an alternate function, select_large_fdset, that supports more.
|
||||
have_func('select_large_fdset', 'sys/select.h')
|
||||
|
||||
if CONFIG['CC'] == 'cc' && (
|
||||
`cc -flags 2>&1` =~ /Sun/ || # detect SUNWspro compiler
|
||||
`cc -V 2>&1` =~ /Sun/ # detect Solaris Studio compiler
|
||||
)
|
||||
# SUN CHAIN
|
||||
add_define 'CC_SUNWspro'
|
||||
$preload = ["\nCXX = CC"] # hack a CXX= line into the makefile
|
||||
$CFLAGS = CONFIG['CFLAGS'] = "-KPIC"
|
||||
CONFIG['CCDLFLAGS'] = "-KPIC"
|
||||
CONFIG['LDSHARED'] = "$(CXX) -G -KPIC -lCstd"
|
||||
CONFIG['LDSHAREDXX'] = "$(CXX) -G -KPIC -lCstd"
|
||||
else
|
||||
# GNU CHAIN
|
||||
# on Unix we need a g++ link, not gcc.
|
||||
CONFIG['LDSHARED'] = "$(CXX) -shared"
|
||||
end
|
||||
|
||||
when /openbsd/
|
||||
# OpenBSD branch contributed by Guillaume Sellier.
|
||||
|
||||
# on Unix we need a g++ link, not gcc. On OpenBSD, linking against libstdc++ have to be explicitly done for shared libs
|
||||
CONFIG['LDSHARED'] = "$(CXX) -shared -lstdc++ -fPIC"
|
||||
CONFIG['LDSHAREDXX'] = "$(CXX) -shared -lstdc++ -fPIC"
|
||||
|
||||
when /darwin/
|
||||
add_define 'OS_DARWIN'
|
||||
|
||||
# on Unix we need a g++ link, not gcc.
|
||||
# Ff line contributed by Daniel Harple.
|
||||
CONFIG['LDSHARED'] = "$(CXX) " + CONFIG['LDSHARED'].split[1..-1].join(' ')
|
||||
|
||||
when /linux/
|
||||
add_define 'HAVE_EPOLL' if have_func('epoll_create', 'sys/epoll.h')
|
||||
|
||||
# on Unix we need a g++ link, not gcc.
|
||||
CONFIG['LDSHARED'] = "$(CXX) -shared"
|
||||
|
||||
when /aix/
|
||||
CONFIG['LDSHARED'] = "$(CXX) -Wl,-bstatic -Wl,-bdynamic -Wl,-G -Wl,-brtl"
|
||||
|
||||
when /cygwin/
|
||||
# For rubies built with Cygwin, CXX may be set to CC, which is just
|
||||
# a wrapper for gcc.
|
||||
# This will compile, but it will not link to the C++ std library.
|
||||
# Explicitly set CXX to use g++.
|
||||
CONFIG['CXX'] = "g++"
|
||||
# on Unix we need a g++ link, not gcc.
|
||||
CONFIG['LDSHARED'] = "$(CXX) -shared"
|
||||
|
||||
else
|
||||
# on Unix we need a g++ link, not gcc.
|
||||
CONFIG['LDSHARED'] = "$(CXX) -shared"
|
||||
end
|
||||
|
||||
# Platform-specific time functions
|
||||
if have_func('clock_gettime')
|
||||
# clock_gettime is POSIX, but the monotonic clocks are not
|
||||
have_const('CLOCK_MONOTONIC_RAW', 'time.h') # Linux
|
||||
have_const('CLOCK_MONOTONIC', 'time.h') # Linux, Solaris, BSDs
|
||||
else
|
||||
have_func('gethrtime') # Older Solaris and HP-UX
|
||||
end
|
||||
|
||||
# Hack so that try_link will test with a C++ compiler instead of a C compiler
|
||||
TRY_LINK.sub!('$(CC)', '$(CXX)')
|
||||
|
||||
# This is our wishlist. We use whichever flags work on the host.
|
||||
# In the future, add -Werror to make sure all warnings are resolved.
|
||||
# deprecated-declarations are used in OS X OpenSSL
|
||||
# ignored-qualifiers are used by the Bindings (would-be void *)
|
||||
# unused-result because GCC 4.6 no longer silences (void) ignore_this(function)
|
||||
# address because on Windows, rb_fd_select checks if &fds is non-NULL, which it cannot be
|
||||
%w(
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wno-deprecated-declarations
|
||||
-Wno-ignored-qualifiers
|
||||
-Wno-unused-result
|
||||
-Wno-address
|
||||
).select do |flag|
|
||||
try_link('int main() {return 0;}', flag)
|
||||
end.each do |flag|
|
||||
CONFIG['CXXFLAGS'] << ' ' << flag
|
||||
end
|
||||
puts "CXXFLAGS=#{CONFIG['CXXFLAGS']}"
|
||||
|
||||
# Solaris C++ compiler doesn't have make_pair()
|
||||
add_define 'HAVE_MAKE_PAIR' if try_link(<<SRC, '-lstdc++')
|
||||
#include <utility>
|
||||
using namespace std;
|
||||
int main(){ pair<const int,int> tuple = make_pair(1,2); }
|
||||
SRC
|
||||
TRY_LINK.sub!('$(CXX)', '$(CC)')
|
||||
|
||||
create_makefile "rubyeventmachine"
|
||||
@@ -0,0 +1,269 @@
|
||||
|
||||
SHELL = /bin/sh
|
||||
|
||||
# V=0 quiet, V=1 verbose. other values don't work.
|
||||
V = 0
|
||||
V0 = $(V:0=)
|
||||
Q1 = $(V:1=)
|
||||
Q = $(Q1:0=@)
|
||||
ECHO1 = $(V:1=@ :)
|
||||
ECHO = $(ECHO1:0=@ echo)
|
||||
NULLCMD = :
|
||||
|
||||
#### Start of system configuration section. ####
|
||||
|
||||
srcdir = .
|
||||
topdir = /usr/include/ruby-3.2.0
|
||||
hdrdir = $(topdir)
|
||||
arch_hdrdir = /usr/include/x86_64-linux-gnu/ruby-3.2.0
|
||||
PATH_SEPARATOR = :
|
||||
VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby
|
||||
prefix = $(DESTDIR)/usr
|
||||
rubysitearchprefix = $(sitearchlibdir)/$(RUBY_BASE_NAME)
|
||||
rubyarchprefix = $(archlibdir)/$(RUBY_BASE_NAME)
|
||||
rubylibprefix = $(libdir)/$(RUBY_BASE_NAME)
|
||||
exec_prefix = $(prefix)
|
||||
vendorarchhdrdir = $(sitearchincludedir)/$(RUBY_VERSION_NAME)/vendor_ruby
|
||||
sitearchhdrdir = $(sitearchincludedir)/$(RUBY_VERSION_NAME)/site_ruby
|
||||
rubyarchhdrdir = $(archincludedir)/$(RUBY_VERSION_NAME)
|
||||
vendorhdrdir = $(rubyhdrdir)/vendor_ruby
|
||||
sitehdrdir = $(rubyhdrdir)/site_ruby
|
||||
rubyhdrdir = $(includedir)/$(RUBY_VERSION_NAME)
|
||||
vendorarchdir = $(rubysitearchprefix)/vendor_ruby/$(ruby_version)
|
||||
vendorlibdir = $(vendordir)/$(ruby_version)
|
||||
vendordir = $(rubylibprefix)/vendor_ruby
|
||||
sitearchdir = $(DESTDIR)/usr/local/lib/x86_64-linux-gnu/site_ruby
|
||||
sitelibdir = $(sitedir)/$(ruby_version)
|
||||
sitedir = $(DESTDIR)/usr/local/lib/site_ruby
|
||||
rubyarchdir = $(rubyarchprefix)/$(ruby_version)
|
||||
rubylibdir = $(rubylibprefix)/$(ruby_version)
|
||||
sitearchincludedir = $(includedir)/$(sitearch)
|
||||
archincludedir = $(includedir)/$(arch)
|
||||
sitearchlibdir = $(libdir)/$(sitearch)
|
||||
archlibdir = $(libdir)/$(arch)
|
||||
ridir = $(datarootdir)/$(RI_BASE_NAME)
|
||||
mandir = $(datarootdir)/man
|
||||
localedir = $(datarootdir)/locale
|
||||
libdir = $(exec_prefix)/lib
|
||||
psdir = $(docdir)
|
||||
pdfdir = $(docdir)
|
||||
dvidir = $(docdir)
|
||||
htmldir = $(docdir)
|
||||
infodir = $(datarootdir)/info
|
||||
docdir = $(datarootdir)/doc/$(PACKAGE)
|
||||
oldincludedir = $(DESTDIR)/usr/include
|
||||
includedir = $(prefix)/include
|
||||
runstatedir = $(DESTDIR)/var/run
|
||||
localstatedir = $(DESTDIR)/var
|
||||
sharedstatedir = $(prefix)/com
|
||||
sysconfdir = $(DESTDIR)/etc
|
||||
datadir = $(datarootdir)
|
||||
datarootdir = $(prefix)/share
|
||||
libexecdir = $(exec_prefix)/libexec
|
||||
sbindir = $(exec_prefix)/sbin
|
||||
bindir = $(exec_prefix)/bin
|
||||
archdir = $(rubyarchdir)
|
||||
|
||||
|
||||
CC_WRAPPER =
|
||||
CC = x86_64-linux-gnu-gcc
|
||||
CXX = x86_64-linux-gnu-g++
|
||||
LIBRUBY = $(LIBRUBY_SO)
|
||||
LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
|
||||
LIBRUBYARG_SHARED = -l$(RUBY_SO_NAME)
|
||||
LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static $(MAINLIBS)
|
||||
empty =
|
||||
OUTFLAG = -o $(empty)
|
||||
COUTFLAG = -o $(empty)
|
||||
CSRCFLAG = $(empty)
|
||||
|
||||
RUBY_EXTCONF_H =
|
||||
cflags = $(optflags) $(debugflags) $(warnflags)
|
||||
cxxflags =
|
||||
optflags = -O3 -fno-fast-math
|
||||
debugflags = -ggdb3
|
||||
warnflags = -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wmisleading-indentation -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wundef
|
||||
cppflags =
|
||||
CCDLFLAGS = -fPIC
|
||||
CFLAGS = $(CCDLFLAGS) -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=BUILDDIR=. -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection -fdebug-prefix-map=BUILDDIR=/usr/src/ruby3.2-3.2.3-1ubuntu0.24.04.8 -fPIC $(ARCH_FLAG)
|
||||
INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir)
|
||||
DEFS =
|
||||
CPPFLAGS = -DBUILD_FOR_RUBY -DOS_UNIX -Wdate-time -D_FORTIFY_SOURCE=3 $(DEFS) $(cppflags)
|
||||
CXXFLAGS = $(CCDLFLAGS) -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=BUILDDIR=. -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection -fdebug-prefix-map=BUILDDIR=/usr/src/ruby3.2-3.2.3-1ubuntu0.24.04.8 $(ARCH_FLAG)
|
||||
ldflags = -L. -Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed
|
||||
dldflags = -Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now
|
||||
ARCH_FLAG =
|
||||
DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG)
|
||||
LDSHARED = $(CXX) -shared
|
||||
LDSHAREDXX = $(CXX) -shared
|
||||
AR = x86_64-linux-gnu-gcc-ar
|
||||
EXEEXT =
|
||||
|
||||
RUBY_INSTALL_NAME = $(RUBY_BASE_NAME)3.2
|
||||
RUBY_SO_NAME = ruby-3.2
|
||||
RUBYW_INSTALL_NAME =
|
||||
RUBY_VERSION_NAME = $(RUBY_BASE_NAME)-$(ruby_version)
|
||||
RUBYW_BASE_NAME = rubyw
|
||||
RUBY_BASE_NAME = ruby
|
||||
|
||||
arch = x86_64-linux-gnu
|
||||
sitearch = $(arch)
|
||||
ruby_version = 3.2.0
|
||||
ruby = $(bindir)/$(RUBY_BASE_NAME)3.2
|
||||
RUBY = $(ruby)
|
||||
BUILTRUBY = $(bindir)/$(RUBY_BASE_NAME)3.2
|
||||
ruby_headers = $(hdrdir)/ruby.h $(hdrdir)/ruby/backward.h $(hdrdir)/ruby/ruby.h $(hdrdir)/ruby/defines.h $(hdrdir)/ruby/missing.h $(hdrdir)/ruby/intern.h $(hdrdir)/ruby/st.h $(hdrdir)/ruby/subst.h $(arch_hdrdir)/ruby/config.h
|
||||
|
||||
RM = rm -f
|
||||
RM_RF = rm -fr
|
||||
RMDIRS = rmdir --ignore-fail-on-non-empty -p
|
||||
MAKEDIRS = /bin/mkdir -p
|
||||
INSTALL = /usr/bin/install -c
|
||||
INSTALL_PROG = $(INSTALL) -m 0755
|
||||
INSTALL_DATA = $(INSTALL) -m 644
|
||||
COPY = cp
|
||||
TOUCH = exit >
|
||||
|
||||
#### End of system configuration section. ####
|
||||
|
||||
preload =
|
||||
libpath = . $(archlibdir)
|
||||
LIBPATH = -L. -L$(archlibdir)
|
||||
DEFFILE =
|
||||
|
||||
CLEANFILES = mkmf.log
|
||||
DISTCLEANFILES =
|
||||
DISTCLEANDIRS =
|
||||
|
||||
extout =
|
||||
extout_prefix =
|
||||
target_prefix =
|
||||
LOCAL_LIBS =
|
||||
LIBS = $(LIBRUBYARG_SHARED) -lm -lpthread -lc
|
||||
ORIG_SRCS = mapper.cpp rubymain.cpp
|
||||
SRCS = $(ORIG_SRCS)
|
||||
OBJS = mapper.o rubymain.o
|
||||
HDRS = $(srcdir)/mapper.h
|
||||
LOCAL_HDRS =
|
||||
TARGET = fastfilereaderext
|
||||
TARGET_NAME = fastfilereaderext
|
||||
TARGET_ENTRY = Init_$(TARGET_NAME)
|
||||
DLLIB = $(TARGET).so
|
||||
EXTSTATIC =
|
||||
STATIC_LIB =
|
||||
|
||||
TIMESTAMP_DIR = .
|
||||
BINDIR = $(bindir)
|
||||
RUBYCOMMONDIR = $(sitedir)$(target_prefix)
|
||||
RUBYLIBDIR = $(sitelibdir)$(target_prefix)
|
||||
RUBYARCHDIR = $(sitearchdir)$(target_prefix)
|
||||
HDRDIR = $(sitehdrdir)$(target_prefix)
|
||||
ARCHHDRDIR = $(sitearchhdrdir)$(target_prefix)
|
||||
TARGET_SO_DIR =
|
||||
TARGET_SO = $(TARGET_SO_DIR)$(DLLIB)
|
||||
CLEANLIBS = $(TARGET_SO) false
|
||||
CLEANOBJS = $(OBJS) *.bak
|
||||
TARGET_SO_DIR_TIMESTAMP = $(TIMESTAMP_DIR)/.sitearchdir.time
|
||||
|
||||
all: $(DLLIB)
|
||||
static: $(STATIC_LIB)
|
||||
.PHONY: all install static install-so install-rb
|
||||
.PHONY: clean clean-so clean-static clean-rb
|
||||
|
||||
clean-static::
|
||||
clean-rb-default::
|
||||
clean-rb::
|
||||
clean-so::
|
||||
clean: clean-so clean-static clean-rb-default clean-rb
|
||||
-$(Q)$(RM_RF) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) .*.time
|
||||
|
||||
distclean-rb-default::
|
||||
distclean-rb::
|
||||
distclean-so::
|
||||
distclean-static::
|
||||
distclean: clean distclean-so distclean-static distclean-rb-default distclean-rb
|
||||
-$(Q)$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
|
||||
-$(Q)$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
|
||||
-$(Q)$(RMDIRS) $(DISTCLEANDIRS) 2> /dev/null || true
|
||||
|
||||
realclean: distclean
|
||||
install: install-so install-rb
|
||||
|
||||
install-so: $(DLLIB) $(TARGET_SO_DIR_TIMESTAMP)
|
||||
$(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
|
||||
clean-static::
|
||||
-$(Q)$(RM) $(STATIC_LIB)
|
||||
install-rb: pre-install-rb do-install-rb install-rb-default
|
||||
install-rb-default: pre-install-rb-default do-install-rb-default
|
||||
pre-install-rb: Makefile
|
||||
pre-install-rb-default: Makefile
|
||||
do-install-rb:
|
||||
do-install-rb-default:
|
||||
pre-install-rb-default:
|
||||
@$(NULLCMD)
|
||||
$(TARGET_SO_DIR_TIMESTAMP):
|
||||
$(Q) $(MAKEDIRS) $(@D) $(RUBYARCHDIR)
|
||||
$(Q) $(TOUCH) $@
|
||||
|
||||
site-install: site-install-so site-install-rb
|
||||
site-install-so: install-so
|
||||
site-install-rb: install-rb
|
||||
|
||||
.SUFFIXES: .c .m .cc .mm .cxx .cpp .o .S
|
||||
|
||||
.cc.o:
|
||||
$(ECHO) compiling $(<)
|
||||
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
||||
|
||||
.cc.S:
|
||||
$(ECHO) translating $(<)
|
||||
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
||||
|
||||
.mm.o:
|
||||
$(ECHO) compiling $(<)
|
||||
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
||||
|
||||
.mm.S:
|
||||
$(ECHO) translating $(<)
|
||||
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
||||
|
||||
.cxx.o:
|
||||
$(ECHO) compiling $(<)
|
||||
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
||||
|
||||
.cxx.S:
|
||||
$(ECHO) translating $(<)
|
||||
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
||||
|
||||
.cpp.o:
|
||||
$(ECHO) compiling $(<)
|
||||
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
||||
|
||||
.cpp.S:
|
||||
$(ECHO) translating $(<)
|
||||
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
||||
|
||||
.c.o:
|
||||
$(ECHO) compiling $(<)
|
||||
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
||||
|
||||
.c.S:
|
||||
$(ECHO) translating $(<)
|
||||
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
||||
|
||||
.m.o:
|
||||
$(ECHO) compiling $(<)
|
||||
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
||||
|
||||
.m.S:
|
||||
$(ECHO) translating $(<)
|
||||
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
||||
|
||||
$(TARGET_SO): $(OBJS) Makefile
|
||||
$(ECHO) linking shared-object $(DLLIB)
|
||||
-$(Q)$(RM) $(@)
|
||||
$(Q) $(LDSHAREDXX) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
|
||||
|
||||
|
||||
|
||||
$(OBJS): $(HDRS) $(ruby_headers)
|
||||
@@ -0,0 +1,109 @@
|
||||
require 'mkmf'
|
||||
|
||||
def check_libs libs = [], fatal = false
|
||||
libs.all? { |lib| have_library(lib) || (abort("could not find library: #{lib}") if fatal) }
|
||||
end
|
||||
|
||||
def check_heads heads = [], fatal = false
|
||||
heads.all? { |head| have_header(head) || (abort("could not find header: #{head}") if fatal)}
|
||||
end
|
||||
|
||||
def add_define(name)
|
||||
$defs.push("-D#{name}")
|
||||
end
|
||||
|
||||
# Eager check devs tools
|
||||
have_devel? if respond_to?(:have_devel?)
|
||||
|
||||
add_define 'BUILD_FOR_RUBY'
|
||||
|
||||
# Minor platform details between *nix and Windows:
|
||||
|
||||
if RUBY_PLATFORM =~ /(mswin|mingw|bccwin)/
|
||||
GNU_CHAIN = ENV['CROSS_COMPILING'] || $1 == 'mingw'
|
||||
OS_WIN32 = true
|
||||
add_define "OS_WIN32"
|
||||
else
|
||||
GNU_CHAIN = true
|
||||
OS_UNIX = true
|
||||
add_define 'OS_UNIX'
|
||||
end
|
||||
|
||||
# Adjust number of file descriptors (FD) on Windows
|
||||
|
||||
if RbConfig::CONFIG["host_os"] =~ /mingw/
|
||||
found = RbConfig::CONFIG.values_at("CFLAGS", "CPPFLAGS").
|
||||
any? { |v| v.include?("FD_SETSIZE") }
|
||||
|
||||
add_define "FD_SETSIZE=32767" unless found
|
||||
end
|
||||
|
||||
# Main platform invariances:
|
||||
|
||||
case RUBY_PLATFORM
|
||||
when /mswin32/, /mingw32/, /bccwin32/
|
||||
check_heads(%w[windows.h winsock.h], true)
|
||||
check_libs(%w[kernel32 rpcrt4 gdi32], true)
|
||||
|
||||
if GNU_CHAIN
|
||||
CONFIG['LDSHAREDXX'] = "$(CXX) -shared -static-libgcc -static-libstdc++"
|
||||
else
|
||||
$defs.push "-EHs"
|
||||
$defs.push "-GR"
|
||||
end
|
||||
|
||||
when /solaris/
|
||||
add_define 'OS_SOLARIS8'
|
||||
check_libs(%w[nsl socket], true)
|
||||
|
||||
if CONFIG['CC'] == 'cc' && (
|
||||
`cc -flags 2>&1` =~ /Sun/ || # detect SUNWspro compiler
|
||||
`cc -V 2>&1` =~ /Sun/ # detect Solaris Studio compiler
|
||||
)
|
||||
# SUN CHAIN
|
||||
add_define 'CC_SUNWspro'
|
||||
$preload = ["\nCXX = CC"] # hack a CXX= line into the makefile
|
||||
$CFLAGS = CONFIG['CFLAGS'] = "-KPIC"
|
||||
CONFIG['CCDLFLAGS'] = "-KPIC"
|
||||
CONFIG['LDSHARED'] = "$(CXX) -G -KPIC -lCstd"
|
||||
CONFIG['LDSHAREDXX'] = "$(CXX) -G -KPIC -lCstd"
|
||||
else
|
||||
# GNU CHAIN
|
||||
# on Unix we need a g++ link, not gcc.
|
||||
CONFIG['LDSHARED'] = "$(CXX) -shared"
|
||||
end
|
||||
|
||||
when /openbsd/
|
||||
# OpenBSD branch contributed by Guillaume Sellier.
|
||||
|
||||
# on Unix we need a g++ link, not gcc. On OpenBSD, linking against libstdc++ have to be explicitly done for shared libs
|
||||
CONFIG['LDSHARED'] = "$(CXX) -shared -lstdc++ -fPIC"
|
||||
CONFIG['LDSHAREDXX'] = "$(CXX) -shared -lstdc++ -fPIC"
|
||||
|
||||
when /darwin/
|
||||
# on Unix we need a g++ link, not gcc.
|
||||
# Ff line contributed by Daniel Harple.
|
||||
CONFIG['LDSHARED'] = "$(CXX) " + CONFIG['LDSHARED'].split[1..-1].join(' ')
|
||||
|
||||
when /linux/
|
||||
# on Unix we need a g++ link, not gcc.
|
||||
CONFIG['LDSHARED'] = "$(CXX) -shared"
|
||||
|
||||
when /aix/
|
||||
CONFIG['LDSHARED'] = "$(CXX) -Wl,-bstatic -Wl,-bdynamic -Wl,-G -Wl,-brtl"
|
||||
|
||||
when /cygwin/
|
||||
# For rubies built with Cygwin, CXX may be set to CC, which is just
|
||||
# a wrapper for gcc.
|
||||
# This will compile, but it will not link to the C++ std library.
|
||||
# Explicitly set CXX to use g++.
|
||||
CONFIG['CXX'] = "g++"
|
||||
# on Unix we need a g++ link, not gcc.
|
||||
CONFIG['LDSHARED'] = "$(CXX) -shared"
|
||||
|
||||
else
|
||||
# on Unix we need a g++ link, not gcc.
|
||||
CONFIG['LDSHARED'] = "$(CXX) -shared"
|
||||
end
|
||||
|
||||
create_makefile "fastfilereaderext"
|
||||
@@ -0,0 +1,214 @@
|
||||
/*****************************************************************************
|
||||
|
||||
$Id: mapper.cpp 4527 2007-07-04 10:21:34Z francis $
|
||||
|
||||
File: mapper.cpp
|
||||
Date: 02Jul07
|
||||
|
||||
Copyright (C) 2007 by Francis Cianfrocca. All Rights Reserved.
|
||||
Gmail: garbagecat10
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of either: 1) the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version; or 2) Ruby's License.
|
||||
|
||||
See the file COPYING for complete licensing information.
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// UNIX implementation
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifdef OS_UNIX
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "mapper.h"
|
||||
|
||||
/******************
|
||||
Mapper_t::Mapper_t
|
||||
******************/
|
||||
|
||||
Mapper_t::Mapper_t (const std::string &filename)
|
||||
{
|
||||
/* We ASSUME we can open the file.
|
||||
* (More precisely, we assume someone else checked before we got here.)
|
||||
*/
|
||||
|
||||
Fd = open (filename.c_str(), O_RDONLY);
|
||||
if (Fd < 0)
|
||||
throw std::runtime_error (strerror (errno));
|
||||
|
||||
struct stat st;
|
||||
if (fstat (Fd, &st))
|
||||
throw std::runtime_error (strerror (errno));
|
||||
FileSize = st.st_size;
|
||||
|
||||
#ifdef OS_WIN32
|
||||
MapPoint = (char*) mmap (0, FileSize, PROT_READ, MAP_SHARED, Fd, 0);
|
||||
#else
|
||||
MapPoint = (const char*) mmap (0, FileSize, PROT_READ, MAP_SHARED, Fd, 0);
|
||||
#endif
|
||||
if (MapPoint == MAP_FAILED)
|
||||
throw std::runtime_error (strerror (errno));
|
||||
}
|
||||
|
||||
|
||||
/*******************
|
||||
Mapper_t::~Mapper_t
|
||||
*******************/
|
||||
|
||||
Mapper_t::~Mapper_t()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
|
||||
/***************
|
||||
Mapper_t::Close
|
||||
***************/
|
||||
|
||||
void Mapper_t::Close()
|
||||
{
|
||||
// Can be called multiple times.
|
||||
// Calls to GetChunk are invalid after a call to Close.
|
||||
if (MapPoint) {
|
||||
#ifdef CC_SUNWspro
|
||||
// TODO: The void * cast works fine on Solaris 11, but
|
||||
// I don't know at what point that changed from older Solaris.
|
||||
munmap ((char*)MapPoint, FileSize);
|
||||
#else
|
||||
munmap ((void*)MapPoint, FileSize);
|
||||
#endif
|
||||
MapPoint = NULL;
|
||||
}
|
||||
if (Fd >= 0) {
|
||||
close (Fd);
|
||||
Fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/******************
|
||||
Mapper_t::GetChunk
|
||||
******************/
|
||||
|
||||
const char *Mapper_t::GetChunk (unsigned start)
|
||||
{
|
||||
return MapPoint + start;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif // OS_UNIX
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// WINDOWS implementation
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef OS_WIN32
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "mapper.h"
|
||||
|
||||
/******************
|
||||
Mapper_t::Mapper_t
|
||||
******************/
|
||||
|
||||
Mapper_t::Mapper_t (const std::string &filename)
|
||||
{
|
||||
/* We ASSUME we can open the file.
|
||||
* (More precisely, we assume someone else checked before we got here.)
|
||||
*/
|
||||
|
||||
hFile = INVALID_HANDLE_VALUE;
|
||||
hMapping = NULL;
|
||||
MapPoint = NULL;
|
||||
FileSize = 0;
|
||||
|
||||
hFile = CreateFile (filename.c_str(), GENERIC_READ|GENERIC_WRITE, FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
throw std::runtime_error ("File not found");
|
||||
|
||||
BY_HANDLE_FILE_INFORMATION i;
|
||||
if (GetFileInformationByHandle (hFile, &i))
|
||||
FileSize = i.nFileSizeLow;
|
||||
|
||||
hMapping = CreateFileMapping (hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
|
||||
if (!hMapping)
|
||||
throw std::runtime_error ("File not mapped");
|
||||
|
||||
#ifdef OS_WIN32
|
||||
MapPoint = (char*) MapViewOfFile (hMapping, FILE_MAP_WRITE, 0, 0, 0);
|
||||
#else
|
||||
MapPoint = (const char*) MapViewOfFile (hMapping, FILE_MAP_WRITE, 0, 0, 0);
|
||||
#endif
|
||||
if (!MapPoint)
|
||||
throw std::runtime_error ("Mappoint not read");
|
||||
}
|
||||
|
||||
|
||||
/*******************
|
||||
Mapper_t::~Mapper_t
|
||||
*******************/
|
||||
|
||||
Mapper_t::~Mapper_t()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
/***************
|
||||
Mapper_t::Close
|
||||
***************/
|
||||
|
||||
void Mapper_t::Close()
|
||||
{
|
||||
// Can be called multiple times.
|
||||
// Calls to GetChunk are invalid after a call to Close.
|
||||
if (MapPoint) {
|
||||
UnmapViewOfFile (MapPoint);
|
||||
MapPoint = NULL;
|
||||
}
|
||||
if (hMapping != NULL) {
|
||||
CloseHandle (hMapping);
|
||||
hMapping = NULL;
|
||||
}
|
||||
if (hFile != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle (hFile);
|
||||
hFile = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/******************
|
||||
Mapper_t::GetChunk
|
||||
******************/
|
||||
|
||||
const char *Mapper_t::GetChunk (unsigned start)
|
||||
{
|
||||
return MapPoint + start;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif // OS_WINDOWS
|
||||
@@ -0,0 +1,59 @@
|
||||
/*****************************************************************************
|
||||
|
||||
$Id: mapper.h 4529 2007-07-04 11:32:22Z francis $
|
||||
|
||||
File: mapper.h
|
||||
Date: 02Jul07
|
||||
|
||||
Copyright (C) 2007 by Francis Cianfrocca. All Rights Reserved.
|
||||
Gmail: garbagecat10
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of either: 1) the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version; or 2) Ruby's License.
|
||||
|
||||
See the file COPYING for complete licensing information.
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef __Mapper__H_
|
||||
#define __Mapper__H_
|
||||
|
||||
|
||||
/**************
|
||||
class Mapper_t
|
||||
**************/
|
||||
|
||||
class Mapper_t
|
||||
{
|
||||
public:
|
||||
Mapper_t (const std::string&);
|
||||
virtual ~Mapper_t();
|
||||
|
||||
const char *GetChunk (unsigned);
|
||||
void Close();
|
||||
size_t GetFileSize() {return FileSize;}
|
||||
|
||||
private:
|
||||
size_t FileSize;
|
||||
|
||||
#ifdef OS_UNIX
|
||||
private:
|
||||
int Fd;
|
||||
const char *MapPoint;
|
||||
#endif // OS_UNIX
|
||||
|
||||
#ifdef OS_WIN32
|
||||
private:
|
||||
HANDLE hFile;
|
||||
HANDLE hMapping;
|
||||
char *MapPoint;
|
||||
#endif // OS_WIN32
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // __Mapper__H_
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/*****************************************************************************
|
||||
|
||||
$Id: rubymain.cpp 4529 2007-07-04 11:32:22Z francis $
|
||||
|
||||
File: rubymain.cpp
|
||||
Date: 02Jul07
|
||||
|
||||
Copyright (C) 2007 by Francis Cianfrocca. All Rights Reserved.
|
||||
Gmail: garbagecat10
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of either: 1) the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version; or 2) Ruby's License.
|
||||
|
||||
See the file COPYING for complete licensing information.
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <ruby.h>
|
||||
#include "mapper.h"
|
||||
|
||||
static VALUE EmModule;
|
||||
static VALUE FastFileReader;
|
||||
static VALUE Mapper;
|
||||
|
||||
|
||||
|
||||
/*********
|
||||
mapper_dt
|
||||
*********/
|
||||
|
||||
static void mapper_dt (void *ptr)
|
||||
{
|
||||
if (ptr)
|
||||
delete (Mapper_t*) ptr;
|
||||
}
|
||||
|
||||
/**********
|
||||
mapper_new
|
||||
**********/
|
||||
|
||||
static VALUE mapper_new (VALUE self, VALUE filename)
|
||||
{
|
||||
Mapper_t *m = new Mapper_t (StringValueCStr (filename));
|
||||
if (!m)
|
||||
rb_raise (rb_eStandardError, "No Mapper Object");
|
||||
VALUE v = Data_Wrap_Struct (Mapper, 0, mapper_dt, (void*)m);
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
/****************
|
||||
mapper_get_chunk
|
||||
****************/
|
||||
|
||||
static VALUE mapper_get_chunk (VALUE self, VALUE start, VALUE length)
|
||||
{
|
||||
Mapper_t *m = NULL;
|
||||
Data_Get_Struct (self, Mapper_t, m);
|
||||
if (!m)
|
||||
rb_raise (rb_eStandardError, "No Mapper Object");
|
||||
|
||||
// TODO, what if some moron sends us a negative start value?
|
||||
unsigned _start = NUM2INT (start);
|
||||
unsigned _length = NUM2INT (length);
|
||||
if ((_start + _length) > m->GetFileSize())
|
||||
rb_raise (rb_eStandardError, "Mapper Range Error");
|
||||
|
||||
const char *chunk = m->GetChunk (_start);
|
||||
if (!chunk)
|
||||
rb_raise (rb_eStandardError, "No Mapper Chunk");
|
||||
return rb_str_new (chunk, _length);
|
||||
}
|
||||
|
||||
/************
|
||||
mapper_close
|
||||
************/
|
||||
|
||||
static VALUE mapper_close (VALUE self)
|
||||
{
|
||||
Mapper_t *m = NULL;
|
||||
Data_Get_Struct (self, Mapper_t, m);
|
||||
if (!m)
|
||||
rb_raise (rb_eStandardError, "No Mapper Object");
|
||||
m->Close();
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
/***********
|
||||
mapper_size
|
||||
***********/
|
||||
|
||||
static VALUE mapper_size (VALUE self)
|
||||
{
|
||||
Mapper_t *m = NULL;
|
||||
Data_Get_Struct (self, Mapper_t, m);
|
||||
if (!m)
|
||||
rb_raise (rb_eStandardError, "No Mapper Object");
|
||||
return INT2NUM (m->GetFileSize());
|
||||
}
|
||||
|
||||
|
||||
/**********************
|
||||
Init_fastfilereaderext
|
||||
**********************/
|
||||
|
||||
extern "C" void Init_fastfilereaderext()
|
||||
{
|
||||
EmModule = rb_define_module ("EventMachine");
|
||||
FastFileReader = rb_define_class_under (EmModule, "FastFileReader", rb_cObject);
|
||||
Mapper = rb_define_class_under (FastFileReader, "Mapper", rb_cObject);
|
||||
|
||||
rb_define_module_function (Mapper, "new", (VALUE(*)(...))mapper_new, 1);
|
||||
rb_define_method (Mapper, "size", (VALUE(*)(...))mapper_size, 0);
|
||||
rb_define_method (Mapper, "close", (VALUE(*)(...))mapper_close, 0);
|
||||
rb_define_method (Mapper, "get_chunk", (VALUE(*)(...))mapper_get_chunk, 2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/*****************************************************************************
|
||||
|
||||
$Id$
|
||||
|
||||
File: kb.cpp
|
||||
Date: 24Aug07
|
||||
|
||||
Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
|
||||
Gmail: blackhedd
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of either: 1) the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version; or 2) Ruby's License.
|
||||
|
||||
See the file COPYING for complete licensing information.
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#include "project.h"
|
||||
|
||||
|
||||
/**************************************
|
||||
KeyboardDescriptor::KeyboardDescriptor
|
||||
**************************************/
|
||||
|
||||
KeyboardDescriptor::KeyboardDescriptor (EventMachine_t *parent_em):
|
||||
EventableDescriptor (0, parent_em),
|
||||
bReadAttemptedAfterClose (false)
|
||||
{
|
||||
#ifdef HAVE_EPOLL
|
||||
EpollEvent.events = EPOLLIN;
|
||||
#endif
|
||||
#ifdef HAVE_KQUEUE
|
||||
MyEventMachine->ArmKqueueReader (this);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/***************************************
|
||||
KeyboardDescriptor::~KeyboardDescriptor
|
||||
***************************************/
|
||||
|
||||
KeyboardDescriptor::~KeyboardDescriptor()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*************************
|
||||
KeyboardDescriptor::Write
|
||||
*************************/
|
||||
|
||||
void KeyboardDescriptor::Write()
|
||||
{
|
||||
// Why are we here?
|
||||
throw std::runtime_error ("bad code path in keyboard handler");
|
||||
}
|
||||
|
||||
|
||||
/*****************************
|
||||
KeyboardDescriptor::Heartbeat
|
||||
*****************************/
|
||||
|
||||
void KeyboardDescriptor::Heartbeat()
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
|
||||
/************************
|
||||
KeyboardDescriptor::Read
|
||||
************************/
|
||||
|
||||
void KeyboardDescriptor::Read()
|
||||
{
|
||||
char c;
|
||||
(void)read (GetSocket(), &c, 1);
|
||||
_GenericInboundDispatch(&c, 1);
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*****************************************************************************
|
||||
|
||||
$Id$
|
||||
|
||||
File: page.cpp
|
||||
Date: 30Apr06
|
||||
|
||||
Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
|
||||
Gmail: blackhedd
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of either: 1) the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version; or 2) Ruby's License.
|
||||
|
||||
See the file COPYING for complete licensing information.
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#include "project.h"
|
||||
|
||||
|
||||
/******************
|
||||
PageList::PageList
|
||||
******************/
|
||||
|
||||
PageList::PageList()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*******************
|
||||
PageList::~PageList
|
||||
*******************/
|
||||
|
||||
PageList::~PageList()
|
||||
{
|
||||
while (HasPages())
|
||||
PopFront();
|
||||
}
|
||||
|
||||
|
||||
/***************
|
||||
PageList::Front
|
||||
***************/
|
||||
|
||||
void PageList::Front (const char **page, int *length)
|
||||
{
|
||||
assert (page && length);
|
||||
|
||||
if (HasPages()) {
|
||||
Page p = Pages.front();
|
||||
*page = p.Buffer;
|
||||
*length = p.Size;
|
||||
}
|
||||
else {
|
||||
*page = NULL;
|
||||
*length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/******************
|
||||
PageList::PopFront
|
||||
******************/
|
||||
|
||||
void PageList::PopFront()
|
||||
{
|
||||
if (HasPages()) {
|
||||
Page p = Pages.front();
|
||||
Pages.pop_front();
|
||||
if (p.Buffer)
|
||||
free ((void*)p.Buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/******************
|
||||
PageList::HasPages
|
||||
******************/
|
||||
|
||||
bool PageList::HasPages()
|
||||
{
|
||||
return (Pages.size() > 0) ? true : false;
|
||||
}
|
||||
|
||||
|
||||
/**************
|
||||
PageList::Push
|
||||
**************/
|
||||
|
||||
void PageList::Push (const char *buf, int size)
|
||||
{
|
||||
if (buf && (size > 0)) {
|
||||
char *copy = (char*) malloc (size);
|
||||
if (!copy)
|
||||
throw std::runtime_error ("no memory in pagelist");
|
||||
memcpy (copy, buf, size);
|
||||
Pages.push_back (Page (copy, size));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*****************************************************************************
|
||||
|
||||
$Id$
|
||||
|
||||
File: page.h
|
||||
Date: 30Apr06
|
||||
|
||||
Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
|
||||
Gmail: blackhedd
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of either: 1) the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version; or 2) Ruby's License.
|
||||
|
||||
See the file COPYING for complete licensing information.
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef __PageManager__H_
|
||||
#define __PageManager__H_
|
||||
|
||||
|
||||
/**************
|
||||
class PageList
|
||||
**************/
|
||||
|
||||
class PageList
|
||||
{
|
||||
struct Page {
|
||||
Page (const char *b, size_t s): Buffer(b), Size(s) {}
|
||||
const char *Buffer;
|
||||
size_t Size;
|
||||
};
|
||||
|
||||
public:
|
||||
PageList();
|
||||
virtual ~PageList();
|
||||
|
||||
void Push (const char*, int);
|
||||
bool HasPages();
|
||||
void Front (const char**, int*);
|
||||
void PopFront();
|
||||
|
||||
private:
|
||||
std::deque<Page> Pages;
|
||||
};
|
||||
|
||||
|
||||
#endif // __PageManager__H_
|
||||
@@ -0,0 +1,354 @@
|
||||
/*****************************************************************************
|
||||
|
||||
$Id$
|
||||
|
||||
File: pipe.cpp
|
||||
Date: 30May07
|
||||
|
||||
Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
|
||||
Gmail: blackhedd
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of either: 1) the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version; or 2) Ruby's License.
|
||||
|
||||
See the file COPYING for complete licensing information.
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#include "project.h"
|
||||
|
||||
|
||||
#ifdef OS_UNIX
|
||||
// THIS ENTIRE FILE IS ONLY COMPILED ON UNIX-LIKE SYSTEMS.
|
||||
|
||||
/******************************
|
||||
PipeDescriptor::PipeDescriptor
|
||||
******************************/
|
||||
|
||||
PipeDescriptor::PipeDescriptor (int fd, pid_t subpid, EventMachine_t *parent_em):
|
||||
EventableDescriptor (fd, parent_em),
|
||||
bReadAttemptedAfterClose (false),
|
||||
OutboundDataSize (0),
|
||||
SubprocessPid (subpid)
|
||||
{
|
||||
#ifdef HAVE_EPOLL
|
||||
EpollEvent.events = EPOLLIN;
|
||||
#endif
|
||||
#ifdef HAVE_KQUEUE
|
||||
MyEventMachine->ArmKqueueReader (this);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*******************************
|
||||
PipeDescriptor::~PipeDescriptor
|
||||
*******************************/
|
||||
|
||||
PipeDescriptor::~PipeDescriptor() NO_EXCEPT_FALSE
|
||||
{
|
||||
// Run down any stranded outbound data.
|
||||
for (size_t i=0; i < OutboundPages.size(); i++)
|
||||
OutboundPages[i].Free();
|
||||
|
||||
/* As a virtual destructor, we come here before the base-class
|
||||
* destructor that closes our file-descriptor.
|
||||
* We have to make sure the subprocess goes down (if it's not
|
||||
* already down) and we have to reap the zombie.
|
||||
*
|
||||
* This implementation is PROVISIONAL and will surely be improved.
|
||||
* The intention here is that we never block, hence the highly
|
||||
* undesirable sleeps. But if we can't reap the subprocess even
|
||||
* after sending it SIGKILL, then something is wrong and we
|
||||
* throw a fatal exception, which is also not something we should
|
||||
* be doing.
|
||||
*
|
||||
* Eventually the right thing to do will be to have the reactor
|
||||
* core respond to SIGCHLD by chaining a handler on top of the
|
||||
* one Ruby may have installed, and dealing with a list of dead
|
||||
* children that are pending cleanup.
|
||||
*
|
||||
* Since we want to have a signal processor integrated into the
|
||||
* client-visible API, let's wait until that is done before cleaning
|
||||
* this up.
|
||||
*
|
||||
* Added a very ugly hack to support passing the subprocess's exit
|
||||
* status to the user. It only makes logical sense for user code to access
|
||||
* the subprocess exit status in the unbind callback. But unbind is called
|
||||
* back during the EventableDescriptor destructor. So by that time there's
|
||||
* no way to call back this object through an object binding, because it's
|
||||
* already been cleaned up. We might have added a parameter to the unbind
|
||||
* callback, but that would probably break a huge amount of existing code.
|
||||
* So the hack-solution is to define an instance variable in the EventMachine
|
||||
* object and stick the exit status in there, where it can easily be accessed
|
||||
* with an accessor visible to user code.
|
||||
* User code should ONLY access the exit status from within the unbind callback.
|
||||
* Otherwise there's no guarantee it'll be valid.
|
||||
* This hack won't make it impossible to run multiple EventMachines in a single
|
||||
* process, but it will make it impossible to reliably nest unbind calls
|
||||
* within other unbind calls. (Not sure if that's even possible.)
|
||||
*/
|
||||
|
||||
assert (MyEventMachine);
|
||||
|
||||
/* Another hack to make the SubprocessPid available to get_subprocess_status */
|
||||
MyEventMachine->SubprocessPid = SubprocessPid;
|
||||
|
||||
/* 01Mar09: Updated to use a small nanosleep in a loop. When nanosleep is interrupted by SIGCHLD,
|
||||
* it resumes the system call after processing the signal (resulting in unnecessary latency).
|
||||
* Calling nanosleep in a loop avoids this problem.
|
||||
*/
|
||||
struct timespec req = {0, 50000000}; // 0.05s
|
||||
int n;
|
||||
|
||||
// wait 0.5s for the process to die
|
||||
for (n=0; n<10; n++) {
|
||||
if (waitpid (SubprocessPid, &(MyEventMachine->SubprocessExitStatus), WNOHANG) != 0) return;
|
||||
nanosleep (&req, NULL);
|
||||
}
|
||||
|
||||
// send SIGTERM and wait another 1s
|
||||
kill (SubprocessPid, SIGTERM);
|
||||
for (n=0; n<20; n++) {
|
||||
nanosleep (&req, NULL);
|
||||
if (waitpid (SubprocessPid, &(MyEventMachine->SubprocessExitStatus), WNOHANG) != 0) return;
|
||||
}
|
||||
|
||||
// send SIGKILL and wait another 5s
|
||||
kill (SubprocessPid, SIGKILL);
|
||||
for (n=0; n<100; n++) {
|
||||
nanosleep (&req, NULL);
|
||||
if (waitpid (SubprocessPid, &(MyEventMachine->SubprocessExitStatus), WNOHANG) != 0) return;
|
||||
}
|
||||
|
||||
// still not dead, give up!
|
||||
throw std::runtime_error ("unable to reap subprocess");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************
|
||||
PipeDescriptor::Read
|
||||
********************/
|
||||
|
||||
void PipeDescriptor::Read()
|
||||
{
|
||||
int sd = GetSocket();
|
||||
if (sd == INVALID_SOCKET) {
|
||||
assert (!bReadAttemptedAfterClose);
|
||||
bReadAttemptedAfterClose = true;
|
||||
return;
|
||||
}
|
||||
|
||||
LastActivity = MyEventMachine->GetCurrentLoopTime();
|
||||
|
||||
int total_bytes_read = 0;
|
||||
char readbuffer [16 * 1024];
|
||||
|
||||
for (int i=0; i < 10; i++) {
|
||||
// Don't read just one buffer and then move on. This is faster
|
||||
// if there is a lot of incoming.
|
||||
// But don't read indefinitely. Give other sockets a chance to run.
|
||||
// NOTICE, we're reading one less than the buffer size.
|
||||
// That's so we can put a guard byte at the end of what we send
|
||||
// to user code.
|
||||
// Use read instead of recv, which on Linux gives a "socket operation
|
||||
// on nonsocket" error.
|
||||
|
||||
|
||||
int r = read (sd, readbuffer, sizeof(readbuffer) - 1);
|
||||
//cerr << "<R:" << r << ">";
|
||||
|
||||
if (r > 0) {
|
||||
total_bytes_read += r;
|
||||
|
||||
// Add a null-terminator at the the end of the buffer
|
||||
// that we will send to the callback.
|
||||
// DO NOT EVER CHANGE THIS. We want to explicitly allow users
|
||||
// to be able to depend on this behavior, so they will have
|
||||
// the option to do some things faster. Additionally it's
|
||||
// a security guard against buffer overflows.
|
||||
readbuffer [r] = 0;
|
||||
_GenericInboundDispatch(readbuffer, r);
|
||||
}
|
||||
else if (r == 0) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// Basically a would-block, meaning we've read everything there is to read.
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (total_bytes_read == 0) {
|
||||
// If we read no data on a socket that selected readable,
|
||||
// it generally means the other end closed the connection gracefully.
|
||||
ScheduleClose (false);
|
||||
//bCloseNow = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*********************
|
||||
PipeDescriptor::Write
|
||||
*********************/
|
||||
|
||||
void PipeDescriptor::Write()
|
||||
{
|
||||
int sd = GetSocket();
|
||||
assert (sd != INVALID_SOCKET);
|
||||
|
||||
LastActivity = MyEventMachine->GetCurrentLoopTime();
|
||||
char output_buffer [16 * 1024];
|
||||
size_t nbytes = 0;
|
||||
|
||||
while ((OutboundPages.size() > 0) && (nbytes < sizeof(output_buffer))) {
|
||||
OutboundPage *op = &(OutboundPages[0]);
|
||||
if ((nbytes + op->Length - op->Offset) < sizeof (output_buffer)) {
|
||||
memcpy (output_buffer + nbytes, op->Buffer + op->Offset, op->Length - op->Offset);
|
||||
nbytes += (op->Length - op->Offset);
|
||||
op->Free();
|
||||
OutboundPages.pop_front();
|
||||
}
|
||||
else {
|
||||
int len = sizeof(output_buffer) - nbytes;
|
||||
memcpy (output_buffer + nbytes, op->Buffer + op->Offset, len);
|
||||
op->Offset += len;
|
||||
nbytes += len;
|
||||
}
|
||||
}
|
||||
|
||||
// We should never have gotten here if there were no data to write,
|
||||
// so assert that as a sanity check.
|
||||
// Don't bother to make sure nbytes is less than output_buffer because
|
||||
// if it were we probably would have crashed already.
|
||||
assert (nbytes > 0);
|
||||
|
||||
assert (GetSocket() != INVALID_SOCKET);
|
||||
int bytes_written = write (GetSocket(), output_buffer, nbytes);
|
||||
#ifdef OS_WIN32
|
||||
int e = WSAGetLastError();
|
||||
#else
|
||||
int e = errno;
|
||||
#endif
|
||||
|
||||
if (bytes_written > 0) {
|
||||
OutboundDataSize -= bytes_written;
|
||||
if ((size_t)bytes_written < nbytes) {
|
||||
int len = nbytes - bytes_written;
|
||||
char *buffer = (char*) malloc (len + 1);
|
||||
if (!buffer)
|
||||
throw std::runtime_error ("bad alloc throwing back data");
|
||||
memcpy (buffer, output_buffer + bytes_written, len);
|
||||
buffer [len] = 0;
|
||||
OutboundPages.push_front (OutboundPage (buffer, len));
|
||||
}
|
||||
#ifdef HAVE_EPOLL
|
||||
EpollEvent.events = EPOLLIN;
|
||||
if (SelectForWrite())
|
||||
EpollEvent.events |= EPOLLOUT;
|
||||
assert (MyEventMachine);
|
||||
MyEventMachine->Modify (this);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
#ifdef OS_UNIX
|
||||
if ((e != EINPROGRESS) && (e != EWOULDBLOCK) && (e != EINTR))
|
||||
#endif
|
||||
#ifdef OS_WIN32
|
||||
if ((e != WSAEINPROGRESS) && (e != WSAEWOULDBLOCK))
|
||||
#endif
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*************************
|
||||
PipeDescriptor::Heartbeat
|
||||
*************************/
|
||||
|
||||
void PipeDescriptor::Heartbeat()
|
||||
{
|
||||
// If an inactivity timeout is defined, then check for it.
|
||||
if (InactivityTimeout && ((MyEventMachine->GetCurrentLoopTime() - LastActivity) >= InactivityTimeout))
|
||||
ScheduleClose (false);
|
||||
//bCloseNow = true;
|
||||
}
|
||||
|
||||
|
||||
/*****************************
|
||||
PipeDescriptor::SelectForRead
|
||||
*****************************/
|
||||
|
||||
bool PipeDescriptor::SelectForRead()
|
||||
{
|
||||
/* Pipe descriptors, being local by definition, don't have
|
||||
* a pending state, so this is simpler than for the
|
||||
* ConnectionDescriptor object.
|
||||
*/
|
||||
return bPaused ? false : true;
|
||||
}
|
||||
|
||||
/******************************
|
||||
PipeDescriptor::SelectForWrite
|
||||
******************************/
|
||||
|
||||
bool PipeDescriptor::SelectForWrite()
|
||||
{
|
||||
/* Pipe descriptors, being local by definition, don't have
|
||||
* a pending state, so this is simpler than for the
|
||||
* ConnectionDescriptor object.
|
||||
*/
|
||||
return (GetOutboundDataSize() > 0) && !bPaused ? true : false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/********************************
|
||||
PipeDescriptor::SendOutboundData
|
||||
********************************/
|
||||
|
||||
int PipeDescriptor::SendOutboundData (const char *data, unsigned long length)
|
||||
{
|
||||
//if (bCloseNow || bCloseAfterWriting)
|
||||
if (IsCloseScheduled())
|
||||
return 0;
|
||||
|
||||
if (!data && (length > 0))
|
||||
throw std::runtime_error ("bad outbound data");
|
||||
char *buffer = (char *) malloc (length + 1);
|
||||
if (!buffer)
|
||||
throw std::runtime_error ("no allocation for outbound data");
|
||||
memcpy (buffer, data, length);
|
||||
buffer [length] = 0;
|
||||
OutboundPages.push_back (OutboundPage (buffer, length));
|
||||
OutboundDataSize += length;
|
||||
#ifdef HAVE_EPOLL
|
||||
EpollEvent.events = (EPOLLIN | EPOLLOUT);
|
||||
assert (MyEventMachine);
|
||||
MyEventMachine->Modify (this);
|
||||
#endif
|
||||
return length;
|
||||
}
|
||||
|
||||
/********************************
|
||||
PipeDescriptor::GetSubprocessPid
|
||||
********************************/
|
||||
|
||||
bool PipeDescriptor::GetSubprocessPid (pid_t *pid)
|
||||
{
|
||||
bool ok = false;
|
||||
if (pid && (SubprocessPid > 0)) {
|
||||
*pid = SubprocessPid;
|
||||
ok = true;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
#endif // OS_UNIX
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
/*****************************************************************************
|
||||
|
||||
$Id$
|
||||
|
||||
File: project.h
|
||||
Date: 06Apr06
|
||||
|
||||
Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
|
||||
Gmail: blackhedd
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of either: 1) the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version; or 2) Ruby's License.
|
||||
|
||||
See the file COPYING for complete licensing information.
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef __Project__H_
|
||||
#define __Project__H_
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
|
||||
#ifdef OS_UNIX
|
||||
#include <signal.h>
|
||||
#include <netdb.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/wait.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <pwd.h>
|
||||
#include <string.h>
|
||||
typedef int SOCKET;
|
||||
#define INVALID_SOCKET -1
|
||||
#define SOCKET_ERROR -1
|
||||
#ifdef OS_SOLARIS8
|
||||
#include <strings.h>
|
||||
#include <sys/un.h>
|
||||
#ifndef AF_LOCAL
|
||||
#define AF_LOCAL AF_UNIX
|
||||
#endif
|
||||
// INADDR_NONE is undefined on Solaris < 8. Thanks to Brett Eisenberg and Tim Pease.
|
||||
#ifndef INADDR_NONE
|
||||
#define INADDR_NONE ((unsigned long)-1)
|
||||
#endif
|
||||
#endif /* OS_SOLARIS8 */
|
||||
|
||||
#ifdef _AIX
|
||||
#include <strings.h>
|
||||
#ifndef AF_LOCAL
|
||||
#define AF_LOCAL AF_UNIX
|
||||
#endif
|
||||
#endif /* _AIX */
|
||||
|
||||
#ifdef OS_DARWIN
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_time.h>
|
||||
#endif /* OS_DARWIN */
|
||||
|
||||
#endif /* OS_UNIX */
|
||||
|
||||
#ifdef OS_WIN32
|
||||
// 21Sep09: windows limits select() to 64 sockets by default, we increase it to 1024 here (before including winsock2.h)
|
||||
// 18Jun12: fd_setsize must be changed in the ruby binary (not in this extension). redefining it also causes segvs, see eventmachine/eventmachine#333
|
||||
//#define FD_SETSIZE 1024
|
||||
|
||||
// WIN32_LEAN_AND_MEAN excludes APIs such as Cryptography, DDE, RPC, Shell, and Windows Sockets.
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <rpc.h>
|
||||
#include <fcntl.h>
|
||||
#include <assert.h>
|
||||
|
||||
// Older versions of MinGW in the Ruby Dev Kit do not provide the getaddrinfo hint flags
|
||||
#ifndef AI_ADDRCONFIG
|
||||
#define AI_ADDRCONFIG 0x0400
|
||||
#endif
|
||||
|
||||
#ifndef AI_NUMERICSERV
|
||||
#define AI_NUMERICSERV 0x0008
|
||||
#endif
|
||||
|
||||
// Use the Win32 wrapper library that Ruby owns to be able to close sockets with the close() function
|
||||
#define RUBY_EXPORT
|
||||
#include <ruby/defines.h>
|
||||
#include <ruby/win32.h>
|
||||
#endif /* OS_WIN32 */
|
||||
|
||||
#if !defined(_MSC_VER) || _MSC_VER > 1500
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#ifdef WITH_SSL
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_EPOLL
|
||||
#include <sys/epoll.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_KQUEUE
|
||||
#include <sys/event.h>
|
||||
#include <sys/queue.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
#include <sys/inotify.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OLD_INOTIFY
|
||||
#include <sys/syscall.h>
|
||||
#include <linux/inotify.h>
|
||||
static inline int inotify_init (void) { return syscall (__NR_inotify_init); }
|
||||
static inline int inotify_add_watch (int fd, const char *name, __u32 mask) { return syscall (__NR_inotify_add_watch, fd, name, mask); }
|
||||
static inline int inotify_rm_watch (int fd, __u32 wd) { return syscall (__NR_inotify_rm_watch, fd, wd); }
|
||||
#define HAVE_INOTIFY 1
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
#define INOTIFY_EVENT_SIZE (sizeof(struct inotify_event))
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_WRITEV
|
||||
#include <sys/uio.h>
|
||||
#endif
|
||||
|
||||
#if __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
typedef void (*EMCallback)(const unsigned long, int, const char*, const unsigned long);
|
||||
#if __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) && (__GNUC__ >= 3)
|
||||
#define UNUSED __attribute__ ((unused))
|
||||
#else
|
||||
#define UNUSED
|
||||
#endif
|
||||
|
||||
#include "binder.h"
|
||||
#include "em.h"
|
||||
#include "ed.h"
|
||||
#include "page.h"
|
||||
#include "ssl.h"
|
||||
#include "eventmachine.h"
|
||||
|
||||
#endif // __Project__H_
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,619 @@
|
||||
/*****************************************************************************
|
||||
|
||||
$Id$
|
||||
|
||||
File: ssl.cpp
|
||||
Date: 30Apr06
|
||||
|
||||
Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
|
||||
Gmail: blackhedd
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of either: 1) the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version; or 2) Ruby's License.
|
||||
|
||||
See the file COPYING for complete licensing information.
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifdef WITH_SSL
|
||||
|
||||
#include "project.h"
|
||||
|
||||
|
||||
bool SslContext_t::bLibraryInitialized = false;
|
||||
|
||||
|
||||
|
||||
static void InitializeDefaultCredentials();
|
||||
static EVP_PKEY *DefaultPrivateKey = NULL;
|
||||
static X509 *DefaultCertificate = NULL;
|
||||
|
||||
static char PrivateMaterials[] = {
|
||||
"-----BEGIN RSA PRIVATE KEY-----\n"
|
||||
"MIICXAIBAAKBgQDCYYhcw6cGRbhBVShKmbWm7UVsEoBnUf0cCh8AX+MKhMxwVDWV\n"
|
||||
"Igdskntn3cSJjRtmgVJHIK0lpb/FYHQB93Ohpd9/Z18pDmovfFF9nDbFF0t39hJ/\n"
|
||||
"AqSzFB3GiVPoFFZJEE1vJqh+3jzsSF5K56bZ6azz38VlZgXeSozNW5bXkQIDAQAB\n"
|
||||
"AoGALA89gIFcr6BIBo8N5fL3aNHpZXjAICtGav+kTUpuxSiaym9cAeTHuAVv8Xgk\n"
|
||||
"H2Wbq11uz+6JMLpkQJH/WZ7EV59DPOicXrp0Imr73F3EXBfR7t2EQDYHPMthOA1D\n"
|
||||
"I9EtCzvV608Ze90hiJ7E3guGrGppZfJ+eUWCPgy8CZH1vRECQQDv67rwV/oU1aDo\n"
|
||||
"6/+d5nqjeW6mWkGqTnUU96jXap8EIw6B+0cUKskwx6mHJv+tEMM2748ZY7b0yBlg\n"
|
||||
"w4KDghbFAkEAz2h8PjSJG55LwqmXih1RONSgdN9hjB12LwXL1CaDh7/lkEhq0PlK\n"
|
||||
"PCAUwQSdM17Sl0Xxm2CZiekTSlwmHrtqXQJAF3+8QJwtV2sRJp8u2zVe37IeH1cJ\n"
|
||||
"xXeHyjTzqZ2803fnjN2iuZvzNr7noOA1/Kp+pFvUZUU5/0G2Ep8zolPUjQJAFA7k\n"
|
||||
"xRdLkzIx3XeNQjwnmLlncyYPRv+qaE3FMpUu7zftuZBnVCJnvXzUxP3vPgKTlzGa\n"
|
||||
"dg5XivDRfsV+okY5uQJBAMV4FesUuLQVEKb6lMs7rzZwpeGQhFDRfywJzfom2TLn\n"
|
||||
"2RdJQQ3dcgnhdVDgt5o1qkmsqQh8uJrJ9SdyLIaZQIc=\n"
|
||||
"-----END RSA PRIVATE KEY-----\n"
|
||||
"-----BEGIN CERTIFICATE-----\n"
|
||||
"MIID6TCCA1KgAwIBAgIJANm4W/Tzs+s+MA0GCSqGSIb3DQEBBQUAMIGqMQswCQYD\n"
|
||||
"VQQGEwJVUzERMA8GA1UECBMITmV3IFlvcmsxETAPBgNVBAcTCE5ldyBZb3JrMRYw\n"
|
||||
"FAYDVQQKEw1TdGVhbWhlYXQubmV0MRQwEgYDVQQLEwtFbmdpbmVlcmluZzEdMBsG\n"
|
||||
"A1UEAxMUb3BlbmNhLnN0ZWFtaGVhdC5uZXQxKDAmBgkqhkiG9w0BCQEWGWVuZ2lu\n"
|
||||
"ZWVyaW5nQHN0ZWFtaGVhdC5uZXQwHhcNMDYwNTA1MTcwNjAzWhcNMjQwMjIwMTcw\n"
|
||||
"NjAzWjCBqjELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE5ldyBZb3JrMREwDwYDVQQH\n"
|
||||
"EwhOZXcgWW9yazEWMBQGA1UEChMNU3RlYW1oZWF0Lm5ldDEUMBIGA1UECxMLRW5n\n"
|
||||
"aW5lZXJpbmcxHTAbBgNVBAMTFG9wZW5jYS5zdGVhbWhlYXQubmV0MSgwJgYJKoZI\n"
|
||||
"hvcNAQkBFhllbmdpbmVlcmluZ0BzdGVhbWhlYXQubmV0MIGfMA0GCSqGSIb3DQEB\n"
|
||||
"AQUAA4GNADCBiQKBgQDCYYhcw6cGRbhBVShKmbWm7UVsEoBnUf0cCh8AX+MKhMxw\n"
|
||||
"VDWVIgdskntn3cSJjRtmgVJHIK0lpb/FYHQB93Ohpd9/Z18pDmovfFF9nDbFF0t3\n"
|
||||
"9hJ/AqSzFB3GiVPoFFZJEE1vJqh+3jzsSF5K56bZ6azz38VlZgXeSozNW5bXkQID\n"
|
||||
"AQABo4IBEzCCAQ8wHQYDVR0OBBYEFPJvPd1Fcmd8o/Tm88r+NjYPICCkMIHfBgNV\n"
|
||||
"HSMEgdcwgdSAFPJvPd1Fcmd8o/Tm88r+NjYPICCkoYGwpIGtMIGqMQswCQYDVQQG\n"
|
||||
"EwJVUzERMA8GA1UECBMITmV3IFlvcmsxETAPBgNVBAcTCE5ldyBZb3JrMRYwFAYD\n"
|
||||
"VQQKEw1TdGVhbWhlYXQubmV0MRQwEgYDVQQLEwtFbmdpbmVlcmluZzEdMBsGA1UE\n"
|
||||
"AxMUb3BlbmNhLnN0ZWFtaGVhdC5uZXQxKDAmBgkqhkiG9w0BCQEWGWVuZ2luZWVy\n"
|
||||
"aW5nQHN0ZWFtaGVhdC5uZXSCCQDZuFv087PrPjAMBgNVHRMEBTADAQH/MA0GCSqG\n"
|
||||
"SIb3DQEBBQUAA4GBAC1CXey/4UoLgJiwcEMDxOvW74plks23090iziFIlGgcIhk0\n"
|
||||
"Df6hTAs7H3MWww62ddvR8l07AWfSzSP5L6mDsbvq7EmQsmPODwb6C+i2aF3EDL8j\n"
|
||||
"uw73m4YIGI0Zw2XdBpiOGkx2H56Kya6mJJe/5XORZedh1wpI7zki01tHYbcy\n"
|
||||
"-----END CERTIFICATE-----\n"};
|
||||
|
||||
/* These private materials were made with:
|
||||
* openssl req -new -x509 -keyout cakey.pem -out cacert.pem -nodes -days 6500
|
||||
* TODO: We need a full-blown capability to work with user-supplied
|
||||
* keypairs and properly-signed certificates.
|
||||
*/
|
||||
|
||||
|
||||
/*****************
|
||||
builtin_passwd_cb
|
||||
*****************/
|
||||
|
||||
extern "C" int builtin_passwd_cb (char *buf UNUSED, int bufsize UNUSED, int rwflag UNUSED, void *userdata UNUSED)
|
||||
{
|
||||
strcpy (buf, "kittycat");
|
||||
return 8;
|
||||
}
|
||||
|
||||
/****************************
|
||||
InitializeDefaultCredentials
|
||||
****************************/
|
||||
|
||||
static void InitializeDefaultCredentials()
|
||||
{
|
||||
BIO *bio = BIO_new_mem_buf (PrivateMaterials, -1);
|
||||
assert (bio);
|
||||
|
||||
if (DefaultPrivateKey) {
|
||||
// we may come here in a restart.
|
||||
EVP_PKEY_free (DefaultPrivateKey);
|
||||
DefaultPrivateKey = NULL;
|
||||
}
|
||||
PEM_read_bio_PrivateKey (bio, &DefaultPrivateKey, builtin_passwd_cb, 0);
|
||||
|
||||
if (DefaultCertificate) {
|
||||
// we may come here in a restart.
|
||||
X509_free (DefaultCertificate);
|
||||
DefaultCertificate = NULL;
|
||||
}
|
||||
PEM_read_bio_X509 (bio, &DefaultCertificate, NULL, 0);
|
||||
|
||||
BIO_free (bio);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**************************
|
||||
SslContext_t::SslContext_t
|
||||
**************************/
|
||||
|
||||
SslContext_t::SslContext_t (bool is_server, const std::string &privkeyfile, const std::string &certchainfile, const std::string &cipherlist, const std::string &ecdh_curve, const std::string &dhparam, int ssl_version) :
|
||||
bIsServer (is_server),
|
||||
pCtx (NULL),
|
||||
PrivateKey (NULL),
|
||||
Certificate (NULL)
|
||||
{
|
||||
/* TODO: the usage of the specified private-key and cert-chain filenames only applies to
|
||||
* client-side connections at this point. Server connections currently use the default materials.
|
||||
* That needs to be fixed asap.
|
||||
* Also, in this implementation, server-side connections use statically defined X-509 defaults.
|
||||
* One thing I'm really not clear on is whether or not you have to explicitly free X509 and EVP_PKEY
|
||||
* objects when we call our destructor, or whether just calling SSL_CTX_free is enough.
|
||||
*/
|
||||
|
||||
if (!bLibraryInitialized) {
|
||||
bLibraryInitialized = true;
|
||||
SSL_library_init();
|
||||
OpenSSL_add_ssl_algorithms();
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_load_error_strings();
|
||||
ERR_load_crypto_strings();
|
||||
|
||||
InitializeDefaultCredentials();
|
||||
}
|
||||
|
||||
pCtx = SSL_CTX_new (bIsServer ? SSLv23_server_method() : SSLv23_client_method());
|
||||
if (!pCtx)
|
||||
throw std::runtime_error ("no SSL context");
|
||||
|
||||
SSL_CTX_set_options (pCtx, SSL_OP_ALL);
|
||||
|
||||
#ifdef SSL_CTRL_CLEAR_OPTIONS
|
||||
SSL_CTX_clear_options (pCtx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1);
|
||||
# ifdef SSL_OP_NO_TLSv1_1
|
||||
SSL_CTX_clear_options (pCtx, SSL_OP_NO_TLSv1_1);
|
||||
# endif
|
||||
# ifdef SSL_OP_NO_TLSv1_2
|
||||
SSL_CTX_clear_options (pCtx, SSL_OP_NO_TLSv1_2);
|
||||
# endif
|
||||
#endif
|
||||
|
||||
if (!(ssl_version & EM_PROTO_SSLv2))
|
||||
SSL_CTX_set_options (pCtx, SSL_OP_NO_SSLv2);
|
||||
|
||||
if (!(ssl_version & EM_PROTO_SSLv3))
|
||||
SSL_CTX_set_options (pCtx, SSL_OP_NO_SSLv3);
|
||||
|
||||
if (!(ssl_version & EM_PROTO_TLSv1))
|
||||
SSL_CTX_set_options (pCtx, SSL_OP_NO_TLSv1);
|
||||
|
||||
#ifdef SSL_OP_NO_TLSv1_1
|
||||
if (!(ssl_version & EM_PROTO_TLSv1_1))
|
||||
SSL_CTX_set_options (pCtx, SSL_OP_NO_TLSv1_1);
|
||||
#endif
|
||||
|
||||
#ifdef SSL_OP_NO_TLSv1_2
|
||||
if (!(ssl_version & EM_PROTO_TLSv1_2))
|
||||
SSL_CTX_set_options (pCtx, SSL_OP_NO_TLSv1_2);
|
||||
#endif
|
||||
|
||||
#ifdef SSL_MODE_RELEASE_BUFFERS
|
||||
SSL_CTX_set_mode (pCtx, SSL_MODE_RELEASE_BUFFERS);
|
||||
#endif
|
||||
|
||||
if (bIsServer) {
|
||||
|
||||
// The SSL_CTX calls here do NOT allocate memory.
|
||||
int e;
|
||||
if (privkeyfile.length() > 0)
|
||||
e = SSL_CTX_use_PrivateKey_file (pCtx, privkeyfile.c_str(), SSL_FILETYPE_PEM);
|
||||
else
|
||||
e = SSL_CTX_use_PrivateKey (pCtx, DefaultPrivateKey);
|
||||
if (e <= 0) ERR_print_errors_fp(stderr);
|
||||
assert (e > 0);
|
||||
|
||||
if (certchainfile.length() > 0)
|
||||
e = SSL_CTX_use_certificate_chain_file (pCtx, certchainfile.c_str());
|
||||
else
|
||||
e = SSL_CTX_use_certificate (pCtx, DefaultCertificate);
|
||||
if (e <= 0) ERR_print_errors_fp(stderr);
|
||||
assert (e > 0);
|
||||
|
||||
if (dhparam.length() > 0) {
|
||||
DH *dh;
|
||||
BIO *bio;
|
||||
|
||||
bio = BIO_new_file(dhparam.c_str(), "r");
|
||||
if (bio == NULL) {
|
||||
char buf [500];
|
||||
snprintf (buf, sizeof(buf)-1, "dhparam: BIO_new_file(%s) failed", dhparam.c_str());
|
||||
throw std::runtime_error (buf);
|
||||
}
|
||||
|
||||
dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
|
||||
|
||||
if (dh == NULL) {
|
||||
BIO_free(bio);
|
||||
char buf [500];
|
||||
snprintf (buf, sizeof(buf)-1, "dhparam: PEM_read_bio_DHparams(%s) failed", dhparam.c_str());
|
||||
throw std::runtime_error (buf);
|
||||
}
|
||||
|
||||
SSL_CTX_set_tmp_dh(pCtx, dh);
|
||||
|
||||
DH_free(dh);
|
||||
BIO_free(bio);
|
||||
}
|
||||
|
||||
if (ecdh_curve.length() > 0) {
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x0090800fL && !defined(OPENSSL_NO_ECDH)
|
||||
int nid;
|
||||
EC_KEY *ecdh;
|
||||
|
||||
nid = OBJ_sn2nid((const char *) ecdh_curve.c_str());
|
||||
if (nid == 0) {
|
||||
char buf [200];
|
||||
snprintf (buf, sizeof(buf)-1, "ecdh_curve: Unknown curve name: %s", ecdh_curve.c_str());
|
||||
throw std::runtime_error (buf);
|
||||
}
|
||||
|
||||
ecdh = EC_KEY_new_by_curve_name(nid);
|
||||
if (ecdh == NULL) {
|
||||
char buf [200];
|
||||
snprintf (buf, sizeof(buf)-1, "ecdh_curve: Unable to create: %s", ecdh_curve.c_str());
|
||||
throw std::runtime_error (buf);
|
||||
}
|
||||
|
||||
SSL_CTX_set_options(pCtx, SSL_OP_SINGLE_ECDH_USE);
|
||||
|
||||
SSL_CTX_set_tmp_ecdh(pCtx, ecdh);
|
||||
|
||||
EC_KEY_free(ecdh);
|
||||
#else
|
||||
throw std::runtime_error ("No openssl ECDH support");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (cipherlist.length() > 0)
|
||||
SSL_CTX_set_cipher_list (pCtx, cipherlist.c_str());
|
||||
else
|
||||
SSL_CTX_set_cipher_list (pCtx, "ALL:!ADH:!LOW:!EXP:!DES-CBC3-SHA:@STRENGTH");
|
||||
|
||||
if (bIsServer) {
|
||||
SSL_CTX_sess_set_cache_size (pCtx, 128);
|
||||
SSL_CTX_set_session_id_context (pCtx, (unsigned char*)"eventmachine", 12);
|
||||
}
|
||||
else {
|
||||
int e;
|
||||
if (privkeyfile.length() > 0) {
|
||||
e = SSL_CTX_use_PrivateKey_file (pCtx, privkeyfile.c_str(), SSL_FILETYPE_PEM);
|
||||
if (e <= 0) ERR_print_errors_fp(stderr);
|
||||
assert (e > 0);
|
||||
}
|
||||
if (certchainfile.length() > 0) {
|
||||
e = SSL_CTX_use_certificate_chain_file (pCtx, certchainfile.c_str());
|
||||
if (e <= 0) ERR_print_errors_fp(stderr);
|
||||
assert (e > 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************
|
||||
SslContext_t::~SslContext_t
|
||||
***************************/
|
||||
|
||||
SslContext_t::~SslContext_t()
|
||||
{
|
||||
if (pCtx)
|
||||
SSL_CTX_free (pCtx);
|
||||
if (PrivateKey)
|
||||
EVP_PKEY_free (PrivateKey);
|
||||
if (Certificate)
|
||||
X509_free (Certificate);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/******************
|
||||
SslBox_t::SslBox_t
|
||||
******************/
|
||||
|
||||
SslBox_t::SslBox_t (bool is_server, const std::string &privkeyfile, const std::string &certchainfile, bool verify_peer, bool fail_if_no_peer_cert, const std::string &snihostname, const std::string &cipherlist, const std::string &ecdh_curve, const std::string &dhparam, int ssl_version, const uintptr_t binding):
|
||||
bIsServer (is_server),
|
||||
bHandshakeCompleted (false),
|
||||
bVerifyPeer (verify_peer),
|
||||
bFailIfNoPeerCert (fail_if_no_peer_cert),
|
||||
pSSL (NULL),
|
||||
pbioRead (NULL),
|
||||
pbioWrite (NULL)
|
||||
{
|
||||
/* TODO someday: make it possible to re-use SSL contexts so we don't have to create
|
||||
* a new one every time we come here.
|
||||
*/
|
||||
|
||||
Context = new SslContext_t (bIsServer, privkeyfile, certchainfile, cipherlist, ecdh_curve, dhparam, ssl_version);
|
||||
assert (Context);
|
||||
|
||||
pbioRead = BIO_new (BIO_s_mem());
|
||||
assert (pbioRead);
|
||||
|
||||
pbioWrite = BIO_new (BIO_s_mem());
|
||||
assert (pbioWrite);
|
||||
|
||||
pSSL = SSL_new (Context->pCtx);
|
||||
assert (pSSL);
|
||||
|
||||
if (snihostname.length() > 0) {
|
||||
SSL_set_tlsext_host_name (pSSL, snihostname.c_str());
|
||||
}
|
||||
|
||||
SSL_set_bio (pSSL, pbioRead, pbioWrite);
|
||||
|
||||
// Store a pointer to the binding signature in the SSL object so we can retrieve it later
|
||||
SSL_set_ex_data(pSSL, 0, (void*) binding);
|
||||
|
||||
if (bVerifyPeer) {
|
||||
int mode = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
|
||||
if (bFailIfNoPeerCert)
|
||||
mode = mode | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
|
||||
SSL_set_verify(pSSL, mode, ssl_verify_wrapper);
|
||||
}
|
||||
|
||||
if (!bIsServer) {
|
||||
int e = SSL_connect (pSSL);
|
||||
if (e != 1)
|
||||
ERR_print_errors_fp(stderr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************
|
||||
SslBox_t::~SslBox_t
|
||||
*******************/
|
||||
|
||||
SslBox_t::~SslBox_t()
|
||||
{
|
||||
// Freeing pSSL will also free the associated BIOs, so DON'T free them separately.
|
||||
if (pSSL) {
|
||||
if (SSL_get_shutdown (pSSL) & SSL_RECEIVED_SHUTDOWN)
|
||||
SSL_shutdown (pSSL);
|
||||
else
|
||||
SSL_clear (pSSL);
|
||||
SSL_free (pSSL);
|
||||
}
|
||||
|
||||
delete Context;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***********************
|
||||
SslBox_t::PutCiphertext
|
||||
***********************/
|
||||
|
||||
bool SslBox_t::PutCiphertext (const char *buf, int bufsize)
|
||||
{
|
||||
assert (buf && (bufsize > 0));
|
||||
|
||||
assert (pbioRead);
|
||||
int n = BIO_write (pbioRead, buf, bufsize);
|
||||
|
||||
return (n == bufsize) ? true : false;
|
||||
}
|
||||
|
||||
|
||||
/**********************
|
||||
SslBox_t::GetPlaintext
|
||||
**********************/
|
||||
|
||||
int SslBox_t::GetPlaintext (char *buf, int bufsize)
|
||||
{
|
||||
if (!SSL_is_init_finished (pSSL)) {
|
||||
int e = bIsServer ? SSL_accept (pSSL) : SSL_connect (pSSL);
|
||||
if (e != 1) {
|
||||
int er = SSL_get_error (pSSL, e);
|
||||
if (er != SSL_ERROR_WANT_READ) {
|
||||
ERR_print_errors_fp(stderr);
|
||||
// Return -1 for a nonfatal error, -2 for an error that should force the connection down.
|
||||
return (er == SSL_ERROR_SSL) ? (-2) : (-1);
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
bHandshakeCompleted = true;
|
||||
// If handshake finished, FALL THROUGH and return the available plaintext.
|
||||
}
|
||||
|
||||
if (!SSL_is_init_finished (pSSL)) {
|
||||
// We can get here if a browser abandons a handshake.
|
||||
// The user can see a warning dialog and abort the connection.
|
||||
//cerr << "<SSL_incomp>";
|
||||
return 0;
|
||||
}
|
||||
|
||||
//cerr << "CIPH: " << SSL_get_cipher (pSSL) << endl;
|
||||
|
||||
int n = SSL_read (pSSL, buf, bufsize);
|
||||
if (n >= 0) {
|
||||
return n;
|
||||
}
|
||||
else {
|
||||
if (SSL_get_error (pSSL, n) == SSL_ERROR_WANT_READ) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**************************
|
||||
SslBox_t::CanGetCiphertext
|
||||
**************************/
|
||||
|
||||
bool SslBox_t::CanGetCiphertext()
|
||||
{
|
||||
assert (pbioWrite);
|
||||
return BIO_pending (pbioWrite) ? true : false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***********************
|
||||
SslBox_t::GetCiphertext
|
||||
***********************/
|
||||
|
||||
int SslBox_t::GetCiphertext (char *buf, int bufsize)
|
||||
{
|
||||
assert (pbioWrite);
|
||||
assert (buf && (bufsize > 0));
|
||||
|
||||
return BIO_read (pbioWrite, buf, bufsize);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**********************
|
||||
SslBox_t::PutPlaintext
|
||||
**********************/
|
||||
|
||||
int SslBox_t::PutPlaintext (const char *buf, int bufsize)
|
||||
{
|
||||
// The caller will interpret the return value as the number of bytes written.
|
||||
// WARNING WARNING WARNING, are there any situations in which a 0 or -1 return
|
||||
// from SSL_write means we should immediately retry? The socket-machine loop
|
||||
// will probably wait for a time-out cycle (perhaps a second) before re-trying.
|
||||
// THIS WOULD CAUSE A PERCEPTIBLE DELAY!
|
||||
|
||||
/* We internally queue any outbound plaintext that can't be dispatched
|
||||
* because we're in the middle of a handshake or something.
|
||||
* When we get called, try to send any queued data first, and then
|
||||
* send the caller's data (or queue it). We may get called with no outbound
|
||||
* data, which means we try to send the outbound queue and that's all.
|
||||
*
|
||||
* Return >0 if we wrote any data, 0 if we didn't, and <0 for a fatal error.
|
||||
* Note that if we return 0, the connection is still considered live
|
||||
* and we are signalling that we have accepted the outbound data (if any).
|
||||
*/
|
||||
|
||||
OutboundQ.Push (buf, bufsize);
|
||||
|
||||
if (!SSL_is_init_finished (pSSL))
|
||||
return 0;
|
||||
|
||||
bool fatal = false;
|
||||
bool did_work = false;
|
||||
int pending = BIO_pending(pbioWrite);
|
||||
|
||||
while (OutboundQ.HasPages() && pending < SSLBOX_WRITE_BUFFER_SIZE) {
|
||||
const char *page;
|
||||
int length;
|
||||
OutboundQ.Front (&page, &length);
|
||||
assert (page && (length > 0));
|
||||
int n = SSL_write (pSSL, page, length);
|
||||
pending = BIO_pending(pbioWrite);
|
||||
|
||||
if (n > 0) {
|
||||
did_work = true;
|
||||
OutboundQ.PopFront();
|
||||
}
|
||||
else {
|
||||
int er = SSL_get_error (pSSL, n);
|
||||
if ((er != SSL_ERROR_WANT_READ) && (er != SSL_ERROR_WANT_WRITE))
|
||||
fatal = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (did_work)
|
||||
return 1;
|
||||
else if (fatal)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**********************
|
||||
SslBox_t::GetPeerCert
|
||||
**********************/
|
||||
|
||||
X509 *SslBox_t::GetPeerCert()
|
||||
{
|
||||
X509 *cert = NULL;
|
||||
|
||||
if (pSSL)
|
||||
cert = SSL_get_peer_certificate(pSSL);
|
||||
|
||||
return cert;
|
||||
}
|
||||
|
||||
/**********************
|
||||
SslBox_t::GetCipherBits
|
||||
**********************/
|
||||
|
||||
int SslBox_t::GetCipherBits()
|
||||
{
|
||||
int bits = -1;
|
||||
if (pSSL)
|
||||
SSL_get_cipher_bits(pSSL, &bits);
|
||||
return bits;
|
||||
}
|
||||
|
||||
/**********************
|
||||
SslBox_t::GetCipherName
|
||||
**********************/
|
||||
|
||||
const char *SslBox_t::GetCipherName()
|
||||
{
|
||||
if (pSSL)
|
||||
return SSL_get_cipher_name(pSSL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**********************
|
||||
SslBox_t::GetCipherProtocol
|
||||
**********************/
|
||||
|
||||
const char *SslBox_t::GetCipherProtocol()
|
||||
{
|
||||
if (pSSL)
|
||||
return SSL_get_cipher_version(pSSL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**********************
|
||||
SslBox_t::GetSNIHostname
|
||||
**********************/
|
||||
|
||||
const char *SslBox_t::GetSNIHostname()
|
||||
{
|
||||
#ifdef TLSEXT_NAMETYPE_host_name
|
||||
if (pSSL)
|
||||
return SSL_get_servername (pSSL, TLSEXT_NAMETYPE_host_name);
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/******************
|
||||
ssl_verify_wrapper
|
||||
*******************/
|
||||
|
||||
extern "C" int ssl_verify_wrapper(int preverify_ok UNUSED, X509_STORE_CTX *ctx)
|
||||
{
|
||||
uintptr_t binding;
|
||||
X509 *cert;
|
||||
SSL *ssl;
|
||||
BUF_MEM *buf;
|
||||
BIO *out;
|
||||
int result;
|
||||
|
||||
cert = X509_STORE_CTX_get_current_cert(ctx);
|
||||
ssl = (SSL*) X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
|
||||
binding = (uintptr_t) SSL_get_ex_data(ssl, 0);
|
||||
|
||||
out = BIO_new(BIO_s_mem());
|
||||
PEM_write_bio_X509(out, cert);
|
||||
BIO_write(out, "\0", 1);
|
||||
BIO_get_mem_ptr(out, &buf);
|
||||
|
||||
ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject(binding));
|
||||
result = (cd->VerifySslPeer(buf->data) == true ? 1 : 0);
|
||||
BIO_free(out);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // WITH_SSL
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/*****************************************************************************
|
||||
|
||||
$Id$
|
||||
|
||||
File: ssl.h
|
||||
Date: 30Apr06
|
||||
|
||||
Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
|
||||
Gmail: blackhedd
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of either: 1) the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version; or 2) Ruby's License.
|
||||
|
||||
See the file COPYING for complete licensing information.
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef __SslBox__H_
|
||||
#define __SslBox__H_
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef WITH_SSL
|
||||
|
||||
/******************
|
||||
class SslContext_t
|
||||
******************/
|
||||
|
||||
class SslContext_t
|
||||
{
|
||||
public:
|
||||
SslContext_t (bool is_server, const std::string &privkeyfile, const std::string &certchainfile, const std::string &cipherlist, const std::string &ecdh_curve, const std::string &dhparam, int ssl_version);
|
||||
virtual ~SslContext_t();
|
||||
|
||||
private:
|
||||
static bool bLibraryInitialized;
|
||||
|
||||
private:
|
||||
bool bIsServer;
|
||||
SSL_CTX *pCtx;
|
||||
|
||||
EVP_PKEY *PrivateKey;
|
||||
X509 *Certificate;
|
||||
|
||||
friend class SslBox_t;
|
||||
};
|
||||
|
||||
|
||||
/**************
|
||||
class SslBox_t
|
||||
**************/
|
||||
|
||||
#define SSLBOX_INPUT_CHUNKSIZE 2019
|
||||
#define SSLBOX_OUTPUT_CHUNKSIZE 2048
|
||||
#define SSLBOX_WRITE_BUFFER_SIZE 8192 // (SSLBOX_OUTPUT_CHUNKSIZE * 4)
|
||||
|
||||
class SslBox_t
|
||||
{
|
||||
public:
|
||||
SslBox_t (bool is_server, const std::string &privkeyfile, const std::string &certchainfile, bool verify_peer, bool fail_if_no_peer_cert, const std::string &snihostname, const std::string &cipherlist, const std::string &ecdh_curve, const std::string &dhparam, int ssl_version, const uintptr_t binding);
|
||||
virtual ~SslBox_t();
|
||||
|
||||
int PutPlaintext (const char*, int);
|
||||
int GetPlaintext (char*, int);
|
||||
|
||||
bool PutCiphertext (const char*, int);
|
||||
bool CanGetCiphertext();
|
||||
int GetCiphertext (char*, int);
|
||||
bool IsHandshakeCompleted() {return bHandshakeCompleted;}
|
||||
|
||||
X509 *GetPeerCert();
|
||||
int GetCipherBits();
|
||||
const char *GetCipherName();
|
||||
const char *GetCipherProtocol();
|
||||
const char *GetSNIHostname();
|
||||
|
||||
void Shutdown();
|
||||
|
||||
protected:
|
||||
SslContext_t *Context;
|
||||
|
||||
bool bIsServer;
|
||||
bool bHandshakeCompleted;
|
||||
bool bVerifyPeer;
|
||||
bool bFailIfNoPeerCert;
|
||||
SSL *pSSL;
|
||||
BIO *pbioRead;
|
||||
BIO *pbioWrite;
|
||||
|
||||
PageList OutboundQ;
|
||||
};
|
||||
|
||||
extern "C" int ssl_verify_wrapper(int, X509_STORE_CTX*);
|
||||
|
||||
#endif // WITH_SSL
|
||||
|
||||
|
||||
#endif // __SslBox__H_
|
||||
|
||||
Reference in New Issue
Block a user