Merge with 0.7
[prosody.git] / net / server_event.lua
index 1a0735869c6c3dce98c6be74675c7c0ba3abc5ca..d041ae4381527d909ae2b3d2cce63ec420031e4a 100644 (file)
@@ -13,7 +13,6 @@
 
 --]]
 
 
 --]]
 
-
 local SCRIPT_NAME           = "server_event.lua"
 local SCRIPT_VERSION        = "0.05"
 local SCRIPT_AUTHOR         = "blastbeat"
 local SCRIPT_NAME           = "server_event.lua"
 local SCRIPT_VERSION        = "0.05"
 local SCRIPT_AUTHOR         = "blastbeat"
@@ -21,15 +20,15 @@ local LAST_MODIFIED         = "2009/11/20"
 
 local cfg = {
        MAX_CONNECTIONS       = 100000,  -- max per server connections (use "ulimit -n" on *nix)
 
 local cfg = {
        MAX_CONNECTIONS       = 100000,  -- max per server connections (use "ulimit -n" on *nix)
-       MAX_HANDSHAKE_ATTEMPS = 10,  -- attemps to finish ssl handshake
-       HANDSHAKE_TIMEOUT     = 1,  -- timout in seconds per handshake attemp
+       MAX_HANDSHAKE_ATTEMPS = 1000,  -- attemps to finish ssl handshake
+       HANDSHAKE_TIMEOUT     = 30,  -- timout in seconds per handshake attemp
        MAX_READ_LENGTH       = 1024 * 1024 * 1024 * 1024,  -- max bytes allowed to read from sockets
        MAX_SEND_LENGTH       = 1024 * 1024 * 1024 * 1024,  -- max bytes size of write buffer (for writing on sockets)
        ACCEPT_DELAY          = 10,  -- seconds to wait until the next attemp of a full server to accept
        READ_TIMEOUT          = 60 * 30,  -- timeout in seconds for read data from socket
        WRITE_TIMEOUT         = 30,  -- timeout in seconds for write data on socket
        MAX_READ_LENGTH       = 1024 * 1024 * 1024 * 1024,  -- max bytes allowed to read from sockets
        MAX_SEND_LENGTH       = 1024 * 1024 * 1024 * 1024,  -- max bytes size of write buffer (for writing on sockets)
        ACCEPT_DELAY          = 10,  -- seconds to wait until the next attemp of a full server to accept
        READ_TIMEOUT          = 60 * 30,  -- timeout in seconds for read data from socket
        WRITE_TIMEOUT         = 30,  -- timeout in seconds for write data on socket
-       CONNECT_TIMEOUT       = 10,  -- timeout in seconds for connection attemps
-       CLEAR_DELAY           = 5,  -- seconds to wait for clearing interface list (and calling ondisconnect listeners) 
+       CONNECT_TIMEOUT       = 20,  -- timeout in seconds for connection attemps
+       CLEAR_DELAY           = 5,  -- seconds to wait for clearing interface list (and calling ondisconnect listeners)
        DEBUG                 = true,  -- show debug messages
 }
 
        DEBUG                 = true,  -- show debug messages
 }
 
@@ -45,7 +44,7 @@ local coroutine = use "coroutine"
 local setmetatable = use "setmetatable"
 
 local ssl = use "ssl"
 local setmetatable = use "setmetatable"
 
 local ssl = use "ssl"
-local socket = use "socket"
+local socket = use "socket" or require "socket"
 
 local log = require ("util.logger").init("socket")
 
 
 local log = require ("util.logger").init("socket")
 
@@ -58,11 +57,11 @@ local bitor = ( function( ) -- thx Rici Lake
        local hasbit = function( x, p )
                return x % ( p + p ) >= p
        end
        local hasbit = function( x, p )
                return x % ( p + p ) >= p
        end
-       return function( x, y ) 
+       return function( x, y )
                local p = 1
                local z = 0
                local limit = x > y and x or y
                local p = 1
                local z = 0
                local limit = x > y and x or y
-               while p <= limit do 
+               while p <= limit do
                        if hasbit( x, p ) or hasbit( y, p ) then
                                z = z + p
                        end
                        if hasbit( x, p ) or hasbit( y, p ) then
                                z = z + p
                        end
@@ -77,6 +76,7 @@ local base = event.new( )
 local EV_READ = event.EV_READ
 local EV_WRITE = event.EV_WRITE
 local EV_TIMEOUT = event.EV_TIMEOUT
 local EV_READ = event.EV_READ
 local EV_WRITE = event.EV_WRITE
 local EV_TIMEOUT = event.EV_TIMEOUT
+local EV_SIGNAL = event.EV_SIGNAL
 
 local EV_READWRITE = bitor( EV_READ, EV_WRITE )
 
 
 local EV_READWRITE = bitor( EV_READ, EV_WRITE )
 
@@ -103,7 +103,7 @@ local interfacelist = ( function( )  -- holds the interfaces for sockets
                                array[ len ] = nil
                        end
                        len = len - 1
                                array[ len ] = nil
                        end
                        len = len - 1
-                       return len    
+                       return len
                else
                        return array
                end
                else
                        return array
                end
@@ -142,8 +142,8 @@ do
                                        self:_close()
                                        debug( "new connection failed. id:", self.id, "error:", self.fatalerror )
                                else
                                        self:_close()
                                        debug( "new connection failed. id:", self.id, "error:", self.fatalerror )
                                else
-                                       if plainssl then  -- start ssl session
-                                               self:_start_ssl( self.listener.onconnect )
+                                       if plainssl and ssl then  -- start ssl session
+                                               self:starttls()
                                        else  -- normal connection
                                                self:_start_session( self.listener.onconnect )
                                        end
                                        else  -- normal connection
                                                self:_start_session( self.listener.onconnect )
                                        end
@@ -159,9 +159,9 @@ do
                if self.type == "client" then
                        local callback = function( )
                                self:_lock( false,  false, false )
                if self.type == "client" then
                        local callback = function( )
                                self:_lock( false,  false, false )
-                               --vdebug( "start listening on client socket with id:", self.id )      
+                               --vdebug( "start listening on client socket with id:", self.id )
                                self.eventread = addevent( base, self.conn, EV_READ, self.readcallback, cfg.READ_TIMEOUT )  -- register callback
                                self.eventread = addevent( base, self.conn, EV_READ, self.readcallback, cfg.READ_TIMEOUT )  -- register callback
-                               self:onconnect()
+                               self:onincoming()
                                self.eventsession = nil
                                return -1
                        end
                                self.eventsession = nil
                                return -1
                        end
@@ -197,7 +197,7 @@ do
                                        local _, err
                                        local attempt = 0
                                        local maxattempt = cfg.MAX_HANDSHAKE_ATTEMPS
                                        local _, err
                                        local attempt = 0
                                        local maxattempt = cfg.MAX_HANDSHAKE_ATTEMPS
-                                       while attempt < 1000 do  -- no endless loop
+                                       while attempt < maxattempt do  -- no endless loop
                                                attempt = attempt + 1
                                                debug( "ssl handshake of client with id:"..tostring(self).."attemp:"..attempt )
                                                if attempt > maxattempt then
                                                attempt = attempt + 1
                                                debug( "ssl handshake of client with id:"..tostring(self).."attemp:"..attempt )
                                                if attempt > maxattempt then
@@ -218,17 +218,18 @@ do
                                                                end
                                                                self:_start_session( onsomething )
                                                                debug( "ssl handshake done" )
                                                                end
                                                                self:_start_session( onsomething )
                                                                debug( "ssl handshake done" )
+                                                               self:onstatus("ssl-handshake-complete");
                                                                self.eventhandshake = nil
                                                                return -1
                                                        end
                                                                self.eventhandshake = nil
                                                                return -1
                                                        end
-                                                       debug( "error during ssl handshake:", err ) 
+                                                       debug( "error during ssl handshake:", err )
                                                        if err == "wantwrite" then
                                                                event = EV_WRITE
                                                        elseif err == "wantread" then
                                                                event = EV_READ
                                                        else
                                                                self.fatalerror = err
                                                        if err == "wantwrite" then
                                                                event = EV_WRITE
                                                        elseif err == "wantread" then
                                                                event = EV_READ
                                                        else
                                                                self.fatalerror = err
-                                                       end            
+                                                       end
                                                end
                                                if self.fatalerror then
                                                        if "onconnect" == arg then
                                                end
                                                if self.fatalerror then
                                                        if "onconnect" == arg then
@@ -244,7 +245,7 @@ do
                                end
                        )
                        debug "starting handshake..."
                                end
                        )
                        debug "starting handshake..."
-                       self:_lock( false, true, true )  -- unlock read/write events, but keep interface locked 
+                       self:_lock( false, true, true )  -- unlock read/write events, but keep interface locked
                        self.eventhandshake = addevent( base, self.conn, EV_READWRITE, handshakecallback, cfg.HANDSHAKE_TIMEOUT )
                        return true
        end
                        self.eventhandshake = addevent( base, self.conn, EV_READWRITE, handshakecallback, cfg.HANDSHAKE_TIMEOUT )
                        return true
        end
@@ -261,7 +262,7 @@ do
                                _ = self.eventsession and self.eventsession:close( )
                                _ = self.eventwritetimeout and self.eventwritetimeout:close( )
                                _ = self.eventreadtimeout and self.eventreadtimeout:close( )
                                _ = self.eventsession and self.eventsession:close( )
                                _ = self.eventwritetimeout and self.eventwritetimeout:close( )
                                _ = self.eventreadtimeout and self.eventreadtimeout:close( )
-                               _ = self.ondisconnect and self:ondisconnect( self.fatalerror )  -- call ondisconnect listener (wont be the case if handshake failed on connect)
+                               _ = self.ondisconnect and self:ondisconnect( self.fatalerror ~= "client to close" and self.fatalerror)  -- call ondisconnect listener (wont be the case if handshake failed on connect)
                                _ = self.conn and self.conn:close( ) -- close connection, must also be called outside of any socket registered events!
                                _ = self._server and self._server:counter(-1);
                                self.eventread, self.eventwrite = nil, nil
                                _ = self.conn and self.conn:close( ) -- close connection, must also be called outside of any socket registered events!
                                _ = self._server and self._server:counter(-1);
                                self.eventread, self.eventwrite = nil, nil
@@ -280,6 +281,10 @@ do
                        self.nointerface, self.noreading, self.nowriting = nointerface, noreading, nowriting
                        return nointerface, noreading, nowriting
        end
                        self.nointerface, self.noreading, self.nowriting = nointerface, noreading, nowriting
                        return nointerface, noreading, nowriting
        end
+       
+       function interface_mt:lock_read(switch)
+               return self:_lock(self.nointerface, switch, self.nowriting);
+       end
 
        function interface_mt:counter(c)
                if c then
 
        function interface_mt:counter(c)
                if c then
@@ -299,7 +304,7 @@ do
                        local err = "send buffer exceeded"
                        debug( "error:", err )  -- to much, check your app
                        return nil, err
                        local err = "send buffer exceeded"
                        debug( "error:", err )  -- to much, check your app
                        return nil, err
-               end 
+               end
                self.writebuffer = self.writebuffer .. data -- new buffer
                self.writebufferlen = total
                if not self.eventwrite then  -- register new write event
                self.writebuffer = self.writebuffer .. data -- new buffer
                self.writebufferlen = total
                if not self.eventwrite then  -- register new write event
@@ -352,6 +357,10 @@ do
                return self._port
        end
        
                return self._port
        end
        
+       function interface_mt:serverport()
+               return self._serverport
+       end
+       
        function interface_mt:ip()
                return self._ip
        end
        function interface_mt:ip()
                return self._ip
        end
@@ -385,17 +394,16 @@ do
                -- No-op, we always use the underlying connection's send
        end
        
                -- No-op, we always use the underlying connection's send
        end
        
-       function interface_mt:starttls()
+       function interface_mt:starttls(sslctx)
                debug( "try to start ssl at client id:", self.id )
                local err
                debug( "try to start ssl at client id:", self.id )
                local err
-               if not self._sslctx then  -- no ssl available
-                       err = "no ssl context available"
-               elseif self._usingssl then  -- startssl was already called
+               self._sslctx = sslctx;
+               if self._usingssl then  -- startssl was already called
                        err = "ssl already active"
                end
                if err then
                        debug( "error:", err )
                        err = "ssl already active"
                end
                if err then
                        debug( "error:", err )
-                       return nil, err      
+                       return nil, err
                end
                self._usingssl = true
                self.startsslcallback = function( )  -- we have to start the handshake outside of a read/write event
                end
                self._usingssl = true
                self.startsslcallback = function( )  -- we have to start the handshake outside of a read/write event
@@ -411,9 +419,22 @@ do
                        self:_lock( true, true, false )
                        debug "ssl session delayed until writebuffer is empty..."
                end
                        self:_lock( true, true, false )
                        debug "ssl session delayed until writebuffer is empty..."
                end
+               self.starttls = false;
                return true
        end
        
                return true
        end
        
+       function interface_mt:setoption(option, value)
+               if self.conn.setoption then
+                       return self.conn:setoption(option, value);
+               end
+               return false, "setoption not implemented";
+       end
+       
+       function interface_mt:setlistener(listener)
+               self.onconnect, self.ondisconnect, self.onincoming, self.ontimeout, self.onstatus
+                       = listener.onconnect, listener.ondisconnect, listener.onincoming, listener.ontimeout, listener.onstatus;
+       end
+       
        -- Stub handlers
        function interface_mt:onconnect()
        end
        -- Stub handlers
        function interface_mt:onconnect()
        end
@@ -423,7 +444,10 @@ do
        end
        function interface_mt:ontimeout()
        end
        end
        function interface_mt:ontimeout()
        end
-end                    
+       function interface_mt:onstatus()
+               debug("server.lua: Dummy onstatus()")
+       end
+end
 
 -- End of client interface methods
 
 
 -- End of client interface methods
 
@@ -449,6 +473,7 @@ do
                        ondisconnect = listener.ondisconnect;  -- will be called when client disconnects
                        onincoming = listener.onincoming;  -- will be called when client sends data
                        ontimeout = listener.ontimeout; -- called when fatal socket timeout occurs
                        ondisconnect = listener.ondisconnect;  -- will be called when client disconnects
                        onincoming = listener.onincoming;  -- will be called when client sends data
                        ontimeout = listener.ontimeout; -- called when fatal socket timeout occurs
+                       onstatus = listener.onstatus; -- called for status changes (e.g. of SSL/TLS)
                        eventread = false, eventwrite = false, eventclose = false,
                        eventhandshake = false, eventstarthandshake = false;  -- event handler
                        eventconnect = false, eventsession = false;  -- more event handler...
                        eventread = false, eventwrite = false, eventclose = false,
                        eventhandshake = false, eventstarthandshake = false;  -- event handler
                        eventconnect = false, eventsession = false;  -- more event handler...
@@ -464,12 +489,11 @@ do
                        
                        -- Properties
                        _ip = ip, _port = port, _server = server, _pattern = pattern,
                        
                        -- Properties
                        _ip = ip, _port = port, _server = server, _pattern = pattern,
+                       _serverport = (server and server:port() or nil),
                        _sslctx = sslctx; -- parameters
                        _usingssl = false;  -- client is using ssl;
                }
                        _sslctx = sslctx; -- parameters
                        _usingssl = false;  -- client is using ssl;
                }
-               if not sslctx then
-                       interface.starttls = false -- don't allow TLS
-               end
+               if not ssl then interface.starttls = false; end
                interface.id = tostring(interface):match("%x+$");
                interface.writecallback = function( event )  -- called on write events
                        --vdebug( "new client write event, id/ip/port:", interface, ip, port )
                interface.id = tostring(interface):match("%x+$");
                interface.writecallback = function( event )  -- called on write events
                        --vdebug( "new client write event, id/ip/port:", interface, ip, port )
@@ -480,7 +504,7 @@ do
                        end
                        if EV_TIMEOUT == event then  -- took too long to write some data to socket -> disconnect
                                interface.fatalerror = "timeout during writing"
                        end
                        if EV_TIMEOUT == event then  -- took too long to write some data to socket -> disconnect
                                interface.fatalerror = "timeout during writing"
-                               debug( "writing failed:", interface.fatalerror ) 
+                               debug( "writing failed:", interface.fatalerror )
                                interface:_close()
                                interface.eventwrite = false
                                return -1
                                interface:_close()
                                interface.eventwrite = false
                                return -1
@@ -514,7 +538,7 @@ do
                                elseif byte then  -- want write again
                                        --vdebug( "writebuffer is not empty:", err )
                                        interface.writebuffer = string_sub( interface.writebuffer, byte + 1, interface.writebufferlen )  -- new buffer
                                elseif byte then  -- want write again
                                        --vdebug( "writebuffer is not empty:", err )
                                        interface.writebuffer = string_sub( interface.writebuffer, byte + 1, interface.writebufferlen )  -- new buffer
-                                       interface.writebufferlen = interface.writebufferlen - byte            
+                                       interface.writebufferlen = interface.writebufferlen - byte
                                        if "wantread" == err then  -- happens only with luasec
                                                local callback = function( )
                                                        interface:_close()
                                        if "wantread" == err then  -- happens only with luasec
                                                local callback = function( )
                                                        interface:_close()
@@ -526,10 +550,10 @@ do
                                                -- hopefully this works with luasec; its simply not possible to use 2 different write events on a socket in luaevent
                                                return -1
                                        end
                                                -- hopefully this works with luasec; its simply not possible to use 2 different write events on a socket in luaevent
                                                return -1
                                        end
-                                       return EV_WRITE, cfg.WRITE_TIMEOUT 
+                                       return EV_WRITE, cfg.WRITE_TIMEOUT
                                else  -- connection was closed during writing or fatal error
                                        interface.fatalerror = err or "fatal error"
                                else  -- connection was closed during writing or fatal error
                                        interface.fatalerror = err or "fatal error"
-                                       debug( "connection failed in write event:", interface.fatalerror ) 
+                                       debug( "connection failed in write event:", interface.fatalerror )
                                        interface:_close()
                                        interface.eventwrite = nil
                                        return -1
                                        interface:_close()
                                        interface.eventwrite = nil
                                        return -1
@@ -546,7 +570,7 @@ do
                        end
                        if EV_TIMEOUT == event then  -- took too long to get some data from client -> disconnect
                                interface.fatalerror = "timeout during receiving"
                        end
                        if EV_TIMEOUT == event then  -- took too long to get some data from client -> disconnect
                                interface.fatalerror = "timeout during receiving"
-                               debug( "connection failed:", interface.fatalerror ) 
+                               debug( "connection failed:", interface.fatalerror )
                                interface:_close()
                                interface.eventread = nil
                                return -1
                                interface:_close()
                                interface.eventread = nil
                                return -1
@@ -562,7 +586,7 @@ do
                                        end
                                end
                                local buffer, err, part = interface.conn:receive( pattern )  -- receive buffer with "pattern"
                                        end
                                end
                                local buffer, err, part = interface.conn:receive( pattern )  -- receive buffer with "pattern"
-                               --vdebug( "read data:", tostring(buffer), "error:", tostring(err), "part:", tostring(part) )        
+                               --vdebug( "read data:", tostring(buffer), "error:", tostring(err), "part:", tostring(part) )
                                buffer = buffer or part or ""
                                local len = string_len( buffer )
                                if len > cfg.MAX_READ_LENGTH then  -- check buffer length
                                buffer = buffer or part or ""
                                local len = string_len( buffer )
                                if len > cfg.MAX_READ_LENGTH then  -- check buffer length
@@ -582,12 +606,12 @@ do
                                                        function( )
                                                                interface:_close()
                                                        end, cfg.READ_TIMEOUT
                                                        function( )
                                                                interface:_close()
                                                        end, cfg.READ_TIMEOUT
-                                               )             
+                                               )
                                                debug( "wantwrite during read attemp, reg it in writecallback but dont know what really happens next..." )
                                                -- to be honest i dont know what happens next, if it is allowed to first read, the write etc...
                                                debug( "wantwrite during read attemp, reg it in writecallback but dont know what really happens next..." )
                                                -- to be honest i dont know what happens next, if it is allowed to first read, the write etc...
-                                       else  -- connection was closed or fatal error            
+                                       else  -- connection was closed or fatal error
                                                interface.fatalerror = err
                                                interface.fatalerror = err
-                                               debug( "connection failed in read event:", interface.fatalerror ) 
+                                               debug( "connection failed in read event:", interface.fatalerror )
                                                interface:_close()
                                                interface.eventread = nil
                                                return -1
                                                interface:_close()
                                                interface.eventread = nil
                                                return -1
@@ -606,7 +630,7 @@ end
 
 local handleserver
 do
 
 local handleserver
 do
-       function handleserver( server, addr, port, pattern, listener, sslctx, startssl )  -- creates an server interface
+       function handleserver( server, addr, port, pattern, listener, sslctx )  -- creates an server interface
                debug "creating server interface..."
                local interface = {
                        _connections = 0;
                debug "creating server interface..."
                local interface = {
                        _connections = 0;
@@ -618,6 +642,9 @@ do
                        readcallback = false; -- read event callback
                        fatalerror = false; -- error message
                        nointerface = true;  -- lock/unlock parameter
                        readcallback = false; -- read event callback
                        fatalerror = false; -- error message
                        nointerface = true;  -- lock/unlock parameter
+                       
+                       _ip = addr, _port = port, _pattern = pattern,
+                       _sslctx = sslctx;
                }
                interface.id = tostring(interface):match("%x+$");
                interface.readcallback = function( event )  -- server handler, called on incoming connections
                }
                interface.id = tostring(interface):match("%x+$");
                interface.readcallback = function( event )  -- server handler, called on incoming connections
@@ -648,12 +675,13 @@ do
                                interface._connections = interface._connections + 1  -- increase connection count
                                local clientinterface = handleclient( client, ip, port, interface, pattern, listener, nil, sslctx )
                                --vdebug( "client id:", clientinterface, "startssl:", startssl )
                                interface._connections = interface._connections + 1  -- increase connection count
                                local clientinterface = handleclient( client, ip, port, interface, pattern, listener, nil, sslctx )
                                --vdebug( "client id:", clientinterface, "startssl:", startssl )
-                               if startssl then
-                                       clientinterface:_start_ssl( clientinterface.onconnect )
+                               if ssl and sslctx then
+                                       clientinterface:starttls(sslctx)
                                else
                                        clientinterface:_start_session( clientinterface.onconnect )
                                end
                                debug( "accepted incoming client connection from:", ip, port )
                                else
                                        clientinterface:_start_session( clientinterface.onconnect )
                                end
                                debug( "accepted incoming client connection from:", ip, port )
+                               
                                client, err = server:accept()    -- try to accept again
                        end
                        return EV_READ
                                client, err = server:accept()    -- try to accept again
                        end
                        return EV_READ
@@ -681,12 +709,12 @@ local addserver = ( function( )
                                debug "fatal error: luasec not found"
                                return nil, "luasec not found"
                        end
                                debug "fatal error: luasec not found"
                                return nil, "luasec not found"
                        end
-                       sslctx, err = ssl.newcontext( sslcfg )
+                       sslctx, err = sslcfg
                        if err then
                                debug( "error while creating new ssl context for server socket:", err )
                                return nil, err
                        end
                        if err then
                                debug( "error while creating new ssl context for server socket:", err )
                                return nil, err
                        end
-               end      
+               end
                local interface = handleserver( server, addr, port, pattern, listener, sslctx, startssl )  -- new server handler
                debug( "new server created with id:", tostring(interface))
                return interface
                local interface = handleserver( server, addr, port, pattern, listener, sslctx, startssl )  -- new server handler
                debug( "new server created with id:", tostring(interface))
                return interface
@@ -705,7 +733,7 @@ do
        function addclient( addr, serverport, listener, pattern, localaddr, localport, sslcfg, startssl )
                local client, err = socket.tcp()  -- creating new socket
                if not client then
        function addclient( addr, serverport, listener, pattern, localaddr, localport, sslcfg, startssl )
                local client, err = socket.tcp()  -- creating new socket
                if not client then
-                       debug( "cannot create socket:", err ) 
+                       debug( "cannot create socket:", err )
                        return nil, err
                end
                client:settimeout( 0 )  -- set nonblocking
                        return nil, err
                end
                client:settimeout( 0 )  -- set nonblocking
@@ -719,10 +747,10 @@ do
                local sslctx
                if sslcfg then  -- handle ssl/new context
                        if not ssl then
                local sslctx
                if sslcfg then  -- handle ssl/new context
                        if not ssl then
-                               debug "need luasec, but not available" 
+                               debug "need luasec, but not available"
                                return nil, "luasec not found"
                        end
                                return nil, "luasec not found"
                        end
-                       sslctx, err = ssl.newcontext( sslcfg )
+                       sslctx, err = sslcfg
                        if err then
                                debug( "cannot create new ssl context:", err )
                                return nil, err
                        if err then
                                debug( "cannot create new ssl context:", err )
                                return nil, err
@@ -774,6 +802,25 @@ local function setquitting(yes)
        end
 end
 
        end
 end
 
+function get_backend()
+       return base:method();
+end
+
+-- We need to hold onto the events to stop them
+-- being garbage-collected
+local signal_events = {}; -- [signal_num] -> event object
+function hook_signal(signal_num, handler)
+       local function _handler(event)
+               local ret = handler();
+               if ret ~= false then -- Continue handling this signal?
+                       return EV_SIGNAL; -- Yes
+               end
+               return -1; -- Close this event
+       end
+       signal_events[signal_num] = base:addevent(signal_num, EV_SIGNAL, _handler);
+       return signal_events[signal_num];
+end
+
 return {
 
        cfg = cfg,
 return {
 
        cfg = cfg,
@@ -785,7 +832,10 @@ return {
        addserver = addserver,
        addclient = addclient,
        wrapclient = wrapclient,
        addserver = addserver,
        addclient = addclient,
        wrapclient = wrapclient,
-       closeallservers = closeallservers,
+       setquitting = setquitting,
+       closeall = closeallservers,
+       get_backend = get_backend,
+       hook_signal = hook_signal,
 
        __NAME = SCRIPT_NAME,
        __DATE = LAST_MODIFIED,
 
        __NAME = SCRIPT_NAME,
        __DATE = LAST_MODIFIED,