diff --git a/ext/project_data.yaml b/ext/project_data.yaml index 982c5b282..ea3e14037 100644 --- a/ext/project_data.yaml +++ b/ext/project_data.yaml @@ -1,56 +1,56 @@ --- project: 'puppet' author: 'Puppet Labs' email: 'info@puppetlabs.com' homepage: 'https://github.com/puppetlabs/puppet' summary: 'Puppet, an automated configuration management tool' description: 'Puppet, an automated configuration management tool' version_file: 'lib/puppet/version.rb' # files and gem_files are space separated lists files: '[A-Z]* install.rb bin lib conf man examples ext tasks spec' # The gem specification bits only work on Puppet >= 3.0rc, NOT 2.7.x and earlier gem_files: '[A-Z]* install.rb bin lib conf man examples ext tasks spec' gem_test_files: 'spec/**/*' gem_executables: 'puppet' gem_default_executables: 'puppet' gem_forge_project: 'puppet' gem_runtime_dependencies: facter: ['> 1.6', '< 3'] hiera: '~> 1.0' rgen: '~> 0.6.5' json_pure: gem_rdoc_options: - --title - "Puppet - Configuration Management" - --main - README.md - --line-numbers gem_platform_dependencies: x86-mingw32: gem_runtime_dependencies: # Pinning versions that require native extensions ffi: '1.9.3' win32-api: '1.4.8' - win32-dir: '~> 0.4.3' - win32-eventlog: '~> 0.5.3' - win32-process: '~> 0.6.5' + win32-dir: '~> 0.4.9' + win32-eventlog: '~> 0.6.1' + win32-process: '~> 0.7.4' win32-security: '~> 0.2.5' win32-service: '~> 0.8.4' win32-taskscheduler: '~> 0.2.2' win32console: '1.3.2' windows-api: '~> 0.4.2' windows-pr: '~> 1.2.2' minitar: '~> 0.5.4' x64-mingw32: gem_runtime_dependencies: ffi: '1.9.3' - win32-dir: '~> 0.4.8' + win32-dir: '~> 0.4.9' win32-eventlog: '~> 0.6.1' win32-process: '~> 0.7.4' win32-security: '~> 0.2.5' win32-service: '~> 0.8.4' win32-taskscheduler: '~> 0.3.0' minitar: '~> 0.5.4' bundle_platforms: x86-mingw32: mingw x64-mingw32: x64_mingw diff --git a/ext/windows/service/daemon.rb b/ext/windows/service/daemon.rb index 718afaf22..b9fca0652 100755 --- a/ext/windows/service/daemon.rb +++ b/ext/windows/service/daemon.rb @@ -1,164 +1,164 @@ #!/usr/bin/env ruby require 'fileutils' require 'win32/daemon' require 'win32/dir' require 'win32/process' require 'win32/eventlog' class WindowsDaemon < Win32::Daemon CREATE_NEW_CONSOLE = 0x00000010 @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 end def service_main(*argsv) argsv = (argsv << ARGV).flatten.compact args = argsv.join(' ') @loglevel = LEVELS.index(argsv.index('--debug') ? :debug : :notice) @LOG_TO_FILE = (argsv.index('--logtofile') ? true : false) if (@LOG_TO_FILE) FileUtils.mkdir_p(File.dirname(LOG_FILE)) args = args.gsub("--logtofile","") end basedir = File.expand_path(File.join(File.dirname(__FILE__), '..')) # The puppet installer registers a 'Puppet' event source. For the moment events will be logged with this key, but # it may be a good idea to split the Service and Puppet events later so it's easier to read in the windows Event Log. # # Example code to register an event source; # 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 puppet = File.join(basedir, 'bin', 'puppet.bat') unless File.exists?(puppet) log_err("File not found: '#{puppet}'") return end log_debug("Using '#{puppet}'") log_notice('Service started') while running? do 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 or state == IDLE log_notice("Executing agent with arguments: #{args}") pid = Process.create(:command_line => "\"#{puppet}\" agent --onetime #{args}", :creation_flags => CREATE_NEW_CONSOLE).process_id log_debug("Process created: #{pid}") else log_debug("Service is paused. Not invoking Puppet agent") end log_debug("Service waiting for #{runinterval} seconds") sleep(runinterval) log_debug('Service woken up') end 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 # The service will not stay in a paused stated, instead it will go back into a running state after a short period of time. This is an issue in the Win32-Service ruby code # Raised bug https://github.com/djberg96/win32-service/issues/11 and is fixed in version 0.8.3. # Because the Pause feature is so rarely used, there is no point in creating a workaround until puppet uses 0.8.3. log_notice('Service pausing. The service will not stay paused. See Puppet Issue PUP-1471 for more information') end def service_resume log_notice('Service resuming') end def service_shutdown log_notice('Host shutting down') end # Interrogation handler is just for debug. Can be commented out or removed entirely. # def service_interrogate # log_debug('Service is being interrogated') # 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 - report_windows_event(Win32::EventLog::INFO,0x01,msg.to_s) + report_windows_event(Win32::EventLog::INFO_TYPE,0x01,msg.to_s) when :info - report_windows_event(Win32::EventLog::INFO,0x01,msg.to_s) + report_windows_event(Win32::EventLog::INFO_TYPE,0x01,msg.to_s) when :notice - report_windows_event(Win32::EventLog::INFO,0x01,msg.to_s) + report_windows_event(Win32::EventLog::INFO_TYPE,0x01,msg.to_s) when :err - report_windows_event(Win32::EventLog::ERR,0x03,msg.to_s) + report_windows_event(Win32::EventLog::ERROR_TYPE,0x03,msg.to_s) else - report_windows_event(Win32::EventLog::WARN,0x02,msg.to_s) + report_windows_event(Win32::EventLog::WARN_TYPE,0x02,msg.to_s) end end end def report_windows_event(type,id,message) begin eventlog = nil eventlog = Win32::EventLog.open("Application") eventlog.report_event( :source => "Puppet", - :event_type => type, # Win32::EventLog::INFO or WARN, ERROR + :event_type => type, # Win32::EventLog::INFO_TYPE, WARN_TYPE or ERROR_TYPE :event_id => id, # 0x01 or 0x02, 0x03 etc. :data => message # "the message" ) rescue Exception => e # Ignore all errors ensure if (!eventlog.nil?) eventlog.close end end end end if __FILE__ == $0 WindowsDaemon.mainloop end diff --git a/lib/puppet/util/log/destinations.rb b/lib/puppet/util/log/destinations.rb index 932007099..eaa8b6119 100644 --- a/lib/puppet/util/log/destinations.rb +++ b/lib/puppet/util/log/destinations.rb @@ -1,228 +1,228 @@ Puppet::Util::Log.newdesttype :syslog do def self.suitable?(obj) Puppet.features.syslog? end def close Syslog.close end def initialize Syslog.close if Syslog.opened? name = "puppet-#{Puppet.run_mode.name}" options = Syslog::LOG_PID | Syslog::LOG_NDELAY # XXX This should really be configurable. str = Puppet[:syslogfacility] begin facility = Syslog.const_get("LOG_#{str.upcase}") rescue NameError raise Puppet::Error, "Invalid syslog facility #{str}", $!.backtrace end @syslog = Syslog.open(name, options, facility) end def handle(msg) # XXX Syslog currently has a bug that makes it so you # cannot log a message with a '%' in it. So, we get rid # of them. if msg.source == "Puppet" msg.to_s.split("\n").each do |line| @syslog.send(msg.level, line.gsub("%", '%%')) end else msg.to_s.split("\n").each do |line| @syslog.send(msg.level, "(%s) %s" % [msg.source.to_s.gsub("%", ""), line.gsub("%", '%%') ] ) end end end end Puppet::Util::Log.newdesttype :file do require 'fileutils' def self.match?(obj) Puppet::Util.absolute_path?(obj) end def close if defined?(@file) @file.close @file = nil end end def flush @file.flush if defined?(@file) end attr_accessor :autoflush def initialize(path) @name = path # first make sure the directory exists # We can't just use 'Config.use' here, because they've # specified a "special" destination. unless Puppet::FileSystem.exist?(Puppet::FileSystem.dir(path)) FileUtils.mkdir_p(File.dirname(path), :mode => 0755) Puppet.info "Creating log directory #{File.dirname(path)}" end # create the log file, if it doesn't already exist file = File.open(path, File::WRONLY|File::CREAT|File::APPEND) # Give ownership to the user and group puppet will run as begin FileUtils.chown(Puppet[:user], Puppet[:group], path) unless Puppet::Util::Platform.windows? rescue ArgumentError, Errno::EPERM Puppet.err "Unable to set ownership of log file" end @file = file @autoflush = Puppet[:autoflush] end def handle(msg) @file.puts("#{msg.time} #{msg.source} (#{msg.level}): #{msg}") @file.flush if @autoflush end end Puppet::Util::Log.newdesttype :logstash_event do require 'time' def format(msg) # logstash_event format is documented at # https://logstash.jira.com/browse/LOGSTASH-675 data = {} data = msg.to_hash data['version'] = 1 data['@timestamp'] = data['time'] data.delete('time') data end def handle(msg) message = format(msg) $stdout.puts message.to_pson end end Puppet::Util::Log.newdesttype :console do require 'puppet/util/colors' include Puppet::Util::Colors def initialize # Flush output immediately. $stderr.sync = true $stdout.sync = true end def handle(msg) levels = { :emerg => { :name => 'Emergency', :color => :hred, :stream => $stderr }, :alert => { :name => 'Alert', :color => :hred, :stream => $stderr }, :crit => { :name => 'Critical', :color => :hred, :stream => $stderr }, :err => { :name => 'Error', :color => :hred, :stream => $stderr }, :warning => { :name => 'Warning', :color => :hred, :stream => $stderr }, :notice => { :name => 'Notice', :color => :reset, :stream => $stdout }, :info => { :name => 'Info', :color => :green, :stream => $stdout }, :debug => { :name => 'Debug', :color => :cyan, :stream => $stdout }, } str = msg.respond_to?(:multiline) ? msg.multiline : msg.to_s str = msg.source == "Puppet" ? str : "#{msg.source}: #{str}" level = levels[msg.level] level[:stream].puts colorize(level[:color], "#{level[:name]}: #{str}") end end # Log to a transaction report. Puppet::Util::Log.newdesttype :report do attr_reader :report match "Puppet::Transaction::Report" def initialize(report) @report = report end def handle(msg) @report << msg end end # Log to an array, just for testing. module Puppet::Test class LogCollector def initialize(logs) @logs = logs end def <<(value) @logs << value end end end Puppet::Util::Log.newdesttype :array do match "Puppet::Test::LogCollector" def initialize(messages) @messages = messages end def handle(msg) @messages << msg end end Puppet::Util::Log.newdesttype :eventlog do def self.suitable?(obj) Puppet.features.eventlog? end def initialize @eventlog = Win32::EventLog.open("Application") end def to_native(level) case level when :debug,:info,:notice - [Win32::EventLog::INFO, 0x01] + [Win32::EventLog::INFO_TYPE, 0x01] when :warning - [Win32::EventLog::WARN, 0x02] + [Win32::EventLog::WARN_TYPE, 0x02] when :err,:alert,:emerg,:crit - [Win32::EventLog::ERROR, 0x03] + [Win32::EventLog::ERROR_TYPE, 0x03] end end def handle(msg) native_type, native_id = to_native(msg.level) @eventlog.report_event( :source => "Puppet", :event_type => native_type, :event_id => native_id, :data => (msg.source and msg.source != 'Puppet' ? "#{msg.source}: " : '') + msg.to_s ) end def close if @eventlog @eventlog.close @eventlog = nil end end end diff --git a/lib/puppet/util/pidlock.rb b/lib/puppet/util/pidlock.rb index 3e8389e23..3ebb4e0c9 100644 --- a/lib/puppet/util/pidlock.rb +++ b/lib/puppet/util/pidlock.rb @@ -1,61 +1,62 @@ require 'fileutils' require 'puppet/util/lockfile' class Puppet::Util::Pidlock def initialize(lockfile) @lockfile = Puppet::Util::Lockfile.new(lockfile) end def locked? clear_if_stale @lockfile.locked? end def mine? Process.pid == lock_pid end def lock return mine? if locked? @lockfile.lock(Process.pid) end def unlock if mine? return @lockfile.unlock else false end end def lock_pid pid = @lockfile.lock_data begin Integer(pid) rescue ArgumentError, TypeError nil end end def file_path @lockfile.file_path end def clear_if_stale return @lockfile.unlock if lock_pid.nil? errors = [Errno::ESRCH] - # Process::Error can only happen, and is only defined, on Windows - errors << Process::Error if defined? Process::Error + # Win32::Process now throws SystemCallError. Since this could be + # defined anywhere, only add when on Windows. + errors << SystemCallError if Puppet::Util::Platform.windows? begin Process.kill(0, lock_pid) rescue *errors @lockfile.unlock end end private :clear_if_stale end