#!/usr/bin/env ruby

#
# = Synopsis
#
# The central puppet server.  Functions as a certificate authority by default.
#
# = Usage
#
#   puppetmasterd [-D|--daemonize] [-d|--debug] [-h|--help]
#       [-l|--logdest <file>|console|syslog] [--nobucket] [--nonodes]
#       [-v|--verbose] [-V|--version]
#
# = Description
#
# This is the puppet central daemon.
#
# = Options
#
# Note that any configuration parameter that's valid in the configuration file
# is also a valid long argument.  For example, 'ssldir' is a valid configuration
# parameter, so you can specify '--ssldir <directory>' as an argument.
#
# See the configuration file documentation at
# http://reductivelabs.com/projects/puppet/reference/configref.html for
# the full list of acceptable parameters. A commented list of all
# configuration options can also be generated by running puppetmasterdd with
# '--genconfig'.
#
# daemonize::
#   Send the process into the background.  This is the default unless
#   +verbose+ or +debug+ is enabled.
#
# debug::
#   Enable full debugging.  Causes the daemon not to go into the background.
#
# help::
#   Print this help message.
#
# logdest::
#   Where to send messages.  Choose between syslog, the console, and a log file.
#   Defaults to sending messages to syslog, or the console
#   if debugging or verbosity is enabled.
#
# nobucket::
#   Do not function as a file bucket.
#
# nonodes::
#   Do not use individual node designations; each node will receive the result
#   of evaluating the entire configuration.
#
# noreports::
#   Do not start the reports server.
#
# verbose::
#   Enable verbosity.  Causes the daemon not to go into the background.
#
# version::
#   Print the puppet version number and exit.
#
# = Example
#
#   puppetmasterd
#
# = Author
#
# Luke Kanies
#
# = Copyright
#
# Copyright (c) 2005 Reductive Labs, LLC
# Licensed under the GNU Public License

# Do an initial trap, so that cancels don't get a stack trace.
trap(:INT) do
    $stderr.puts "Cancelling startup"
    exit(0)
end

require 'getoptlong'
require 'puppet'
require 'puppet/sslcertificates'

options = [
	[ "--daemonize", "-D",			GetoptLong::NO_ARGUMENT ],
	[ "--debug",	"-d",			GetoptLong::NO_ARGUMENT ],
	[ "--help",		"-h",			GetoptLong::NO_ARGUMENT ],
	[ "--logdest",	"-l",			GetoptLong::REQUIRED_ARGUMENT ],
	[ "--noca",         			GetoptLong::NO_ARGUMENT ],
	[ "--nobucket",        			GetoptLong::NO_ARGUMENT ],
	[ "--noreports",       			GetoptLong::NO_ARGUMENT ],
	[ "--nonodes",         			GetoptLong::NO_ARGUMENT ],
	[ "--verbose",	"-v",			GetoptLong::NO_ARGUMENT ],
    [ "--version",  "-V",           GetoptLong::NO_ARGUMENT ]
]

# Add all of the config parameters as valid options.
Puppet.config.addargs(options)

result = GetoptLong.new(*options)

master = {}
ca = {}
report = {}
fs = {}
bucket = {}

options = {
    :havereport => true,
    :havebucket => true,
    :havemaster => true,
    :setdest => false,
    :verbose => false,
    :debug => false
}

begin
    result.each { |opt,arg|
        case opt
            when "--daemonize"
                options[:daemonize] = true
            when "--debug"
                options[:debug] = true
            when "--help"
                if Puppet.features.usage?
                    RDoc::usage && exit
                else
                    puts "No help available unless you have RDoc::usage installed"
                    exit
                end
            when "--noreports"
                options[:havereport] = false
            when "--nomaster"
                options[:havemaster] = false
            when "--nobucket"
                options[:havebucket] = false
            when "--nonodes"
                master[:UseNodes] = false
            when "--logdest"
                begin
                    Puppet::Util::Log.newdestination(arg)
                    options[:setdest] = true
                rescue => detail
                    if Puppet[:debug]
                        puts detail.backtrace
                    end
                    $stderr.puts detail.to_s
                end
            when "--version"
                puts "%s" % Puppet.version
                exit
            when "--verbose"
                options[:verbose] = true
            else
                Puppet.config.handlearg(opt, arg)
        end
    }
rescue GetoptLong::InvalidOption => detail
    $stderr.puts "Try '#{$0} --help'"
    #$stderr.puts detail
    exit(1)
end

# Handle the logging settings.
if options[:debug] or options[:verbose]
    if options[:debug]
        Puppet::Util::Log.level = :debug
    else
        Puppet::Util::Log.level = :info
    end

    unless options[:daemonize]
        Puppet::Util::Log.newdestination(:console)
        options[:setdest] = true
    end
end

unless options[:setdest]
    Puppet::Util::Log.newdestination(:syslog)
end

# Now parse the config
Puppet.parse_config

Puppet.genconfig
Puppet.genmanifest

require 'etc'

# Default to daemonizing, but if verbose or debug is specified,
# default to staying in the foreground.
unless options.include?(:daemonize)
    if Puppet::Util::Log.level == :debug or Puppet::Util::Log.level == :info
        options[:daemonize] = false
    else
        options[:daemonize] = true
    end
end

handlers = {
    :Status => {},
}

if options[:havemaster]
    handlers[:Master] = master
end

if options[:havereport]
    handlers[:Report] = report
end

if Puppet[:ca]
    handlers[:CA] = ca
end

if options[:havebucket]
    handlers[:FileBucket] = bucket
end

if Puppet[:parseonly]
    begin
        Puppet::Network::Handler.master.new(master)
    rescue => detail
        if Puppet[:trace]
            puts detail.backtrace
        end
        $stderr.puts detail
        exit(32)
    end
    # we would have already exited if the file weren't syntactically correct
    exit(0)
end

if File.exists?(Puppet[:fileserverconfig])
    fs[:Config] = Puppet[:fileserverconfig]
#else
#    Puppet.notice "File server config %s does not exist; skipping file serving" %
#        Puppet[:fileserverconfig]
end

if fs.include?(:Config)
    handlers[:FileServer] = fs
end

begin
    case Puppet[:servertype]
    when "webrick"
        # use the default, um, everything
        require 'puppet/network/server/webrick'
        server = Puppet::Network::Server::WEBrick.new(:Handlers => handlers)
    when "mongrel":
        require 'puppet/network/server/mongrel'
        handler = Puppet::Network::Server::Mongrel.new(handlers)
        addr = Puppet[:bindaddress]
        if addr == ""
            addr =  "127.0.0.1"
        end
        server = Mongrel::HttpServer.new(addr, Puppet[:masterport])
        server.register("/", handler)
    else
        Puppet.err "Invalid server type %s" % Puppet[:servertype]
        exit(45)
    end
rescue => detail
    if Puppet[:trace]
        puts detail.backtrace
    end
    $stderr.puts detail
    exit(1)
end

if Process.uid == 0
    begin
        Puppet::Util.chuser
    rescue => detail
        if Puppet[:debug]
            puts detail.backtrace
        end
        $stderr.puts "Could not change user to %s: %s" % [Puppet[:user], detail]
        exit(39)
    end
end

# Mongrel doesn't shut down like webrick; we really need to write plugins for it.
if Puppet[:servertype] == "webrick"
    Puppet.newservice(server)
end
Puppet.settraps

if options[:daemonize]
    server.daemonize
end

Puppet.notice "Starting Puppet server version %s" % [Puppet.version]
case Puppet[:servertype]
when "webrick"
    Puppet.start
when "mongrel":
    server.run.join
end

# $Id: puppetmasterd 2594 2007-06-15 23:11:43Z luke $
