1
0
mirror of https://github.com/craigerl/aprsd.git synced 2026-06-16 04:48:48 -04:00
Commit Graph

1378 Commits

Author SHA1 Message Date
hemna db3a0428c0 Fix DupePacketFilter checking wrong packet's processed flag
DupePacketFilter.filter() was checking packet.processed (the newly
arrived duplicate) instead of found.processed (the previously stored
packet). Since all freshly decoded packets have processed=False by
default, the dupe detection branch was never reachable — every
duplicate message was passed through and re-processed.

This caused KM6LYW's retransmit of msg:9028 (via a different
digipeater path 63s after first receipt) to trigger NearestPlugin
a second time, sending 4 reply packets instead of 2 and resetting
the AckPacket retry counter back to (1 of 3).

Fix: check found.processed instead of packet.processed.
Update existing tests to reflect the correct variable under test.
Add regression test test_filter_aprs_retransmit_via_different_digi
that reproduces the exact production scenario.
2026-06-02 11:17:24 -04:00
hemna b064ac97a4 feat: add APRS Chat bulletin script 2026-05-21 14:52:03 -04:00
hemna 5cc918e5c2 fix: add socket lock to prevent TX/RX race causing stream corruption on retransmits
The RX reader thread sets setblocking(0) and the TX writer (via aprslib
sendall) sets setblocking(1) on the same socket without synchronization.
This race condition causes partial writes where other stations' APRS-IS
stream data gets concatenated onto retransmitted packets.

Add a shared _socket_lock between send() and _socket_readlines() so the
socket blocking mode is never changed by one thread while the other is
mid-operation.
2026-05-14 16:55:57 -04:00
hemna 9146ff76c9 fix: remove stale .client attribute access in send_message command
APRSDClient no longer has a .client property after the driver refactor
(commit 1c39546). Instantiating APRSDClient() is sufficient to trigger
connection via auto_connect=True.
2026-05-13 23:17:38 -04:00
hemna 0c515d45fe fix: filter stale BeaconPackets from PacketTrack on load from disk
Older versions persisted BeaconPackets to packettrack.json. On restart
these zombie beacons would be retransmitted by the scheduler. Now
PacketTrack.load() strips any BeaconPackets from the persisted data.

Workaround: delete ~/.config/aprsd/packettrack.json before restarting.
2026-05-13 12:14:21 -04:00
hemna d3281cff0b test: add tests for beacon/ack flood prevention and scheduler timing guards
Tests cover:
- BeaconPacket skipped in PacketTrack.tx() (fire-and-forget)
- AckPacket send_count not reset when same ack already tracked
- Heavy traffic scenario with 5 digi paths for same message
- Scheduler timing guards prevent threadpool race conditions
- Scheduler cleanup of max-retry packets
- MessagePacket still allows re-send (existing behavior preserved)
2026-05-13 11:44:49 -04:00
hemna d8134c4531 fix: prevent beacon and ack packet floods from PacketTrack retries
BeaconPackets are now skipped in PacketTrack — they are fire-and-forget
and never receive an ack, so tracking them caused the scheduler to
re-transmit them as unwanted duplicates.

AckPackets already being tracked are no longer reset when the same
message arrives via multiple digipeater paths, which was restarting
the retry counter and flooding RF with duplicate acks.

Added timing guards in both scheduler loops to prevent threadpool race
conditions where multiple workers could fire before send_count was
incremented.
2026-05-13 11:36:25 -04:00
hemna 3a47571b60 Merge pull request #224 from craigerl/dependabot/uv/uv-0.11.6
Bump uv from 0.9.26 to 0.11.6
2026-04-29 09:16:53 -04:00
dependabot[bot] 4ecdec0aa4 Bump uv from 0.9.26 to 0.11.6
Bumps [uv](https://github.com/astral-sh/uv) from 0.9.26 to 0.11.6.
- [Release notes](https://github.com/astral-sh/uv/releases)
- [Changelog](https://github.com/astral-sh/uv/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/uv/compare/0.9.26...0.11.6)

---
updated-dependencies:
- dependency-name: uv
  dependency-version: 0.11.6
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-10 19:49:05 +00:00
hemna 490ff41cdc feat: Add configurable stats_store_interval for stats file saves
Add stats_store_interval config option to control how frequently
the statsstore.json file is written to disk. Default remains 10
seconds for backward compatibility.

This allows reducing disk I/O in production deployments and
can help avoid potential file corruption issues when external
processes read the stats file.
2026-03-27 17:37:16 -04:00
hemna 27413ab8cf Merge pull request #223 from craigerl/feature/configurable-stale-timeout
feat: Add configurable stale_timeout for APRS-IS connections
2026-03-27 10:27:09 -04:00
hemna 314f4da180 fix: Restore max_delta after custom stale_timeout test
The singleton's max_delta was being modified by test_init_custom_stale_timeout
and not restored, causing test_is_stale_connection_false to fail because
it expected 2 minutes but got 60 seconds.
2026-03-27 10:18:07 -04:00
hemna f6eb383caf fix: Update test to work with singleton pattern
The APRSISDriver uses @singleton decorator which transforms the class
into a function. The test was incorrectly trying to use __new__ which
doesn't work with decorated singletons. Instead, re-initialize the
existing instance after changing the config.
2026-03-27 10:13:55 -04:00
hemna 930339d4cf feat: Add configurable stale_timeout for APRS-IS connections
Add a new 'stale_timeout' configuration option to the aprs_network config
group that allows users to customize how long to wait before considering
an APRS-IS connection stale.

Problem:
The stale connection threshold was hardcoded to 2 minutes. In environments
with frequent network hiccups or when using certain APRS-IS servers that
may drop connections silently, 2 minutes can be too long to wait before
reconnecting, resulting in significant data loss.

Solution:
- Add 'stale_timeout' option to aprsd/conf/client.py with default of 120s
- Update APRSISDriver.__init__ to use the config value
- Maintain backward compatibility by defaulting to 120s if not configured
- Update tests to handle the new configuration option

Usage:
  [aprs_network]
  stale_timeout = 60  # Reconnect after 60 seconds without data

The default remains 120 seconds (2 minutes) for backward compatibility.
2026-03-27 10:05:44 -04:00
hemna a2e07a2279 Merge pull request #221 from craigerl/fix-urllib3-security
security: bump urllib3 from 2.6.2 to 2.6.3
2026-03-24 13:49:17 -04:00
hemna 3a12ccb842 security: bump urllib3 from 2.6.2 to 2.6.3
Fixes CVE-2026-21441 (8.9 High severity) - decompression-bomb safeguards
of the streaming API were bypassed when HTTP redirects were followed.

Closes #210
2026-03-24 13:43:44 -04:00
github-actions[bot] 5463b8d5b5 chore: update AUTHORS [skip ci] 2026-03-24 17:32:35 +00:00
hemna f2526efe1d Merge pull request #219 from craigerl/feature-daemon-threads-event-refactor
Refactor threads to use daemon threads and Event-based timing
2026-03-24 13:32:26 -04:00
hemna 96b017d59e chore: update uv.lock for uv 0.11.0 compatibility 2026-03-24 13:26:33 -04:00
hemna 8d8648e9dd style(threads): add return type to loop() and use modern type hints
- Add -> bool return type annotation to abstract loop() method
- Replace 'from typing import List' with built-in list[] (Python 3.9+)
2026-03-24 13:22:37 -04:00
hemna bf258e4bcf chore(tests): fix unused variable linter warning in test_stats.py 2026-03-24 13:22:37 -04:00
hemna 4ab59c6cf3 refactor(main): update signal handler for Event-based thread shutdown
- Replace time.sleep(1.5) with thread_list.join_non_daemon(timeout=5.0)
- Remove unused import time since time.sleep is no longer used
- Remove outdated commented-out code
- Improve log message (removed '10 seconds' reference)
2026-03-24 13:22:37 -04:00
hemna 505c0fa8a8 refactor(threads): migrate APRSRegistryThread to Event-based timing
- Set self.period=CONF.aprs_registry.frequency_seconds in __init__
- Remove counter-based conditional (loop every N seconds pattern)
- Replace time.sleep(1) with self.wait()
- Remove _loop_cnt tracking (use inherited loop_count from base)
- Remove unused time import
2026-03-24 13:22:37 -04:00
hemna 85ebf8a274 refactor(threads): migrate TX threads to Event-based timing
- PacketSendSchedulerThread: Add daemon=False, replace time.sleep with self.wait
- AckSendSchedulerThread: Add daemon=False, replace time.sleep with self.wait
- SendPacketThread: Replace time.sleep with self.wait, remove manual loop_count
- SendAckThread: Replace time.sleep with self.wait, remove manual loop_count
- BeaconSendThread: Set self.period=CONF.beacon_interval, remove counter-based
  conditional, replace time.sleep with self.wait, remove _loop_cnt tracking
- Update tests to use new Event-based API
2026-03-24 13:22:37 -04:00
hemna bc9ce61e59 refactor(threads): migrate RX threads to Event-based timing
- APRSDRXThread: Replace time.sleep with self.wait for interruptible waits
- APRSDRXThread.stop(): Use _shutdown_event.set() instead of thread_stop
- APRSDRXThread: Error recovery waits check for shutdown signal
- APRSDFilterThread: Use queue timeout with self.period for interruptible wait
- Remove unused time import
- Update tests to use new Event-based API
2026-03-24 13:22:37 -04:00
hemna 343ec3e81c refactor(threads): migrate stats threads to Event-based timing 2026-03-24 13:22:37 -04:00
hemna 44b8bc572d refactor(threads): migrate KeepAliveThread to Event-based timing 2026-03-24 13:22:36 -04:00
hemna 43ba69e352 feat(threads): add join_non_daemon() to APRSDThreadList
Allows graceful shutdown by waiting for non-daemon threads to complete
while allowing daemon threads to be terminated immediately.
2026-03-24 13:22:36 -04:00
hemna b7a37322e1 refactor(threads): add daemon, period, Event-based shutdown to APRSDThread
- Add daemon=True class attribute (subclasses override to False)
- Add period=1 class attribute for wait interval
- Replace thread_stop boolean with _shutdown_event (threading.Event)
- Add wait() method for interruptible sleeps
- Update tests for new Event-based API

BREAKING: thread_stop boolean replaced with _shutdown_event.
Code checking thread.thread_stop directly must use thread._shutdown_event.is_set()
2026-03-24 13:22:36 -04:00
hemna bc9b15d47a Add implementation plan for daemon threads and Event-based timing refactor 2026-03-24 13:22:36 -04:00
hemna d8747317df Add design spec for daemon threads and Event-based timing refactor 2026-03-24 13:22:36 -04:00
github-actions[bot] 4cc90a53ed chore: update AUTHORS [skip ci] 2026-03-24 17:12:56 +00:00
dependabot[bot] 425ad469b0 Bump marshmallow from 3.26.1 to 3.26.2 (#207)
Bumps [marshmallow](https://github.com/marshmallow-code/marshmallow) from 3.26.1 to 3.26.2.
- [Changelog](https://github.com/marshmallow-code/marshmallow/blob/3.26.2/CHANGELOG.rst)
- [Commits](https://github.com/marshmallow-code/marshmallow/compare/3.26.1...3.26.2)

---
updated-dependencies:
- dependency-name: marshmallow
  dependency-version: 3.26.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-24 13:12:45 -04:00
hemna bbc2ccd302 Merge pull request #214 from craigerl/fix/cli-command-issues
Fix CLI command inconsistencies
2026-02-28 09:33:31 -05:00
hemna 698d218572 Fix JSON serialization of UnknownPacket in stats
The SimpleJSONEncoder didn't handle dataclasses like UnknownPacket,
causing a TypeError when saving stats to disk. Added support for
dataclasses using dataclasses.asdict().
2026-02-27 23:40:14 -05:00
hemna fcfb349d29 Fix CLI command inconsistencies
- Refactor duplicate plugin discovery code into aprsd/utils/package.py
- Fix inconsistent --profile option in listen.py (now uses common_options)
- Add common_options decorator to completion command for consistency
- Improve healthcheck error message for missing APRSClientStats
- Consolidate signal handler in listen.py to use shared one from main.py
2026-02-27 23:35:38 -05:00
hemna 7172d6352f address failure for healthcheck 2026-02-19 13:33:55 -05:00
hemna bc8a24bea4 Fix master-build.yml: remove zero-width spaces from GitHub Actions template syntax 2026-02-19 08:33:18 -05:00
hemna ac35c44bc2 another attempt at master build for github 2026-02-18 21:58:54 -05:00
hemna 8483d93965 try and fix master build 2026-02-18 14:30:59 -05:00
hemna 0248f40604 fixed some type declaration inconsistencies. 2026-02-18 14:20:52 -05:00
hemna 6ea9889369 make consumer call signature consistent. 2026-02-18 14:11:25 -05:00
hemna 2b7e42802b update the keepalive for kiss 2026-02-18 14:00:52 -05:00
hemna c99a9c919d fixed inconsistent driver send() declaration.
The KISS drivers aren't adhering to the protocol for
defining the send() method.  both now specify that they
return a bool.
2026-02-18 13:49:40 -05:00
hemna 202c689658 Replace insecure pickle serialization with JSON
SECURITY FIX: Replace pickle.load() with json.load() to eliminate
remote code execution vulnerability from malicious pickle files.

Changes:
- Update ObjectStoreMixin to use JSON instead of pickle
- Add PacketJSONDecoder to reconstruct Packet objects from JSON
- Change file extension from .p to .json
- Add warning when old pickle files detected
- Add OrderedDict restoration for PacketList
- Update all tests to work with JSON format

Users with existing pickle files must run:
  aprsd dev migrate-pickle

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-17 16:07:55 -05:00
hemna 0701db2629 docs on listen 2026-02-10 19:09:07 -05:00
hemna c5ca4f11af Added new APRSDPushStatsThread
This allows an aprsd server instance to push it's to a remote
location.
2026-02-10 18:49:23 -05:00
hemna 008fe3c83e Fixed an issue with dev command 2026-02-07 17:17:15 -05:00
hemna 3128f24ef7 reverse the threaded plugin processing.
Considering how little processing each plugin has, it's
a bit overkill for now to have a threaded processing of plugins.

Also had issues where the help plugin was responding when it shouldn't.
2026-02-06 17:31:05 -05:00
hemna 6968f16cec update multiarch build on tag 2026-02-05 17:23:35 -05:00