~launchpad-pqm/launchpad/devel

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
# Copyright 2009 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).

"""Start and stop the Mailman processes."""

__metaclass__ = type
__all__ = [
    'start_mailman',
    'stop_mailman',
    ]


import errno
import os
import signal
import subprocess
import sys

import canonical
from lp.services.mailman.config import configure_prefix
from lp.services.mailman.monkeypatches import monkey_patch


def mailmanctl(command, quiet=False, config=None, *additional_arguments):
    """Run mailmanctl command.

    :param command: the command to use.
    :param quiet: when this is true, no output will happen unless, an error
        happens.
    :param config: The LaunchpadConfig object to take configuration from.
        Defaults to the global one.
    :param additional_arguments: additional command arguments to pass to the
        mailmanctl program.
    :raises RuntimeError: when quiet is True and the command failed.
    """
    if config is None:
        config = lp.services.config.config
    mailman_path = configure_prefix(config.mailman.build_prefix)
    mailman_bin = os.path.join(mailman_path, 'bin')
    args = ['./mailmanctl']
    args.extend(additional_arguments)
    args.append(command)
    if quiet:
        stdout = subprocess.PIPE
        stderr = subprocess.STDOUT
    else:
        stdout = None
        stderr = None
    env = dict(os.environ)
    env['LPCONFIG'] = config.instance_name
    process = subprocess.Popen(
        args, cwd=mailman_bin, stdout=stdout, stderr=stderr, env=env)
    code = process.wait()
    if code:
        if quiet:
            raise RuntimeError(
                'mailmanctl %s failed: %d\n%s' % (
                    command, code, process.stdout.read()))
        else:
            print >> sys.stderr, 'mailmanctl %s failed: %d' % (command, code)


def stop_mailman(quiet=False, config=None):
    """Alias for mailmanctl('stop')."""
    mailmanctl('stop', quiet, config)
    # Further, if the Mailman master pid file was not removed, then the
    # master watcher, and probably one of its queue runners, did not die.
    # Kill it hard and clean up after it.
    if config is None:
        config = lp.services.config.config
    mailman_path = configure_prefix(config.mailman.build_prefix)
    master_pid_path = os.path.join(mailman_path, 'data', 'master-qrunner.pid')
    try:
        master_pid_file = open(master_pid_path)
    except IOError, error:
        if error.errno == errno.ENOENT:
            # It doesn't exist, so we're all done.
            return
        raise
    try:
        master_pid = int(master_pid_file.read().strip())
    finally:
        master_pid_file.close()
    try:
        # Kill the entire process group.
        os.kill(master_pid, -signal.SIGKILL)
    except OSError, error:
        if error.errno == errno.ESRCH:
            # The process does not exist.  It could be a zombie that has yet
            # to be waited on, but let's not worry about that.
            return
        raise
    try:
        os.remove(master_pid_path)
    except OSError, error:
        if error.errno != errno.ENOENT:
            raise
    lock_dir = os.path.join(mailman_path, 'locks')
    for filename in os.listdir(lock_dir):
        os.remove(os.path.join(lock_dir, filename))


def start_mailman(quiet=False, config=None):
    """Start the Mailman master qrunner.

    The client of start_mailman() is responsible for ensuring that
    stop_mailman() is called at the appropriate time.

    :param quiet: when this is true, no output will happen unless, an error
        happens.
    :param config: The LaunchpadConfig object to take configuration from.
        Defaults to the global one.
    :raises RuntimeException: when Mailman fails to start successfully.
    """
    if config is None:
        config = lp.services.config.config
    # We need the Mailman bin directory so we can run some of Mailman's
    # command line scripts.
    mailman_path = configure_prefix(config.mailman.build_prefix)
    mailman_bin = os.path.join(mailman_path, 'bin')

    # Monkey-patch the installed Mailman 2.1 tree.
    monkey_patch(mailman_path, config)
    # Start Mailman.  Pass in the -s flag so that any stale master pid files
    # will get deleted.  "Stale" means the process that owned the pid no
    # longer exists, so this can't hurt anything.
    mailmanctl('start', quiet, config, '-s')