#!/usr/bin/env ruby

#
# = Synopsis
#
# The central puppet server.  Functions as a certificate authority by default.
#
# = Usage
#
#   puppetmasterd [-D|--daemonize|--no-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.
#
# no-daemonize::
#   Do not send the process into the background.
#
# debug::
#   Enable full debugging.
#
# 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.
#
# 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/network/handler'
require 'puppet/sslcertificates'

options = [
	[ "--debug",	"-d",			GetoptLong::NO_ARGUMENT ],
	[ "--help",		"-h",			GetoptLong::NO_ARGUMENT ],
	[ "--logdest",	"-l",			GetoptLong::REQUIRED_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.settings.addargs(options)

result = GetoptLong.new(*options)

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

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

begin
    result.each { |opt,arg|
        case opt
            # First check to see if the argument is a valid configuration parameter;
            # if so, set it. NOTE: there is a catch-all at the bottom for defaults.rb
            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.settings.handlearg(opt, arg)
        end
    }
rescue GetoptLong::InvalidOption => detail
    $stderr.puts "Try '#{$0} --help'"
    #$stderr.puts detail
    exit(1)
end

# Now parse the config
Puppet.parse_config

# 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 Puppet[:daemonize]
        Puppet::Util::Log.newdestination(:console)
        options[:setdest] = true
    end
end

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

if Puppet.settings.print_configs?
    exit(Puppet.settings.print_configs ? 0 : 1)
end

# A temporary solution, to at least make the master work for now.
Puppet::Node::Facts.terminus_class = :yaml

# Cache our nodes in yaml.  Currently not configurable.
Puppet::Node.cache_class = :yaml

require 'etc'

handlers = {
    :Status => {},
    :FileServer => {}
}

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

webserver = server = nil
begin
    case Puppet[:servertype]
    when "webrick"
        # use the default, um, everything
        require 'puppet/network/http_server/webrick'
        webserver = server = Puppet::Network::HTTPServer::WEBrick.new(:Handlers => handlers)
    when "mongrel":
        require 'puppet/network/http_server/mongrel'
        server = Puppet::Network::HTTPServer::Mongrel.new(handlers)
        addr = Puppet[:bindaddress]
        if addr == ""
            addr =  "127.0.0.1"
        end
        webserver = Mongrel::HttpServer.new(addr, Puppet[:masterport])
        webserver.register("/", server)
    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 Puppet[:daemonize]
    server.daemonize
end

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

