summaryrefslogtreecommitdiffstats
path: root/lib/puppet/network/server.rb
blob: d424b9c4a8004288c58101ce0f2a7a5746887975 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
require 'puppet/network/http'
require 'puppet/util/pidlock'

class Puppet::Network::Server
  attr_reader :server_type, :protocols, :address, :port

  # Put the daemon into the background.
  def daemonize
    if pid = fork
      Process.detach(pid)
      exit(0)
    end

    # Get rid of console logging
    Puppet::Util::Log.close(:console)

    Process.setsid
    Dir.chdir("/")
    begin
      $stdin.reopen "/dev/null"
      $stdout.reopen "/dev/null", "a"
      $stderr.reopen $stdout
      Puppet::Util::Log.reopen
    rescue => detail
      Puppet::Util.secure_open("/tmp/daemonout", "w") { |f|
        f.puts "Could not start #{Puppet[:name]}: #{detail}"
      }
      raise "Could not start #{Puppet[:name]}: #{detail}"
    end
  end

  # Create a pidfile for our daemon, so we can be stopped and others
  # don't try to start.
  def create_pidfile
    Puppet::Util.sync(Puppet[:name]).synchronize(Sync::EX) do
      raise "Could not create PID file: #{pidfile}" unless Puppet::Util::Pidlock.new(pidfile).lock
    end
  end

  # Remove the pid file for our daemon.
  def remove_pidfile
    Puppet::Util.sync(Puppet[:name]).synchronize(Sync::EX) do
      locker = Puppet::Util::Pidlock.new(pidfile)
      locker.unlock or Puppet.err "Could not remove PID file #{pidfile}" if locker.locked?
    end
  end

  # Provide the path to our pidfile.
  def pidfile
    Puppet[:pidfile]
  end

  def initialize(args = {})
    valid_args = [:handlers, :xmlrpc_handlers, :port]
    bad_args = args.keys.find_all { |p| ! valid_args.include?(p) }.collect { |p| p.to_s }.join(",")
    raise ArgumentError, "Invalid argument(s) #{bad_args}" unless bad_args == ""
    @server_type = Puppet[:servertype] or raise "No servertype configuration found."  # e.g.,  WEBrick, Mongrel, etc.
    http_server_class || raise(ArgumentError, "Could not determine HTTP Server class for server type [#{@server_type}]")

    @port = args[:port] || Puppet[:masterport] || raise(ArgumentError, "Must specify :port or configure Puppet :masterport")
    @address = determine_bind_address

    @protocols = [ :rest, :xmlrpc ]
    @listening = false
    @routes = {}
    @xmlrpc_routes = {}
    self.register(args[:handlers]) if args[:handlers]
    self.register_xmlrpc(args[:xmlrpc_handlers]) if args[:xmlrpc_handlers]

    # Make sure we have all of the directories we need to function.
    Puppet.settings.use(:main, :ssl, Puppet[:name])
  end

  # Register handlers for REST networking, based on the Indirector.
  def register(*indirections)
    raise ArgumentError, "Indirection names are required." if indirections.empty?
    indirections.flatten.each do |name|
      Puppet::Indirector::Indirection.model(name) || raise(ArgumentError, "Cannot locate indirection '#{name}'.")
      @routes[name.to_sym] = true
    end
  end

  # Unregister Indirector handlers.
  def unregister(*indirections)
    raise "Cannot unregister indirections while server is listening." if listening?
    indirections = @routes.keys if indirections.empty?

    indirections.flatten.each do |i|
      raise(ArgumentError, "Indirection [#{i}] is unknown.") unless @routes[i.to_sym]
    end

    indirections.flatten.each do |i|
      @routes.delete(i.to_sym)
    end
  end

  # Register xmlrpc handlers for backward compatibility.
  def register_xmlrpc(*namespaces)
    raise ArgumentError, "XMLRPC namespaces are required." if namespaces.empty?
    namespaces.flatten.each do |name|
      Puppet::Network::Handler.handler(name) || raise(ArgumentError, "Cannot locate XMLRPC handler for namespace '#{name}'.")
      @xmlrpc_routes[name.to_sym] = true
    end
  end

  # Unregister xmlrpc handlers.
  def unregister_xmlrpc(*namespaces)
    raise "Cannot unregister xmlrpc handlers while server is listening." if listening?
    namespaces = @xmlrpc_routes.keys if namespaces.empty?

    namespaces.flatten.each do |i|
      raise(ArgumentError, "XMLRPC handler '#{i}' is unknown.") unless @xmlrpc_routes[i.to_sym]
    end

    namespaces.flatten.each do |i|
      @xmlrpc_routes.delete(i.to_sym)
    end
  end

  def listening?
    @listening
  end

  def listen
    raise "Cannot listen -- already listening." if listening?
    @listening = true
    http_server.listen(:address => address, :port => port, :handlers => @routes.keys, :xmlrpc_handlers => @xmlrpc_routes.keys, :protocols => protocols)
  end

  def unlisten
    raise "Cannot unlisten -- not currently listening." unless listening?
    http_server.unlisten
    @listening = false
  end

  def http_server_class
    http_server_class_by_type(@server_type)
  end

  def start
    create_pidfile
    listen
  end

  def stop
    unlisten
    remove_pidfile
  end

  private

  def http_server
    @http_server ||= http_server_class.new
  end

  def http_server_class_by_type(kind)
    Puppet::Network::HTTP.server_class_by_type(kind)
  end

  def determine_bind_address
    tmp = Puppet[:bindaddress]
    return tmp if tmp != ""
    server_type.to_s == "webrick" ? "0.0.0.0" : "127.0.0.1"
  end
end