32

Is it possible to run a ruby application as a Windows Service? I see that there is a related question which discusses running a Java Application as a Windows Service, how can you do this with a Ruby application?

Community
  • 1
  • 1
Kyle Burton
  • 26,788
  • 9
  • 50
  • 60

5 Answers5

25

Check out the following library: Win32Utils. You can create a simple service that you can start/stop/restart at your leisure. I'm currently using it to manage a Mongrel instance for a Windows hosted Rails app and it works flawlessly.

user229044
  • 232,980
  • 40
  • 330
  • 338
mwilliams
  • 9,946
  • 13
  • 50
  • 71
  • 1
    In regards to my comment, I stand corrected, I used a special Mongrel service gem and installed it like so: mongrel_rails service::install -N c:\web\AppName -p 4002 -e production But you should be able to accomplish what you need to with the above links. – mwilliams Oct 02 '08 at 17:25
  • For your second link 'Running asa a Windows Service' the site Insitiki is reporting the following 'This web is password-protected. Please enter the password.'. Do you have access to share or another way we can view this content? – Evolve Dec 14 '12 at 00:51
16

When trying the Win32Utils one really need to studie the doc and look over the net before finding some simple working example. This seems to work today 2008-10-02:

gem install win32-service

Update 2012-11-20: According to https://stackoverflow.com/users/1374569/paul the register_bar.rb should now be

Service.create( :service_name => 'some_service',
                :host => nil,
                :service_type       => Service::WIN32_OWN_PROCESS,
                :description        => 'A custom service I wrote just for fun',
                :start_type         => Service::AUTO_START,
                :error_control      => Service::ERROR_NORMAL,
                :binary_path_name   => 'c:\usr\ruby\bin\rubyw.exe -C c:\tmp\ bar.rb',
               :load_order_group   => 'Network',
               :dependencies       => ['W32Time','Schedule'],
               :display_name       => 'This is some service'       )

bar.rb

create the application/daemon

LOG_FILE = 'C:\\test.log'

begin
  require "rubygems"
  require 'win32/daemon'

  include Win32

  class DemoDaemon < Daemon

    def service_main
      while running?
      sleep 10
      File.open("c:\\test.log", "a"){ |f| f.puts "Service is running #{Time.now}" } 
    end
  end 

    def service_stop
      File.open("c:\\test.log", "a"){ |f| f.puts "***Service stopped #{Time.now}" }
      exit! 
    end
  end

  DemoDaemon.mainloop
rescue Exception => err
  File.open(LOG_FILE,'a+'){ |f| f.puts " ***Daemon failure #{Time.now} err=#{err} " }
  raise
end 

bar.rb is the service but we must create and register first! this can be done with sc create some_service

but if we are going to use ruby and win32utils we should do a

register_bar.rb

 require "rubygems"
require "win32/service"
   include Win32



   # Create a new service
   Service.create('some_service', nil,
      :service_type       => Service::WIN32_OWN_PROCESS,
      :description        => 'A custom service I wrote just for fun',
      :start_type         => Service::AUTO_START,
      :error_control      => Service::ERROR_NORMAL,
      :binary_path_name   => 'c:\usr\ruby\bin\rubyw.exe -C c:\tmp\ bar.rb',
      :load_order_group   => 'Network',
      :dependencies       => ['W32Time','Schedule'],
     
      :display_name       => 'This is some service'
   )

Note, there is a space between c:\tmp\ bar.rb in 'c:\usr\ruby\bin\rubyw.exe -C c:\tmp\ bar.rb'

Run ruby register_bar.rb and now one can start the service either from the windows service control panel or

sc start some_service

and watch c:test.log be filled with Service is running Thu Oct 02 22:06:47 +0200 2008

For the simple of have something to work with it is easier to remove the service register and create a new one instead of modifying a existing one

unregister_bar.rb

 require "rubygems"
    require "win32/service"
       include Win32
    
    Service.delete("some_service")

Credits to the people http://rubypane.blogspot.com/2008/05/windows-service-using-win32-service-and_29.html

http://rubyforge.org/docman/view.php/85/595/service.html

Community
  • 1
  • 1
Jonke
  • 6,525
  • 2
  • 25
  • 40
  • 1
    This doesn't work as written - I think it's based on an old version of win32/daemon. Try the example files here instead: http://rubyforge.org/frs/download.php/30036/win32-service-0.6.1.zip – Dave Nolan Mar 18 '09 at 11:02
  • This, as said, worked at the time recorded in the example. The win32 seems to change freq and I don't really feel the need to update this example every time. – Jonke Mar 19 '09 at 15:54
  • 1
    No problem with the donwvote but i think I would have prefered an aswer with your link, or really good, an update strip down example here, based on that link. – Jonke Mar 23 '09 at 07:43
  • 1
    This is a very nice and short tutorial on how to run Ruby service on Window, I will try it out later. It looks much easier than doing it with Python. Thanks for sharing it :) – Helen Neely Sep 16 '09 at 08:19
  • @Jonke In your first example, if you place the `begin` after the class definition, directly before the `DemoDaemon.mainloop`, would the script still operate the same? – Paul Hoffer Mar 22 '11 at 05:14
  • @phoffer: It was a long time since I wrote the example but I think a put everything in a begin rescue because that way I could track some really hard to understand errors that expose themself when you actually run the service as the local service account, there were some differences between XP, win 2003 server, win 2008 server, vista and differences between the gem of win32 versions. – Jonke Mar 22 '11 at 08:43
4

Here is a code template to do firedeamon :)

#####################################################################
#  runneur.rb :  service which run (continuously) a process
#                   'do only one simple thing, but do it well'
#####################################################################
# Usage:
#   .... duplicate this file : it will be the core-service....
#   .... modify constantes in beginning of this script....
#   .... modify stop_sub_process() at end  of this script for clean stop of sub-application..
#
#   > ruby runneur.rb install   foo     ; foo==name of service, 
#   > ruby runneur.rb uninstall foo
#   > type d:\deamon.log"       ; runneur traces
#   > type d:\d.log             ; service traces
#
#####################################################################
class String; def to_dos() self.tr('/','\\') end end
class String; def from_dos() self.tr('\\','/') end end

rubyexe="d:/usr/Ruby/ruby19/bin/rubyw.exe".to_dos

# example with spawn of a ruby process...

SERVICE_SCRIPT="D:/usr/Ruby/local/text.rb"
SERVICE_DIR="D:/usr/Ruby/local".to_dos
SERVICE_LOG="d:/d.log".to_dos           # log of stdout/stderr of sub-process
RUNNEUR_LOG="d:/deamon.log"             # log of runneur

LCMD=[rubyexe,SERVICE_SCRIPT]   # service will do system('ruby text.rb')
SLEEP_INTER_RUN=4               # at each dead of sub-process, wait n seconds before rerun

################### Installation / Desintallation ###################
if ARGV[0]
    require 'win32/service'
    include Win32

    name= ""+(ARGV[1] || $0.split('.')[0])
    if ARGV[0]=="install"
        path = "#{File.dirname(File.expand_path($0))}/#{$0}".tr('/', '\\')
        cmd = rubyexe + " " + path
        print "Service #{name} installed with\n cmd=#{cmd} ? " ; rep=$stdin.gets.chomp
        exit! if rep !~ /[yo]/i

        Service.new(
         :service_name     => name,
         :display_name     => name,
         :description      => "Run of #{File.basename(SERVICE_SCRIPT.from_dos)} at #{SERVICE_DIR}",
         :binary_path_name => cmd,
         :start_type       => Service::AUTO_START,
         :service_type     => Service::WIN32_OWN_PROCESS | Service::INTERACTIVE_PROCESS
        )
        puts "Service #{name} installed"
        Service.start(name, nil)
        sleep(3)
        while Service.status(name).current_state != 'running'
            puts 'One moment...' + Service.status(name).current_state
            sleep 1
        end
        while Service.status(name).current_state != 'running'
            puts ' One moment...' + Service.status(name).current_state
            sleep 1
        end
        puts 'Service ' + name+ ' started'      
    elsif ARGV[0]=="desinstall" || ARGV[0]=="uninstall"
        if Service.status(name).current_state != 'stopped'
            Service.stop(name)
            while Service.status(name).current_state != 'stopped'
                puts 'One moment...' + Service.status(name).current_state
                sleep 1
            end
        end
        Service.delete(name)
        puts "Service #{name} stopped and uninstalled"

    else
        puts "Usage:\n > ruby #{$0} install|desinstall [service-name]"
    end 
    exit!
end

#################################################################
#  service runneur : service code 
#################################################################
require 'win32/daemon'
include Win32

Thread.abort_on_exception=true
class Daemon
    def initialize
        @state='stopped'
        super
        log("******************** Runneur #{File.basename(SERVICE_SCRIPT)} Service start ***********************")
    end
    def log(*t)
        txt= block_given?()  ? (yield() rescue '?') : t.join(" ")
        File.open(RUNNEUR_LOG, "a"){ |f| f.puts "%26s | %s" % [Time.now,txt] } rescue nil
    end
    def service_pause
        #put activity in pause
        @state='pause'
        stop_sub_process
        log { "service is paused" }
    end
    def service_resume
        #quit activity from pause
        @state='run'
        log { "service is resumes" }
    end
    def service_interrogate
        # respond to quistion status
        log { "service is interogate" }
    end
    def service_shutdown 
        # stop activities before shutdown
        log { "service is stoped for shutdown" }
    end

    def service_init
        log { "service is starting" }
    end
    def service_main
        @state='run'
        while running?
        begin
            if @state=='run'
                log { "starting subprocess #{LCMD.join(' ')} in #{SERVICE_DIR}" }
                @pid=::Process.spawn(*LCMD,{
                    chdir: SERVICE_DIR, 
                    out: SERVICE_LOG, err: :out
                }) 
                log { "sub-process is running : #{@pid}" }
                a=::Process.waitpid(@pid)
                @pid=nil
                log { "sub-process is dead (#{a.inspect})" }
                sleep(SLEEP_INTER_RUN) if @state=='run'
            else
                sleep 3
                log { "service is sleeping" } if @state!='run'
            end
        rescue Exception => e
            log { e.to_s + " " + e.backtrace.join("\n   ")}
            sleep 4
        end
        end
    end

    def service_stop
     @state='stopped'
     stop_sub_process
     log { "service is stoped" }
     exit!
    end
    def stop_sub_process
        ::Process.kill("KILL",@pid) if @pid
        @pid=nil
    end
end

Daemon.mainloop
raubarede
  • 423
  • 3
  • 6
0

You can write (or download) a wrapper service. The wrapper can call the ruby.exe to execute your program. Same trick works for Java, VB, etc.

Ken
  • 2,092
  • 1
  • 19
  • 16
0

You should be able to accomplish this in IronRuby since you would have the .NET framework behind you.

GEOCHET
  • 21,119
  • 15
  • 74
  • 98