1 /*****************************************************************************
2 * services_discovery.c : Services discovery using lua scripts
3 *****************************************************************************
4 * Copyright (C) 2010 VideoLAN and AUTHORS
6 * Authors: Fabio Ritrovato <sephiroth87 at videolan dot org>
7 * RĂ©mi Duraffort <ivoire at videolan -dot- org>
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.
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.
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 *****************************************************************************/
29 #include <vlc_common.h>
30 #include <vlc_services_discovery.h>
35 /*****************************************************************************
37 *****************************************************************************/
38 static void *Run( void * );
39 static int DoSearch( services_discovery_t
*p_sd
, const char *psz_query
);
40 static int FillDescriptor( services_discovery_t
*, services_discovery_descriptor_t
* );
41 static int Control( services_discovery_t
*p_sd
, int i_command
, va_list args
);
43 // When successful, the returned string is stored on top of the lua
44 // stack and remains valid as long as it is kept in the stack.
45 static const char *vlclua_sd_description( vlc_object_t
*obj
, lua_State
*L
,
46 const char *filename
)
48 lua_getglobal( L
, "descriptor" );
49 if( !lua_isfunction( L
, -1 ) )
51 msg_Warn( obj
, "No 'descriptor' function in '%s'", filename
);
56 if( lua_pcall( L
, 0, 1, 0 ) )
58 msg_Warn( obj
, "Error while running script %s, "
59 "function descriptor(): %s", filename
,
60 lua_tostring( L
, -1 ) );
65 lua_getfield( L
, -1, "title" );
66 if ( !lua_isstring( L
, -1 ) )
68 msg_Warn( obj
, "'descriptor' function in '%s' returned no title",
74 return lua_tostring( L
, -1 );
77 int vlclua_probe_sd( vlc_object_t
*obj
, const char *name
)
79 vlc_probe_t
*probe
= (vlc_probe_t
*)obj
;
81 char *filename
= vlclua_find_file( "sd", name
);
82 if( filename
== NULL
)
84 // File suddenly disappeared - maybe a race condition, no problem
85 msg_Err( probe
, "Couldn't probe lua services discovery script \"%s\".",
87 return VLC_PROBE_CONTINUE
;
90 lua_State
*L
= luaL_newstate();
93 msg_Err( probe
, "Could not create new Lua State" );
98 if( vlclua_add_modules_path( L
, filename
) )
100 msg_Err( probe
, "Error while setting the module search path for %s",
106 if( vlclua_dofile( obj
, L
, filename
) )
108 msg_Err( probe
, "Error loading script %s: %s", filename
,
109 lua_tostring( L
, -1 ) );
112 return VLC_PROBE_CONTINUE
;
114 const char *description
= vlclua_sd_description( obj
, L
, filename
);
115 if( description
== NULL
)
119 char *name_esc
= config_StringEscape( name
);
121 if( asprintf( &chain
, "lua{sd='%s'}", name_esc
) != -1 )
123 r
= vlc_sd_probe_Add( probe
, chain
, description
, SD_CAT_INTERNET
);
133 static const char * const ppsz_sd_options
[] = { "sd", NULL
};
135 /*****************************************************************************
137 *****************************************************************************/
138 struct services_discovery_sys_t
150 static const luaL_Reg p_reg
[] = { { NULL
, NULL
} };
152 /*****************************************************************************
153 * Open: initialize and create stuff
154 *****************************************************************************/
155 int Open_LuaSD( vlc_object_t
*p_this
)
157 if( lua_Disabled( p_this
) )
160 services_discovery_t
*p_sd
= ( services_discovery_t
* )p_this
;
161 services_discovery_sys_t
*p_sys
;
165 if( !( p_sys
= malloc( sizeof( services_discovery_sys_t
) ) ) )
168 if( !strcmp( p_sd
->psz_name
, "lua" ) ||
169 !strcmp( p_sd
->psz_name
, "luasd" ) )
171 // We want to load the module name "lua"
172 // This module can be used to load lua script not registered
173 // as builtin lua SD modules.
174 config_ChainParse( p_sd
, "lua-", ppsz_sd_options
, p_sd
->p_cfg
);
175 psz_name
= var_GetString( p_sd
, "lua-sd" );
179 // We are loading a builtin lua sd module.
180 psz_name
= strdup(p_sd
->psz_name
);
184 p_sd
->pf_control
= Control
;
185 p_sys
->psz_filename
= vlclua_find_file( "sd", psz_name
);
186 if( !p_sys
->psz_filename
)
188 msg_Err( p_sd
, "Couldn't find lua services discovery script \"%s\".",
197 msg_Err( p_sd
, "Could not create new Lua State" );
200 vlclua_set_this( L
, p_sd
);
202 luaL_register_namespace( L
, "vlc", p_reg
);
207 luaopen_strings( L
);
208 luaopen_variables( L
);
210 luaopen_gettext( L
);
214 if( vlclua_add_modules_path( L
, p_sys
->psz_filename
) )
216 msg_Warn( p_sd
, "Error while setting the module search path for %s",
217 p_sys
->psz_filename
);
220 if( vlclua_dofile( VLC_OBJECT(p_sd
), L
, p_sys
->psz_filename
) )
222 msg_Err( p_sd
, "Error loading script %s: %s", p_sys
->psz_filename
,
223 lua_tostring( L
, lua_gettop( L
) ) );
228 // No strdup(), just don't remove the string from the lua stack
229 p_sd
->description
= vlclua_sd_description( VLC_OBJECT(p_sd
), L
,
230 p_sys
->psz_filename
);
231 if( p_sd
->description
== NULL
)
232 p_sd
->description
= p_sd
->psz_name
;
235 vlc_mutex_init( &p_sys
->lock
);
236 vlc_cond_init( &p_sys
->cond
);
237 TAB_INIT( p_sys
->i_query
, p_sys
->ppsz_query
);
239 if( vlc_clone( &p_sys
->thread
, Run
, p_sd
, VLC_THREAD_PRIORITY_LOW
) )
241 TAB_CLEAN( p_sys
->i_query
, p_sys
->ppsz_query
);
242 vlc_cond_destroy( &p_sys
->cond
);
243 vlc_mutex_destroy( &p_sys
->lock
);
251 free( p_sys
->psz_filename
);
256 /*****************************************************************************
258 *****************************************************************************/
259 void Close_LuaSD( vlc_object_t
*p_this
)
261 services_discovery_t
*p_sd
= ( services_discovery_t
* )p_this
;
262 services_discovery_sys_t
*p_sys
= p_sd
->p_sys
;
264 vlc_cancel( p_sys
->thread
);
265 vlc_join( p_sys
->thread
, NULL
);
267 for( int i
= 0; i
< p_sys
->i_query
; i
++ )
268 free( p_sys
->ppsz_query
[i
] );
269 TAB_CLEAN( p_sys
->i_query
, p_sys
->ppsz_query
);
271 vlc_cond_destroy( &p_sys
->cond
);
272 vlc_mutex_destroy( &p_sys
->lock
);
273 free( p_sys
->psz_filename
);
274 lua_close( p_sys
->L
);
278 /*****************************************************************************
279 * Run: Thread entry-point
280 ****************************************************************************/
281 static void* Run( void *data
)
283 services_discovery_t
*p_sd
= ( services_discovery_t
* )data
;
284 services_discovery_sys_t
*p_sys
= p_sd
->p_sys
;
285 lua_State
*L
= p_sys
->L
;
287 int cancel
= vlc_savecancel();
289 lua_getglobal( L
, "main" );
290 if( !lua_isfunction( L
, lua_gettop( L
) ) || lua_pcall( L
, 0, 1, 0 ) )
292 msg_Err( p_sd
, "Error while running script %s, "
293 "function main(): %s", p_sys
->psz_filename
,
294 lua_tostring( L
, lua_gettop( L
) ) );
296 vlc_restorecancel( cancel
);
299 msg_Dbg( p_sd
, "LuaSD script loaded: %s", p_sys
->psz_filename
);
301 /* Force garbage collection, because the core will keep the SD
302 * open, but lua will never gc until lua_close(). */
303 lua_gc( L
, LUA_GCCOLLECT
, 0 );
305 vlc_restorecancel( cancel
);
307 /* Main loop to handle search requests */
308 vlc_mutex_lock( &p_sys
->lock
);
309 mutex_cleanup_push( &p_sys
->lock
);
312 /* Wait for a request */
313 if( !p_sys
->i_query
)
315 vlc_cond_wait( &p_sys
->cond
, &p_sys
->lock
);
319 /* Execute one query (protected against cancellation) */
320 char *psz_query
= p_sys
->ppsz_query
[p_sys
->i_query
- 1];
321 TAB_ERASE(p_sys
->i_query
, p_sys
->ppsz_query
, p_sys
->i_query
- 1);
322 vlc_mutex_unlock( &p_sys
->lock
);
324 cancel
= vlc_savecancel();
325 DoSearch( p_sd
, psz_query
);
327 /* Force garbage collection, because the core will keep the SD
328 * open, but lua will never gc until lua_close(). */
329 lua_gc( L
, LUA_GCCOLLECT
, 0 );
330 vlc_restorecancel( cancel
);
332 vlc_mutex_lock( &p_sys
->lock
);
335 vlc_assert_unreachable();
338 /*****************************************************************************
339 * Control: services discrovery control
340 ****************************************************************************/
341 static int Control( services_discovery_t
*p_sd
, int i_command
, va_list args
)
343 services_discovery_sys_t
*p_sys
= p_sd
->p_sys
;
349 const char *psz_query
= va_arg( args
, const char * );
350 vlc_mutex_lock( &p_sys
->lock
);
351 TAB_APPEND( p_sys
->i_query
, p_sys
->ppsz_query
, strdup( psz_query
) );
352 vlc_cond_signal( &p_sys
->cond
);
353 vlc_mutex_unlock( &p_sys
->lock
);
357 case SD_CMD_DESCRIPTOR
:
359 services_discovery_descriptor_t
*p_desc
= va_arg( args
,
360 services_discovery_descriptor_t
* );
361 return FillDescriptor( p_sd
, p_desc
);
368 /*****************************************************************************
369 * DoSearch: search for a given query
370 ****************************************************************************/
371 static int DoSearch( services_discovery_t
*p_sd
, const char *psz_query
)
373 services_discovery_sys_t
*p_sys
= p_sd
->p_sys
;
374 lua_State
*L
= p_sys
->L
;
376 /* Lookup for the 'search' function */
377 lua_getglobal( L
, "search" );
378 if( !lua_isfunction( L
, lua_gettop( L
) ) )
380 msg_Err( p_sd
, "The script '%s' does not define any 'search' function",
381 p_sys
->psz_filename
);
387 lua_pushstring( L
, psz_query
);
389 /* Call the 'search' function */
390 if( lua_pcall( L
, 1, 0, 0 ) )
392 msg_Err( p_sd
, "Error while running the script '%s': %s",
393 p_sys
->psz_filename
, lua_tostring( L
, lua_gettop( L
) ) );
401 /** List of capabilities */
402 static const char *const ppsz_capabilities
[] = {
407 /*****************************************************************************
408 * FillDescriptor: call the descriptor function and fill the structure
409 ****************************************************************************/
410 static int FillDescriptor( services_discovery_t
*p_sd
,
411 services_discovery_descriptor_t
*p_desc
)
413 services_discovery_sys_t
*p_sys
= p_sd
->p_sys
;
414 int i_ret
= VLC_EGENERIC
;
416 /* Create a new lua thread */
417 lua_State
*L
= luaL_newstate();
419 if( vlclua_dofile( VLC_OBJECT(p_sd
), L
, p_sys
->psz_filename
) )
421 msg_Err( p_sd
, "Error loading script %s: %s", p_sys
->psz_filename
,
422 lua_tostring( L
, -1 ) );
426 /* Call the "descriptor" function */
427 lua_getglobal( L
, "descriptor" );
428 if( !lua_isfunction( L
, -1 ) || lua_pcall( L
, 0, 1, 0 ) )
430 msg_Warn( p_sd
, "Error getting the descriptor in '%s': %s",
431 p_sys
->psz_filename
, lua_tostring( L
, -1 ) );
435 /* Get the different fields of the returned table */
436 lua_getfield( L
, -1, "short_description" );
437 p_desc
->psz_short_desc
= luaL_strdupornull( L
, -1 );
440 lua_getfield( L
, -1, "icon" );
441 p_desc
->psz_icon_url
= luaL_strdupornull( L
, -1 );
444 lua_getfield( L
, -1, "url" );
445 p_desc
->psz_url
= luaL_strdupornull( L
, -1 );
448 lua_getfield( L
, -1, "capabilities" );
449 p_desc
->i_capabilities
= 0;
450 if( lua_istable( L
, -1 ) )
452 /* List all table entries */
454 while( lua_next( L
, -2 ) != 0 )
456 /* Key is at index -2 and value at index -1 */
457 const char *psz_cap
= luaL_checkstring( L
, -1 );
459 const char *psz_iter
;
460 for( psz_iter
= *ppsz_capabilities
; psz_iter
;
461 psz_iter
= ppsz_capabilities
[ ++i_cap
] )
463 if( !strcmp( psz_iter
, psz_cap
) )
465 p_desc
->i_capabilities
|= 1 << i_cap
;
473 msg_Warn( p_sd
, "Services discovery capability '%s' unknown in "
474 "script '%s'", psz_cap
, p_sys
->psz_filename
);