Showing posts with label services. Show all posts
Showing posts with label services. Show all posts

2008-06-27

Making the service a bit more robust

Windows Services series Update 2008-6-27
Having got the service up and running I wanted to be able to test the new service before running it as a service. I also needed to make it a bit more robust. This is a polled service, so every so often it will run and do something. I had my first version up and running it died but randomly after a few days (I guessed this was due to occasional problems in the guts of it eg FTP failures).
To be able to debug it easily I broke it into two parts. One file has all the guts of the service:

class ProcessFiles

def processOnce()
sleep(3)
end

end

This allows the code to be testing in a simple wrapper and run from the command line:

require 'process_files'

s = ProcessFiles.new()
while true
s.processOnce
puts "Slept for #{sleep 10} seconds"
end


Alternatively here is the a sample daemon code taken from the demo plus I added a timestamp and a comment on close:

LOG_FILE = 'C:\\test.log'
require 'process_files'


begin
require 'win32/daemon'

include Win32

class DemoDaemon < Daemon

def service_main
my_service
= ProcessFiles.new()
while running?
begin
my_service.processOnce
rescue Exception => err
File.open(LOG_FILE, "a"){ |f| f.puts "Service had a failure at #{Time.now} error=#{err} " }

sleep 10
end
sleep 3
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

2008-05-29

Windows Service using win32-service and win32/daemon

Windows Services series Update 2008-6-13
Rubyforge has got some good documentation on creating a daemon so I will start with that. The first thing to do is to get the demo code which doesn't come in the gem install of win32-services but can be got by downloading the zipped code.

Run through the demo (on a local drive not a networked share as the networked shares are not visible to services) and see how you go. I extracted a small version and installed with the manual service installer to make sure I could build a service.

Here is a sample daemon code taken from the demo plus I added a timestamp and a comment on close:
LOG_FILE = 'C:\\test.log'

begin
require 'win32/daemon'

include Win32

class DemoDaemon < Daemon

def service_main
while running?
sleep 3
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


So I copied the code to NetBeans and tried to run. I had installed win32-services (gem install win32-services), I was using Ruby 1.86 and not Jruby as the interpreter.

Make sure it has include Win32 (which is not in some early versions of the demo code) I get
daemon1.rb:28: undefined method 'mainloop' for #

With I get this due to trying to run the Daemon from the command line:
daemon1.rb:29:in `mainloop': Service_Main thread exited abnormally (Win32::Daemon::Error)

However if you create a service and then run it it will work see the following with sc.

C:\>sc create test1 binPath= "C:\ruby\bin\rubyw.exe -C c:\temp\testDaemon\lib\ main.rb" type= own start= auto
[SC] CreateService SUCCESS

C:\>sc start test1

SERVICE_NAME: test1
TYPE : 10 WIN32_OWN_PROCESS
STATE : 4 RUNNING
(STOPPABLE,PAUSABLE,ACCEPTS_SHUTDOWN)
WIN32_EXIT_CODE : 0 (0x0)
SERVICE_EXIT_CODE : 0 (0x0)
CHECKPOINT : 0x0
WAIT_HINT : 0x0
PID : 5972
FLAGS :
C:\>sc stop test1

SERVICE_NAME: test1
TYPE : 10 WIN32_OWN_PROCESS
STATE : 3 STOP_PENDING
(STOPPABLE,PAUSABLE,ACCEPTS_SHUTDOWN)
WIN32_EXIT_CODE : 0 (0x0)
SERVICE_EXIT_CODE : 0 (0x0)
CHECKPOINT : 0x0
WAIT_HINT : 0x0

C:\>

And looking at c:\Test.log you can see the results.

2008-05-28

Windows Service using SrvAny

Microsoft have a good page on debugging Troubleshooting SrvAny using Cmd.exe which I found helpful.
Here is someone who has done the same thing for WEBrick as a Windows Service. (ps the old method of installing parameters seems to work for srvany on XP).
You can write a very small program and make it run.

File.open("c:\\simpleDaemon.log", "a"){ |f| f.puts "Service started #{Time.now}" }
#while true #Would normally use an endless while loop so that service would not finish
(1..3).each {|i|
sleep 3
File.open("c:\\simpleDaemon.log", "a"){ |f| f.puts "Service ran at #{Time.now}" }
}
File.open("c:\\simpleDaemon.log", "a"){ |f| f.puts "Service closed #{Time.now}" }

This can be run happily from the commandline or NetBeans as normal.
So now you can create a service using the command line;
c:>sc create test1 binpath= "C:\Program Files\Windows Resource Kits\Tools\srvany.exe"
or
c:>"\Program Files\Windows Resource Kits\Tools\instsrv" test1 "C:\Program Files\Windows Resource Kits\Tools\srvany.exe"
instsrv results
Now you have to arrange the parameters using Regedit to get the program to run.
However using SrvAny has a number of problems. If the program stops working then you have no way of knowing what the problems was due to. Also the service itself still appears to be running. The solution to this is to use the Daemon class from win32.

Services on Windows series

There seem to be a number of different ways to create services on Windows using Ruby. I am going to try some of them out and see how well each one works. I was using Ruby 1.8.6.
There are two parts to this. The first is managing the service process : creating,stopping ,starting etc
The second part is packaging a Ruby script so that it can be run as that service.

The approaches I will write about are:
My plan is to summarise here the strengths and weaknesses and then to write another page for each one on exactly how to make each one work.