5fc757a0f96c5fe5545e7328cd9f95bd47e17231
[vlc.git] / modules / lua / libs / net.c
1 /*****************************************************************************
2  * net.c: Network related functions
3  *****************************************************************************
4  * Copyright (C) 2007-2008 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Antoine Cellerier <dionoea at videolan tod org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <assert.h>
32 #include <errno.h>
33 #ifdef _WIN32
34 #include <io.h>
35 #endif
36 #ifdef HAVE_POLL
37 #include <poll.h>       /* poll structures and defines */
38 #endif
39 #include <sys/stat.h>
40
41 #include <vlc_common.h>
42 #include <vlc_network.h>
43 #include <vlc_url.h>
44 #include <vlc_fs.h>
45
46 #include "../vlc.h"
47 #include "../libs.h"
48 #include "misc.h"
49
50 static vlclua_dtable_t *vlclua_get_dtable( lua_State *L )
51 {
52     return vlclua_get_object( L, vlclua_get_dtable );
53 }
54
55 /** Maps an OS file descriptor to a VLC Lua file descriptor */
56 static int vlclua_fd_map( lua_State *L, int fd )
57 {
58     vlclua_dtable_t *dt = vlclua_get_dtable( L );
59
60     if( (unsigned)fd < 3u )
61         return -1;
62
63 #ifndef NDEBUG
64     for( unsigned i = 0; i < dt->fdc; i++ )
65         assert( dt->fdv[i] != fd );
66 #endif
67
68     if( dt->fdc >= 64 )
69         return -1;
70
71     int *fdv = realloc( dt->fdv, (dt->fdc + 1) * sizeof (dt->fdv[0]) );
72     if( unlikely(fdv == NULL) )
73         return -1;
74
75     dt->fdv = fdv;
76     dt->fdv[dt->fdc] = fd;
77     fd = 3 + dt->fdc;
78     dt->fdc++;
79     return fd;
80 }
81
82 static int vlclua_fd_map_safe( lua_State *L, int fd )
83 {
84     int luafd = vlclua_fd_map( L, fd );
85     if( luafd == -1 )
86         net_Close( fd );
87     return luafd;
88 }
89
90 /** Gets the OS file descriptor mapped to a VLC Lua file descriptor */
91 static int vlclua_fd_get( lua_State *L, unsigned idx )
92 {
93     vlclua_dtable_t *dt = vlclua_get_dtable( L );
94
95     if( idx < 3u )
96         return idx;
97     idx -= 3;
98     return (idx < dt->fdc) ? dt->fdv[idx] : -1;
99 }
100
101 /** Gets the VLC Lua file descriptor mapped from an OS file descriptor */
102 static int vlclua_fd_get_lua( lua_State *L, int fd )
103 {
104     vlclua_dtable_t *dt = vlclua_get_dtable( L );
105
106     if( (unsigned)fd < 3u )
107         return fd;
108     for( unsigned i = 0; i < dt->fdc; i++ )
109         if( dt->fdv[i] == fd )
110             return 3 + i;
111     return -1;
112 }
113
114 /** Unmaps an OS file descriptor from VLC Lua */
115 static void vlclua_fd_unmap( lua_State *L, unsigned idx )
116 {
117     vlclua_dtable_t *dt = vlclua_get_dtable( L );
118     int fd = -1;
119
120     if( idx < 3u )
121         return; /* Never close stdin/stdout/stderr. */
122
123     idx -= 3;
124     if( idx >= dt->fdc )
125         return;
126
127     fd = dt->fdv[idx];
128     dt->fdc--;
129     memmove( dt->fdv + idx, dt->fdv + idx + 1,
130              (dt->fdc - idx) * sizeof (dt->fdv[0]) );
131     /* realloc() not really needed */
132 #ifndef NDEBUG
133     for( unsigned i = 0; i < dt->fdc; i++ )
134         assert( dt->fdv[i] != fd );
135 #endif
136 }
137
138 static void vlclua_fd_unmap_safe( lua_State *L, unsigned idx )
139 {
140     int fd = vlclua_fd_get( L, idx );
141
142     vlclua_fd_unmap( L, idx );
143     if( fd != -1 )
144         net_Close( fd );
145 }
146
147 /*****************************************************************************
148  *
149  *****************************************************************************/
150 static int vlclua_url_parse( lua_State *L )
151 {
152     const char *psz_url = luaL_checkstring( L, 1 );
153     const char *psz_option = luaL_optstring( L, 2, NULL );
154     vlc_url_t url;
155
156     vlc_UrlParse( &url, psz_url, psz_option?*psz_option:0 );
157
158     lua_newtable( L );
159     lua_pushstring( L, url.psz_protocol );
160     lua_setfield( L, -2, "protocol" );
161     lua_pushstring( L, url.psz_username );
162     lua_setfield( L, -2, "username" );
163     lua_pushstring( L, url.psz_password );
164     lua_setfield( L, -2, "password" );
165     lua_pushstring( L, url.psz_host );
166     lua_setfield( L, -2, "host" );
167     lua_pushinteger( L, url.i_port );
168     lua_setfield( L, -2, "port" );
169     lua_pushstring( L, url.psz_path );
170     lua_setfield( L, -2, "path" );
171     lua_pushstring( L, url.psz_option );
172     lua_setfield( L, -2, "option" );
173
174     vlc_UrlClean( &url );
175
176     return 1;
177 }
178
179 /*****************************************************************************
180  * Net listen
181  *****************************************************************************/
182 static int vlclua_net_listen_close( lua_State * );
183 static int vlclua_net_accept( lua_State * );
184 static int vlclua_net_fds( lua_State * );
185
186 static const luaL_Reg vlclua_net_listen_reg[] = {
187     { "accept", vlclua_net_accept },
188     { "fds", vlclua_net_fds },
189     { NULL, NULL }
190 };
191
192 static int vlclua_net_listen_tcp( lua_State *L )
193 {
194     vlc_object_t *p_this = vlclua_get_this( L );
195     const char *psz_host = luaL_checkstring( L, 1 );
196     int i_port = luaL_checkint( L, 2 );
197     int *pi_fd = net_ListenTCP( p_this, psz_host, i_port );
198     if( pi_fd == NULL )
199         return luaL_error( L, "Cannot listen on %s:%d", psz_host, i_port );
200
201     for( unsigned i = 0; pi_fd[i] != -1; i++ )
202         if( vlclua_fd_map( L, pi_fd[i] ) == -1 )
203         {
204             while( i > 0 )
205                 vlclua_fd_unmap( L, vlclua_fd_get_lua( L, pi_fd[--i] ) );
206
207             net_ListenClose( pi_fd );
208             return luaL_error( L, "Cannot listen on %s:%d", psz_host, i_port );
209         }
210
211     int **ppi_fd = lua_newuserdata( L, sizeof( int * ) );
212     *ppi_fd = pi_fd;
213
214     if( luaL_newmetatable( L, "net_listen" ) )
215     {
216         lua_newtable( L );
217         luaL_register( L, NULL, vlclua_net_listen_reg );
218         lua_setfield( L, -2, "__index" );
219         lua_pushcfunction( L, vlclua_net_listen_close );
220         lua_setfield( L, -2, "__gc" );
221     }
222
223     lua_setmetatable( L, -2 );
224     return 1;
225 }
226
227 static int vlclua_net_listen_close( lua_State *L )
228 {
229     int **ppi_fd = (int**)luaL_checkudata( L, 1, "net_listen" );
230     int *pi_fd = *ppi_fd;
231
232     for( unsigned i = 0; pi_fd[i] != -1; i++ )
233         vlclua_fd_unmap( L, vlclua_fd_get_lua( L, pi_fd[i] ) );
234
235     net_ListenClose( pi_fd );
236     return 0;
237 }
238
239 static int vlclua_net_fds( lua_State *L )
240 {
241     int **ppi_fd = (int**)luaL_checkudata( L, 1, "net_listen" );
242     int *pi_fd = *ppi_fd;
243
244     int i_count = 0;
245     while( pi_fd[i_count] != -1 )
246         lua_pushinteger( L, vlclua_fd_get_lua( L, pi_fd[i_count++] ) );
247
248     return i_count;
249 }
250
251 static int vlclua_net_accept( lua_State *L )
252 {
253     vlc_object_t *p_this = vlclua_get_this( L );
254     int **ppi_fd = (int**)luaL_checkudata( L, 1, "net_listen" );
255     int i_fd = net_Accept( p_this, *ppi_fd );
256
257     lua_pushinteger( L, vlclua_fd_map_safe( L, i_fd ) );
258     return 1;
259 }
260
261 /*****************************************************************************
262  *
263  *****************************************************************************/
264 static int vlclua_net_connect_tcp( lua_State *L )
265 {
266     vlc_object_t *p_this = vlclua_get_this( L );
267     const char *psz_host = luaL_checkstring( L, 1 );
268     int i_port = luaL_checkint( L, 2 );
269     int i_fd = net_Connect( p_this, psz_host, i_port, SOCK_STREAM, IPPROTO_TCP );
270     lua_pushinteger( L, vlclua_fd_map_safe( L, i_fd ) );
271     return 1;
272 }
273
274 static int vlclua_net_close( lua_State *L )
275 {
276     int i_fd = luaL_checkint( L, 1 );
277     vlclua_fd_unmap_safe( L, i_fd );
278     return 0;
279 }
280
281 static int vlclua_net_send( lua_State *L )
282 {
283     int fd = vlclua_fd_get( L, luaL_checkint( L, 1 ) );
284     size_t i_len;
285     const char *psz_buffer = luaL_checklstring( L, 2, &i_len );
286
287     i_len = luaL_optint( L, 3, i_len );
288     lua_pushinteger( L, (fd != -1) ? send( fd, psz_buffer, i_len, 0 ) : -1 );
289     return 1;
290 }
291
292 static int vlclua_net_recv( lua_State *L )
293 {
294     int fd = vlclua_fd_get( L, luaL_checkint( L, 1 ) );
295     size_t i_len = luaL_optint( L, 2, 1 );
296     char psz_buffer[i_len];
297
298     ssize_t i_ret = (fd != -1) ? recv( fd, psz_buffer, i_len, 0 ) : -1;
299     if( i_ret > 0 )
300         lua_pushlstring( L, psz_buffer, i_ret );
301     else
302         lua_pushnil( L );
303     return 1;
304 }
305
306 #ifndef _WIN32
307 /*****************************************************************************
308  *
309  *****************************************************************************/
310 /* Takes a { fd : events } table as first arg and modifies it to { fd : revents } */
311 static int vlclua_net_poll( lua_State *L )
312 {
313     vlclua_dtable_t *dt = vlclua_get_dtable( L );
314
315     luaL_checktype( L, 1, LUA_TTABLE );
316
317     int i_fds = 1;
318     lua_pushnil( L );
319     while( lua_next( L, 1 ) )
320     {
321         i_fds++;
322         lua_pop( L, 1 );
323     }
324
325     struct pollfd *p_fds = xmalloc( i_fds * sizeof( *p_fds ) );
326     int *luafds = xmalloc( i_fds * sizeof( *luafds ) );
327
328     lua_pushnil( L );
329     int i = 1;
330     p_fds[0].fd = dt->fd[0];
331     p_fds[0].events = POLLIN;
332     while( lua_next( L, 1 ) )
333     {
334         luafds[i] = luaL_checkinteger( L, -2 );
335         p_fds[i].fd = vlclua_fd_get( L, luafds[i] );
336         p_fds[i].events = luaL_checkinteger( L, -1 );
337         p_fds[i].events &= POLLIN | POLLOUT | POLLPRI;
338         lua_pop( L, 1 );
339         i++;
340     }
341
342     int i_ret;
343     do
344         i_ret = poll( p_fds, i_fds, -1 );
345     while( i_ret == -1 && errno == EINTR );
346
347     for( i = 1; i < i_fds; i++ )
348     {
349         lua_pushinteger( L, luafds[i] );
350         lua_pushinteger( L, p_fds[i].revents );
351         lua_settable( L, 1 );
352     }
353     lua_pushinteger( L, i_ret );
354
355     if( p_fds[0].revents )
356         i_ret = luaL_error( L, "Interrupted." );
357     else
358         i_ret = 1;
359     free( luafds );
360     free( p_fds );
361
362     return i_ret;
363 }
364
365 /*****************************************************************************
366  *
367  *****************************************************************************/
368 /*
369 static int vlclua_fd_open( lua_State *L )
370 {
371 }
372 */
373
374 static int vlclua_fd_write( lua_State *L )
375 {
376     int fd = vlclua_fd_get( L, luaL_checkint( L, 1 ) );
377     size_t i_len;
378     const char *psz_buffer = luaL_checklstring( L, 2, &i_len );
379
380     i_len = luaL_optint( L, 3, i_len );
381     lua_pushinteger( L, (fd != -1) ? write( fd, psz_buffer, i_len ) : -1 );
382     return 1;
383 }
384
385 static int vlclua_fd_read( lua_State *L )
386 {
387     int fd = vlclua_fd_get( L, luaL_checkint( L, 1 ) );
388     size_t i_len = luaL_optint( L, 2, 1 );
389     char psz_buffer[i_len];
390
391     ssize_t i_ret = (fd != -1) ? read( fd, psz_buffer, i_len ) : -1;
392     if( i_ret > 0 )
393         lua_pushlstring( L, psz_buffer, i_ret );
394     else
395         lua_pushnil( L );
396     return 1;
397 }
398 #endif
399
400 /*****************************************************************************
401  *
402  *****************************************************************************/
403 static int vlclua_stat( lua_State *L )
404 {
405     const char *psz_path = luaL_checkstring( L, 1 );
406     struct stat s;
407     if( vlc_stat( psz_path, &s ) )
408         return 0;
409         //return luaL_error( L, "Couldn't stat %s.", psz_path );
410     lua_newtable( L );
411     if( S_ISREG( s.st_mode ) )
412         lua_pushliteral( L, "file" );
413     else if( S_ISDIR( s.st_mode ) )
414         lua_pushliteral( L, "dir" );
415 #ifdef S_ISCHR
416     else if( S_ISCHR( s.st_mode ) )
417         lua_pushliteral( L, "character device" );
418 #endif
419 #ifdef S_ISBLK
420     else if( S_ISBLK( s.st_mode ) )
421         lua_pushliteral( L, "block device" );
422 #endif
423 #ifdef S_ISFIFO
424     else if( S_ISFIFO( s.st_mode ) )
425         lua_pushliteral( L, "fifo" );
426 #endif
427 #ifdef S_ISLNK
428     else if( S_ISLNK( s.st_mode ) )
429         lua_pushliteral( L, "symbolic link" );
430 #endif
431 #ifdef S_ISSOCK
432     else if( S_ISSOCK( s.st_mode ) )
433         lua_pushliteral( L, "socket" );
434 #endif
435     else
436         lua_pushliteral( L, "unknown" );
437     lua_setfield( L, -2, "type" );
438     lua_pushinteger( L, s.st_mode );
439     lua_setfield( L, -2, "mode" );
440     lua_pushinteger( L, s.st_uid );
441     lua_setfield( L, -2, "uid" );
442     lua_pushinteger( L, s.st_gid );
443     lua_setfield( L, -2, "gid" );
444     lua_pushinteger( L, s.st_size );
445     lua_setfield( L, -2, "size" );
446     lua_pushinteger( L, s.st_atime );
447     lua_setfield( L, -2, "access_time" );
448     lua_pushinteger( L, s.st_mtime );
449     lua_setfield( L, -2, "modification_time" );
450     lua_pushinteger( L, s.st_ctime );
451     lua_setfield( L, -2, "creation_time" );
452     return 1;
453 }
454
455 static int vlclua_opendir( lua_State *L )
456 {
457     const char *psz_dir = luaL_checkstring( L, 1 );
458     DIR *p_dir;
459     int i = 0;
460
461     if( ( p_dir = vlc_opendir( psz_dir ) ) == NULL )
462         return luaL_error( L, "cannot open directory `%s'.", psz_dir );
463
464     lua_newtable( L );
465     for( ;; )
466     {
467         const char *psz_filename = vlc_readdir( p_dir );
468         if( !psz_filename ) break;
469         i++;
470         lua_pushstring( L, psz_filename );
471         lua_rawseti( L, -2, i );
472     }
473     closedir( p_dir );
474     return 1;
475 }
476
477 /*****************************************************************************
478  *
479  *****************************************************************************/
480 static const luaL_Reg vlclua_net_intf_reg[] = {
481     { "listen_tcp", vlclua_net_listen_tcp },
482     { "connect_tcp", vlclua_net_connect_tcp },
483     { "close", vlclua_net_close },
484     { "send", vlclua_net_send },
485     { "recv", vlclua_net_recv },
486 #ifndef _WIN32
487     { "poll", vlclua_net_poll },
488     { "read", vlclua_fd_read },
489     { "write", vlclua_fd_write },
490 #endif
491     /* The following functions do not depend on intf_thread_t and do not really
492      * belong in net.* but are left here for backward compatibility: */
493     { "url_parse", vlclua_url_parse },
494     { "stat", vlclua_stat }, /* Not really "net" */
495     { "opendir", vlclua_opendir }, /* Not really "net" */
496     { NULL, NULL }
497 };
498
499 static void luaopen_net_intf( lua_State *L )
500 {
501     lua_newtable( L );
502     luaL_register( L, NULL, vlclua_net_intf_reg );
503 #define ADD_CONSTANT( value )    \
504     lua_pushinteger( L, POLL##value ); \
505     lua_setfield( L, -2, "POLL"#value );
506     ADD_CONSTANT( IN )
507     ADD_CONSTANT( PRI )
508     ADD_CONSTANT( OUT )
509     ADD_CONSTANT( ERR )
510     ADD_CONSTANT( HUP )
511     ADD_CONSTANT( NVAL )
512     lua_setfield( L, -2, "net" );
513 }
514
515 int vlclua_fd_init( lua_State *L, vlclua_dtable_t *dt )
516 {
517 #ifndef _WIN32
518     if( vlc_pipe( dt->fd ) )
519         return -1;
520 #endif
521     dt->fdv = NULL;
522     dt->fdc = 0;
523     vlclua_set_object( L, vlclua_get_dtable, dt );
524     luaopen_net_intf( L );
525     return 0;
526 }
527
528 void vlclua_fd_interrupt( vlclua_dtable_t *dt )
529 {
530 #ifndef _WIN32
531     close( dt->fd[1] );
532     dt->fd[1] = -1;
533 #endif
534 }
535
536 /** Releases all (leaked) VLC Lua file descriptors. */
537 void vlclua_fd_cleanup( vlclua_dtable_t *dt )
538 {
539     for( unsigned i = 0; i < dt->fdc; i++ )
540          net_Close( dt->fdv[i] );
541     free( dt->fdv );
542 #ifndef _WIN32
543     if( dt->fd[1] != -1 )
544         close( dt->fd[1] );
545     close( dt->fd[0] );
546 #endif
547 }