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
|
#!../bin/py
# Copyright 2009 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
# This module uses relative imports.
# pylint: disable-msg=W0403
__metaclass__ = type
import logging
from optparse import OptionParser
import os
from signal import (
SIGKILL,
SIGTERM,
)
import time
from lp.services.config import config
from lp.services.mailman.runmailman import stop_mailman
from lp.services.pidfile import (
get_pid,
pidfile_path,
remove_pidfile,
)
from lp.services.scripts import (
logger,
logger_options,
)
def main():
parser = OptionParser('Usage: %prog [options] [SERVICE ...]')
parser.add_option("-w", "--wait", metavar="SECS",
default=20, type="int",
help="Wait up to SECS seconds for processes "
"to die before retrying with SIGKILL")
logger_options(parser, logging.INFO)
(options, args) = parser.parse_args()
log = logger(options)
if len(args) < 1:
parser.error('No service name provided')
pids = [] # List of pids we tried to kill.
services = args[:]
# Mailman is special, but only stop it if it was launched.
if 'mailman' in services:
if config.mailman.launch:
stop_mailman()
services.remove('mailman')
for service in services:
log.debug("PID file is %s", pidfile_path(service))
try:
pid = get_pid(service)
except ValueError, error:
log.error(error)
continue
if pid is not None:
log.info("Killing %s (%d)", service, pid)
try:
os.kill(pid, SIGTERM)
pids.append((service, pid))
except OSError, x:
log.error(
"Unable to SIGTERM %s (%d) - %s",
service, pid, x.strerror)
else:
log.debug("No PID file for %s", service)
wait_for_pids(pids, options.wait, log)
# Anything that didn't die, kill harder with SIGKILL.
for service, pid in pids:
if not process_exists(pid):
continue
log.warn(
"SIGTERM failed to kill %s (%d). Trying SIGKILL", service, pid)
try:
os.kill(pid, SIGKILL)
except OSError, x:
log.error(
"Unable to SIGKILL %s (%d) - %s", service, pid, x.strerror)
wait_for_pids(pids, options.wait, log)
# Report anything still left running after a SIGKILL.
for service, pid in pids:
if process_exists(pid):
log.error("SIGKILL didn't terminate %s (%d)", service, pid)
# Remove any pidfiles that didn't get cleaned up if there is no
# corresponding process (from an unkillable process, or maybe some
# other job has relaunched it while we were not looking).
for service in services:
pid = get_pid(service)
if pid is not None and not process_exists(pid):
try:
remove_pidfile(service)
except OSError:
pass
def process_exists(pid):
"""True if the given process exists."""
try:
pgid = os.getpgid(pid)
except OSError, x:
if x.errno == 3:
return False
log.error("Unknown exception from getpgid - %s", str(x))
return True
def wait_for_pids(pids, wait, log):
"""
Wait until all signalled processes are dead, or until we hit the
timeout.
Processes discovered to be dead are removed from the list.
:param pids: A list of (service, pid).
:param wait: How many seconds to wait.
"""
wait_start = time.time()
while len(pids) > 0 and time.time() < wait_start + wait:
for service, pid in pids[:]: # Copy pids because we mutate it.
if not process_exists(pid):
pids.remove((service, pid))
time.sleep(0.1)
|