Example #1
0
    def index(self):
        stats = self._stats()
        LOG.debug(
            "watch list? {}".format(self.config["aprsd"]["watch_list"], ), )
        wl = packets.WatchList()
        if wl.is_enabled():
            watch_count = len(wl)
            watch_age = wl.max_delta()
        else:
            watch_count = 0
            watch_age = 0

        sl = packets.SeenList()
        seen_count = len(sl)

        pm = plugin.PluginManager()
        plugins = pm.get_plugins()
        plugin_count = len(plugins)

        if self.config["aprs"].get("enabled", True):
            transport = "aprs-is"
            aprs_connection = (
                "APRS-IS Server: <a href='http://status.aprs2.net' >"
                "{}</a>".format(stats["stats"]["aprs-is"]["server"]))
        else:
            # We might be connected to a KISS socket?
            if client.KISSClient.kiss_enabled(self.config):
                transport = client.KISSClient.transport(self.config)
                if transport == client.TRANSPORT_TCPKISS:
                    aprs_connection = ("TCPKISS://{}:{}".format(
                        self.config["kiss"]["tcp"]["host"],
                        self.config["kiss"]["tcp"]["port"],
                    ))
                elif transport == client.TRANSPORT_SERIALKISS:
                    aprs_connection = ("SerialKISS://{}@{} baud".format(
                        self.config["kiss"]["serial"]["device"],
                        self.config["kiss"]["serial"]["baudrate"],
                    ))

        stats["transport"] = transport
        stats["aprs_connection"] = aprs_connection

        return flask.render_template(
            "index.html",
            initial_stats=stats,
            aprs_connection=aprs_connection,
            callsign=self.config["aprs"]["login"],
            version=aprsd.__version__,
            config_json=json.dumps(self.config.data),
            watch_count=watch_count,
            watch_age=watch_age,
            seen_count=seen_count,
            plugin_count=plugin_count,
        )
Example #2
0
def server(loglevel, quiet, disable_validation, config_file):
    """Start the aprsd server process."""

    signal.signal(signal.SIGINT, signal_handler)

    click.echo("Load config")
    config = utils.parse_config(config_file)

    # Force setting the config to the modules that need it
    # TODO(Walt): convert these modules to classes that can
    # Accept the config as a constructor param, instead of this
    # hacky global setting
    email.CONFIG = config
    messaging.CONFIG = config

    setup_logging(config, loglevel, quiet)
    LOG.info("APRSD Started version: {}".format(aprsd.__version__))

    # TODO(walt): Make email processing/checking optional?
    # Maybe someone only wants this to process messages with plugins only.
    valid = email.validate_email_config(config, disable_validation)
    if not valid:
        LOG.error("Failed to validate email config options")
        sys.exit(-1)

    # start the email thread
    email.start_thread()

    # Create the initial PM singleton and Register plugins
    plugin_manager = plugin.PluginManager(config)
    plugin_manager.setup_plugins()
    cl = client.Client(config)

    # setup and run the main blocking loop
    while True:
        # Now use the helper which uses the singleton
        aprs_client = client.get_client()

        # setup the consumer of messages and block until a messages
        try:
            # This will register a packet consumer with aprslib
            # When new packets come in the consumer will process
            # the packet
            aprs_client.consumer(process_packet, raw=False)
        except aprslib.exceptions.ConnectionDrop:
            LOG.error("Connection dropped, reconnecting")
            time.sleep(5)
            # Force the deletion of the client object connected to aprs
            # This will cause a reconnect, next time client.get_client()
            # is called
            cl.reset()
Example #3
0
def process_message_packet(packet):
    LOG.info("Got a message packet")
    fromcall = packet["from"]
    message = packet.get("message_text", None)

    msg_number = packet.get("msgNo", None)
    if msg_number:
        ack = msg_number
    else:
        ack = "0"

    messaging.log_message("Received Message",
                          packet["raw"],
                          message,
                          fromcall=fromcall,
                          ack=ack)

    found_command = False
    # Get singleton of the PM
    pm = plugin.PluginManager()
    try:
        results = pm.run(fromcall=fromcall, message=message, ack=ack)
        for reply in results:
            found_command = True
            # A plugin can return a null message flag which signals
            # us that they processed the message correctly, but have
            # nothing to reply with, so we avoid replying with a usage string
            if reply is not messaging.NULL_MESSAGE:
                LOG.debug("Sending '{}'".format(reply))
                messaging.send_message(fromcall, reply)
            else:
                LOG.debug("Got NULL MESSAGE from plugin")

        if not found_command:
            plugins = pm.get_plugins()
            names = [x.command_name for x in plugins]
            names.sort()

            reply = "Usage: {}".format(", ".join(names))
            messaging.send_message(fromcall, reply)
    except Exception as ex:
        LOG.exception("Plugin failed!!!", ex)
        reply = "A Plugin failed! try again?"
        messaging.send_message(fromcall, reply)

    # let any threads do their thing, then ack
    # send an ack last
    messaging.send_ack(fromcall, ack)
    LOG.debug("Packet processing complete")
Example #4
0
def test_plugin(
    loglevel,
    config_file,
    plugin_path,
    fromcall,
    message,
):
    """APRSD Plugin test app."""

    config = utils.parse_config(config_file)
    email.CONFIG = config

    setup_logging(config, loglevel, False)
    LOG.info("Test APRSD PLugin version: {}".format(aprsd.__version__))
    if type(message) is tuple:
        message = " ".join(message)
    LOG.info("P'{}'  F'{}'   C'{}'".format(plugin_path, fromcall, message))
    client.Client(config)

    pm = plugin.PluginManager(config)
    obj = pm._create_class(plugin_path, plugin.APRSDPluginBase, config=config)

    reply = obj.run(fromcall, message, 1)
    LOG.info("Result = '{}'".format(reply))
Example #5
0
    def plugins(self):
        pm = plugin.PluginManager()
        pm.reload_plugins()

        return "reloaded"
Example #6
0
    def loop(self):
        """Process a packet recieved from aprs-is server."""
        packet = self.packet
        packets.PacketList().add(packet)

        fromcall = packet["from"]
        tocall = packet.get("addresse", None)
        msg = packet.get("message_text", None)
        msg_id = packet.get("msgNo", "0")
        msg_response = packet.get("response", None)
        # LOG.debug(f"Got packet from '{fromcall}' - {packet}")

        # We don't put ack packets destined for us through the
        # plugins.
        if tocall == self.config["aprs"]["login"] and msg_response == "ack":
            self.process_ack_packet(packet)
        else:
            # It's not an ACK for us, so lets run it through
            # the plugins.
            messaging.log_message(
                "Received Message",
                packet["raw"],
                msg,
                fromcall=fromcall,
                msg_num=msg_id,
            )

            # Only ack messages that were sent directly to us
            if tocall == self.config["aprs"]["login"]:
                stats.APRSDStats().msgs_rx_inc()
                # let any threads do their thing, then ack
                # send an ack last
                ack = messaging.AckMessage(
                    self.config["aprs"]["login"],
                    fromcall,
                    msg_id=msg_id,
                )
                ack.send()

            pm = plugin.PluginManager()
            try:
                results = pm.run(packet)
                wl = packets.WatchList()
                wl.update_seen(packet)
                replied = False
                for reply in results:
                    if isinstance(reply, list):
                        # one of the plugins wants to send multiple messages
                        replied = True
                        for subreply in reply:
                            LOG.debug(f"Sending '{subreply}'")
                            if isinstance(subreply, messaging.Message):
                                subreply.send()
                            else:
                                msg = messaging.TextMessage(
                                    self.config["aprs"]["login"],
                                    fromcall,
                                    subreply,
                                )
                                msg.send()
                    elif isinstance(reply, messaging.Message):
                        # We have a message based object.
                        LOG.debug(f"Sending '{reply}'")
                        reply.send()
                        replied = True
                    else:
                        replied = True
                        # A plugin can return a null message flag which signals
                        # us that they processed the message correctly, but have
                        # nothing to reply with, so we avoid replying with a
                        # usage string
                        if reply is not messaging.NULL_MESSAGE:
                            LOG.debug(f"Sending '{reply}'")

                            msg = messaging.TextMessage(
                                self.config["aprs"]["login"],
                                fromcall,
                                reply,
                            )
                            msg.send()

                # If the message was for us and we didn't have a
                # response, then we send a usage statement.
                if tocall == self.config["aprs"]["login"] and not replied:
                    LOG.warning("Sending help!")
                    msg = messaging.TextMessage(
                        self.config["aprs"]["login"],
                        fromcall,
                        "Unknown command! Send 'help' message for help",
                    )
                    msg.send()
            except Exception as ex:
                LOG.error("Plugin failed!!!")
                LOG.exception(ex)
                # Do we need to send a reply?
                if tocall == self.config["aprs"]["login"]:
                    reply = "A Plugin failed! try again?"
                    msg = messaging.TextMessage(
                        self.config["aprs"]["login"],
                        fromcall,
                        reply,
                    )
                    msg.send()

        LOG.debug("Packet processing complete")
Example #7
0
    def stats(self):
        now = datetime.datetime.now()
        if self._email_thread_last_time:
            last_update = str(now - self._email_thread_last_time)
        else:
            last_update = "never"

        if self._aprsis_keepalive:
            last_aprsis_keepalive = str(now - self._aprsis_keepalive)
        else:
            last_aprsis_keepalive = "never"

        pm = plugin.PluginManager()
        plugins = pm.get_plugins()
        plugin_stats = {}

        def full_name_with_qualname(obj):
            return "{}.{}".format(
                obj.__class__.__module__,
                obj.__class__.__qualname__,
            )

        for p in plugins:
            plugin_stats[full_name_with_qualname(p)] = {
                "enabled": p.enabled,
                "rx": p.rx_count,
                "tx": p.tx_count,
                "version": p.version,
            }

        wl = packets.WatchList()
        sl = packets.SeenList()

        stats = {
            "aprsd": {
                "version": aprsd.__version__,
                "uptime": utils.strfdelta(self.uptime),
                "memory_current": self.memory,
                "memory_current_str": utils.human_size(self.memory),
                "memory_peak": self.memory_peak,
                "memory_peak_str": utils.human_size(self.memory_peak),
                "watch_list": wl.get_all(),
                "seen_list": sl.get_all(),
            },
            "aprs-is": {
                "server": self.aprsis_server,
                "callsign": self.config["aprs"]["login"],
                "last_update": last_aprsis_keepalive,
            },
            "messages": {
                "tracked": self.msgs_tracked,
                "sent": self.msgs_tx,
                "recieved": self.msgs_rx,
                "ack_sent": self.ack_tx,
                "ack_recieved": self.ack_rx,
                "mic-e recieved": self.msgs_mice_rx,
            },
            "email": {
                "enabled": self.config["aprsd"]["email"]["enabled"],
                "sent": self._email_tx,
                "recieved": self._email_rx,
                "thread_last_update": last_update,
            },
            "plugins": plugin_stats,
        }
        return stats
Example #8
0
def server(ctx, flush):
    """Start the aprsd server gateway process."""
    ctx.obj["config_file"]
    loglevel = ctx.obj["loglevel"]
    quiet = ctx.obj["quiet"]
    config = ctx.obj["config"]

    signal.signal(signal.SIGINT, aprsd_main.signal_handler)
    signal.signal(signal.SIGTERM, aprsd_main.signal_handler)

    if not quiet:
        click.echo("Load config")

    level, msg = utils._check_version()
    if level:
        LOG.warning(msg)
    else:
        LOG.info(msg)
    LOG.info(f"APRSD Started version: {aprsd.__version__}")

    flat_config = utils.flatten_dict(config)
    LOG.info("Using CONFIG values:")
    for x in flat_config:
        if "password" in x or "aprsd.web.users.admin" in x:
            LOG.info(f"{x} = XXXXXXXXXXXXXXXXXXX")
        else:
            LOG.info(f"{x} = {flat_config[x]}")

    if config["aprsd"].get("trace", False):
        trace.setup_tracing(["method", "api"])
    stats.APRSDStats(config)

    # Initialize the client factory and create
    # The correct client object ready for use
    client.ClientFactory.setup(config)
    # Make sure we have 1 client transport enabled
    if not client.factory.is_client_enabled():
        LOG.error("No Clients are enabled in config.")
        sys.exit(-1)

    if not client.factory.is_client_configured():
        LOG.error("APRS client is not properly configured in config file.")
        sys.exit(-1)

    # Creates the client object
    LOG.info("Creating client connection")
    client.factory.create().client

    # Now load the msgTrack from disk if any
    packets.PacketList(config=config)
    if flush:
        LOG.debug("Deleting saved MsgTrack.")
        messaging.MsgTrack(config=config).flush()
        packets.WatchList(config=config)
        packets.SeenList(config=config)
    else:
        # Try and load saved MsgTrack list
        LOG.debug("Loading saved MsgTrack object.")
        messaging.MsgTrack(config=config).load()
        packets.WatchList(config=config).load()
        packets.SeenList(config=config).load()

    # Create the initial PM singleton and Register plugins
    LOG.info("Loading Plugin Manager and registering plugins")
    plugin_manager = plugin.PluginManager(config)
    plugin_manager.setup_plugins()

    rx_thread = threads.APRSDRXThread(
        msg_queues=threads.msg_queues,
        config=config,
    )
    rx_thread.start()

    messaging.MsgTrack().restart()

    keepalive = threads.KeepAliveThread(config=config)
    keepalive.start()

    web_enabled = config.get("aprsd.web.enabled", default=False)

    if web_enabled:
        aprsd_main.flask_enabled = True
        (socketio, app) = flask.init_flask(config, loglevel, quiet)
        socketio.run(
            app,
            host=config["aprsd"]["web"]["host"],
            port=config["aprsd"]["web"]["port"],
        )

    # If there are items in the msgTracker, then save them
    LOG.info("APRSD Exiting.")
    return 0
Example #9
0
def test_plugin(
    ctx,
    aprs_login,
    plugin_path,
    load_all,
    number,
    message,
):
    """Test an individual APRSD plugin given a python path."""
    config = ctx.obj["config"]

    if not aprs_login:
        if not config.exists("aprs.login"):
            click.echo("Must set --aprs_login or APRS_LOGIN")
            ctx.exit(-1)
            return
        else:
            fromcall = config.get("aprs.login")
    else:
        fromcall = aprs_login

    if not plugin_path:
        click.echo(ctx.get_help())
        click.echo("")
        ctx.fail("Failed to provide -p option to test a plugin")
        return

    if type(message) is tuple:
        message = " ".join(message)

    if config["aprsd"].get("trace", False):
        trace.setup_tracing(["method", "api"])

    client.Client(config)
    stats.APRSDStats(config)
    messaging.MsgTrack(config=config)
    packets.WatchList(config=config)
    packets.SeenList(config=config)

    pm = plugin.PluginManager(config)
    if load_all:
        pm.setup_plugins()
    else:
        pm._init()
    obj = pm._create_class(plugin_path, plugin.APRSDPluginBase, config=config)
    if not obj:
        click.echo(ctx.get_help())
        click.echo("")
        ctx.fail(f"Failed to create object from plugin path '{plugin_path}'")
        ctx.exit()

    # Register the plugin they wanted tested.
    LOG.info(
        "Testing plugin {} Version {}".format(
            obj.__class__,
            obj.version,
        ), )
    pm._pluggy_pm.register(obj)
    login = config["aprs"]["login"]

    packet = {
        "from": fromcall,
        "addresse": login,
        "message_text": message,
        "format": "message",
        "msgNo": 1,
    }
    LOG.info(f"P'{plugin_path}'  F'{fromcall}'   C'{message}'")

    for x in range(number):
        reply = pm.run(packet)
        # Plugin might have threads, so lets stop them so we can exit.
        # obj.stop_threads()
        LOG.info(f"Result{x} = '{reply}'")
    pm.stop()
Example #10
0
File: main.py Project: wa9ecj/aprsd
def server(
    loglevel,
    quiet,
    disable_validation,
    config_file,
    flush,
):
    """Start the aprsd server process."""
    global flask_enabled
    signal.signal(signal.SIGINT, signal_handler)

    if not quiet:
        click.echo("Load config")
    config = utils.parse_config(config_file)

    # Force setting the config to the modules that need it
    # TODO(Walt): convert these modules to classes that can
    # Accept the config as a constructor param, instead of this
    # hacky global setting
    email.CONFIG = config

    setup_logging(config, loglevel, quiet)
    if config["aprsd"].get("trace", False):
        trace.setup_tracing(["method", "api"])
    LOG.info("APRSD Started version: {}".format(aprsd.__version__))
    stats.APRSDStats(config)

    email_enabled = config["aprsd"]["email"].get("enabled", False)

    if email_enabled:
        # TODO(walt): Make email processing/checking optional?
        # Maybe someone only wants this to process messages with plugins only.
        valid = email.validate_email_config(config, disable_validation)
        if not valid:
            LOG.error("Failed to validate email config options")
            sys.exit(-1)
    else:
        LOG.info("Email services not enabled.")

    # Create the initial PM singleton and Register plugins
    plugin_manager = plugin.PluginManager(config)
    plugin_manager.setup_plugins()
    try:
        cl = client.Client(config)
        cl.client
    except LoginError:
        sys.exit(-1)

    # Now load the msgTrack from disk if any
    if flush:
        LOG.debug("Deleting saved MsgTrack.")
        messaging.MsgTrack().flush()
    else:
        # Try and load saved MsgTrack list
        LOG.debug("Loading saved MsgTrack object.")
        messaging.MsgTrack().load()

    rx_msg_queue = queue.Queue(maxsize=20)
    tx_msg_queue = queue.Queue(maxsize=20)
    msg_queues = {"rx": rx_msg_queue, "tx": tx_msg_queue}

    rx_thread = threads.APRSDRXThread(msg_queues=msg_queues, config=config)
    tx_thread = threads.APRSDTXThread(msg_queues=msg_queues, config=config)

    if email_enabled:
        email_thread = email.APRSDEmailThread(msg_queues=msg_queues,
                                              config=config)
        email_thread.start()

    rx_thread.start()
    tx_thread.start()

    messaging.MsgTrack().restart()

    keepalive = threads.KeepAliveThread()
    keepalive.start()

    try:
        web_enabled = utils.check_config_option(config,
                                                ["aprsd", "web", "enabled"])
    except Exception:
        web_enabled = False

    if web_enabled:
        flask_enabled = True
        app = flask.init_flask(config)
        app.run(
            host=config["aprsd"]["web"]["host"],
            port=config["aprsd"]["web"]["port"],
        )

    # If there are items in the msgTracker, then save them
    LOG.info("APRSD Exiting.")
    return 0