Well that was easier than I expected
* daemon.rb now defaults to logging in the Event Log and optionally to the
windows.log file
* The ImagePath string now looks like;
"C:\Program Files (x86)\Puppet Labs\Puppet
Enterprise\sys\ruby\bin\ruby.exe" -rubygems -C"C:\Program Files
(x86)\Puppet Labs\Puppet Enterprise\service" "C:\Program Files (x86)\Puppet
Labs\Puppet Enterprise\service\daemon.rb"
I'm not sure if the "-rubygems" is required but it is in the daemon.bat
file.
--
You received this message because you are subscribed to the Google Groups
"Puppet Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/puppet-users/8239aa60-ad19-4b42-9bd7-fed09229f55b%40googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
#!/usr/bin/env ruby
require 'fileutils'
require 'win32/daemon'
require 'win32/dir'
require 'win32/process'
require 'win32/eventlog'
require 'windows/synchronize'
require 'windows/handle'
class WindowsDaemon < Win32::Daemon
include Windows::Synchronize
include Windows::Handle
include Windows::Process
@LOG_TO_FILE = false
LOG_FILE = File.expand_path(File.join(Dir::COMMON_APPDATA, 'PuppetLabs',
'puppet', 'var', 'log', 'windows.log'))
LEVELS = [:debug, :info, :notice, :err]
LEVELS.each do |level|
define_method("log_#{level}") do |msg|
log(msg, level)
end
end
def service_init
# Anything in here needs to happen VERY quickly as this service will not
send a SERVICE_STARTED event until this function has completed. Typically this
needs to be less than two seconds
end
def service_main(*argv)
args = argv.join(' ')
@LOG_TO_FILE = (argv.index('--logtofile') ? true : false)
@loglevel = LEVELS.index(argv.index('--debug') ? :debug : :notice)
if (@LOG_TO_FILE)
FileUtils.mkdir_p(File.dirname(LOG_FILE))
end
puppetpid = -1
basedir = File.expand_path(File.join(File.dirname(__FILE__), '..'))
puppet = File.join(basedir, 'bin', 'puppet.bat')
# Puppet itself does register this event source as well, but it may well
happen that this service may have different message types some day
eventlogdll = File.expand_path(File.join(basedir, 'puppet', 'ext',
'windows', 'eventlog', 'puppetres.dll'))
if (File.exists?(eventlogdll))
Win32::EventLog.add_event_source(
'source' => "Application",
'key_name' => "Puppet Agent",
'category_count' => 3,
'event_message_file' => eventlogdll,
'category_message_file' => eventlogdll
)
end
# Logging can now occur as all event sinks have been configured.
log_notice("Starting service: #{args}")
while running? do
return if !running?
log_notice('Service is running')
unless File.exists?(puppet)
log_err("File not found: '#{puppet}'")
return
end
return if !running?
log_debug("Using '#{puppet}'")
begin
runinterval = %x{ "#{puppet}" agent --configprint runinterval }.to_i
if runinterval == 0
runinterval = 1800
log_err("Failed to determine runinterval, defaulting to
#{runinterval} seconds")
end
rescue Exception => e
log_exception(e)
runinterval = 1800
end
if (state == RUNNING || state == IDLE)
puppetpid = Process.create(:command_line => "\"#{puppet}\" agent
--onetime #{args}").process_id
log_debug("Process created: #{puppetpid}")
else
log_debug("Service is not in a state to start Puppet")
end
log_debug("Service waiting for #{runinterval} seconds")
sleep(runinterval)
log_debug('Service woken up')
end
# TODO: Check if puppetpid is still running. If so raise a warning in the
eventlog and log. Do I let the Puppet run continue or kill the process?
# If you kill the process, it will only kill the CMD.EXE, not the child
RUBY process.
# Use Win32::Process.kill(0,puppetpid) to see if it's alive
log_notice('Service stopped')
rescue Exception => e
log_exception(e)
end
def service_stop
log_notice('Service stopping')
Thread.main.wakeup
end
def service_pause
# I don't know why it does, but the service state eventually comes out of
Paused and goes into Running
# I suspect this is more of a Ruby Win32Daemon issue than this script.
#
# Yep, confirmed:
# From the Win32 Services Gem; daemon.c
# ...Program Files (x86)\Puppet Labs\Puppet
Enterprise\sys\ruby\lib\ruby\gems\1.9.1\gems\win32-service-0.7.2-x86-mingw32\ext\win32\daemon.c
# Line 240: // Set the status of the service.
# Line 241: SetTheServiceStatus(dwState, NO_ERROR, 0, 0);
#
# The preceding switch statement sets the dwState to RUNNING when a
SERVICE_INTERROGATE event occurs, which is about every 60 seconds and then
tells the SCM that this service is RUNNING
# This is a fairly old version of the Win32 Daemon. v0.8.2 has been
released but it looks like it has the same logic flow (Lines 107 to 132)
# I need to raise a bug report in the Win32 Daemon Gem for this.
log_notice('Service pausing. The service will not stay paused and will
eventually go back into a running state.')
end
def service_resume
log_notice('Service resuming')
end
def service_shutdown
log_notice('Host is shutting down')
end
# Interrogation handler is just for debug. Can be commented out or removed
entirely.
def service_interrogate
log_debug('Service is being intertogated')
end
def log_exception(e)
log_err(e.message)
log_err(e.backtrace.join("\n"))
end
def log(msg, level)
if LEVELS.index(level) >= @loglevel
if (@LOG_TO_FILE)
File.open(LOG_FILE, 'a') { |f| f.puts("#{Time.now} Puppet (#{level}):
#{msg}") }
end
case level
when :debug
raise_windows_event(Win32::EventLog::INFO,0x01,msg.to_s)
when :info
raise_windows_event(Win32::EventLog::INFO,0x01,msg.to_s)
when :notice
raise_windows_event(Win32::EventLog::INFO,0x01,msg.to_s)
when :err
raise_windows_event(Win32::EventLog::ERR,0x03,msg.to_s)
else
raise_windows_event(Win32::EventLog::WARN,0x02,msg.to_s)
end
end
end
def raise_windows_event(type,id,message)
begin
eventlog = Win32::EventLog.open("Application")
eventlog.report_event(
:source => "Puppet Agent",
:event_type => type, # Win32::EventLog::INFO or WARN, ERROR
:event_id => id, # 0x01 or 0x02, 0x03 etc.
:data => message # "the message"
)
eventlog.close
rescue Exception => e
# Ignore all errors
end
end
end
if __FILE__ == $0
WindowsDaemon.mainloop
end