summaryrefslogtreecommitdiffstats
path: root/lib/puppet/daemon.rb
blob: 7e2fc75559a902de01f357ec5093a53b5a939de6 (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
require 'puppet'
require 'puppet/util/pidlock'
require 'puppet/external/event-loop'
require 'puppet/application'

# A module that handles operations common to all daemons.  This is included
# into the Server and Client base classes.
class Puppet::Daemon
    attr_accessor :agent, :server, :argv

    def daemonname
        Puppet[:name]
    end

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

        create_pidfile()

        # 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.err "Could not start #{Puppet[:name]}: #{detail}"
            Puppet::Util::secure_open("/tmp/daemonout", "w") { |f|
                f.puts "Could not start #{Puppet[:name]}: #{detail}"
            }
            exit(12)
        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

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

    def reexec
        raise Puppet::DevError, "Cannot reexec unless ARGV arguments are set" unless argv
        command = $0 + " " + argv.join(" ")
        Puppet.notice "Restarting with '#{command}'"
        stop(:exit => false)
        exec(command)
    end

    def reload
        return unless agent
        if agent.running?
            Puppet.notice "Not triggering already-running agent"
            return
        end

        agent.run
    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

    def restart
        Puppet::Application.restart!
        reexec unless agent and agent.running?
    end

    def reopen_logs
        Puppet::Util::Log.reopen
    end

    # Trap a couple of the main signals.  This should probably be handled
    # in a way that anyone else can register callbacks for traps, but, eh.
    def set_signal_traps
        signals = {:INT => :stop, :TERM => :stop }
        # extended signals not supported under windows
        signals.update({:HUP => :restart, :USR1 => :reload, :USR2 => :reopen_logs }) unless Puppet.features.microsoft_windows?
        signals.each do |signal, method|
            trap(signal) do
                Puppet.notice "Caught #{signal}; calling #{method}"
                send(method)
            end
        end
    end

    # Stop everything
    def stop(args = {:exit => true})
        Puppet::Application.stop!

        server.stop if server

        remove_pidfile()

        Puppet::Util::Log.close_all

        exit if args[:exit]
    end

    def start
        set_signal_traps

        create_pidfile

        raise Puppet::DevError, "Daemons must have an agent, server, or both" unless agent or server
        agent.start if agent
        server.start if server

        EventLoop.current.run
    end
end