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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
|
#
# Author:: AJ Christensen (<aj@junglist.gen.nz>)
# Copyright:: Copyright (c) 2008 Opscode, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# I love you Merb (lib/merb-core/server.rb)
require 'chef/config'
require 'etc'
class Chef
class Daemon
class << self
attr_accessor :name
# Daemonize the current process, managing pidfiles and process uid/gid
#
# === Parameters
# name<String>:: The name to be used for the pid file
#
def daemonize(name)
@name = name
pid = pid_from_file
unless running?
remove_pid_file()
Chef::Log.info("Daemonizing..")
begin
exit if fork
Process.setsid
exit if fork
Chef::Log.info("Forked, in #{Process.pid}. Privileges: #{Process.euid} #{Process.egid}")
File.umask Chef::Config[:umask]
$stdin.reopen("/dev/null")
$stdout.reopen("/dev/null", "a")
$stderr.reopen($stdout)
save_pid_file
at_exit { remove_pid_file }
rescue NotImplementedError => e
Chef::Application.fatal!("There is no fork: #{e.message}")
end
else
Chef::Application.fatal!("Chef is already running pid #{pid}")
end
end
# Check if Chef is running based on the pid_file
# ==== Returns
# Boolean::
# True if Chef is running
# False if Chef is not running
#
def running?
if pid_from_file.nil?
false
else
Process.kill(0, pid_from_file)
true
end
rescue Errno::ESRCH, Errno::ENOENT
false
rescue Errno::EACCES => e
Chef::Application.fatal!("You don't have access to the PID file at #{pid_file}: #{e.message}")
end
# Check if this process if forked from a Chef daemon
# ==== Returns
# Boolean::
# True if this process is forked
# False if this process is not forked
#
def forked?
if running? and Process.ppid == pid_from_file.to_i
# chef daemon is running and this process is a child of it
true
elsif not running? and Process.ppid == 1
# an orphaned fork, its parent becomes init, launchd, etc. after chef daemon dies
true
else
false
end
end
# Gets the pid file for @name
# ==== Returns
# String::
# Location of the pid file for @name
def pid_file
Chef::Config[:pid_file] or "/tmp/#{@name}.pid"
end
# Suck the pid out of pid_file
# ==== Returns
# Integer::
# The PID from pid_file
# nil::
# Returned if the pid_file does not exist.
#
def pid_from_file
File.read(pid_file).chomp.to_i
rescue Errno::ENOENT, Errno::EACCES
nil
end
# Store the PID on the filesystem
# This uses the Chef::Config[:pid_file] option, or "/tmp/name.pid" otherwise
#
def save_pid_file
file = pid_file
begin
FileUtils.mkdir_p(File.dirname(file))
rescue Errno::EACCES => e
Chef::Application.fatal!("Failed store pid in #{File.dirname(file)}, permission denied: #{e.message}")
end
begin
File.open(file, "w") { |f| f.write(Process.pid.to_s) }
rescue Errno::EACCES => e
Chef::Application.fatal!("Couldn't write to pidfile #{file}, permission denied: #{e.message}")
end
end
# Delete the PID from the filesystem
def remove_pid_file
if not forked? then
FileUtils.rm(pid_file) if File.exists?(pid_file)
end
end
# Change process user/group to those specified in Chef::Config
#
def change_privilege
Dir.chdir("/")
if Chef::Config[:user] and Chef::Config[:group]
Chef::Log.info("About to change privilege to #{Chef::Config[:user]}:#{Chef::Config[:group]}")
_change_privilege(Chef::Config[:user], Chef::Config[:group])
elsif Chef::Config[:user]
Chef::Log.info("About to change privilege to #{Chef::Config[:user]}")
_change_privilege(Chef::Config[:user])
end
end
# Change privileges of the process to be the specified user and group
#
# ==== Parameters
# user<String>:: The user to change the process to.
# group<String>:: The group to change the process to.
#
# ==== Alternatives
# If group is left out, the user will be used (changing to user:user)
#
def _change_privilege(user, group=user)
uid, gid = Process.euid, Process.egid
begin
target_uid = Etc.getpwnam(user).uid
rescue ArgumentError => e
Chef::Application.fatal!("Failed to get UID for user #{user}, does it exist? #{e.message}")
return false
end
begin
target_gid = Etc.getgrnam(group).gid
rescue ArgumentError => e
Chef::Application.fatal!("Failed to get GID for group #{group}, does it exist? #{e.message}")
return false
end
if (uid != target_uid) or (gid != target_gid)
Process.initgroups(user, target_gid)
Process::GID.change_privilege(target_gid)
Process::UID.change_privilege(target_uid)
end
true
rescue Errno::EPERM => e
Chef::Application.fatal!("Permission denied when trying to change #{uid}:#{gid} to #{target_uid}:#{target_gid}. #{e.message}")
end
end
end
end
|