]> git.sesse.net Git - vlc/blob - modules/lua/intf.c
lua: raise an erro from net.poll() when quitting
[vlc] / modules / lua / intf.c
1 /*****************************************************************************
2  * intf.c: Generic lua interface 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 <sys/types.h>
32 #include <sys/stat.h>
33
34 #include <vlc_common.h>
35 #include <vlc_interface.h>
36 #include <vlc_fs.h>
37
38 #include "vlc.h"
39 #include "libs.h"
40
41 /*****************************************************************************
42  * Prototypes
43  *****************************************************************************/
44 static void *Run( void * );
45
46 static const char * const ppsz_intf_options[] = { "intf", "config", NULL };
47
48 /*****************************************************************************
49  *
50  *****************************************************************************/
51 static inline void luaL_register_submodule( lua_State *L, const char *psz_name,
52                                             const luaL_Reg *l )
53 {
54     lua_newtable( L );
55     luaL_register( L, NULL, l );
56     lua_setfield( L, -2, psz_name );
57 }
58
59 static char *MakeConfig( intf_thread_t *p_intf, const char *name )
60 {
61     char *psz_config = NULL;
62
63     if( !strcmp( name, "http" ) )
64     {
65         char *psz_http_src = var_InheritString( p_intf, "http-src" );
66         bool b_http_index = var_InheritBool( p_intf, "http-index" );
67         if( psz_http_src )
68         {
69             char *psz_esc = config_StringEscape( psz_http_src );
70
71             if( asprintf( &psz_config, "http={dir='%s',no_index=%s}", psz_esc,
72                           b_http_index ? "true" : "false" ) == -1 )
73                 psz_config = NULL;
74             free( psz_esc );
75             free( psz_http_src );
76         }
77         else
78         {
79             if( asprintf( &psz_config, "http={no_index=%s}",
80                           b_http_index ? "true" : "false" ) == -1 )
81                 psz_config = NULL;
82         }
83     }
84     else if( !strcmp( name, "telnet" ) )
85     {
86         char *psz_host = var_InheritString( p_intf, "telnet-host" );
87         if( !strcmp( psz_host, "*console" ) )
88             ;
89         else
90         {
91             vlc_url_t url;
92             vlc_UrlParse( &url, psz_host, 0 );
93             unsigned i_port = var_InheritInteger( p_intf, "telnet-port" );
94             if ( url.i_port != 0 )
95             {
96                 if ( i_port == TELNETPORT_DEFAULT )
97                     i_port = url.i_port;
98                 else if ( url.i_port != i_port )
99                     msg_Warn( p_intf, "ignoring port %d (using %d)",
100                               url.i_port, i_port );
101             }
102
103             char *psz_esc_host = config_StringEscape( url.psz_host );
104             free( psz_host );
105             vlc_UrlClean( &url );
106
107             if( asprintf( &psz_host, "telnet://%s:%d",
108                           psz_esc_host ? psz_esc_host : "", i_port ) == -1 )
109                 psz_host = NULL;
110             free( psz_esc_host );
111         }
112
113         char *psz_passwd = var_InheritString( p_intf, "telnet-password" );
114
115         char *psz_esc_passwd = config_StringEscape( psz_passwd );
116
117         if( asprintf( &psz_config, "telnet={host='%s',password='%s'}",
118                       psz_host, psz_esc_passwd ) == -1 )
119             psz_config = NULL;
120
121         free( psz_esc_passwd );
122         free( psz_passwd );
123         free( psz_host );
124     }
125     else if( !strcmp( name, "cli" ) )
126     {
127         char *psz_rc_host = var_InheritString( p_intf, "rc-host" );
128         if( !psz_rc_host )
129             psz_rc_host = var_InheritString( p_intf, "cli-host" );
130         if( psz_rc_host )
131         {
132             char *psz_esc_host = config_StringEscape( psz_rc_host );
133
134             if( asprintf( &psz_config, "cli={host='%s'}", psz_esc_host ) == -1 )
135                 psz_config = NULL;
136             free( psz_esc_host );
137             free( psz_rc_host );
138         }
139     }
140
141     return psz_config;
142 }
143
144 static char *StripPasswords( const char *psz_config )
145 {
146     unsigned n = 0;
147     const char *p = psz_config;
148     while ((p = strstr(p, "password=")) != NULL)
149     {
150         n++;
151         p++;
152     }
153     if (n == 0)
154         return strdup(psz_config);
155  
156     char *psz_log = malloc(strlen(psz_config) + n * strlen("******") + 1);
157     if (psz_log == NULL)
158         return NULL;
159     psz_log[0] = '\0';
160
161     for (p = psz_config; ; )
162     {
163         const char *pwd = strstr(p, "password=");
164         if (pwd == NULL)
165         {
166             /* Copy the last, ending bit */
167             strcat(psz_log, p);
168             break;
169         }
170         pwd += strlen("password=");
171
172         char delim[3] = ",}";
173         if (*pwd == '\'' || *pwd == '"')
174         {
175             delim[0] = *pwd++;
176             delim[1] = '\0';
177         }
178
179         strncat(psz_log, p, pwd - p);
180         strcat(psz_log, "******");
181
182         /* Advance to the delimiter at the end of the password */
183         p = pwd - 1;
184         do
185         {
186             p = strpbrk(p + 1, delim);
187             if (p == NULL)
188                 /* Oops, unbalanced quotes or brackets */
189                 return psz_log;
190         }
191         while (*(p - 1) == '\\');
192     }
193     return psz_log;
194 }
195
196 static const luaL_Reg p_reg[] = { { NULL, NULL } };
197
198 static int Start_LuaIntf( vlc_object_t *p_this, const char *name )
199 {
200     intf_thread_t *p_intf = (intf_thread_t*)p_this;
201     intf_sys_t *p_sys;
202     lua_State *L;
203
204     config_ChainParse( p_intf, "lua-", ppsz_intf_options, p_intf->p_cfg );
205
206     if( name == NULL )
207     {
208         char *n = var_InheritString( p_this, "lua-intf" );
209         if( unlikely(n == NULL) )
210             return VLC_EGENERIC;
211         name = p_intf->psz_header = n;
212     }
213     else
214         /* Cleaned up by vlc_object_release() */
215         p_intf->psz_header = strdup( name );
216
217     p_intf->p_sys = (intf_sys_t*)malloc( sizeof(intf_sys_t) );
218     if( !p_intf->p_sys )
219     {
220         free( p_intf->psz_header );
221         p_intf->psz_header = NULL;
222         return VLC_ENOMEM;
223     }
224     p_sys = p_intf->p_sys;
225     p_sys->psz_filename = vlclua_find_file( "intf", name );
226     if( !p_sys->psz_filename )
227     {
228         msg_Err( p_intf, "Couldn't find lua interface script \"%s\".",
229                  name );
230         goto error;
231     }
232     msg_Dbg( p_intf, "Found lua interface script: %s", p_sys->psz_filename );
233
234     L = luaL_newstate();
235     if( !L )
236     {
237         msg_Err( p_intf, "Could not create new Lua State" );
238         goto error;
239     }
240
241     vlclua_set_this( L, p_intf );
242
243     luaL_openlibs( L );
244
245     /* register our functions */
246     luaL_register( L, "vlc", p_reg );
247
248     /* register submodules */
249     luaopen_config( L );
250     luaopen_volume( L );
251     luaopen_httpd( L );
252     luaopen_input( L );
253     luaopen_msg( L );
254     luaopen_misc( L );
255     luaopen_net( L );
256     luaopen_object( L );
257     luaopen_osd( L );
258     luaopen_playlist( L );
259     luaopen_sd( L );
260     luaopen_stream( L );
261     luaopen_strings( L );
262     luaopen_variables( L );
263     luaopen_video( L );
264     luaopen_vlm( L );
265     luaopen_volume( L );
266     luaopen_gettext( L );
267     luaopen_xml( L );
268     luaopen_equalizer( L );
269 #if defined(_WIN32) && !VLC_WINSTORE_APP
270     luaopen_win( L );
271 #endif
272
273     /* clean up */
274     lua_pop( L, 1 );
275
276     /* Setup the module search path */
277     if( vlclua_add_modules_path( L, p_sys->psz_filename ) )
278     {
279         msg_Warn( p_intf, "Error while setting the module search path for %s",
280                   p_sys->psz_filename );
281         lua_close( L );
282         goto error;
283     }
284
285     /*
286      * Get the lua-config string.
287      * If the string is empty, try with the old http-* or telnet-* options
288      * and build the right configuration line
289      */
290     bool b_config_set = false;
291     char *psz_config = var_InheritString( p_intf, "lua-config" );
292     if( !psz_config )
293         psz_config = MakeConfig( p_intf, name );
294
295     if( psz_config )
296     {
297         char *psz_buffer;
298         if( asprintf( &psz_buffer, "config={%s}", psz_config ) != -1 )
299         {
300             char *psz_log = StripPasswords( psz_buffer );
301             if( psz_log != NULL )
302             {
303                 msg_Dbg( p_intf, "Setting config variable: %s", psz_log );
304                 free( psz_log );
305             }
306
307             if( luaL_dostring( L, psz_buffer ) == 1 )
308                 msg_Err( p_intf, "Error while parsing \"lua-config\"." );
309             free( psz_buffer );
310             lua_getglobal( L, "config" );
311             if( lua_istable( L, -1 ) )
312             {
313                 if( !strcmp( name, "cli" ) )
314                 {
315                     lua_getfield( L, -1, "rc" );
316                     if( lua_istable( L, -1 ) )
317                     {
318                         /* msg_Warn( p_intf, "The `rc' lua interface script "
319                                           "was renamed `cli', please update "
320                                           "your configuration!" ); */
321                         lua_setfield( L, -2, "cli" );
322                     }
323                     else
324                         lua_pop( L, 1 );
325                 }
326                 lua_getfield( L, -1, name );
327                 if( lua_istable( L, -1 ) )
328                 {
329                     lua_setglobal( L, "config" );
330                     b_config_set = true;
331                 }
332             }
333         }
334         free( psz_config );
335     }
336
337     if( !b_config_set )
338     {
339         lua_newtable( L );
340         lua_setglobal( L, "config" );
341     }
342
343     /* Wrapper for legacy telnet config */
344     if ( !strcmp( name, "telnet" ) )
345     {
346         /* msg_Warn( p_intf, "The `telnet' lua interface script was replaced "
347                           "by `cli', please update your configuration!" ); */
348
349         char *wrapped_file = vlclua_find_file( "intf", "cli" );
350         if( !wrapped_file )
351         {
352             msg_Err( p_intf, "Couldn't find lua interface script \"cli\", "
353                              "needed by telnet wrapper" );
354             lua_close( p_sys->L );
355             goto error;
356         }
357         lua_pushstring( L, wrapped_file );
358         lua_setglobal( L, "wrapped_file" );
359         free( wrapped_file );
360     }
361
362     p_sys->L = L;
363
364     if( vlc_pipe( p_sys->fd ) )
365     {
366         lua_close( p_sys->L );
367         goto error;
368     }
369
370     if( vlc_clone( &p_sys->thread, Run, p_intf, VLC_THREAD_PRIORITY_LOW ) )
371     {
372         close( p_sys->fd[1] );
373         close( p_sys->fd[0] );
374         lua_close( p_sys->L );
375         goto error;
376     }
377
378     return VLC_SUCCESS;
379 error:
380     free( p_sys->psz_filename );
381     free( p_sys );
382     free( p_intf->psz_header );
383     p_intf->psz_header = NULL;
384     return VLC_EGENERIC;
385 }
386
387 void Close_LuaIntf( vlc_object_t *p_this )
388 {
389     intf_thread_t *p_intf = (intf_thread_t*)p_this;
390     intf_sys_t *p_sys = p_intf->p_sys;
391
392     close( p_sys->fd[1] );
393     vlc_join( p_sys->thread, NULL );
394
395     lua_close( p_sys->L );
396     close( p_sys->fd[0] );
397     free( p_sys->psz_filename );
398     free( p_sys );
399 }
400
401 static void *Run( void *data )
402 {
403     intf_thread_t *p_intf = data;
404     intf_sys_t *p_sys = p_intf->p_sys;
405     lua_State *L = p_sys->L;
406
407     if( luaL_dofile( L, p_sys->psz_filename ) )
408     {
409         msg_Err( p_intf, "Error loading script %s: %s", p_sys->psz_filename,
410                  lua_tostring( L, lua_gettop( L ) ) );
411         lua_pop( L, 1 );
412     }
413     return NULL;
414 }
415
416 int Open_LuaIntf( vlc_object_t *p_this )
417 {
418     return Start_LuaIntf( p_this, NULL );
419 }
420
421 int Open_LuaHTTP( vlc_object_t *p_this )
422 {
423     return Start_LuaIntf( p_this, "http" );
424 }
425
426 int Open_LuaCLI( vlc_object_t *p_this )
427 {
428     return Start_LuaIntf( p_this, "cli" );
429 }
430
431 int Open_LuaTelnet( vlc_object_t *p_this )
432 {
433     char *pw = var_CreateGetNonEmptyString( p_this, "telnet-password" );
434     if( pw == NULL )
435     {
436         msg_Err( p_this, "password not configured" );
437         msg_Info( p_this, "Please specify the password in the preferences." );
438         return VLC_EGENERIC;
439     }
440     free( pw );
441     return Start_LuaIntf( p_this, "telnet" );
442 }