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>
31 #include <vlc_queue.h>
36 /*****************************************************************************
38 *****************************************************************************/
39 static void *Run( void * );
40 static int DoSearch( services_discovery_t
*p_sd
, const char *psz_query
);
41 static int FillDescriptor( services_discovery_t
*, services_discovery_descriptor_t
* );
42 static int Control( services_discovery_t
*p_sd
, int i_command
, va_list args
);
44 // When successful, the returned string is stored on top of the lua
45 // stack and remains valid as long as it is kept in the stack.
46 static const char *vlclua_sd_description( vlc_object_t
*obj
, lua_State
*L
,
47 const char *filename
)
49 lua_getglobal( L
, "descriptor" );
50 if( !lua_isfunction( L
, -1 ) )
52 msg_Warn( obj
, "No 'descriptor' function in '%s'", filename
);
57 if( lua_pcall( L
, 0, 1, 0 ) )
59 msg_Warn( obj
, "Error while running script %s, "
60 "function descriptor(): %s", filename
,
61 lua_tostring( L
, -1 ) );
66 lua_getfield( L
, -1, "title" );
67 if ( !lua_isstring( L
, -1 ) )
69 msg_Warn( obj
, "'descriptor' function in '%s' returned no title",
75 return lua_tostring( L
, -1 );
78 int vlclua_probe_sd( vlc_object_t
*obj
, const char *name
)
80 vlc_probe_t
*probe
= (vlc_probe_t
*)obj
;
82 char *filename
= vlclua_find_file( "sd", name
);
83 if( filename
== NULL
)
85 // File suddenly disappeared - maybe a race condition, no problem
86 msg_Err( probe
, "Couldn't probe lua services discovery script \"%s\".",
88 return VLC_PROBE_CONTINUE
;
91 lua_State
*L
= luaL_newstate();
94 msg_Err( probe
, "Could not create new Lua State" );
99 if( vlclua_add_modules_path( L
, filename
) )
101 msg_Err( probe
, "Error while setting the module search path for %s",
107 if( vlclua_dofile( obj
, L
, filename
) )
109 msg_Err( probe
, "Error loading script %s: %s", filename
,
110 lua_tostring( L
, -1 ) );
113 return VLC_PROBE_CONTINUE
;
115 const char *description
= vlclua_sd_description( obj
, L
, filename
);
116 if( description
== NULL
)
120 char *name_esc
= config_StringEscape( name
);
122 if( asprintf( &chain
, "lua{sd='%s'}", name_esc
) != -1 )
124 r
= vlc_sd_probe_Add( probe
, chain
, description
, SD_CAT_INTERNET
);
134 static const char * const ppsz_sd_options
[] = { "sd", NULL
};
136 /*****************************************************************************
138 *****************************************************************************/
147 } services_discovery_sys_t
;
148 static const luaL_Reg p_reg
[] = { { NULL
, NULL
} };
152 struct sd_query
*next
;
156 /*****************************************************************************
157 * Open: initialize and create stuff
158 *****************************************************************************/
159 int Open_LuaSD( vlc_object_t
*p_this
)
161 if( lua_Disabled( p_this
) )
164 services_discovery_t
*p_sd
= ( services_discovery_t
* )p_this
;
165 services_discovery_sys_t
*p_sys
;
169 if( !( p_sys
= malloc( sizeof( services_discovery_sys_t
) ) ) )
172 if( !strcmp( p_sd
->psz_name
, "lua" ) ||
173 !strcmp( p_sd
->psz_name
, "luasd" ) )
175 // We want to load the module name "lua"
176 // This module can be used to load lua script not registered
177 // as builtin lua SD modules.
178 config_ChainParse( p_sd
, "lua-", ppsz_sd_options
, p_sd
->p_cfg
);
179 psz_name
= var_GetString( p_sd
, "lua-sd" );
183 // We are loading a builtin lua sd module.
184 psz_name
= strdup(p_sd
->psz_name
);
188 p_sd
->pf_control
= Control
;
189 p_sys
->psz_filename
= vlclua_find_file( "sd", psz_name
);
190 if( !p_sys
->psz_filename
)
192 msg_Err( p_sd
, "Couldn't find lua services discovery script \"%s\".",
201 msg_Err( p_sd
, "Could not create new Lua State" );
204 vlclua_set_this( L
, p_sd
);
206 luaL_register_namespace( L
, "vlc", p_reg
);
211 luaopen_strings( L
);
212 luaopen_variables( L
);
214 luaopen_gettext( L
);
218 if( vlclua_add_modules_path( L
, p_sys
->psz_filename
) )
220 msg_Warn( p_sd
, "Error while setting the module search path for %s",
221 p_sys
->psz_filename
);
224 if( vlclua_dofile( VLC_OBJECT(p_sd
), L
, p_sys
->psz_filename
) )
226 msg_Err( p_sd
, "Error loading script %s: %s", p_sys
->psz_filename
,
227 lua_tostring( L
, lua_gettop( L
) ) );
232 // No strdup(), just don't remove the string from the lua stack
233 p_sd
->description
= vlclua_sd_description( VLC_OBJECT(p_sd
), L
,
234 p_sys
->psz_filename
);
235 if( p_sd
->description
== NULL
)
236 p_sd
->description
= p_sd
->psz_name
;
240 vlc_queue_Init( &p_sys
->queue
, offsetof (struct sd_query
, next
) );
242 if( vlc_clone( &p_sys
->thread
, Run
, p_sd
, VLC_THREAD_PRIORITY_LOW
) )
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_queue_Kill( &p_sys
->queue
, &p_sys
->dead
);
265 vlc_join( p_sys
->thread
, NULL
);
266 free( p_sys
->psz_filename
);
267 lua_close( p_sys
->L
);
271 /*****************************************************************************
272 * Run: Thread entry-point
273 ****************************************************************************/
274 static void* Run( void *data
)
276 services_discovery_t
*p_sd
= ( services_discovery_t
* )data
;
277 services_discovery_sys_t
*p_sys
= p_sd
->p_sys
;
278 lua_State
*L
= p_sys
->L
;
280 lua_getglobal( L
, "main" );
281 if( !lua_isfunction( L
, lua_gettop( L
) ) || lua_pcall( L
, 0, 1, 0 ) )
283 msg_Err( p_sd
, "Error while running script %s, "
284 "function main(): %s", p_sys
->psz_filename
,
285 lua_tostring( L
, lua_gettop( L
) ) );
289 msg_Dbg( p_sd
, "LuaSD script loaded: %s", p_sys
->psz_filename
);
291 /* Force garbage collection, because the core will keep the SD
292 * open, but lua will never gc until lua_close(). */
293 lua_gc( L
, LUA_GCCOLLECT
, 0 );
295 /* Main loop to handle search requests */
296 struct sd_query
*query
;
298 while( (query
= vlc_queue_DequeueKillable( &p_sys
->queue
,
299 &p_sys
->dead
)) != NULL
)
301 /* Execute one query */
302 DoSearch( p_sd
, query
->query
);
304 /* Force garbage collection, because the core will keep the SD
305 * open, but lua will never gc until lua_close(). */
306 lua_gc( L
, LUA_GCCOLLECT
, 0 );
311 /*****************************************************************************
312 * Control: services discrovery control
313 ****************************************************************************/
314 static int Control( services_discovery_t
*p_sd
, int i_command
, va_list args
)
316 services_discovery_sys_t
*p_sys
= p_sd
->p_sys
;
322 const char *psz_query
= va_arg( args
, const char * );
323 size_t len
= strlen(psz_query
) + 1;
324 struct sd_query
*query
= malloc(sizeof (*query
) + len
);
326 if (unlikely(query
== NULL
))
330 memcpy(query
->query
, psz_query
, len
);
331 vlc_queue_Enqueue(&p_sys
->queue
, query
);
335 case SD_CMD_DESCRIPTOR
:
337 services_discovery_descriptor_t
*p_desc
= va_arg( args
,
338 services_discovery_descriptor_t
* );
339 return FillDescriptor( p_sd
, p_desc
);
346 /*****************************************************************************
347 * DoSearch: search for a given query
348 ****************************************************************************/
349 static int DoSearch( services_discovery_t
*p_sd
, const char *psz_query
)
351 services_discovery_sys_t
*p_sys
= p_sd
->p_sys
;
352 lua_State
*L
= p_sys
->L
;
354 /* Lookup for the 'search' function */
355 lua_getglobal( L
, "search" );
356 if( !lua_isfunction( L
, lua_gettop( L
) ) )
358 msg_Err( p_sd
, "The script '%s' does not define any 'search' function",
359 p_sys
->psz_filename
);
365 lua_pushstring( L
, psz_query
);
367 /* Call the 'search' function */
368 if( lua_pcall( L
, 1, 0, 0 ) )
370 msg_Err( p_sd
, "Error while running the script '%s': %s",
371 p_sys
->psz_filename
, lua_tostring( L
, lua_gettop( L
) ) );
379 /** List of capabilities */
380 static const char *const ppsz_capabilities
[] = {
385 /*****************************************************************************
386 * FillDescriptor: call the descriptor function and fill the structure
387 ****************************************************************************/
388 static int FillDescriptor( services_discovery_t
*p_sd
,
389 services_discovery_descriptor_t
*p_desc
)
391 services_discovery_sys_t
*p_sys
= p_sd
->p_sys
;
392 int i_ret
= VLC_EGENERIC
;
394 /* Create a new lua thread */
395 lua_State
*L
= luaL_newstate();
397 if( vlclua_dofile( VLC_OBJECT(p_sd
), L
, p_sys
->psz_filename
) )
399 msg_Err( p_sd
, "Error loading script %s: %s", p_sys
->psz_filename
,
400 lua_tostring( L
, -1 ) );
404 /* Call the "descriptor" function */
405 lua_getglobal( L
, "descriptor" );
406 if( !lua_isfunction( L
, -1 ) || lua_pcall( L
, 0, 1, 0 ) )
408 msg_Warn( p_sd
, "Error getting the descriptor in '%s': %s",
409 p_sys
->psz_filename
, lua_tostring( L
, -1 ) );
413 /* Get the different fields of the returned table */
414 lua_getfield( L
, -1, "short_description" );
415 p_desc
->psz_short_desc
= luaL_strdupornull( L
, -1 );
418 lua_getfield( L
, -1, "icon" );
419 p_desc
->psz_icon_url
= luaL_strdupornull( L
, -1 );
422 lua_getfield( L
, -1, "url" );
423 p_desc
->psz_url
= luaL_strdupornull( L
, -1 );
426 lua_getfield( L
, -1, "capabilities" );
427 p_desc
->i_capabilities
= 0;
428 if( lua_istable( L
, -1 ) )
430 /* List all table entries */
432 while( lua_next( L
, -2 ) != 0 )
434 /* Key is at index -2 and value at index -1 */
435 const char *psz_cap
= luaL_checkstring( L
, -1 );
437 const char *psz_iter
;
438 for( psz_iter
= *ppsz_capabilities
; psz_iter
;
439 psz_iter
= ppsz_capabilities
[ ++i_cap
] )
441 if( !strcmp( psz_iter
, psz_cap
) )
443 p_desc
->i_capabilities
|= 1 << i_cap
;
451 msg_Warn( p_sd
, "Services discovery capability '%s' unknown in "
452 "script '%s'", psz_cap
, p_sys
->psz_filename
);