MythTV Script to allow multiple Telnet connections
Sat Jan 12, 2008 11:41:08 PM by Travis
So, my roomate Brock has a MythTV box set up in our apartment, which we use as a free, open-source alternative to Tivo and other commercial DVR products.
One of the nifty things that myth provides is a Telnet interface for controlling the system: just telnet into a certain port, and you can enter in commands to control the system remotely. Works great if you don't have a remote control for the computer that's running myth. Brock and I both have GUI programs we can run on our laptops to provide a nice remote-control-esque interface to the Telnet connection (he uses one he found for Mac, I use one I developed in C# (but that's another blog post)).
It's a great setup, except for one problem: only one user can be connected to myth's telnet interface at a time. So, if Brock is using his remote control program, and I use mine, it automatically disconnects Brock. It's a stupid limitation, in my opinion, but that's the way it is.
Earlier this evening, Brock suggested a way to get around that problem: set up a simple telnet proxy server, which allows multiple clients to connect to it, and forwards all commands over a single connection to the myth telnet interface. Since I was in a mood to code, I decided I'd see if I could write up a simple Ruby program to do just that.
An hour and a half later, I had it working. Well, kinda - it's a little rough, and it doesn't forward the server's replies back to the clients, but it's enough to have multiple remotes simultaneously connected. I've pasted the code at the bottom of this post, as it's only about 90 lines. I'm really rather amazed that it was so easy, largely in part to the excellent gserver library, which may be my new favorite standard library class ever.
Edit 1/17: Updated the code to a cleaner, slightly more concise version.
I love ruby.
#!/usr/bin/ruby require "gserver" require "net/telnet" require "thread" class TelnetProxy < GServer def initialize(*args) super(*args) @@total_num_clients = 0 @cmds = Queue.new end def start(*args) start_myth_connection super(*args) end def serve(io) @@total_num_clients += 1 my_client_id = @@total_num_clients puts("New Client #{my_client_id} detected") # send the initial login msg welcome_msg = "MythFrontend Network Control\nType 'help' for usage information---------------------------------\n# " io.write(welcome_msg) loop do puts "Client #{my_client_id} is still alive" # every 2 seconds check for newly received commands if IO.select([io], nil, nil, 2) # retrieve the data cmd = io.gets puts "Client #{my_client_id} got cmd '#{cmd}'" # if the command was "exit", then disconnect the user, but not the main connection if cmd =~ /exit/ puts "Closing client #{my_client_id} connection..." break end # forward the command to myth @cmds << cmd puts "Client #{my_client_id} placed cmd on queue" # send the response to the user response = "OK\n# " io.write(response) end end end private def start_myth_connection @myth_thread = Thread.new do loop do myth = Net::Telnet::new("Host" => "myth", "Port" => 6545, "Timeout" => 10, "Prompt" => /# /) puts "Connected to myth" cmds_to_run = [] until myth.closed? # get command from the shared buffer (sleeps if none available) cmd = @cmds.pop # send the commands to myth puts "Sending command to myth: #{cmd}" myth.cmd(cmd) puts "Cmd sent" end # until myth.closed? puts "Lost connection to myth" end # loop do end end end proxy = TelnetProxy.new(6546, "myth") proxy.start proxy.join
Comments
Add a Comment
Preview