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 authentication
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.misc.should_die() do
42 -- accept new connections and select active clients
43 local write, read = h:accept_and_select()
45 -- handle clients in write mode
46 for _, client in pairs(write) do
49 client:switch_status( host.status.read )
52 -- handle clients in read mode
53 for _, client in pairs(read) do
54 local str = client:recv(1000)
55 str = string.gsub(str,"\r?\n$","")
56 client.buffer = "Got `"..str.."'.\r\n"
57 client:switch_status( host.status.write )
61 For complete examples see existing VLC Lua interface modules (ie telnet.lua)
62 --]==========================================================================]
64 module("host",package.seeall)
66 status = { init = 0, read = 1, write = 2, password = 3 }
67 client_type = { net = 1, stdio = 2, fifo = 3 }
69 function is_flag_set(val, flag)
70 return (((val - (val % flag)) / flag) % 2 ~= 0)
77 local status_callbacks = {}
80 local function fd_client( client )
81 if client.status == status.read then
88 local function send( client, data, len )
90 return vlc.net.send( client.wfd, data, len )
92 return vlc.net.send( client.wfd, data or client.buffer )
96 local function recv( client, len )
98 return vlc.net.recv( client.rfd, len )
100 return vlc.net.recv( client.rfd )
104 local function write( client, data )
105 return vlc.net.write( client.wfd, data or client.buffer )
108 local function read( client, len )
110 return vlc.net.read( client.rfd, len )
112 return vlc.net.read( client.rfd )
116 local function del_client( client )
117 if client.type == client_type.stdio then
118 client:send( "Cannot delete stdin/stdout client.\n" )
121 for i, c in pairs(clients) do
123 if client.type == client_type.net then
124 if client.wfd ~= client.rfd then
125 vlc.net.close( client.rfd )
127 vlc.net.close( client.wfd )
133 vlc.msg.err("couldn't find client to remove.")
136 local function switch_status( client, s )
137 if client.status == s then return end
139 if status_callbacks[s] then
140 status_callbacks[s]( client )
144 -- append a line to a client's (output) buffer
145 local function append( client, string )
146 client.buffer = client.buffer .. string .. "\r\n"
149 local function new_client( h, fd, wfd, t )
150 if fd < 0 then return end
152 if t == client_type.net then
155 else if t == client_type.stdio or t == client_type.fifo then
159 error("Unknown client type", t )
161 local client = { -- data
164 status = status.init,
173 switch_status = switch_status,
176 client:send( "VLC media player "..vlc.misc.version().."\n" )
177 table.insert(clients, client)
178 client:switch_status(status.password)
182 local function _listen_tcp( h, host, port )
183 if listeners.tcp and listeners.tcp[host]
184 and listeners.tcp[host][port] then
185 error("Already listening on tcp host `"..host..":"..tostring(port).."'")
187 if not listeners.tcp then
190 if not listeners.tcp[host] then
191 listeners.tcp[host] = {}
193 local listener = vlc.net.listen_tcp( host, port )
194 listeners.tcp[host][port] = listener
195 if not listeners.tcp.list then
196 -- FIXME: if host == "list" we'll have a problem
197 listeners.tcp.list = {}
198 local m = { __mode = "v" } -- week values
199 setmetatable( listeners.tcp.list, m )
201 table.insert( listeners.tcp.list, listener )
204 local function _listen_stdio( h )
206 if listeners.stdio then
207 error("Already listening on stdio")
209 new_client( h, 0, 1, client_type.stdio )
210 listeners.stdio = true
213 local function _listen( h, url )
214 if type(url)==type({}) then
215 for _,u in pairs(url) do
219 vlc.msg.info( "Listening on host \""..url.."\"." )
220 if url == "*console" then
223 u = vlc.net.url_parse( url )
224 h:listen_tcp( u.host, u.port )
229 local function _accept_and_select( h, timeout )
230 local function filter_client( fds, status, event )
231 for _, client in pairs(clients) do
232 if client.status == status then
233 fds[client:fd()] = event
239 filter_client( pollfds, status.read, vlc.net.POLLIN )
240 filter_client( pollfds, status.password, vlc.net.POLLIN )
241 filter_client( pollfds, status.write, vlc.net.POLLOUT )
242 if listeners.tcp then
243 for _, listener in pairs(listeners.tcp.list) do
244 for _, fd in pairs({listener:fds()}) do
245 pollfds[fd] = vlc.net.POLLIN
250 local ret = vlc.net.poll( pollfds )
254 for _, client in pairs(clients) do
255 if is_flag_set(pollfds[client:fd()], vlc.net.POLLERR)
256 or is_flag_set(pollfds[client:fd()], vlc.net.POLLHUP)
257 or is_flag_set(pollfds[client:fd()], vlc.net.POLLNVAL) then
259 elseif is_flag_set(pollfds[client:fd()], vlc.net.POLLOUT) then
260 table.insert(wclients,client)
261 elseif is_flag_set(pollfds[client:fd()], vlc.net.POLLIN) then
262 table.insert(rclients,client)
265 if listeners.tcp then
266 for _, listener in pairs(listeners.tcp.list) do
267 for _, fd in pairs({listener:fds()}) do
268 if is_flag_set(pollfds[fd], vlc.net.POLLIN) then
269 local afd = listener:accept()
270 new_client( h, afd, afd, client_type.net )
278 return wclients, rclients
281 local function destructor( h )
283 for _,client in pairs(clients) do
284 client:send("Shutting down.")
285 if client.type == client_type.tcp then
286 if client.wfd ~= client.rfd then
287 vlc.net.close(client.rfd)
289 vlc.net.close(client.wfd)
294 local function _broadcast( h, msg )
295 for _,client in pairs(clients) do
302 status_callbacks = status_callbacks,
305 listen_tcp = _listen_tcp,
306 listen_stdio = _listen_stdio,
307 accept_and_select = _accept_and_select,
308 broadcast = _broadcast,
313 __metatable = "Nothing to see here. Move along.",