Add bin/mailman reopen and bin/mailman restart
[mailman.git] / src / mailman / commands / cli_control.py
blobcab3d0fd9141b1b8b2c487218ceb24538c4233b2
1 # Copyright (C) 2009 by the Free Software Foundation, Inc.
3 # This file is part of GNU Mailman.
5 # GNU Mailman is free software: you can redistribute it and/or modify it under
6 # the terms of the GNU General Public License as published by the Free
7 # Software Foundation, either version 3 of the License, or (at your option)
8 # any later version.
10 # GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
11 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 # more details.
15 # You should have received a copy of the GNU General Public License along with
16 # GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
18 """Module stuff."""
20 from __future__ import absolute_import, unicode_literals
22 __metaclass__ = type
23 __all__ = [
24 'Reopen',
25 'Restart',
26 'Start',
27 'Stop',
31 import os
32 import sys
33 import signal
34 import logging
36 from zope.interface import implements
38 from mailman.config import config
39 from mailman.i18n import _
40 from mailman.interfaces.command import ICLISubCommand
43 qlog = logging.getLogger('mailman.qrunner')
47 class Start:
48 """Start the Mailman daemons."""
50 implements(ICLISubCommand)
52 name = 'start'
54 def add(self, parser, command_parser):
55 """See `ICLISubCommand`."""
56 command_parser.add_argument(
57 '-f', '--force',
58 default=False, action='store_true',
59 help=_("""\
60 If the master watcher finds an existing master lock, it will
61 normally exit with an error message. With this option,the master
62 will perform an extra level of checking. If a process matching
63 the host/pid described in the lock file is running, the master
64 will still exit, requiring you to manually clean up the lock. But
65 if no matching process is found, the master will remove the
66 apparently stale lock and make another attempt to claim the master
67 lock."""))
68 command_parser.add_argument(
69 '-u', '--run-as-user',
70 default=True, action='store_false',
71 help=_("""\
72 Normally, this script will refuse to run if the user id and group
73 id are not set to the 'mailman' user and group (as defined when
74 you configured Mailman). If run as root, this script will change
75 to this user and group before the check is made.
77 This can be inconvenient for testing and debugging purposes, so
78 the -u flag means that the step that sets and checks the uid/gid
79 is skipped, and the program is run as the current user and group.
80 This flag is not recommended for normal production environments.
82 Note though, that if you run with -u and are not in the mailman
83 group, you may have permission problems, such as begin unable to
84 delete a list's archives through the web. Tough luck!"""))
85 command_parser.add_argument(
86 '-q', '--quiet',
87 default=False, action='store_true',
88 help=_("""\
89 Don't print status messages. Error messages are still printed to
90 standard error."""))
92 def process(self, args):
93 """See `ICLISubCommand`."""
94 def log(message):
95 if not args.quiet:
96 print message
97 # Daemon process startup according to Stevens, Advanced Programming in
98 # the UNIX Environment, Chapter 13.
99 pid = os.fork()
100 if pid:
101 # parent
102 log(_("Starting Mailman's master qrunner"))
103 return
104 # child: Create a new session and become the session leader, but since
105 # we won't be opening any terminal devices, don't do the
106 # ultra-paranoid suggestion of doing a second fork after the setsid()
107 # call.
108 os.setsid()
109 # Instead of cd'ing to root, cd to the Mailman runtime directory.
110 os.chdir(config.VAR_DIR)
111 # Exec the master watcher.
112 execl_args = [
113 sys.executable, sys.executable,
114 os.path.join(config.BIN_DIR, 'master'),
116 if args.force:
117 execl_args.append('--force')
118 if args.config:
119 execl_args.extend(['-C', args.config])
120 qlog.debug('starting: %s', execl_args)
121 os.execl(*execl_args)
122 # We should never get here.
123 raise RuntimeError('os.execl() failed')
127 def kill_watcher(sig):
128 try:
129 with open(config.PIDFILE) as fp:
130 pid = int(fp.read().strip())
131 except (IOError, ValueError) as error:
132 # For i18n convenience
133 print >> sys.stderr, _('PID unreadable in: $config.PIDFILE')
134 print >> sys.stderr, error
135 print >> sys.stderr, _('Is qrunner even running?')
136 return
137 try:
138 os.kill(pid, sig)
139 except OSError as error:
140 if e.errno != errno.ESRCH:
141 raise
142 print >> sys.stderr, _('No child with pid: $pid')
143 print >> sys.stderr, e
144 print >> sys.stderr, _('Stale pid file removed.')
145 os.unlink(config.PIDFILE)
149 class SignalCommand:
150 """Common base class for simple, signal sending commands."""
152 implements(ICLISubCommand)
154 name = None
155 message = None
156 signal = None
158 def add(self, parser, command_parser):
159 """See `ICLISubCommand`."""
160 command_parser.add_argument(
161 '-q', '--quiet',
162 default=False, action='store_true',
163 help=_("""\
164 Don't print status messages. Error messages are still printed to
165 standard error."""))
167 def process(self, args):
168 """See `ICLISubCommand`."""
169 if not args.quiet:
170 print _(self.message)
171 kill_watcher(self.signal)
174 class Stop(SignalCommand):
175 """Stop the Malman daemons."""
177 name = 'stop'
178 message = _("Shutting down Mailman's master qrunner")
179 signal = signal.SIGTERM
182 class Reopen(SignalCommand):
183 """Reopen the Mailman daemons."""
185 name = 'reopen'
186 message = _('Reopening the Mailman qrunners')
187 signal = signal.SIGHUP
190 class Restart(SignalCommand):
191 """Stop the Mailman daemons."""
193 implements(ICLISubCommand)
195 name = 'restart'
196 message = _('Restarting the Mailman qrunners')
197 signal = signal.SIGUSR1