From: Jason Wold Date: 2004-04-25T04:38:25+09:00 Subject: Re: Playing with sockets... > > Very cool, Jason, thanks. > You might find this interesting too. That was actually part of my attempt to generalize IO for taking commands from multiple sources. It is still not generalized enough (if FIFO checks) but work is still in process. This code starts a thread listening on three different sources. It currently only works on *nix but I hope to generalize the FIFO listener to also work on windows: # start servers to listen for commands on FIFO, TCP PORT, and STDIO # FIXME make listen OS independant, use windows named pipe on windows def listen(*listeners) # if no arguments then listen on STDIN listeners = ["STDIO"] if listeners.size == 0 #capitalize list of listeners listeners.map!{ |l| l.upcase } mutex = Mutex.new #restart all threads if we aren't running the threads we think we are #FIXME #restart(@threads.keys) if @threads.values.sort != (Thread.list - [Thread.main]).sort # TODO turn mkfifo into OS independant method that returns correct IO object `mkfifo "#{FIFO_CONTROL}"` unless test(?p, FIFO_CONTROL) # set an IO object for each listener to be called later io = {} io["FIFO"] = Proc.new { File.open(FIFO_CONTROL, File::NONBLOCK | File::RDONLY ) } io["STDIO"] = Proc.new { IO.new(1) } io["TCP"] = Proc.new{ TCPServer.new('localhost', PORT).accept } writeable=%w(STDIO TCP) # thread to aggregate cmd_buffer and process it # Thread.new do puts "Started Command Buffer processing thread" if $DEBUG cmd="" # run until we get a quit command # while ! cmd.match(/^(quit|q)$/i) # join buffers and zero them out mutex.synchronize do VALID_LISTENERS.each do |vl| @cmd_buffer["MAIN"] += @cmd_buffer[vl] @cmd_buffer[vl] = [] end end # step through aggregate buffer, clean it, and run it while @cmd_buffer["MAIN"].size > 0 puts "CMD BUFFER: " + @cmd_buffer["MAIN"].join(":") if $DEBUG mutex.synchronize do cmd=@cmd_buffer["MAIN"].shift end # next if string is characters or numbers #FIXME (how do you internationalize untainting?) next if cmd.match(/[^A-Za-z0-9 ]/) cmd.untaint run_cmd(cmd) end sleep 1 end end listeners.sort.each do |listener| # next unless we have a handler for this type next unless VALID_LISTENERS.include?(listener) # do we already have a thread listening? unless @threads[listener].respond_to?("alive") && @threads[listener].alive? @threads[listener] = Thread.new do cmd = "" puts "Started #{listener} thread" if $DEBUG # call the proc object that creates the io object f = io[listener].call while ! cmd.match(/^quit|q/i) # Have to poll FIFO's and will get eof if nothing there (sleep(1);next) if listener == "FIFO" and f.eof # FIFO's are read only in server thread (f.print "HA>";f.flush) if writeable.include?(listener) cmd = f.readline.chomp next if cmd.empty? puts "Got command #{cmd} from #{listener}" if $DEBUG (f.close; next) if cmd.match(/^(quit|q)$/i) mutex.synchronize { @cmd_buffer[listener] << cmd } end end end end # prompt user or give focus to STDIO thread if it exists # if @threads["STDIO"].nil? puts "Listening on " + listeners.join(" and ") puts "Enter quit to exit." until gets.match(/^(quit|q)$/) end else @threads["STDIO"].join end end > > Cheers, > Hal > >