summaryrefslogtreecommitdiff
blob: 7e67dcc740f8b2e0066bfd06180cc6ccba0324f4 (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
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
427
428
# 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!
# At the time of writing, the only culprit I know of is courier-imap.
# There may be others!

RC_GOT_DAEMON="yes"

[[ ${RC_GOT_FUNCTIONS} != "yes" ]] && source /sbin/functions.sh
[[ ${RC_GOT_SERVICES} != "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 "${myservice}" )"
	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/${myservice}"
	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