1 --[==========================================================================[
2 host.lua: VLC Lua interface command line host module
3 --[==========================================================================[
4 Copyright (C) 2007 the VideoLAN team
7 Authors: Antoine Cellerier <dionoea 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 --]==========================================================================]
24 --[==========================================================================[
30 -- Bypass any authentification
31 function on_password( client )
32 client:switch_status( host.status.read )
34 h.status_callbacks[host.status.password] = on_password
36 h:listen( "localhost:4212" )
37 h:listen( "*console" )
38 --or h:listen( { "localhost:4212", "*console" } )
41 while not vlc.should_die() do
42 -- accept new connections
45 -- select active clients
46 local write, read = h:select( 0.1 ) -- 0.1 is a timeout in seconds
48 -- handle clients in write mode
49 for _, client in pairs(write) do
52 client:switch_status( host.status.read )
55 -- handle clients in read mode
56 for _, client in pairs(read) do
57 local str = client:recv(1000)
58 str = string.gsub(str,"\r?\n$","")
59 client.buffer = "Got `"..str.."'.\r\n"
60 client:switch_status( host.status.write )
64 For complete examples see existing VLC Lua interface modules (ie telnet.lua)
65 --]==========================================================================]
67 module("host",package.seeall)
69 status = { init = 0, read = 1, write = 2, password = 3 }
70 client_type = { net = 1, stdio = 2, fifo = 3 }
76 local status_callbacks = {}
79 local function new_fd_set()
81 return function(fd,...) return foo(fd.fds,...) end
84 fds = vlc.fd.new_fd_set(),
86 zero = foo_fds( vlc.fd.fd_zero ),
87 set = foo_fds( vlc.fd.fd_set ),
88 isset = foo_fds( vlc.fd.fd_isset ),
89 clr = foo_fds( vlc.fd.fd_clr ),
94 local fds_read = new_fd_set()
95 local fds_write = new_fd_set()
98 local function client_accept( clients, listen )
100 if #clients == 0 then
105 return vlc.net.accept( listen, wait )
108 local function fd_client( client )
109 if client.status == status.read then
116 local function send( client, data, len )
118 return vlc.net.send( client.wfd, data, len )
120 return vlc.net.send( client.wfd, data or client.buffer )
124 local function recv( client, len )
126 return vlc.net.recv( client.rfd, len )
128 return vlc.net.recv( client.rfd )
132 local function write( client, data )
133 return vlc.fd.write( client.wfd, data or client.buffer )
136 local function read( client, len )
138 return vlc.fd.read( client.rfd, len )
140 return vlc.fd.read( client.rfd )
144 local function del_client( client )
145 if client.type == client_type.stdio then
146 client:send( "Cannot delete stdin/stdout client.\n" )
149 for i, c in pairs(clients) do
151 if client.type == client_type.net then
152 if client.wfd ~= client.rfd then
153 vlc.net.close( client.rfd )
155 vlc.net.close( client.wfd )
161 vlc.msg.err("couldn't find client to remove.")
164 local function switch_status( client, s )
165 if client.status == s then return end
167 if status_callbacks[s] then
168 status_callbacks[s]( client )
172 -- append a line to a client's (output) buffer
173 local function append( client, string )
174 client.buffer = client.buffer .. string .. "\r\n"
177 local function new_client( h, fd, wfd, t )
178 if fd < 0 then return end
180 if t == client_type.net then
183 else if t == client_type.stdio or t == client_type.fifo then
187 error("Unknown client type", t )
189 local client = { -- data
192 status = status.init,
200 switch_status = switch_status,
203 client:send( "VLC media player "..vlc.version().."\n" )
204 table.insert(clients, client)
205 client:switch_status(status.password)
208 function filter_client( fd, status, status2 )
211 for _, client in pairs(clients) do
212 if client.status == status or client.status == status2 then
213 fd:set( client:fd() )
214 l = math.max( l, client:fd() )
221 local function _listen_tcp( h, host, port )
222 if listeners.tcp and listeners.tcp[host]
223 and listeners.tcp[host][port] then
224 error("Already listening on tcp host `"..host..":"..tostring(port).."'")
226 if not listeners.tcp then
229 if not listeners.tcp[host] then
230 listeners.tcp[host] = {}
232 local listener = vlc.net.listen_tcp( host, port )
233 listeners.tcp[host][port] = listener
234 if not listeners.tcp.list then
235 -- FIXME: if host == "list" we'll have a problem
236 listeners.tcp.list = {}
237 local m = { __mode = "v" } -- week values
238 setmetatable( listeners.tcp.list, m )
240 table.insert( listeners.tcp.list, listener )
243 local function _listen_stdio( h )
245 if listeners.stdio then
246 error("Already listening on stdio")
248 new_client( h, 0, 1, client_type.stdio )
249 listeners.stdio = true
252 local function _listen( h, url )
253 if type(url)==type({}) then
254 for _,u in pairs(url) do
258 vlc.msg.info( "Listening on host \""..url.."\"." )
259 if url == "*console" then
262 u = vlc.net.url_parse( url )
263 h:listen_tcp( u.host, u.port )
268 local function _accept( h )
269 if listeners.tcp then
271 if #clients == 0 and not listeners.stdio and #listeners.tcp.list == 1 then
272 wait = -1 -- blocking
276 for _, listener in pairs(listeners.tcp.list) do
277 local fd = vlc.net.accept( listener, wait )
278 new_client( h, fd, fd, client_type.net )
283 local function _select( h, timeout )
284 local nfds = math.max( filter_client( fds_read, status.read, status.password ),
285 filter_client( fds_write, status.write ) ) + 1
286 local ret = vlc.net.select( nfds, fds_read.fds, fds_write.fds,
291 for _, client in pairs(clients) do
292 if fds_write:isset( client:fd() ) then
293 table.insert(wclients,client)
295 if fds_read:isset( client:fd() ) then
296 table.insert(rclients,client)
300 return wclients, rclients
303 local function destructor( h )
305 for _,client in pairs(clients) do
306 client:send("Shutting down.")
307 if client.type == client_type.tcp then
308 if client.wfd ~= client.rfd then
309 vlc.net.close(client.rfd)
311 vlc.net.close(client.wfd)
315 if listeners.tcp then
316 for _, listener in pairs(listeners.tcp.list) do
317 vlc.net.listen_close( listener )
322 local function _broadcast( h, msg )
323 for _,client in pairs(clients) do
330 status_callbacks = status_callbacks,
333 listen_tcp = _listen_tcp,
334 listen_stdio = _listen_stdio,
337 broadcast = _broadcast,
342 __metatable = "Nothing to see here. Move along.",