# helper functions for daemons require 'puppet' module Puppet # A module that handles operations common to all daemons. This is included # into the Server and Client base classes. module Daemon include Puppet::Util Puppet.config.setdefaults(:puppet, :setpidfile => [true, "Whether to store a PID file for the daemon."]) def daemonname #$0.sub(/.+#{File::SEPARATOR}/,'') Puppet.name end # The path to the pid file for this server def pidfile File.join(Puppet[:rundir], daemonname() + ".pid") end # Put the daemon into the background. def daemonize if pid = fork() Process.detach(pid) exit(0) end # Get rid of console logging Puppet::Log.close(:console) Process.setsid Dir.chdir("/") begin $stdin.reopen "/dev/null" $stdout.reopen "/dev/null", "a" $stderr.reopen $stdout Puppet::Log.reopen rescue => detail File.open("/tmp/daemonout", "w") { |f| f.puts "Could not start %s: %s" % [Puppet.name, detail] } Puppet.err "Could not start %s: %s" % [Puppet.name, detail] exit(12) end end def fqdn unless defined? @fqdn and @fqdn hostname = Facter["hostname"].value domain = Facter["domain"].value @fqdn = [hostname, domain].join(".") end return @fqdn end def httplog args = [] # yuck; separate http logs file = nil Puppet.config.use(:puppet, :certificates, Puppet.name) if Puppet.name == "puppetmasterd" file = Puppet[:masterhttplog] else file = Puppet[:httplog] end # # unless FileTest.exists?(File.dirname(file)) # Puppet.recmkdir(File.dirname(file)) # end args << file if Puppet[:debug] args << WEBrick::Log::DEBUG end log = WEBrick::Log.new(*args) return log end # Read in an existing certificate. def readcert return unless @secureinit Puppet.config.use(:puppet, :certificates) # verify we've got all of the certs set up and such if defined? @cert and defined? @key and @cert and @key return true end unless defined? @fqdn self.fqdn end # we are not going to encrypt our key, but we need at a minimum # a keyfile and a certfile @certfile = File.join(Puppet[:certdir], [@fqdn, "pem"].join(".")) @cacertfile = File.join(Puppet[:certdir], ["ca", "pem"].join(".")) @keyfile = File.join(Puppet[:privatekeydir], [@fqdn, "pem"].join(".")) @publickeyfile = File.join(Puppet[:publickeydir], [@fqdn, "pem"].join(".")) if File.exists?(@keyfile) # load the key @key = OpenSSL::PKey::RSA.new(File.read(@keyfile)) else return false end if File.exists?(@certfile) if File.exists?(@cacertfile) @cacert = OpenSSL::X509::Certificate.new(File.read(@cacertfile)) else raise Puppet::Error, "Found cert file with no ca cert file" end @cert = OpenSSL::X509::Certificate.new(File.read(@certfile)) else return false end return true end # Request a certificate from the remote system. This does all of the work # of creating the cert request, contacting the remote system, and # storing the cert locally. def requestcert unless @secureinit raise Puppet::DevError, "Tried to request cert without initialized security" end retrieved = false Puppet.config.use(:puppet, :certificates) # create the directories involved # FIXME it's a stupid hack that i have to do this # [Puppet[:certdir], Puppet[:privatekeydir], Puppet[:csrdir], # Puppet[:publickeydir]].each { |dir| # unless FileTest.exists?(dir) # Puppet.recmkdir(dir, 0770) # end # } if self.readcert Puppet.info "Certificate already exists; not requesting" return true end unless defined? @key and @key # create a new one and store it Puppet.info "Creating a new SSL key at %s" % @keyfile @key = OpenSSL::PKey::RSA.new(Puppet[:keylength]) File.open(@keyfile, "w", 0660) { |f| f.print @key.to_pem } File.open(@publickeyfile, "w", 0660) { |f| f.print @key.public_key.to_pem } end unless defined? @driver Puppet.err "Cannot request a certificate without a defined target" return false end Puppet.info "Creating a new certificate request for %s" % @fqdn name = OpenSSL::X509::Name.new([["CN", @fqdn]]) @csr = OpenSSL::X509::Request.new @csr.version = 0 @csr.subject = name @csr.public_key = @key.public_key @csr.sign(@key, OpenSSL::Digest::MD5.new) Puppet.info "Requesting certificate" begin cert, cacert = @driver.getcert(@csr.to_pem) rescue => detail if Puppet[:debug] puts detail.backtrace end raise Puppet::Error.new("Certificate retrieval failed: %s" % detail) end if cert.nil? or cert == "" return nil end File.open(@certfile, "w", 0660) { |f| f.print cert } File.open(@cacertfile, "w", 0660) { |f| f.print cacert } begin @cert = OpenSSL::X509::Certificate.new(cert) @cacert = OpenSSL::X509::Certificate.new(cacert) retrieved = true rescue => detail raise Puppet::Error.new( "Invalid certificate: %s" % detail ) end unless @cert.check_private_key(@key) raise Puppet::DevError, "Received invalid certificate" end return retrieved end # Remove the pid file def rmpidfile threadlock(:pidfile) do if defined? @pidfile and @pidfile and FileTest.exists?(@pidfile) begin File.unlink(@pidfile) rescue => detail Puppet.err "Could not remove PID file %s: %s" % [@pidfile, detail] end end end end # Create the pid file. def setpidfile return unless Puppet[:setpidfile] threadlock(:pidfile) do Puppet.config.use(:puppet) @pidfile = self.pidfile if FileTest.exists?(@pidfile) if defined? $setpidfile return else raise Puppet::Error, "A PID file already exists for #{Puppet.name} at #{@pidfile}. Not starting." end end Puppet.info "Creating PID file to %s" % @pidfile begin File.open(@pidfile, "w") { |f| f.puts $$ } rescue => detail Puppet.err "Could not create PID file: %s" % detail exit(74) end $setpidfile = true end end # Shut down our server def shutdown # Remove our pid file rmpidfile() # And close all logs except the console. Puppet::Log.destinations.reject { |d| d == :console }.each do |dest| Puppet::Log.close(dest) end super end def start setpidfile() super end end end # $Id: daemon.rb 1315 2006-06-27 05:15:51Z luke $