net.c 14.4 KB
Newer Older
1
2
3
/*****************************************************************************
 * net.c: Network related functions
 *****************************************************************************
4
 * Copyright (C) 2007-2008 the VideoLAN team
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 *
 * Authors: Antoine Cellerier <dionoea at videolan tod org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of 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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
26
27
28
29
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

30
#include <assert.h>
31
#include <errno.h>
32
#ifdef _WIN32
33
34
#include <io.h>
#endif
35
#ifdef HAVE_POLL_H
36
37
38
#include <poll.h>       /* poll structures and defines */
#endif
#include <sys/stat.h>
39

40
#include <vlc_common.h>
41
42
#include <vlc_network.h>
#include <vlc_url.h>
43
#include <vlc_fs.h>
44
#include <vlc_interrupt.h>
45

46
47
#include "../vlc.h"
#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
}

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
55
56
57
58
59
60
vlc_interrupt_t *vlclua_set_interrupt( lua_State *L )
{
    vlclua_dtable_t *dt = vlclua_get_dtable( L );
    return vlc_interrupt_set( dt->interrupt );
}

61
62
63
/** Maps an OS file descriptor to a VLC Lua file descriptor */
static int vlclua_fd_map( lua_State *L, int fd )
{
64
    vlclua_dtable_t *dt = vlclua_get_dtable( L );
65
66
67
68
69

    if( (unsigned)fd < 3u )
        return -1;

#ifndef NDEBUG
70
71
    for( unsigned i = 0; i < dt->fdc; i++ )
        assert( dt->fdv[i] != fd );
72
73
#endif

74
75
76
77
78
79
80
81
82
    for( unsigned i = 0; i < dt->fdc; i++ )
    {
        if( dt->fdv[i] == -1 )
        {
            dt->fdv[i] = fd;
            return 3 + i;
        }
    }

83
    if( dt->fdc >= 64 )
84
85
        return -1;

86
    int *fdv = realloc( dt->fdv, (dt->fdc + 1) * sizeof (dt->fdv[0]) );
87
88
89
    if( unlikely(fdv == NULL) )
        return -1;

90
91
92
93
    dt->fdv = fdv;
    dt->fdv[dt->fdc] = fd;
    fd = 3 + dt->fdc;
    dt->fdc++;
94
    return fd;
95
96
97
98
99
100
101
102
103
104
105
106
107
}

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 )
{
108
    vlclua_dtable_t *dt = vlclua_get_dtable( L );
109
110
111
112

    if( idx < 3u )
        return idx;
    idx -= 3;
113
    return (idx < dt->fdc) ? dt->fdv[idx] : -1;
114
115
116
117
118
}

/** Gets the VLC Lua file descriptor mapped from an OS file descriptor */
static int vlclua_fd_get_lua( lua_State *L, int fd )
{
119
    vlclua_dtable_t *dt = vlclua_get_dtable( L );
120
121
122

    if( (unsigned)fd < 3u )
        return fd;
123
124
    for( unsigned i = 0; i < dt->fdc; i++ )
        if( dt->fdv[i] == fd )
125
126
127
128
129
130
131
            return 3 + i;
    return -1;
}

/** Unmaps an OS file descriptor from VLC Lua */
static void vlclua_fd_unmap( lua_State *L, unsigned idx )
{
132
    vlclua_dtable_t *dt = vlclua_get_dtable( L );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
133
    int fd;
134
135
136
137
138

    if( idx < 3u )
        return; /* Never close stdin/stdout/stderr. */

    idx -= 3;
139
    if( idx >= dt->fdc )
140
        return;
141

142
    fd = dt->fdv[idx];
143
144
145
    dt->fdv[idx] = -1;
    while( dt->fdc > 0 && dt->fdv[dt->fdc - 1] == -1 )
        dt->fdc--;
146
    /* realloc() not really needed */
147
#ifndef NDEBUG
148
149
    for( unsigned i = 0; i < dt->fdc; i++ )
        assert( dt->fdv[i] != fd );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
150
151
#else
    (void) fd;
152
153
154
155
156
157
158
159
160
161
162
163
#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 );
}

164
165
166
167
168
/*****************************************************************************
 * Net listen
 *****************************************************************************/
static int vlclua_net_listen_close( lua_State * );
static int vlclua_net_accept( lua_State * );
169
static int vlclua_net_fds( lua_State * );
170
171
172

static const luaL_Reg vlclua_net_listen_reg[] = {
    { "accept", vlclua_net_accept },
173
    { "fds", vlclua_net_fds },
174
175
176
177
    { NULL, NULL }
};

static int vlclua_net_listen_tcp( lua_State *L )
178
179
180
{
    vlc_object_t *p_this = vlclua_get_this( L );
    const char *psz_host = luaL_checkstring( L, 1 );
181
    int i_port = luaL_checkinteger( L, 2 );
182
183
184
    int *pi_fd = net_ListenTCP( p_this, psz_host, i_port );
    if( pi_fd == NULL )
        return luaL_error( L, "Cannot listen on %s:%d", psz_host, i_port );
185

186
187
188
189
190
191
192
193
194
195
    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 );
        }

196
197
198
199
200
201
202
203
204
205
206
207
208
    int **ppi_fd = lua_newuserdata( L, sizeof( int * ) );
    *ppi_fd = pi_fd;

    if( luaL_newmetatable( L, "net_listen" ) )
    {
        lua_newtable( L );
        luaL_register( L, NULL, vlclua_net_listen_reg );
        lua_setfield( L, -2, "__index" );
        lua_pushcfunction( L, vlclua_net_listen_close );
        lua_setfield( L, -2, "__gc" );
    }

    lua_setmetatable( L, -2 );
209
210
211
    return 1;
}

212
static int vlclua_net_listen_close( lua_State *L )
213
{
214
    int **ppi_fd = (int**)luaL_checkudata( L, 1, "net_listen" );
215
216
217
218
219
220
    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 );
221
222
223
    return 0;
}

224
225
226
227
228
229
230
static int vlclua_net_fds( lua_State *L )
{
    int **ppi_fd = (int**)luaL_checkudata( L, 1, "net_listen" );
    int *pi_fd = *ppi_fd;

    int i_count = 0;
    while( pi_fd[i_count] != -1 )
231
        lua_pushinteger( L, vlclua_fd_get_lua( L, pi_fd[i_count++] ) );
232
233
234
235

    return i_count;
}

236
static int vlclua_net_accept( lua_State *L )
237
238
{
    vlc_object_t *p_this = vlclua_get_this( L );
239
    int **ppi_fd = (int**)luaL_checkudata( L, 1, "net_listen" );
240
    int i_fd = net_Accept( p_this, *ppi_fd );
241

242
    lua_pushinteger( L, vlclua_fd_map_safe( L, i_fd ) );
243
244
245
    return 1;
}

246
247
248
/*****************************************************************************
 *
 *****************************************************************************/
jetru's avatar
jetru committed
249
250
251
252
static int vlclua_net_connect_tcp( lua_State *L )
{
    vlc_object_t *p_this = vlclua_get_this( L );
    const char *psz_host = luaL_checkstring( L, 1 );
253
    int i_port = luaL_checkinteger( L, 2 );
254
    int i_fd = net_ConnectTCP( p_this, psz_host, i_port );
255
    lua_pushinteger( L, vlclua_fd_map_safe( L, i_fd ) );
jetru's avatar
jetru committed
256
257
258
    return 1;
}

259
static int vlclua_net_close( lua_State *L )
260
{
261
    int i_fd = luaL_checkinteger( L, 1 );
262
    vlclua_fd_unmap_safe( L, i_fd );
263
264
265
    return 0;
}

266
static int vlclua_net_send( lua_State *L )
267
{
268
    int fd = vlclua_fd_get( L, luaL_checkinteger( L, 1 ) );
269
270
    size_t i_len;
    const char *psz_buffer = luaL_checklstring( L, 2, &i_len );
271

272
    i_len = (size_t)luaL_optinteger( L, 3, i_len );
273
    lua_pushinteger( L,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
274
        (fd != -1) ? vlc_send( fd, psz_buffer, i_len, 0 ) : -1 );
275
276
277
    return 1;
}

278
static int vlclua_net_recv( lua_State *L )
279
{
280
    int fd = vlclua_fd_get( L, luaL_checkinteger( L, 1 ) );
281
    size_t i_len = (size_t)luaL_optinteger( L, 2, 1 );
282
    char psz_buffer[i_len];
283
284

    ssize_t i_ret = (fd != -1) ? recv( fd, psz_buffer, i_len, 0 ) : -1;
285
286
287
288
    if( i_ret > 0 )
        lua_pushlstring( L, psz_buffer, i_ret );
    else
        lua_pushnil( L );
289
    return 1;
290
291
}

292
293
294
/*****************************************************************************
 *
 *****************************************************************************/
295
296
297
298
299
/* Takes a { fd : events } table as first arg and modifies it to { fd : revents } */
static int vlclua_net_poll( lua_State *L )
{
    luaL_checktype( L, 1, LUA_TTABLE );

300
    int i_fds = 0;
301
302
303
304
305
306
    lua_pushnil( L );
    while( lua_next( L, 1 ) )
    {
        i_fds++;
        lua_pop( L, 1 );
    }
307
308

    struct pollfd *p_fds = xmalloc( i_fds * sizeof( *p_fds ) );
309
    int *luafds = xmalloc( i_fds * sizeof( *luafds ) );
310

311
    lua_pushnil( L );
312
    for( int i = 0; lua_next( L, 1 ); i++ )
313
    {
314
        luafds[i] = luaL_checkinteger( L, -2 );
315
        p_fds[i].fd = vlclua_fd_get( L, luafds[i] );
316
        p_fds[i].events = luaL_checkinteger( L, -1 );
317
        p_fds[i].events &= POLLIN | POLLOUT | POLLPRI;
318
319
320
        lua_pop( L, 1 );
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
321
    vlc_interrupt_t *oint = vlclua_set_interrupt( L );
322
    int ret = 1, val = -1;
323

324
    do
325
326
327
328
329
330
331
332
    {
        if( vlc_killed() )
            break;
        val = vlc_poll_i11e( p_fds, i_fds, -1 );
    }
    while( val == -1 && errno == EINTR );

    vlc_interrupt_set( oint );
333

334
    for( int i = 0; i < i_fds; i++ )
335
    {
336
        lua_pushinteger( L, luafds[i] );
337
        lua_pushinteger( L, (val >= 0) ? p_fds[i].revents : 0 );
338
339
        lua_settable( L, 1 );
    }
340
    lua_pushinteger( L, val );
341

ivoire's avatar
ivoire committed
342
    free( luafds );
343
    free( p_fds );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
344
345
346

    if( val == -1 )
        return luaL_error( L, "Interrupted." );
347
    return ret;
348
349
}

350
351
352
/*****************************************************************************
 *
 *****************************************************************************/
353
/*
354
static int vlclua_fd_open( lua_State *L )
355
356
357
358
{
}
*/

359
#ifndef _WIN32
360
static int vlclua_fd_write( lua_State *L )
361
{
362
    int fd = vlclua_fd_get( L, luaL_checkinteger( L, 1 ) );
363
364
    size_t i_len;
    const char *psz_buffer = luaL_checklstring( L, 2, &i_len );
365

366
    i_len = (size_t)luaL_optinteger( L, 3, i_len );
367
    lua_pushinteger( L, (fd != -1) ? vlc_write( fd, psz_buffer, i_len ) : -1 );
368
369
370
    return 1;
}

371
static int vlclua_fd_read( lua_State *L )
372
{
373
    int fd = vlclua_fd_get( L, luaL_checkinteger( L, 1 ) );
374
    size_t i_len = (size_t)luaL_optinteger( L, 2, 1 );
375
    char psz_buffer[i_len];
376
377

    ssize_t i_ret = (fd != -1) ? read( fd, psz_buffer, i_len ) : -1;
378
379
380
381
    if( i_ret > 0 )
        lua_pushlstring( L, psz_buffer, i_ret );
    else
        lua_pushnil( L );
382
383
    return 1;
}
384
#endif
385

386
387
388
389
/*****************************************************************************
 *
 *****************************************************************************/
static int vlclua_stat( lua_State *L )
390
391
392
{
    const char *psz_path = luaL_checkstring( L, 1 );
    struct stat s;
393
    if( vlc_stat( psz_path, &s ) )
394
395
396
397
        return 0;
        //return luaL_error( L, "Couldn't stat %s.", psz_path );
    lua_newtable( L );
    if( S_ISREG( s.st_mode ) )
398
        lua_pushliteral( L, "file" );
399
    else if( S_ISDIR( s.st_mode ) )
400
        lua_pushliteral( L, "dir" );
401
402
#ifdef S_ISCHR
    else if( S_ISCHR( s.st_mode ) )
403
        lua_pushliteral( L, "character device" );
404
405
406
#endif
#ifdef S_ISBLK
    else if( S_ISBLK( s.st_mode ) )
407
        lua_pushliteral( L, "block device" );
408
409
410
#endif
#ifdef S_ISFIFO
    else if( S_ISFIFO( s.st_mode ) )
411
        lua_pushliteral( L, "fifo" );
412
413
414
#endif
#ifdef S_ISLNK
    else if( S_ISLNK( s.st_mode ) )
415
        lua_pushliteral( L, "symbolic link" );
416
417
418
#endif
#ifdef S_ISSOCK
    else if( S_ISSOCK( s.st_mode ) )
419
        lua_pushliteral( L, "socket" );
420
421
#endif
    else
422
        lua_pushliteral( L, "unknown" );
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
    lua_setfield( L, -2, "type" );
    lua_pushinteger( L, s.st_mode );
    lua_setfield( L, -2, "mode" );
    lua_pushinteger( L, s.st_uid );
    lua_setfield( L, -2, "uid" );
    lua_pushinteger( L, s.st_gid );
    lua_setfield( L, -2, "gid" );
    lua_pushinteger( L, s.st_size );
    lua_setfield( L, -2, "size" );
    lua_pushinteger( L, s.st_atime );
    lua_setfield( L, -2, "access_time" );
    lua_pushinteger( L, s.st_mtime );
    lua_setfield( L, -2, "modification_time" );
    lua_pushinteger( L, s.st_ctime );
    lua_setfield( L, -2, "creation_time" );
    return 1;
}

441
static int vlclua_opendir( lua_State *L )
442
443
444
445
{
    const char *psz_dir = luaL_checkstring( L, 1 );
    DIR *p_dir;
    int i = 0;
446

447
    if( ( p_dir = vlc_opendir( psz_dir ) ) == NULL )
448
449
450
451
452
        return luaL_error( L, "cannot open directory `%s'.", psz_dir );

    lua_newtable( L );
    for( ;; )
    {
453
        const char *psz_filename = vlc_readdir( p_dir );
454
455
456
457
458
459
460
461
        if( !psz_filename ) break;
        i++;
        lua_pushstring( L, psz_filename );
        lua_rawseti( L, -2, i );
    }
    closedir( p_dir );
    return 1;
}
462
463
464
465

/*****************************************************************************
 *
 *****************************************************************************/
466
static const luaL_Reg vlclua_net_intf_reg[] = {
467
    { "listen_tcp", vlclua_net_listen_tcp },
jetru's avatar
jetru committed
468
    { "connect_tcp", vlclua_net_connect_tcp },
469
470
471
    { "close", vlclua_net_close },
    { "send", vlclua_net_send },
    { "recv", vlclua_net_recv },
472
    { "poll", vlclua_net_poll },
473
#ifndef _WIN32
474
475
    { "read", vlclua_fd_read },
    { "write", vlclua_fd_write },
476
#endif
477
478
    /* The following functions do not depend on intf_thread_t and do not really
     * belong in net.* but are left here for backward compatibility: */
479
    { "url_parse", vlclua_url_parse /* deprecated since 3.0.0 */ },
480
481
482
483
484
    { "stat", vlclua_stat }, /* Not really "net" */
    { "opendir", vlclua_opendir }, /* Not really "net" */
    { NULL, NULL }
};

485
static void luaopen_net_intf( lua_State *L )
486
487
{
    lua_newtable( L );
488
    luaL_register( L, NULL, vlclua_net_intf_reg );
489
490
491
492
493
494
495
496
497
#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 )
498
499
    lua_setfield( L, -2, "net" );
}
500

501
502
int vlclua_fd_init( lua_State *L, vlclua_dtable_t *dt )
{
503
504
    dt->interrupt = vlc_interrupt_create();
    if( unlikely(dt->interrupt == NULL) )
505
506
507
508
509
510
511
512
513
514
        return -1;
    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 )
{
515
    vlc_interrupt_kill( dt->interrupt );
516
517
518
519
520
521
}

/** Releases all (leaked) VLC Lua file descriptors. */
void vlclua_fd_cleanup( vlclua_dtable_t *dt )
{
    for( unsigned i = 0; i < dt->fdc; i++ )
522
523
        if( dt->fdv[i] != -1 )
            net_Close( dt->fdv[i] );
524
    free( dt->fdv );
525
    vlc_interrupt_destroy(dt->interrupt);
526
}