Esempio n. 1
0
def reloadproto(irc, source, args):
    """<protocol module name>

    Reloads the given protocol module without restart. You will have to manually disconnect and reconnect any network using the module for changes to apply."""
    permissions.check_permissions(irc, source, ['networks.reloadproto'])
    try:
        name = args[0]
    except IndexError:
        irc.error('Not enough arguments (needs 1: protocol module name)')
        return

    # Reload the dependency libraries first
    importlib.reload(pylinkirc.classes)
    log.debug('networks.reloadproto: reloading %s', pylinkirc.classes)

    for common_name in pylinkirc.protocols.common_modules:
        module = utils._get_protocol_module(common_name)
        log.debug('networks.reloadproto: reloading %s', module)
        importlib.reload(module)

    proto = utils._get_protocol_module(name)
    log.debug('networks.reloadproto: reloading %s', proto)
    importlib.reload(proto)

    irc.reply(
        "Done. You will have to manually disconnect and reconnect any network using the %r module for changes to apply."
        % name)
Esempio n. 2
0
def rehash():
    """Rehashes the PyLink daemon."""
    log.info('Reloading PyLink configuration...')
    old_conf = conf.conf.copy()
    fname = conf.fname
    new_conf = conf.load_conf(fname, errors_fatal=False, logger=log)
    conf.conf = new_conf

    # Reset any file logger options.
    _stop_file_loggers()
    files = new_conf['logging'].get('files')
    if files:
        for filename, config in files.items():
            _make_file_logger(filename, config.get('loglevel'))

    log.debug('rehash: updating console log level')
    world.console_handler.setLevel(_get_console_log_level())
    login._make_cryptcontext()  # refresh password hashing settings

    for network, ircobj in world.networkobjects.copy().items():
        # Server was removed from the config file, disconnect them.
        log.debug('rehash: checking if %r is in new conf still.', network)
        if network not in new_conf['servers']:
            log.debug(
                'rehash: removing connection to %r (removed from config).',
                network)
            remove_network(ircobj)
        else:
            # XXX: we should really just add abstraction to Irc to update config settings...
            ircobj.serverdata = new_conf['servers'][network]

            ircobj.autoconnect_active_multiplier = 1

            # Clear the IRC object's channel loggers and replace them with
            # new ones by re-running log_setup().
            while ircobj.loghandlers:
                log.removeHandler(ircobj.loghandlers.pop())

            ircobj.log_setup()

    utils._reset_module_dirs()

    for network, sdata in new_conf['servers'].items():
        # Connect any new networks or disconnected networks if they aren't already.
        if network not in world.networkobjects:
            try:
                proto = utils._get_protocol_module(sdata['protocol'])

                # API note: 2.0.x style of starting network connections
                world.networkobjects[network] = newirc = proto.Class(network)
                newirc.connect()
            except:
                log.exception(
                    'Failed to initialize network %r, skipping it...', network)

    log.info('Finished reloading PyLink configuration.')
Esempio n. 3
0
def _main():
    conf.load_conf(args.config)

    from pylinkirc.log import log
    from pylinkirc import classes, utils, coremods, selectdriver

    # Write and check for an existing PID file unless specifically told not to.
    if not args.no_pid:
        pidfile = '%s.pid' % conf.confname
        pid_exists = False
        pid = None
        if os.path.exists(pidfile):
            try:
                with open(pidfile) as f:
                    pid = int(f.read())
            except OSError:
                log.exception("Could not read PID file %s:", pidfile)
            else:
                pid_exists = True

            if psutil is not None and os.name == 'posix':
                # FIXME: Haven't tested this on other platforms, so not turning it on by default.
                try:
                    proc = psutil.Process(pid)
                except psutil.NoSuchProcess:  # Process doesn't exist!
                    pid_exists = False
                    log.info(
                        "Ignoring stale PID %s from PID file %r: no such process exists.",
                        pid, pidfile)
                else:
                    # This PID got reused for something that isn't us?
                    if not any('pylink' in arg.lower()
                               for arg in proc.cmdline()):
                        log.info(
                            "Ignoring stale PID %s from PID file %r: process command line %r is not us",
                            pid, pidfile, proc.cmdline())
                        pid_exists = False

        if pid and pid_exists:
            if args.rehash:
                os.kill(pid, signal.SIGUSR1)
                log.info('OK, rehashed PyLink instance %s (config %r)', pid,
                         args.config)
                sys.exit()
            elif args.stop or args.restart:  # Handle --stop and --restart options
                os.kill(pid, signal.SIGTERM)

                log.info(
                    "Waiting for PyLink instance %s (config %r) to stop...",
                    pid, args.config)
                while os.path.exists(pidfile):
                    # XXX: this is ugly, but os.waitpid() only works on non-child processes on Windows
                    time.sleep(0.2)
                log.info("Successfully killed PID %s for config %r.", pid,
                         args.config)

                if args.stop:
                    sys.exit()
            else:
                log.error("PID file %r exists; aborting!", pidfile)

                if psutil is None:
                    log.error(
                        "If PyLink didn't shut down cleanly last time it ran, or you're upgrading "
                        "from PyLink < 1.1-dev, delete %r and start the server again.",
                        pidfile)
                    if os.name == 'posix':
                        log.error(
                            "Alternatively, you can install psutil for Python 3 (pip3 install psutil), "
                            "which will allow this launcher to detect stale PID files and ignore them."
                        )
                sys.exit(1)

        elif args.stop or args.restart or args.rehash:  # XXX: also repetitive
            # --stop and --restart should take care of stale PIDs.
            if pid:
                world._should_remove_pid = True
                log.error(
                    'Cannot stop/rehash PyLink: no process with PID %s exists.',
                    pid)
            else:
                log.error(
                    'Cannot stop/rehash PyLink: PID file %r does not exist or cannot be read.',
                    pidfile)
            sys.exit(1)

        world._should_remove_pid = True

    log.info('PyLink %s starting...', __version__)

    world.daemon = args.daemonize
    if args.daemonize:
        if args.no_pid:
            print(
                'ERROR: Combining --no-pid and --daemonize is not supported.')
            sys.exit(1)
        elif os.name != 'posix':
            print(
                'ERROR: Daemonization is not supported outside POSIX systems.')
            sys.exit(1)
        else:
            log.info('Forking into the background.')
            log.removeHandler(world.console_handler)

            # Adapted from https://stackoverflow.com/questions/5975124/
            if os.fork():
                # Fork and exit the parent process.
                os._exit(0)

            os.setsid()  # Decouple from our lovely terminal
            if os.fork():
                # Fork again to prevent starting zombie apocalypses.
                os._exit(0)
    else:
        # For foreground sessions, set the terminal window title.
        # See https://bbs.archlinux.org/viewtopic.php?id=85567 &
        #     https://stackoverflow.com/questions/7387276/
        if os.name == 'nt':
            import ctypes
            ctypes.windll.kernel32.SetConsoleTitleW("PyLink %s" % __version__)
        elif os.name == 'posix':
            sys.stdout.write("\x1b]2;PyLink %s\x07" % __version__)

    if not args.no_pid:
        # Write the PID file only after forking.
        with open(pidfile, 'w') as f:
            f.write(str(os.getpid()))

    # Load configured plugins
    to_load = conf.conf['plugins']
    utils._reset_module_dirs()

    for plugin in to_load:
        try:
            world.plugins[plugin] = pl = utils._load_plugin(plugin)
        except Exception as e:
            log.exception('Failed to load plugin %r: %s: %s', plugin,
                          type(e).__name__, str(e))
        else:
            if hasattr(pl, 'main'):
                log.debug('Calling main() function of plugin %r', pl)
                pl.main()

    # Initialize all the networks one by one
    for network, sdata in conf.conf['servers'].items():
        try:
            protoname = sdata['protocol']
        except (KeyError, TypeError):
            log.error(
                "(%s) Configuration error: No protocol module specified, aborting.",
                network)
        else:
            # Fetch the correct protocol module.
            try:
                proto = utils._get_protocol_module(protoname)

                # Create and connect the network.
                world.networkobjects[network] = irc = proto.Class(network)
                log.debug('Connecting to network %r', network)
                irc.connect()
            except:
                log.exception(
                    '(%s) Failed to connect to network %r, skipping it...',
                    network, network)
                continue

    world.started.set()
    log.info("Loaded plugins: %s", ', '.join(sorted(world.plugins.keys())))
    selectdriver.start()