[Backgroundrb-devel] "tailing" a running system command?

Charles Brian Quinn me at seebq.com
Wed Aug 16 17:19:49 EDT 2006

We're working on a super cool new tool to help automate capistrano and
rake deployment task running on remote servers, and we do something
similiar to this using backgrounDRb (thanks Ezra!).

It is based off our RailsDay 2006 entry:
http://heartbeat.highgroove.com/ -- enough with the bragging, here is
how we do it:

your worker now needs to have an @output class variable (instead of progress).

in your worker, you can run the task one of two ways which might or
might not work:

        # you can use Kernel.system to make the call which I don't
know how you get output
        if not Kernel.system("ping google)
          errors.add_to_base(" your error here ") # this is
technically in a model hence the errors obj

        # or:
        cmd_to_run = "ping google"

        stdin, stdout, stderr = Open3.popen3(cmd_to_run)

        s_stderr = stderr.read.to_s
        s_stdout = stdout.read.to_s

        # if you'd like to see it
        logger.info "stdout: #{s_stdout}"
        logger.info "stderr: #{s_stderr}"

        if s_stderr.empty? && s_stdout.starts_with?("error stuff here")
          return true
          errors.add_to_base("error msg here")
          return false

But, as for setting your @output, well, you'd need to run inside a
loop or perhaps even in another thread within the bg worker thread.
We don't need to do this because we're executing our task using ssh
and we have an open channel and just do @output << in the loop as it
runs.  You may not be able to use the popen3 calls above, to be honest
-- the read pretty much kills that idea. Our worker method that
appends output looks like:

    Net::SSH.start( host,
                    :username => username,
                    :password => password,
                    :port => port ) do |session|
      session.open_channel do |channel|

        channel.on_data do |ch, data|
          @output << "#{data}"

        channel.on_extended_data do |ch, type, data|
          @output << "#{data}"

        channel.exec( "ping google\n" )

Maybe you can find a simliar asynchronous call for executing methods,
that updates output as it runs.

Now, in your controller:

the method, let's say execute:

def execute
  session[:job_key] = Middleman....  # call your worker

and for the view we used rjs to update a page (a lightbox actually):

  <div id="task_output">
    Connecting to Server...


page << "showBG();"
page << "showIbox('#{url_for(:action => 'output', :id =>
page << "output = new
Ajax.PeriodicalUpdater('task_output','#{url_for(:action =>
'update_output', :id=> @task.id)}',2);"

back to your controller, it can be as simple as:

  def update_output
    @job = MiddleMan.get_worker(session[:job_key])
    render :inline => '<%= simple_format(@job.output) %>'

The hard part will be appending @output as it's running.  I know I
didn't answer the @output side, but hopefully helped with the
backgroundrb worker side.

cheers and good luck, let us know how it goes.

On 8/16/06, mattp at digimonkey.com <mattp at digimonkey.com> wrote:
> Dunno if anyone saw the short question at the end of my last thread,
> so i'm reposting in a thread with the appropriate subject:
> Here's what I need to do --
> Run a system-level command like 'ping -t www.google.com' and have it
> periodically update a <div> with the command's ouput.  Is this possible?
> (the trick here is that I want *periodic* updates of the div... i want
> to see incremental output as 'ping' is running, just like if I were
> looking at console...)
> _______________________________________________
> Backgroundrb-devel mailing list
> Backgroundrb-devel at rubyforge.org
> http://rubyforge.org/mailman/listinfo/backgroundrb-devel

Charles Brian Quinn
self-promotion: www.seebq.com
highgroove studios: www.highgroove.com
slingshot hosting: www.slingshothosting.com

More information about the Backgroundrb-devel mailing list