mirror of
https://github.com/smittix/intercept.git
synced 2026-04-24 06:40:00 -07:00
fix: prevent root-owned data files from breaking radiosonde start
Running via sudo creates data/radiosonde/ as root. On next run the config write fails with an unhandled OSError, Flask returns an HTML 500, and the frontend shows a cryptic JSON parse error. Three-layer fix: - start.sh: pre-create known data dirs before chown, add certs/ to the list, export INTERCEPT_SUDO_UID/GID for runtime use - generate_station_cfg: catch OSError with actionable message, chown newly created files to the real user via _fix_data_ownership() - start_radiosonde: wrap config generation in try/except so it returns JSON instead of letting Flask emit an HTML error page Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -243,13 +243,40 @@ radius_temporary_block = False
|
||||
sonde_time_threshold = 3
|
||||
"""
|
||||
|
||||
with open(cfg_path, 'w') as f:
|
||||
f.write(cfg)
|
||||
try:
|
||||
with open(cfg_path, 'w') as f:
|
||||
f.write(cfg)
|
||||
except OSError as e:
|
||||
logger.error(f"Cannot write station.cfg to {cfg_path}: {e}")
|
||||
raise RuntimeError(
|
||||
f"Cannot write radiosonde config to {cfg_path}: {e}. "
|
||||
f"Fix permissions with: sudo chown -R $(whoami) {cfg_dir}"
|
||||
) from e
|
||||
|
||||
# When running as root via sudo, fix ownership so next non-root run
|
||||
# can still read/write these files.
|
||||
_fix_data_ownership(cfg_dir)
|
||||
|
||||
logger.info(f"Generated station.cfg at {cfg_path}")
|
||||
return cfg_path
|
||||
|
||||
|
||||
def _fix_data_ownership(path: str) -> None:
|
||||
"""Recursively chown a path to the real (non-root) user when running via sudo."""
|
||||
uid = os.environ.get('INTERCEPT_SUDO_UID')
|
||||
gid = os.environ.get('INTERCEPT_SUDO_GID')
|
||||
if uid is None or gid is None:
|
||||
return
|
||||
try:
|
||||
uid_int, gid_int = int(uid), int(gid)
|
||||
for dirpath, dirnames, filenames in os.walk(path):
|
||||
os.chown(dirpath, uid_int, gid_int)
|
||||
for fname in filenames:
|
||||
os.chown(os.path.join(dirpath, fname), uid_int, gid_int)
|
||||
except OSError as e:
|
||||
logger.warning(f"Could not fix ownership of {path}: {e}")
|
||||
|
||||
|
||||
def parse_radiosonde_udp(udp_port: int) -> None:
|
||||
"""Thread function: listen for radiosonde_auto_rx UDP JSON telemetry."""
|
||||
global radiosonde_running, _udp_socket
|
||||
@@ -532,17 +559,22 @@ def start_radiosonde():
|
||||
}), 409
|
||||
|
||||
# Generate config
|
||||
cfg_path = generate_station_cfg(
|
||||
freq_min=freq_min,
|
||||
freq_max=freq_max,
|
||||
gain=gain,
|
||||
device_index=device_int,
|
||||
ppm=ppm,
|
||||
bias_t=bias_t,
|
||||
latitude=latitude,
|
||||
longitude=longitude,
|
||||
gpsd_enabled=gpsd_enabled,
|
||||
)
|
||||
try:
|
||||
cfg_path = generate_station_cfg(
|
||||
freq_min=freq_min,
|
||||
freq_max=freq_max,
|
||||
gain=gain,
|
||||
device_index=device_int,
|
||||
ppm=ppm,
|
||||
bias_t=bias_t,
|
||||
latitude=latitude,
|
||||
longitude=longitude,
|
||||
gpsd_enabled=gpsd_enabled,
|
||||
)
|
||||
except (OSError, RuntimeError) as e:
|
||||
app_module.release_sdr_device(device_int, sdr_type_str)
|
||||
logger.error(f"Failed to generate radiosonde config: {e}")
|
||||
return jsonify({'status': 'error', 'message': str(e)}), 500
|
||||
|
||||
# Build command - auto_rx -c expects a config DIRECTORY containing station.cfg
|
||||
cfg_dir = os.path.dirname(os.path.abspath(cfg_path))
|
||||
|
||||
15
start.sh
15
start.sh
@@ -79,12 +79,25 @@ export INTERCEPT_HOST="$HOST"
|
||||
export INTERCEPT_PORT="$PORT"
|
||||
|
||||
# ── Fix ownership of user data dirs when run via sudo ────────────────────────
|
||||
# When invoked via sudo the server process runs as root, so every file it
|
||||
# creates (configs, logs, database) ends up owned by root. On the *next*
|
||||
# startup we fix that retroactively, and we also pre-create known runtime
|
||||
# directories so they get correct ownership from the start.
|
||||
if [[ "$(id -u)" -eq 0 && -n "${SUDO_USER:-}" ]]; then
|
||||
for dir in instance data; do
|
||||
# Pre-create directories that routes may need at runtime
|
||||
mkdir -p "$SCRIPT_DIR/instance" \
|
||||
"$SCRIPT_DIR/data/radiosonde/logs" \
|
||||
"$SCRIPT_DIR/data/weather_sat"
|
||||
|
||||
for dir in instance data certs; do
|
||||
if [[ -d "$SCRIPT_DIR/$dir" ]]; then
|
||||
chown -R "$SUDO_USER" "$SCRIPT_DIR/$dir"
|
||||
fi
|
||||
done
|
||||
|
||||
# Export real user identity so Python can chown runtime-created files
|
||||
export INTERCEPT_SUDO_UID="$(id -u "$SUDO_USER")"
|
||||
export INTERCEPT_SUDO_GID="$(id -g "$SUDO_USER")"
|
||||
fi
|
||||
|
||||
# ── Dependency check (delegate to intercept.py) ─────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user