def test_master_state(self): my_lock = Lock(self.lock_file) # Mailman is not running. state, lock = master.master_state(self.lock_file) self.assertEqual(state, master.WatcherState.none) # Acquire the lock as if another process had already started the # master. my_lock.lock() try: state, lock = master.master_state(self.lock_file) finally: my_lock.unlock() self.assertEqual(state, master.WatcherState.conflict)
def test_master_state(self): my_lock = Lock(self.lock_file) # Mailman is not running. state, lock = master.master_state(self.lock_file) self.assertEqual(state, master.WatcherState.none) # Acquire the lock as if another process had already started the # master. Use a timeout to avoid this test deadlocking. my_lock.lock(timedelta(seconds=60)) try: state, lock = master.master_state(self.lock_file) finally: my_lock.unlock() self.assertEqual(state, master.WatcherState.conflict)
def process(self, args): """See `ICLISubCommand`.""" # Although there's a potential race condition here, it's a better user # experience for the parent process to refuse to start twice, rather # than having it try to start the master, which will error exit. status, lock = master_state() if status is WatcherState.conflict: self.parser.error(_('GNU Mailman is already running')) elif status in (WatcherState.stale_lock, WatcherState.host_mismatch): if args.force is None: self.parser.error( _('A previous run of GNU Mailman did not exit ' 'cleanly. Try using --force.')) def log(message): # noqa if not args.quiet: print(message) # Try to find the path to a valid, existing configuration file, and # refuse to start if one cannot be found. if args.config is not None: config_path = args.config elif config.filename is not None: config_path = config.filename else: config_path = os.path.join(config.VAR_DIR, 'etc', 'mailman.cfg') if not os.path.exists(config_path): print(_("""\ No valid configuration file could be found, so Mailman will refuse to start. Use -C/--config to specify a valid configuration file."""), file=sys.stderr) sys.exit(1) # Daemon process startup according to Stevens, Advanced Programming in # the UNIX Environment, Chapter 13. pid = os.fork() if pid: # parent log(_("Starting Mailman's master runner")) return # child: Create a new session and become the session leader, but since # we won't be opening any terminal devices, don't do the # ultra-paranoid suggestion of doing a second fork after the setsid() # call. os.setsid() # Instead of cd'ing to root, cd to the Mailman runtime directory. # However, before we do that, set an environment variable used by the # subprocesses to calculate their path to the $VAR_DIR. os.environ['MAILMAN_VAR_DIR'] = config.VAR_DIR os.chdir(config.VAR_DIR) # Exec the master watcher. execl_args = [ sys.executable, sys.executable, os.path.join(config.BIN_DIR, 'master'), ] if args.force: execl_args.append('--force') # Always pass the config file path to the master projects, so there's # no confusion about which cfg is being used. execl_args.extend(['-C', config_path]) qlog.debug('starting: %s', execl_args) os.execl(*execl_args) # We should never get here. raise RuntimeError('os.execl() failed')
def process(self, args): """See `ICLISubCommand`.""" # Although there's a potential race condition here, it's a better user # experience for the parent process to refuse to start twice, rather # than having it try to start the master, which will error exit. status, lock = master_state() if status is WatcherState.conflict: self.parser.error(_('GNU Mailman is already running')) elif status in (WatcherState.stale_lock, WatcherState.host_mismatch): if args.force is None: self.parser.error( _('A previous run of GNU Mailman did not exit ' 'cleanly. Try using --force.')) def log(message): if not args.quiet: print(message) # Try to find the path to a valid, existing configuration file, and # refuse to start if one cannot be found. if args.config is not None: config_path = args.config elif config.filename is not None: config_path = config.filename else: config_path = os.path.join(config.VAR_DIR, 'etc', 'mailman.cfg') if not os.path.exists(config_path): print(_("""\ No valid configuration file could be found, so Mailman will refuse to start. Use -C/--config to specify a valid configuration file."""), file=sys.stderr) sys.exit(1) # Daemon process startup according to Stevens, Advanced Programming in # the UNIX Environment, Chapter 13. pid = os.fork() if pid: # parent log(_("Starting Mailman's master runner")) return # child: Create a new session and become the session leader, but since # we won't be opening any terminal devices, don't do the # ultra-paranoid suggestion of doing a second fork after the setsid() # call. os.setsid() # Instead of cd'ing to root, cd to the Mailman runtime directory. # However, before we do that, set an environment variable used by the # subprocesses to calculate their path to the $VAR_DIR. os.environ['MAILMAN_VAR_DIR'] = config.VAR_DIR os.chdir(config.VAR_DIR) # Exec the master watcher. execl_args = [ sys.executable, sys.executable, os.path.join(config.BIN_DIR, 'master'), ] if args.force: execl_args.append('--force') # Always pass the config file path to the master projects, so there's # no confusion about which cfg is being used. execl_args.extend(['-C', config_path]) qlog.debug('starting: %s', execl_args) os.execl(*execl_args) # We should never get here. raise RuntimeError('os.execl() failed')
def start(ctx, force, generate_alias_file, run_as_user, quiet): # Although there's a potential race condition here, it's a better user # experience for the parent process to refuse to start twice, rather than # having it try to start the master, which will error exit. status, lock = master_state() if status is WatcherState.conflict: ctx.fail(_('GNU Mailman is already running')) elif status in (WatcherState.stale_lock, WatcherState.host_mismatch): if not force: ctx.fail( _('A previous run of GNU Mailman did not exit ' 'cleanly ({}). Try using --force'.format(status.name))) # Daemon process startup according to Stevens, Advanced Programming in the # UNIX Environment, Chapter 13. pid = os.fork() if pid: # parent if not quiet: print(_("Starting Mailman's master runner")) if generate_alias_file: if not quiet: print(_("Generating MTA alias maps")) call_name(config.mta.incoming).regenerate() return # child: Create a new session and become the session leader, but since we # won't be opening any terminal devices, don't do the ultra-paranoid # suggestion of doing a second fork after the setsid() call. os.setsid() # Instead of cd'ing to root, cd to the Mailman runtime directory. However, # before we do that, set an environment variable used by the subprocesses # to calculate their path to the $VAR_DIR. os.environ['MAILMAN_VAR_DIR'] = config.VAR_DIR os.chdir(config.VAR_DIR) # Exec the master watcher. execl_args = [ sys.executable, sys.executable, os.path.join(config.BIN_DIR, 'master'), ] if force: execl_args.append('--force') # Always pass the configuration file path to the master process, so there's # no confusion about which one is being used. execl_args.extend(['-C', config.filename]) qlog.debug('starting: %s', execl_args) os.execl(*execl_args) # We should never get here. raise RuntimeError('os.execl() failed')
def status(): status, lock = master_state() if status is WatcherState.none: message = _('GNU Mailman is not running') elif status is WatcherState.conflict: hostname, pid, tempfile = lock.details message = _('GNU Mailman is running (master pid: $pid)') elif status is WatcherState.stale_lock: hostname, pid, tempfile = lock.details message = _('GNU Mailman is stopped (stale pid: $pid)') else: hostname, pid, tempfile = lock.details fqdn_name = socket.getfqdn() # noqa: F841 assert status is WatcherState.host_mismatch, ( 'Invalid enum value: %s' % status) message = _('GNU Mailman is in an unexpected state ' '($hostname != $fqdn_name)') print(message) sys.exit(status.value)
def process(self, args): """See `ICLISubCommand`.""" status, lock = master_state() if status is WatcherState.none: message = _('GNU Mailman is not running') elif status is WatcherState.conflict: hostname, pid, tempfile = lock.details message = _('GNU Mailman is running (master pid: $pid)') elif status is WatcherState.stale_lock: hostname, pid, tempfile = lock.details message = _('GNU Mailman is stopped (stale pid: $pid)') else: hostname, pid, tempfile = lock.details fqdn_name = socket.getfqdn() assert status is WatcherState.host_mismatch, ( 'Invalid enum value: %s' % status) message = _('GNU Mailman is in an unexpected state ' '($hostname != $fqdn_name)') print(message) return status.value
def process(self, args): """See `ICLISubCommand`.""" status, lock = master_state() if status is WatcherState.none: message = _('GNU Mailman is not running') elif status is WatcherState.conflict: hostname, pid, tempfile = lock.details message = _('GNU Mailman is running (master pid: $pid)') elif status is WatcherState.stale_lock: hostname, pid, tempfile = lock.details message =_('GNU Mailman is stopped (stale pid: $pid)') else: hostname, pid, tempfile = lock.details fqdn_name = socket.getfqdn() assert status is WatcherState.host_mismatch, ( 'Invalid enum value: %s' % status) message = _('GNU Mailman is in an unexpected state ' '($hostname != $fqdn_name)') print(message) return status.value