~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/canonical/launchpad/daemons/tachandler.py

Merge devel.

Show diffs side-by-side

added added

removed removed

Lines of Context:
7
7
 
8
8
__metaclass__ = type
9
9
 
10
 
__all__ = ['TacTestSetup', 'ReadyService', 'TacException']
11
 
 
12
 
 
13
 
# This file is used by launchpad-buildd, so it cannot import any
14
 
# Launchpad code!
 
10
__all__ = [
 
11
    'TacTestSetup',
 
12
    'ReadyService',
 
13
    'TacException',
 
14
    'kill_by_pidfile',
 
15
    'remove_if_exists',
 
16
    'two_stage_kill',
 
17
    ]
 
18
 
 
19
 
15
20
import errno
16
21
import sys
17
22
import os
22
27
from twisted.application import service
23
28
from twisted.python import log
24
29
 
 
30
# This file is used by launchpad-buildd, so it cannot import any
 
31
# Launchpad code!
 
32
 
 
33
 
 
34
def two_stage_kill(pid, poll_interval=0.1, num_polls=50):
 
35
    """Kill process 'pid' with SIGTERM. If it doesn't die, SIGKILL it.
 
36
 
 
37
    :param pid: The pid of the process to kill.
 
38
    :param poll_interval: The polling interval used to check if the
 
39
        process is still around.
 
40
    :param num_polls: The number of polls to do before doing a SIGKILL.
 
41
    """
 
42
    # Kill the process.
 
43
    try:
 
44
        os.kill(pid, SIGTERM)
 
45
    except OSError, e:
 
46
        if e.errno in (errno.ESRCH, errno.ECHILD):
 
47
            # Process has already been killed.
 
48
            return
 
49
 
 
50
    # Poll until the process has ended.
 
51
    for i in range(num_polls):
 
52
        try:
 
53
            # Reap the child process and get its return value. If it's not
 
54
            # gone yet, continue.
 
55
            new_pid, result = os.waitpid(pid, os.WNOHANG)
 
56
            if new_pid:
 
57
                return result
 
58
            time.sleep(poll_interval)
 
59
        except OSError, e:
 
60
            if e.errno in (errno.ESRCH, errno.ECHILD):
 
61
                # Raised if the process is gone by the time we try to get the
 
62
                # return value.
 
63
                return
 
64
 
 
65
    # The process is still around, so terminate it violently.
 
66
    try:
 
67
        os.kill(pid, SIGKILL)
 
68
    except OSError:
 
69
        # Already terminated
 
70
        pass
 
71
 
 
72
 
 
73
def kill_by_pidfile(pidfile_path):
 
74
    """Kill a process identified by the pid stored in a file.
 
75
 
 
76
    The pid file is removed from disk.
 
77
    """
 
78
    if not os.path.exists(pidfile_path):
 
79
        return
 
80
    try:
 
81
        # Get the pid.
 
82
        pid = open(pidfile_path, 'r').read().split()[0]
 
83
        try:
 
84
            pid = int(pid)
 
85
        except ValueError:
 
86
            # pidfile contains rubbish
 
87
            return
 
88
 
 
89
        two_stage_kill(pid)
 
90
    finally:
 
91
        remove_if_exists(pidfile_path)
 
92
 
 
93
 
 
94
def remove_if_exists(path):
 
95
    """Remove the given file if it exists."""
 
96
    try:
 
97
        os.remove(path)
 
98
    except OSError, e:
 
99
        if e.errno != errno.ENOENT:
 
100
            raise
 
101
 
25
102
 
26
103
twistd_script = os.path.abspath(os.path.join(
27
104
    os.path.dirname(__file__),
48
125
        # started. If it sees an old logfile, then it will find the LOG_MAGIC
49
126
        # string and return immediately, provoking hard-to-diagnose race
50
127
        # conditions. Delete the logfile to make sure this does not happen.
51
 
        self._removeFile(self.logfile)
 
128
        remove_if_exists(self.logfile)
52
129
 
53
130
        self.setUpRoot()
54
131
        args = [sys.executable, twistd_script, '-o', '-y', self.tacfile,
107
184
    def tearDown(self):
108
185
        self.killTac()
109
186
 
110
 
    def _removeFile(self, filename):
111
 
        """Remove the given file if it exists."""
112
 
        try:
113
 
            os.remove(filename)
114
 
        except OSError, e:
115
 
            if e.errno != errno.ENOENT:
116
 
                raise
117
 
 
118
187
    def killTac(self):
119
188
        """Kill the TAC file if it is running."""
120
189
        pidfile = self.pidfile
121
 
        if not os.path.exists(pidfile):
122
 
            return
123
 
 
124
 
        # Get the pid.
125
 
        pid = open(pidfile, 'r').read().strip()
126
 
        try:
127
 
            pid = int(pid)
128
 
        except ValueError:
129
 
            # pidfile contains rubbish
130
 
            return
131
 
 
132
 
        # Kill the process.
133
 
        try:
134
 
            os.kill(pid, SIGTERM)
135
 
        except OSError, e:
136
 
            if e.errno in (errno.ESRCH, errno.ECHILD):
137
 
                # Process has already been killed.
138
 
                return
139
 
 
140
 
        # Poll until the process has ended.
141
 
        for i in range(50):
142
 
            try:
143
 
                os.kill(pid, 0)
144
 
                time.sleep(0.1)
145
 
            except OSError, e:
146
 
                break
147
 
        else:
148
 
            # The process is still around, so terminate it violently.
149
 
            try:
150
 
                os.kill(pid, SIGKILL)
151
 
            except OSError:
152
 
                # Already terminated
153
 
                pass
 
190
        kill_by_pidfile(pidfile)
154
191
 
155
192
    def setUpRoot(self):
156
193
        """Override this.