Add support to daemon-util for distributions without start-stop-daemon
[ganeti-local] / daemons / daemon-util.in
1 #!/bin/bash
2 #
3
4 # Copyright (C) 2009, 2011, 2012 Google Inc.
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 # 02110-1301, USA.
20
21 set -e
22
23 defaults_file=@SYSCONFDIR@/default/ganeti
24
25 # This is a list of all daemons and the order in which they're started. The
26 # order is important as there are dependencies between them. On shutdown,
27 # they're stopped in reverse order.
28 DAEMONS=(
29   ganeti-noded
30   ganeti-masterd
31   ganeti-rapi
32   )
33
34 if [[ "@CUSTOM_ENABLE_CONFD@" == True ]]; then
35   DAEMONS+=( ganeti-confd )
36 fi
37
38 NODED_ARGS=
39 MASTERD_ARGS=
40 CONFD_ARGS=
41 RAPI_ARGS=
42
43 # Read defaults file if it exists
44 if [[ -s $defaults_file ]]; then
45   . $defaults_file
46 fi
47
48 # Meant to facilitate use utilities in /etc/rc.d/init.d/functions in case
49 # start-stop-daemon is not available.
50 _ignore_error() {
51   eval "$@" || :
52 }
53
54 _daemon_pidfile() {
55   echo "@LOCALSTATEDIR@/run/ganeti/$1.pid"
56 }
57
58 _daemon_executable() {
59   echo "@PREFIX@/sbin/$1"
60 }
61
62 _daemon_usergroup() {
63   case "$1" in
64     masterd)
65       echo "@GNTMASTERUSER@:@GNTMASTERDGROUP@"
66       ;;
67     confd)
68       echo "@GNTCONFDUSER@:@GNTCONFDGROUP@"
69       ;;
70     rapi)
71       echo "@GNTRAPIUSER@:@GNTRAPIGROUP@"
72       ;;
73     noded)
74       echo "@GNTNODEDUSER@:@GNTDAEMONSGROUP@"
75       ;;
76     *)
77       echo "root:@GNTDAEMONSGROUP@"
78       ;;
79   esac
80 }
81
82 # Checks whether the local machine is part of a cluster
83 check_config() {
84   local server_pem=@LOCALSTATEDIR@/lib/ganeti/server.pem
85   local fname
86
87   for fname in $server_pem; do
88     if [[ ! -f $fname ]]; then
89       echo "Missing configuration file $fname" >&2
90       return 1
91     fi
92   done
93
94   return 0
95 }
96
97 # Checks the exit code of a daemon
98 check_exitcode() {
99   if [[ "$#" -lt 1 ]]; then
100     echo 'Missing exit code.' >&2
101     return 1
102   fi
103
104   local rc="$1"; shift
105
106   case "$rc" in
107     0) ;;
108     11)
109       echo "not master"
110     ;;
111     *)
112       echo "exit code $rc"
113       return 1
114     ;;
115   esac
116
117   return 0
118 }
119
120 # Prints path to PID file for a daemon.
121 daemon_pidfile() {
122   if [[ "$#" -lt 1 ]]; then
123     echo 'Missing daemon name.' >&2
124     return 1
125   fi
126
127   local name="$1"; shift
128
129   _daemon_pidfile $name
130 }
131
132 # Prints path to daemon executable.
133 daemon_executable() {
134   if [[ "$#" -lt 1 ]]; then
135     echo 'Missing daemon name.' >&2
136     return 1
137   fi
138
139   local name="$1"; shift
140
141   _daemon_executable $name
142 }
143
144 # Prints a list of all daemons in the order in which they should be started
145 list_start_daemons() {
146   local name
147
148   for name in "${DAEMONS[@]}"; do
149     echo "$name"
150   done
151 }
152
153 # Prints a list of all daemons in the order in which they should be stopped
154 list_stop_daemons() {
155   list_start_daemons | tac
156 }
157
158 # Checks whether a daemon name is known
159 is_daemon_name() {
160   if [[ "$#" -lt 1 ]]; then
161     echo 'Missing daemon name.' >&2
162     return 1
163   fi
164
165   local name="$1"; shift
166
167   for i in "${DAEMONS[@]}"; do
168     if [[ "$i" == "$name" ]]; then
169       return 0
170     fi
171   done
172
173   echo "Unknown daemon name '$name'" >&2
174   return 1
175 }
176
177 # Checks whether daemon is running
178 check() {
179   if [[ "$#" -lt 1 ]]; then
180     echo 'Missing daemon name.' >&2
181     return 1
182   fi
183
184   local name="$1"; shift
185   local pidfile=$(_daemon_pidfile $name)
186   local daemonexec=$(_daemon_executable $name)
187
188   if type -p start-stop-daemon >/dev/null; then
189     start-stop-daemon --stop --signal 0 --quiet \
190       --pidfile $pidfile
191   else
192     _ignore_error status \
193       -p $pidfile \
194       $daemonexec
195   fi
196 }
197
198 # Starts a daemon
199 start() {
200   if [[ "$#" -lt 1 ]]; then
201     echo 'Missing daemon name.' >&2
202     return 1
203   fi
204
205   local name="$1"; shift
206   local pidfile=$(_daemon_pidfile $name)
207   local usergroup=$(_daemon_usergroup $plain_name)
208   local daemonexec=$(_daemon_executable $name)
209
210   if [[ "$name" == ganeti-confd &&
211         "@CUSTOM_ENABLE_CONFD@" == False ]]; then
212     echo 'ganeti-confd disabled at build time' >&2
213     return 1
214   fi
215
216   # Convert daemon name to uppercase after removing "ganeti-" prefix
217   local plain_name=${name#ganeti-}
218   local ucname=$(tr a-z A-Z <<<$plain_name)
219
220   # Read $<daemon>_ARGS and $EXTRA_<daemon>_ARGS
221   eval local args="\"\$${ucname}_ARGS \$EXTRA_${ucname}_ARGS\""
222
223   @PKGLIBDIR@/ensure-dirs
224
225   if type -p start-stop-daemon >/dev/null; then
226     start-stop-daemon --start --quiet --oknodo \
227       --pidfile $pidfile \
228       --startas $daemonexec \
229       --chuid $usergroup \
230       -- $args "$@"
231   else
232     # TODO: Find a way to start daemon with a group, until then the group must
233     # be removed
234     _ignore_error daemon \
235       --pidfile $pidfile \
236       --user ${usergroup%:*} \
237       $daemonexec $args "$@"
238   fi
239 }
240
241 # Stops a daemon
242 stop() {
243   if [[ "$#" -lt 1 ]]; then
244     echo 'Missing daemon name.' >&2
245     return 1
246   fi
247
248   local name="$1"; shift
249   local pidfile=$(_daemon_pidfile $name)
250
251   if type -p start-stop-daemon >/dev/null; then
252     start-stop-daemon --stop --quiet --oknodo --retry 30 \
253       --pidfile $pidfile
254   else
255     _ignore_error killproc -p $pidfile $name
256   fi
257 }
258
259 # Starts a daemon if it's not yet running
260 check_and_start() {
261   local name="$1"
262
263   if ! check $name; then
264     start $name
265   fi
266 }
267
268 # Starts the master role
269 start_master() {
270   start ganeti-masterd
271   start ganeti-rapi
272 }
273
274 # Stops the master role
275 stop_master() {
276   stop ganeti-rapi
277   stop ganeti-masterd
278 }
279
280 # Start all daemons
281 start_all() {
282   for i in $(list_start_daemons); do
283     local rc=0
284
285     # Try to start daemon
286     start $i || rc=$?
287
288     if ! errmsg=$(check_exitcode $rc); then
289       echo "$errmsg" >&2
290       return 1
291     fi
292   done
293
294   return 0
295 }
296
297 # Stop all daemons
298 stop_all() {
299   for i in $(list_stop_daemons); do
300     stop $i
301   done
302 }
303
304 # Reloads the SSH keys
305 reload_ssh_keys() {
306   @RPL_SSH_INITD_SCRIPT@ restart
307 }
308
309 # Read @SYSCONFDIR@/rc.d/init.d/functions if start-stop-daemon not available
310 if ! type -p start-stop-daemon >/dev/null && \
311    [[ -f @SYSCONFDIR@/rc.d/init.d/functions ]]; then
312   _ignore_error . @SYSCONFDIR@/rc.d/init.d/functions
313 fi
314
315 if [[ "$#" -lt 1 ]]; then
316   echo "Usage: $0 <action>" >&2
317   exit 1
318 fi
319
320 orig_action=$1; shift
321
322 if [[ "$orig_action" == *_* ]]; then
323   echo "Command must not contain underscores" >&2
324   exit 1
325 fi
326
327 # Replace all dashes (-) with underlines (_)
328 action=${orig_action//-/_}
329
330 # Is it a known function?
331 if ! declare -F "$action" >/dev/null 2>&1; then
332   echo "Unknown command: $orig_action" >&2
333   exit 1
334 fi
335
336 # Call handler function
337 $action "$@"