Search code examples
rubyinputbackground-process

Ruby: How to pass data to a program running in background


Although Ruby is my language of choice right now, I don't mind so much if anyone answers this with examples in another language, as I am more interested in the concept.

If I have a script running In the background; how would I program that script so that I could pass data input to it whilst it is still running?

If I were running it in a terminal In the foreground, the solution would be as simple as creating a Thread, and killing that Thread upon STDIN at which point a seperate function would invoke to handle the User Input. However considering that this script is running in the background, that method is not practical. I assume that arguments are the best way to interface with the program (although I could be wrong on that).

Of course there are plenty of methods to pass data to the background program, such as linking it to a file, which I then write data too and the program then reads. However I want this data to be inputted strictly through the terminal.

To elaborate: There is an Operating System process which is perpetually running in the background, however I can pass data to it to alter its function by running a command such as: program_name -newdata, and I am trying to program the same functionality into one of my own scripts.

Thanks in advance.


Solution

  • You can use message passing by using unix sockets. You can do it in one executable file, but for simplicity I do it in 2 separate.

    In the program_name_background file you can kick of a UNIXServer that is listening for unix sockets and parsing messages (and run whatever you need in a different thread):

    #!/usr/bin/ruby
    
    require 'socket'
    require 'json'
    require 'FileUtils'
    
    socket_path = "/tmp/program_name_background.sock"
    
    at_exit { FileUtils.rm socket_path }
    
    server = UNIXServer.new socket_path
    
    loop do
      client = server.accept
      message = client.read
      json_message = JSON.parse message, symbolize_names: true
      p json_message
    
      break if json_message[:type] == "SIGTERM"
    end
    

    In the program_name_control file you can read attributes from the command line and send message to the background process:

    #!/usr/bin/ruby
    
    require 'socket'
    require 'json'
    
    case ARGV[0]
    when "-newdata"
      message = {
        type: :newdata,
        data: ARGV[1]
      }
    when "stop"
      message = { type: :SIGTERM }
    else
      p "Error: unknown param: #{ARGV[0]}"
      exit
    end
    
    client = UNIXSocket.open "/tmp/program_name_background.sock"
    client.print JSON.generate(message)
    client.close
    

    For parsing options you can use the built-in OptionParser

    You can test it like:

    • Start the background process: ./program_name_background
    • Send a message from a different tab: ./program_name_control -newdata 118

    You can also set up symlinks to those files from the /usr/local/bin folder so you can call them from anywhere without the leading ./

    You can daemonizing the background process so it wont be attached to the terminal.