예제 #1
0
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')
 def withlist(callable):
     callable_path = '%s.%s' % (__name__, callable)
     site_list = 'unused_mailman_site_list'
     prefix_path = configure_prefix(config.mailman.build_prefix)
     mailman_bin = os.path.join(prefix_path, 'bin')
     command = './withlist -q -r %s %s' % (callable_path, site_list)
     return subprocess.call(command.split(), cwd=mailman_bin)
예제 #3
0
 def withlist(callable):
     callable_path = '%s.%s' % (__name__, callable)
     site_list = 'unused_mailman_site_list'
     prefix_path = configure_prefix(config.mailman.build_prefix)
     mailman_bin = os.path.join(prefix_path, 'bin')
     command = './withlist -q -r %s %s' % (callable_path, site_list)
     return subprocess.call(command.split(), cwd=mailman_bin)
예제 #4
0
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')
예제 #5
0
 def deleteMailmanList(self, list_name):
     """Delete all Mailman data structures for `list_name`."""
     mailman_bindir = os.path.normpath(os.path.join(
         configure_prefix(config.mailman.build_prefix), 'bin'))
     process = subprocess.Popen(('./rmlist', '-a', list_name),
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE,
                                cwd=mailman_bindir)
     stdout, stderr = process.communicate()
     if process.returncode == 0:
         self.logger.info('%s', stdout)
     else:
         self.logger.error('rmlist command failed with exit status: %s',
                           process.returncode)
         self.logger.error('STDOUT:\n%s\nSTDERR:\n%s', stdout, stderr)
예제 #6
0
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)
예제 #7
0
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 as 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 as 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 as error:
        if error.errno != errno.ENOENT:
            raise
    lock_dir = os.path.join(mailman_path, 'locks')
    for entry in scandir.scandir(lock_dir):
        os.remove(entry.path)
예제 #8
0
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 as 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 as 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 as 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))
예제 #9
0
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)
예제 #10
0
    def main(self):
        """See `LaunchpadScript`."""
        source_url = None
        if len(self.args) == 0:
            self.parser.error('Missing source_url')
        elif len(self.args) > 1:
            self.parser.error('Too many arguments')
        else:
            source_url = self.args[0]

        # We need to get to the Mailman API.  Set up the paths so that Mailman
        # can be imported.  This can't be done at module global scope.
        mailman_path = configure_prefix(config.mailman.build_prefix)
        sys.path.append(mailman_path)

        retcode = self.syncMailmanDirectories(source_url)
        if retcode != 0:
            return retcode

        self.fixHostnames()

        # All done; commit the database changes.
        self.txn.commit()
        return 0
예제 #11
0
 def setUp(self):
     super(TestMMCfgLaunchpadConfigTestCase, self).setUp()
     # Generate a mailman config using this environment's config.
     mailman_path = configure_prefix(config.mailman.build_prefix)
     monkey_patch(mailman_path, config)
     reload(mm_cfg)
예제 #12
0
def build_mailman():
    # Build and install Mailman if it is enabled and not yet built.
    if not config.mailman.build:
        # There's nothing to do.
        return 0
    mailman_path = configure_prefix(config.mailman.build_prefix)
    mailman_bin = os.path.join(mailman_path, 'bin')
    var_dir = os.path.abspath(config.mailman.build_var_dir)
    executable = os.path.abspath('bin/py')

    # If we can import the package, we assume Mailman is properly built at
    # the least.  This does not catch re-installs that might be necessary
    # should our copy in sourcecode be updated.  Do that manually.
    try:
        import Mailman
    except ImportError:
        need_build = need_install = True
    else:
        need_build = need_install = False
        # Make sure that the configure prefix is correct, in case this tree
        # was moved after building Mailman.
        try:
            from Mailman import Defaults
        except ImportError:
            need_build = need_install = True
        else:
            if Defaults.PYTHON != executable:
                need_build = need_install = True
                # We'll need to remove this; "make install" won't overwrite
                # the existing file, and then installation will fail due to
                # it having the wrong sys.path.
                try:
                    os.unlink(
                        os.path.join(mailman_path, 'Mailman', 'mm_cfg.py'))
                except OSError as e:
                    if e.errno != errno.ENOENT:
                        raise
    if not need_install:
        # Also check for Launchpad-specific bits stuck into the source tree by
        # monkey_patch(), in case this is half-installed.  See
        # <https://bugs.launchpad.net/launchpad-registry/+bug/683486>.
        try:
            from Mailman.Queue import XMLRPCRunner
            from Mailman.Handlers import LPModerate
        except ImportError:
            # Monkey patches not present, redo install and patch steps.
            need_install = True

    # Make sure the target directories exist and have the correct
    # permissions, otherwise configure will complain.
    user, group = as_username_groupname(config.mailman.build_user_group)
    # Now work backwards to get the uid and gid
    try:
        uid = pwd.getpwnam(user).pw_uid
    except KeyError:
        print('No user found:', user, file=sys.stderr)
        sys.exit(1)
    try:
        gid = grp.getgrnam(group).gr_gid
    except KeyError:
        print('No group found:', group, file=sys.stderr)
        sys.exit(1)

    # Ensure that the var_dir exists, is owned by the user:group, and has
    # the necessary permissions.  Set the mode separately after the
    # makedirs() call because some platforms ignore mkdir()'s mode (though
    # I think Linux does not ignore it -- better safe than sorry).
    try:
        os.makedirs(var_dir)
    except OSError as e:
        if e.errno != errno.EEXIST:
            raise
    else:
        # Just created the var directory, will need to install mailmain bits.
        need_install = True
    os.chown(var_dir, uid, gid)
    os.chmod(var_dir, 0o2775)

    # Skip mailman setup if nothing so far has shown a reinstall needed.
    if not need_install:
        return 0

    mailman_source = os.path.join('sourcecode', 'mailman')
    if config.mailman.build_host_name:
        build_host_name = config.mailman.build_host_name
    else:
        build_host_name = socket.getfqdn()

    # Build and install the Mailman software.  Note that we don't care about
    # --with-cgi-gid because we're not going to use that Mailman subsystem.
    configure_args = (
        './configure',
        '--prefix',
        mailman_path,
        '--with-var-prefix=' + var_dir,
        '--with-python=' + executable,
        '--with-username='******'--with-groupname=' + group,
        '--with-mail-gid=' + group,
        '--with-mailhost=' + build_host_name,
        '--with-urlhost=' + build_host_name,
    )
    if need_build:
        # Configure.
        retcode = subprocess.call(configure_args, cwd=mailman_source)
        if retcode:
            print('Could not configure Mailman:', file=sys.stderr)
            sys.exit(retcode)
        # Make.
        retcode = subprocess.call(('make', ), cwd=mailman_source)
        if retcode:
            print('Could not make Mailman.', file=sys.stderr)
            sys.exit(retcode)
    retcode = subprocess.call(('make', 'install'), cwd=mailman_source)
    if retcode:
        print('Could not install Mailman.', file=sys.stderr)
        sys.exit(retcode)
    # Symlink Mailman's Python modules into the import path.
    try:
        os.unlink(os.path.join('lib', 'Mailman'))
    except OSError as e:
        if e.errno != errno.ENOENT:
            raise
    os.symlink(os.path.join('mailman', 'Mailman'),
               os.path.join('lib', 'Mailman'))
    # Try again to import the package.
    try:
        import Mailman
    except ImportError:
        print('Could not import the Mailman package', file=sys.stderr)
        return 1

    # Check to see if the site list exists.  The output can go to /dev/null
    # because we don't really care about it.  The site list exists if
    # config_list returns a zero exit status, otherwise it doesn't
    # (probably).  Before we can do this however, we must monkey patch
    # Mailman, otherwise mm_cfg.py won't be set up correctly.
    monkey_patch(mailman_path, config)

    import Mailman.mm_cfg
    retcode = subprocess.call(
        ('./config_list', '-o', '/dev/null', Mailman.mm_cfg.MAILMAN_SITE_LIST),
        cwd=mailman_bin,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE)

    if retcode:
        addr, password = configure_siteowner(
            config.mailman.build_site_list_owner)

        # The site list does not yet exist, so create it now.
        retcode = subprocess.call(
            ('./newlist', '--quiet', '--emailhost=' + build_host_name,
             Mailman.mm_cfg.MAILMAN_SITE_LIST, addr, password),
            cwd=mailman_bin)
        if retcode:
            print('Could not create site list', file=sys.stderr)
            return retcode

    retcode = configure_site_list(mailman_bin,
                                  Mailman.mm_cfg.MAILMAN_SITE_LIST)
    if retcode:
        print('Could not configure site list', file=sys.stderr)
        return retcode

    # Create a directory to hold the gzip'd tarballs for the directories of
    # deactivated lists.
    try:
        os.mkdir(os.path.join(Mailman.mm_cfg.VAR_PREFIX, 'backups'))
    except OSError as e:
        if e.errno != errno.EEXIST:
            raise

    return 0
예제 #13
0
def build_mailman():
    # Build and install Mailman if it is enabled and not yet built.
    if not config.mailman.build:
        # There's nothing to do.
        return 0
    mailman_path = configure_prefix(config.mailman.build_prefix)
    mailman_bin = os.path.join(mailman_path, 'bin')
    var_dir = os.path.abspath(config.mailman.build_var_dir)

    # If we can import the package, we assume Mailman is properly built at
    # the least.  This does not catch re-installs that might be necessary
    # should our copy in sourcecode be updated.  Do that manually.
    sys.path.append(mailman_path)
    try:
        import Mailman
    except ImportError:
        # sys.path_importer_cache is a mapping of elements of sys.path to
        # importer objects used to handle them. In Python2.5+ when an element
        # of sys.path is found to not exist on disk, a NullImporter is created
        # and cached - this causes Python to never bother re-inspecting the
        # disk for that path element. We must clear that cache element so that
        # our second attempt to import MailMan after building it will actually
        # check the disk.
        del sys.path_importer_cache[mailman_path]
        need_build = need_install = True
    else:
        need_build = need_install = False
        # Also check for Launchpad-specific bits stuck into the source tree by
        # monkey_patch(), in case this is half-installed.  See
        # <https://bugs.launchpad.net/launchpad-registry/+bug/683486>.
        try:
            from Mailman.Queue import XMLRPCRunner
            from Mailman.Handlers import LPModerate
        except ImportError:
            # Monkey patches not present, redo install and patch steps.
            need_install = True

    # Make sure the target directories exist and have the correct
    # permissions, otherwise configure will complain.
    user, group = as_username_groupname(config.mailman.build_user_group)
    # Now work backwards to get the uid and gid
    try:
        uid = pwd.getpwnam(user).pw_uid
    except KeyError:
        print >> sys.stderr, 'No user found:', user
        sys.exit(1)
    try:
        gid = grp.getgrnam(group).gr_gid
    except KeyError:
        print >> sys.stderr, 'No group found:', group
        sys.exit(1)

    # Ensure that the var_dir exists, is owned by the user:group, and has
    # the necessary permissions.  Set the mode separately after the
    # makedirs() call because some platforms ignore mkdir()'s mode (though
    # I think Linux does not ignore it -- better safe than sorry).
    try:
        os.makedirs(var_dir)
    except OSError as e:
        if e.errno != errno.EEXIST:
            raise
    else:
        # Just created the var directory, will need to install mailmain bits.
        need_install = True
    os.chown(var_dir, uid, gid)
    os.chmod(var_dir, 02775)

    # Skip mailman setup if nothing so far has shown a reinstall needed.
    if not need_install:
        return 0

    mailman_source = os.path.join('sourcecode', 'mailman')
    if config.mailman.build_host_name:
        build_host_name = config.mailman.build_host_name
    else:
        build_host_name = socket.getfqdn()

    # Build and install the Mailman software.  Note that we don't care about
    # --with-cgi-gid because we're not going to use that Mailman subsystem.
    executable = os.path.abspath('bin/py')
    configure_args = (
        './configure',
        '--prefix', mailman_path,
        '--with-var-prefix=' + var_dir,
        '--with-python=' + executable,
        '--with-username='******'--with-groupname=' + group,
        '--with-mail-gid=' + group,
        '--with-mailhost=' + build_host_name,
        '--with-urlhost=' + build_host_name,
        )
    if need_build:
        # Configure.
        retcode = subprocess.call(configure_args, cwd=mailman_source)
        if retcode:
            print >> sys.stderr, 'Could not configure Mailman:'
            sys.exit(retcode)
        # Make.
        retcode = subprocess.call(('make', ), cwd=mailman_source)
        if retcode:
            print >> sys.stderr, 'Could not make Mailman.'
            sys.exit(retcode)
    retcode = subprocess.call(('make', 'install'), cwd=mailman_source)
    if retcode:
        print >> sys.stderr, 'Could not install Mailman.'
        sys.exit(retcode)
    # Try again to import the package.
    try:
        import Mailman
    except ImportError:
        print >> sys.stderr, 'Could not import the Mailman package'
        return 1

    # Check to see if the site list exists.  The output can go to /dev/null
    # because we don't really care about it.  The site list exists if
    # config_list returns a zero exit status, otherwise it doesn't
    # (probably).  Before we can do this however, we must monkey patch
    # Mailman, otherwise mm_cfg.py won't be set up correctly.
    monkey_patch(mailman_path, config)

    import Mailman.mm_cfg
    retcode = subprocess.call(
        ('./config_list', '-o', '/dev/null',
         Mailman.mm_cfg.MAILMAN_SITE_LIST),
        cwd=mailman_bin,
        stdout=subprocess.PIPE, stderr=subprocess.PIPE)

    if retcode:
        addr, password = configure_siteowner(
            config.mailman.build_site_list_owner)

        # The site list does not yet exist, so create it now.
        retcode = subprocess.call(
            ('./newlist', '--quiet',
             '--emailhost=' + build_host_name,
             Mailman.mm_cfg.MAILMAN_SITE_LIST,
             addr, password),
            cwd=mailman_bin)
        if retcode:
            print >> sys.stderr, 'Could not create site list'
            return retcode

    retcode = configure_site_list(
        mailman_bin, Mailman.mm_cfg.MAILMAN_SITE_LIST)
    if retcode:
        print >> sys.stderr, 'Could not configure site list'
        return retcode

    # Create a directory to hold the gzip'd tarballs for the directories of
    # deactivated lists.
    try:
        os.mkdir(os.path.join(Mailman.mm_cfg.VAR_PREFIX, 'backups'))
    except OSError as e:
        if e.errno != errno.EEXIST:
            raise

    return 0