]> git.sesse.net Git - vlc/blobdiff - modules/lua/libs/net.c
lua: vector out files descriptable table code
[vlc] / modules / lua / libs / net.c
index ea6124598c7e9cd8bfdd2674a1213846993c6f6a..fe2e37f0a155094b55472f5d153153efd1bb3a74 100644 (file)
@@ -28,8 +28,9 @@
 # include "config.h"
 #endif
 
+#include <assert.h>
 #include <errno.h>
-#ifdef WIN32
+#ifdef _WIN32
 #include <io.h>
 #endif
 #ifdef HAVE_POLL
 
 #include "../vlc.h"
 #include "../libs.h"
+#include "misc.h"
+
+static vlclua_dtable_t *vlclua_get_dtable( lua_State *L )
+{
+    return vlclua_get_object( L, vlclua_get_dtable );
+}
+
+/** Maps an OS file descriptor to a VLC Lua file descriptor */
+static int vlclua_fd_map( lua_State *L, int fd )
+{
+    vlclua_dtable_t *dt = vlclua_get_dtable( L );
+
+    if( (unsigned)fd < 3u )
+        return -1;
+
+#ifndef NDEBUG
+    for( unsigned i = 0; i < dt->fdc; i++ )
+        assert( dt->fdv[i] != fd );
+#endif
+
+    if( dt->fdc >= 64 )
+        return -1;
+
+    int *fdv = realloc( dt->fdv, (dt->fdc + 1) * sizeof (dt->fdv[0]) );
+    if( unlikely(fdv == NULL) )
+        return -1;
+
+    dt->fdv = fdv;
+    dt->fdv[dt->fdc] = fd;
+    fd = 3 + dt->fdc;
+    dt->fdc++;
+    return fd;
+}
+
+static int vlclua_fd_map_safe( lua_State *L, int fd )
+{
+    int luafd = vlclua_fd_map( L, fd );
+    if( luafd == -1 )
+        net_Close( fd );
+    return luafd;
+}
+
+/** Gets the OS file descriptor mapped to a VLC Lua file descriptor */
+static int vlclua_fd_get( lua_State *L, unsigned idx )
+{
+    vlclua_dtable_t *dt = vlclua_get_dtable( L );
+
+    if( idx < 3u )
+        return idx;
+    idx -= 3;
+    return (idx < dt->fdc) ? dt->fdv[idx] : -1;
+}
+
+/** Gets the VLC Lua file descriptor mapped from an OS file descriptor */
+static int vlclua_fd_get_lua( lua_State *L, int fd )
+{
+    vlclua_dtable_t *dt = vlclua_get_dtable( L );
+
+    if( (unsigned)fd < 3u )
+        return fd;
+    for( unsigned i = 0; i < dt->fdc; i++ )
+        if( dt->fdv[i] == fd )
+            return 3 + i;
+    return -1;
+}
+
+/** Unmaps an OS file descriptor from VLC Lua */
+static void vlclua_fd_unmap( lua_State *L, unsigned idx )
+{
+    vlclua_dtable_t *dt = vlclua_get_dtable( L );
+    int fd = -1;
+
+    if( idx < 3u )
+        return; /* Never close stdin/stdout/stderr. */
+
+    idx -= 3;
+    if( idx >= dt->fdc )
+        return;
+
+    fd = dt->fdv[idx];
+    dt->fdc--;
+    memmove( dt->fdv + idx, dt->fdv + idx + 1,
+             (dt->fdc - idx) * sizeof (dt->fdv[0]) );
+    /* realloc() not really needed */
+#ifndef NDEBUG
+    for( unsigned i = 0; i < dt->fdc; i++ )
+        assert( dt->fdv[i] != fd );
+#endif
+}
+
+static void vlclua_fd_unmap_safe( lua_State *L, unsigned idx )
+{
+    int fd = vlclua_fd_get( L, idx );
+
+    vlclua_fd_unmap( L, idx );
+    if( fd != -1 )
+        net_Close( fd );
+}
 
 /*****************************************************************************
  *
@@ -99,6 +198,16 @@ static int vlclua_net_listen_tcp( lua_State *L )
     if( pi_fd == NULL )
         return luaL_error( L, "Cannot listen on %s:%d", psz_host, i_port );
 
+    for( unsigned i = 0; pi_fd[i] != -1; i++ )
+        if( vlclua_fd_map( L, pi_fd[i] ) == -1 )
+        {
+            while( i > 0 )
+                vlclua_fd_unmap( L, vlclua_fd_get_lua( L, pi_fd[--i] ) );
+
+            net_ListenClose( pi_fd );
+            return luaL_error( L, "Cannot listen on %s:%d", psz_host, i_port );
+        }
+
     int **ppi_fd = lua_newuserdata( L, sizeof( int * ) );
     *ppi_fd = pi_fd;
 
@@ -118,7 +227,12 @@ static int vlclua_net_listen_tcp( lua_State *L )
 static int vlclua_net_listen_close( lua_State *L )
 {
     int **ppi_fd = (int**)luaL_checkudata( L, 1, "net_listen" );
-    net_ListenClose( *ppi_fd );
+    int *pi_fd = *ppi_fd;
+
+    for( unsigned i = 0; pi_fd[i] != -1; i++ )
+        vlclua_fd_unmap( L, vlclua_fd_get_lua( L, pi_fd[i] ) );
+
+    net_ListenClose( pi_fd );
     return 0;
 }
 
@@ -129,7 +243,7 @@ static int vlclua_net_fds( lua_State *L )
 
     int i_count = 0;
     while( pi_fd[i_count] != -1 )
-        lua_pushinteger( L, pi_fd[i_count++] );
+        lua_pushinteger( L, vlclua_fd_get_lua( L, pi_fd[i_count++] ) );
 
     return i_count;
 }
@@ -140,7 +254,7 @@ static int vlclua_net_accept( lua_State *L )
     int **ppi_fd = (int**)luaL_checkudata( L, 1, "net_listen" );
     int i_fd = net_Accept( p_this, *ppi_fd );
 
-    lua_pushinteger( L, i_fd );
+    lua_pushinteger( L, vlclua_fd_map_safe( L, i_fd ) );
     return 1;
 }
 
@@ -153,14 +267,14 @@ static int vlclua_net_connect_tcp( lua_State *L )
     const char *psz_host = luaL_checkstring( L, 1 );
     int i_port = luaL_checkint( L, 2 );
     int i_fd = net_Connect( p_this, psz_host, i_port, SOCK_STREAM, IPPROTO_TCP );
-    lua_pushinteger( L, i_fd );
+    lua_pushinteger( L, vlclua_fd_map_safe( L, i_fd ) );
     return 1;
 }
 
 static int vlclua_net_close( lua_State *L )
 {
     int i_fd = luaL_checkint( L, 1 );
-    net_Close( i_fd );
+    vlclua_fd_unmap_safe( L, i_fd );
     return 0;
 }
 
@@ -170,7 +284,7 @@ static int vlclua_net_send( lua_State *L )
     size_t i_len;
     const char *psz_buffer = luaL_checklstring( L, 2, &i_len );
     i_len = luaL_optint( L, 3, i_len );
-    i_len = send( i_fd, psz_buffer, i_len, 0 );
+    i_len = send( vlclua_fd_get( L, i_fd ), psz_buffer, i_len, 0 );
     lua_pushinteger( L, i_len );
     return 1;
 }
@@ -180,7 +294,7 @@ static int vlclua_net_recv( lua_State *L )
     int i_fd = luaL_checkint( L, 1 );
     size_t i_len = luaL_optint( L, 2, 1 );
     char psz_buffer[i_len];
-    ssize_t i_ret = recv( i_fd, psz_buffer, i_len, 0 );
+    ssize_t i_ret = recv( vlclua_fd_get( L, i_fd ), psz_buffer, i_len, 0 );
     if( i_ret > 0 )
         lua_pushlstring( L, psz_buffer, i_ret );
     else
@@ -188,30 +302,38 @@ static int vlclua_net_recv( lua_State *L )
     return 1;
 }
 
+#ifndef _WIN32
 /*****************************************************************************
  *
  *****************************************************************************/
 /* Takes a { fd : events } table as first arg and modifies it to { fd : revents } */
 static int vlclua_net_poll( lua_State *L )
 {
+    vlclua_dtable_t *dt = vlclua_get_dtable( L );
+
     luaL_checktype( L, 1, LUA_TTABLE );
 
-    int i_fds = 0;
+    int i_fds = 1;
     lua_pushnil( L );
     while( lua_next( L, 1 ) )
     {
         i_fds++;
         lua_pop( L, 1 );
     }
-    struct pollfd *p_fds = malloc( i_fds * sizeof( struct pollfd ) );
-    vlc_cleanup_push( free, p_fds );
+
+    struct pollfd *p_fds = xmalloc( i_fds * sizeof( *p_fds ) );
+    int *luafds = xmalloc( i_fds * sizeof( *luafds ) );
+
     lua_pushnil( L );
-    int i = 0;
+    int i = 1;
+    p_fds[0].fd = dt->fd[0];
+    p_fds[0].events = POLLIN;
     while( lua_next( L, 1 ) )
     {
-        p_fds[i].fd = luaL_checkinteger( L, -2 );
+        luafds[i] = luaL_checkinteger( L, -2 );
+        p_fds[i].fd = vlclua_fd_get( L, luafds[i] );
         p_fds[i].events = luaL_checkinteger( L, -1 );
-        p_fds[i].revents = 0;
+        p_fds[i].events &= POLLIN | POLLOUT | POLLPRI;
         lua_pop( L, 1 );
         i++;
     }
@@ -219,17 +341,24 @@ static int vlclua_net_poll( lua_State *L )
     int i_ret;
     do
         i_ret = poll( p_fds, i_fds, -1 );
-    while( i_ret == -1 );
+    while( i_ret == -1 && errno == EINTR );
 
-    for( i = 0; i < i_fds; i++ )
+    for( i = 1; i < i_fds; i++ )
     {
-        lua_pushinteger( L, p_fds[i].fd );
+        lua_pushinteger( L, luafds[i] );
         lua_pushinteger( L, p_fds[i].revents );
         lua_settable( L, 1 );
     }
     lua_pushinteger( L, i_ret );
-    vlc_cleanup_run();
-    return 1;
+
+    if( p_fds[0].revents )
+        i_ret = luaL_error( L, "Interrupted." );
+    else
+        i_ret = 1;
+    free( luafds );
+    free( p_fds );
+
+    return i_ret;
 }
 
 /*****************************************************************************
@@ -248,7 +377,7 @@ static int vlclua_fd_write( lua_State *L )
     ssize_t i_ret;
     const char *psz_buffer = luaL_checklstring( L, 2, &i_len );
     i_len = luaL_optint( L, 3, i_len );
-    i_ret = write( i_fd, psz_buffer, i_len );
+    i_ret = write( vlclua_fd_get( L, i_fd ), psz_buffer, i_len );
     lua_pushinteger( L, i_ret );
     return 1;
 }
@@ -258,13 +387,14 @@ static int vlclua_fd_read( lua_State *L )
     int i_fd = luaL_checkint( L, 1 );
     size_t i_len = luaL_optint( L, 2, 1 );
     char psz_buffer[i_len];
-    ssize_t i_ret = read( i_fd, psz_buffer, i_len );
+    ssize_t i_ret = read( vlclua_fd_get( L, i_fd ), psz_buffer, i_len );
     if( i_ret > 0 )
         lua_pushlstring( L, psz_buffer, i_ret );
     else
         lua_pushnil( L );
     return 1;
 }
+#endif
 
 /*****************************************************************************
  *
@@ -333,12 +463,11 @@ static int vlclua_opendir( lua_State *L )
     lua_newtable( L );
     for( ;; )
     {
-        char *psz_filename = vlc_readdir( p_dir );
+        const char *psz_filename = vlc_readdir( p_dir );
         if( !psz_filename ) break;
         i++;
         lua_pushstring( L, psz_filename );
         lua_rawseti( L, -2, i );
-        free( psz_filename );
     }
     closedir( p_dir );
     return 1;
@@ -347,33 +476,85 @@ static int vlclua_opendir( lua_State *L )
 /*****************************************************************************
  *
  *****************************************************************************/
-static const luaL_Reg vlclua_net_reg[] = {
-    { "url_parse", vlclua_url_parse },
+static const luaL_Reg vlclua_net_intf_reg[] = {
     { "listen_tcp", vlclua_net_listen_tcp },
     { "connect_tcp", vlclua_net_connect_tcp },
     { "close", vlclua_net_close },
     { "send", vlclua_net_send },
     { "recv", vlclua_net_recv },
+#ifndef _WIN32
     { "poll", vlclua_net_poll },
     { "read", vlclua_fd_read },
     { "write", vlclua_fd_write },
+#endif
+    /* The following functions do not depend on intf_thread_t and do not really
+     * belong in net.* but are left here for backward compatibility: */
+    { "url_parse", vlclua_url_parse },
+    { "stat", vlclua_stat }, /* Not really "net" */
+    { "opendir", vlclua_opendir }, /* Not really "net" */
+    { NULL, NULL }
+};
+
+static void luaopen_net_intf( lua_State *L )
+{
+    lua_newtable( L );
+    luaL_register( L, NULL, vlclua_net_intf_reg );
+#define ADD_CONSTANT( value )    \
+    lua_pushinteger( L, POLL##value ); \
+    lua_setfield( L, -2, "POLL"#value );
+    ADD_CONSTANT( IN )
+    ADD_CONSTANT( PRI )
+    ADD_CONSTANT( OUT )
+    ADD_CONSTANT( ERR )
+    ADD_CONSTANT( HUP )
+    ADD_CONSTANT( NVAL )
+    lua_setfield( L, -2, "net" );
+}
+
+int vlclua_fd_init( lua_State *L, vlclua_dtable_t *dt )
+{
+#ifndef _WIN32
+    if( vlc_pipe( dt->fd ) )
+        return -1;
+#endif
+    dt->fdv = NULL;
+    dt->fdc = 0;
+    vlclua_set_object( L, vlclua_get_dtable, dt );
+    luaopen_net_intf( L );
+    return 0;
+}
+
+void vlclua_fd_interrupt( vlclua_dtable_t *dt )
+{
+#ifndef _WIN32
+    close( dt->fd[1] );
+    dt->fd[1] = -1;
+#endif
+}
+
+/** Releases all (leaked) VLC Lua file descriptors. */
+void vlclua_fd_cleanup( vlclua_dtable_t *dt )
+{
+    for( unsigned i = 0; i < dt->fdc; i++ )
+         net_Close( dt->fdv[i] );
+    free( dt->fdv );
+#ifndef _WIN32
+    if( dt->fd[1] != -1 )
+        close( dt->fd[1] );
+    close( dt->fd[0] );
+#endif
+}
+
+static const luaL_Reg vlclua_net_generic_reg[] = {
+    { "url_parse", vlclua_url_parse },
     { "stat", vlclua_stat }, /* Not really "net" */
     { "opendir", vlclua_opendir }, /* Not really "net" */
     { NULL, NULL }
 };
 
-void luaopen_net( lua_State *L )
+void luaopen_net_generic( lua_State *L )
 {
     lua_newtable( L );
-    luaL_register( L, NULL, vlclua_net_reg );
-#define ADD_CONSTANT( name, value )    \
-    lua_pushinteger( L, value ); \
-    lua_setfield( L, -2, name );
-    ADD_CONSTANT( "POLLIN", POLLIN )
-    ADD_CONSTANT( "POLLPRI", POLLPRI )
-    ADD_CONSTANT( "POLLOUT", POLLOUT )
-    ADD_CONSTANT( "POLLERR", POLLERR )
-    ADD_CONSTANT( "POLLHUP", POLLHUP )
-    ADD_CONSTANT( "POLLNVAL", POLLNVAL )
+    luaL_register( L, NULL, vlclua_net_generic_reg );
     lua_setfield( L, -2, "net" );
 }