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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
|
# Copyright 1999-2006 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# RC functions to work with daemons
# Basically we're a fancy wrapper for start-stop-daemon
# and should be called as such. This means that our init scripts
# should work as is with zero modification :)
# Actually, the above is a small as lie we have some init scripts which try to
# get start-stop-daemon to launch a shell script. While this does work with
# the start-stop-daemon program in /sbin, it does cause a problem for us
# when we're testing for the daemon to be running. I (Roy Marples) view this
# as behaviour by design as start-stop-daemon should not be used to run shell
# scripts!
RC_GOT_DAEMON="yes"
[[ ${RC_GOT_FUNCTIONS} != "yes" ]] && source /sbin/functions.sh
[[ ${RC_GOT_SVCNAMES} != "yes" ]] && source "${svclib}/sh/rc-services.sh"
RC_RETRY_KILL="no"
RC_RETRY_TIMEOUT=1
RC_RETRY_COUNT=5
RC_FAIL_ON_ZOMBIE="no"
RC_KILL_CHILDREN="no"
RC_WAIT_ON_START="0.1"
# void rc_shift_args(void)
#
# Proccess vars - makes things easier by using the shift command
# and indirect variables
rc_shift_args() {
local addvar
while [[ $# != "0" ]]; do
if [[ $1 != "-"* && -n ${addvar} ]]; then
if [[ -z ${!addvar} ]]; then
eval "${addvar}=\"$1\""
else
eval "${addvar}=\"${!addvar} $1\""
fi
shift
continue
fi
unset addvar
case "$1" in
-S|--start)
stopping=false
;;
-K|--stop)
stopping=true
;;
-n|--name)
addvar="name"
;;
-x|--exec|-a|--startas)
addvar="cmd"
;;
-p|--pidfile)
addvar="pidfile"
;;
--pidfile=*)
pidfile="${1##--pidfile=}"
;;
--pid=*)
pidfile="${1##--pid=}"
;;
-R|--retry)
unset RC_RETRY_COUNT
addvar="RC_RETRY_COUNT"
;;
-s|--signal)
addvar="signal"
;;
-t|--test|-o|--oknodo)
nothing=true
;;
esac
shift
done
[[ -z ${RC_RETRY_COUNT} ]] && RC_RETRY_COUNT=5
}
# void rc_setup_daemon_vars(void)
#
# Setup our vars based on the start-stop-daemon command
rc_setup_daemon_vars() {
local name i
local -a sargs=( "${args%% \'--\' *}" )
local -a eargs
local x="${args// \'--\' /}"
[[ ${x} != "${args}" ]] && eargs=( "${args##* \'--\' }" )
eval rc_shift_args "${sargs[@]}"
[[ -z ${cmd} ]] && cmd="${name}"
# We may want to launch the daemon with a custom command
# This is mainly useful for debugging with apps like valgrind, strace
local bash_service="$( bash_variable "${SVCNAME}" )"
if [[ -n ${RC_DAEMON} ]]; then
local -a d=( ${RC_DAEMON} )
if ${stopping}; then
args="--stop"
else
args="--start"
fi
# Add -- or - arguments as s-s-d options
j=${#d[@]}
for (( i=0; i<j; i++ )); do
[[ ${d[i]:0:1} != "-" ]] && break
args="${args} ${d[i]}"
unset d[i]
done
d=( "${d[@]}" )
eval args=\"${args} --exec '${d[0]}' -- ${d[@]:1} '${cmd}' ${eargs[@]}\"
! ${stopping} && cmd="${d[0]}"
fi
return 0
}
# bool rc_try_kill_pid(int pid, char* signal, bool session)
#
# Repeatedly kill the pid with the given signal until it dies
# If session is true then we use tread pid as session and send it
# via pkill
# Returns 0 if successfuly otherwise 1
rc_try_kill_pid() {
local pid="$1" signal="${2:-TERM}" session="${3:-false}" i s p e
# We split RC_RETRY_TIMEOUT into tenths of seconds
# So we return as fast as possible
s=$(( ${RC_RETRY_TIMEOUT}/10 )).$(( ${RC_RETRY_TIMEOUT}%10 ))
for (( i=0; i<RC_RETRY_COUNT*10; i++ )); do
if ${session} ; then
if [[ -x /usr/bin/pkill ]]; then
pkill "-${signal}" -s "${pid}"
pgrep -s "${pid}" >/dev/null || return 0
else
local pids="$(ps eo pid,sid | sed -n "s/ ${pid}\$//p")"
[[ -z ${pids} ]] && return 0
kill -s "${signal}" ${pids} 2>/dev/null
e=false
for p in ${pids}; do
if [[ -d "/proc/${p}" ]]; then
e=true
break
fi
done
${e} || return 0
fi
else
kill -s "${signal}" "${pid}" 2>/dev/null
[[ ! -d "/proc/${pid}" ]] && return 0
fi
LC_ALL=C /bin/sleep "${s}"
done
return 1
}
# bool rc_kill_pid(int pid, bool session)
#
# Kills the given pid/session
# Returns 1 if we fail to kill the pid (if it's valid) otherwise 0
rc_kill_pid() {
local pid="$1" session="${2:-false}"
rc_try_kill_pid "${pid}" "${signal}" "${session}" && return 0
[[ ${RC_RETRY_KILL} == "yes" ]] \
&& rc_try_kill_pid "${pid}" KILL "${session}" && return 0
return 1
}
# char* pidof(char* cmd, ...)
#
# Returns a space seperated list of pids associated with the command
# This is to handle the rpc.nfsd program which acts weird
pidof() {
local arg args
for arg in "$@"; do
[[ ${arg##*/} == "rpc.nfsd" ]] && arg="${arg%/*}/nfsd"
args="${args} '"${arg}"'"
done
eval /bin/pidof -x ${args}
}
# bool is_daemon_running(char* cmd, char* pidfile)
#
# Returns 0 if the given daemon is running, otherwise 1
# If a pidfile is supplied, the pid inside it must match
# a pid in the list of pidof ${cmd}
is_daemon_running() {
local cmd pidfile pids pid
if [[ $# == "1" ]]; then
cmd="$1"
else
local i j="$#"
for (( i=0; i<j-1; i++ )); do
cmd="${cmd} $1"
shift
done
pidfile="$1"
fi
pids="$( pidof ${cmd} )"
[[ -z ${pids} ]] && return 1
[[ -s ${pidfile} ]] || return 0
read pid < "${pidfile}"
pids=" ${pids} "
[[ ${pids// ${pid} /} != "${pids}" ]]
}
# int rc_start_daemon(void)
#
# We don't do anyting fancy - just pass the given options
# to start-stop-daemon and return the value
rc_start_daemon() {
eval /sbin/start-stop-daemon "${args}"
local retval="$?"
[[ ${retval} != "0" ]] && return "${retval}"
[[ ${RC_WAIT_ON_START} == "0" ]] && return "${retval}"
# Give the daemon upto 1 second to fork after s-s-d returns
# Some daemons like acpid and vsftpd need this when system is under load
# Seems to be only daemons that do not create pid files though ...
local i=0
for ((i=0; i<10; i++)); do
is_daemon_running ${cmd} "${pidfile}" && break
LC_ALL=C /bin/sleep "0.1"
done
# We pause for RC_WAIT_ON_START seconds and then
# check if the daemon is still running - this is mainly
# to handle daemons who launch and then fail due to invalid
# configuration files
LC_ALL=C /bin/sleep "${RC_WAIT_ON_START}"
is_daemon_running ${cmd} "${pidfile}"
retval="$?"
[[ ${retval} == "0" ]] && return 0
# Stop if we can to clean things up
[[ -n ${pidfile} ]] && rc_stop_daemon
return "${retval}"
}
# bool rc_stop_daemon(void)
#
# Instead of calling start-stop-daemon we instead try and
# kill the process ourselves and any children left over
# Returns 0 if everything was successful otherwise 1
rc_stop_daemon() {
local pid pids retval="0"
if [[ -n ${cmd} ]]; then
if ! is_daemon_running ${cmd} "${pidfile}" ; then
[[ ${RC_FAIL_ON_ZOMBIE} == "yes" ]] && return 1
fi
pids="$( pidof ${cmd} )"
fi
if [[ -s ${pidfile} ]]; then
read pid < "${pidfile}"
# Check that the given program is actually running the pid
if [[ -n ${pids} ]]; then
pids=" ${pids} "
[[ ${pids// ${pid} /} == "${pids}" ]] && return 1
fi
pids="${pid}"
fi
# If there's nothing to kill then return without error
[[ -z ${pids} ]] && return 0
# We may not have pgrep to find our children, so we provide
# two methods
if [[ ${RC_KILL_CHILDREN} == "yes" ]]; then
if [[ -x /usr/bin/pgrep ]]; then
pids="${pids} $(pgrep -P "${pids// /,}")"
else
local npids
for pid in ${pids} ; do
npids="${npids} $(ps eo pid,ppid | sed -n "s/ ${pid}\$//p")"
done
pids="${pids} ${npids}"
fi
fi
for pid in ${pids}; do
if [[ ${RC_FAIL_ON_ZOMBIE} == "yes" ]]; then
ps p "${pid}" &>/dev/null || return 1
fi
if rc_kill_pid "${pid}" false ; then
# Remove the pidfile if the process didn't
[[ -f ${pidfile} ]] && rm -f "${pidfile}"
else
retval=1
fi
if [[ ${RC_KILL_CHILDREN} == "yes" ]]; then
rc_kill_pid "${pid}" true || retval=1
fi
done
return "${retval}"
}
# void update_service_status(char *service)
#
# Loads the service state file and ensures that all listed daemons are still
# running - hopefully on their correct pids too
# If not, we stop the service
update_service_status() {
local service="$1" daemonfile="${svcdir}/daemons/$1" i
local -a RC_DAEMONS=() RC_PIDFILES=()
# We only care about marking started services as stopped if the daemon(s)
# for it are no longer running
! service_started "${service}" && return
[[ ! -f ${daemonfile} ]] && return
# OK, now check that every daemon launched is active
# If the --start command was any good a pidfile was specified too
source "${daemonfile}"
for (( i=0; i<${#RC_DAEMONS[@]}; i++ )); do
if ! is_daemon_running ${RC_DAEMONS[i]} "${RC_PIDFILES[i]}" ; then
if [[ -e "/etc/init.d/${service}" ]]; then
( /etc/init.d/"${service}" stop &>/dev/null )
break
fi
fi
done
}
# int start-stop-daemon(...)
#
# Provide a wrapper to start-stop-daemon
# Return the result of start_daemon or stop_daemon depending on
# how we are called
start-stop-daemon() {
local args="$( requote "$@" )" result i
local cmd pidfile pid stopping signal nothing=false
local daemonfile="${svcdir}/daemons/${SVCNAME}"
local -a RC_DAEMONS=() RC_PIDFILES=()
[[ -e ${daemonfile} ]] && source "${daemonfile}"
rc_setup_daemon_vars
# We pass --oknodo and --test directly to start-stop-daemon and return
if ${nothing}; then
eval /sbin/start-stop-daemon "${args}"
return "$?"
fi
if ${stopping}; then
rc_stop_daemon
result="$?"
if [[ ${result} == "0" ]]; then
# We stopped the daemon successfully
# so we remove it from our state
for (( i=0; i<${#RC_DAEMONS[@]}; i++ )); do
# We should really check for valid cmd AND pidfile
# But most called to --stop only set the pidfile
if [[ ${RC_DAEMONS[i]} == "{cmd}" \
|| ${RC_PIDFILES[i]}="${pidfile}" ]]; then
unset RC_DAEMONS[i] RC_PIDFILES[i]
RC_DAEMONS=( "${RC_DAEMONS[@]}" )
RC_PIDFILES=( "${RC_PIDFILES[@]}" )
break
fi
done
fi
else
rc_start_daemon
result="$?"
if [[ ${result} == "0" ]]; then
# We started the daemon sucessfully
# so we add it to our state
local max="${#RC_DAEMONS[@]}"
for (( i=0; i<${max}; i++ )); do
if [[ ${RC_DAEMONS[i]} == "{cmd}" \
&& ${RC_PIDFILES[i]}="${pidfile}" ]]; then
break
fi
done
if [[ ${i} == "${max}" ]]; then
RC_DAEMONS[max]="${cmd}"
RC_PIDFILES[max]="${pidfile}"
fi
fi
fi
# Write the new list of daemon states for this service
if [[ ${#RC_DAEMONS[@]} == "0" ]]; then
[[ -f ${daemonfile} ]] && rm -f "${daemonfile}"
else
echo "RC_DAEMONS[0]=\"${RC_DAEMONS[0]}\"" > "${daemonfile}"
echo "RC_PIDFILES[0]=\"${RC_PIDFILES[0]}\"" >> "${daemonfile}"
for (( i=1; i<${#RC_DAEMONS[@]}; i++ )); do
echo "RC_DAEMONS[${i}]=\"${RC_DAEMONS[i]}\"" >> "${daemonfile}"
echo "RC_PIDFILES[${i}]=\"${RC_PIDFILES[i]}\"" >> "${daemonfile}"
done
fi
return "${result}"
}
# vim:ts=4
|