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

# 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
            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

    # 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
            unless Puppet::Util::Pidlock.new(pidfile).lock
                raise "Could not create PID file: %s" % [pidfile]
            end
        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 '%s'" % 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)
            if locker.locked?
                locker.unlock or Puppet.err "Could not remove PID file %s" % [pidfile]
            end
        end
    end

    def restart
        if agent and agent.running?
            agent.configure_delayed_restart
        else
            reexec
        end
    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
        {:INT => :stop, :TERM => :stop, :HUP => :restart, :USR1 => :reload, :USR2 => :reopen_logs}.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})
        server.stop if server

        agent.stop if agent

        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