Files
fgAI/start.sh
2026-06-21 15:04:23 +02:00

247 lines
7.7 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
VENV_DIR="${FGAI_VENV_DIR:-$ROOT_DIR/.venv}"
PORT="${FGAI_SYSLOG_PORT:-5514}"
HOST="${FGAI_SYSLOG_HOST:-0.0.0.0}"
LOG_FILE="${FGAI_SYSLOG_FILE:-$ROOT_DIR/logs/fg_syslog.jsonl}"
LOG_ROTATE_BYTES="${FGAI_LOG_ROTATE_BYTES:-26214400}"
LOG_ROTATE_COUNT="${FGAI_LOG_ROTATE_COUNT:-14}"
LISTENER_LOG="${FGAI_LISTENER_LOG:-$ROOT_DIR/logs/fgai-listener.log}"
POLICY_FILE="${FGAI_POLICY_FILE:-$ROOT_DIR/exports/policies.json}"
STATE_FILE="${FGAI_STATE_FILE:-$ROOT_DIR/state/fgai-status.json}"
BASELINE_DB="${FGAI_BASELINE_DB:-$ROOT_DIR/state/fgai-baseline.sqlite3}"
MONITOR_INTERVAL="${FGAI_MONITOR_INTERVAL:-10}"
LLM_ENABLED="${FGAI_LLM:-0}"
LLM_INTERVAL="${FGAI_LLM_INTERVAL:-300}"
LLM_TIMEOUT="${OLLAMA_TIMEOUT:-180}"
LLM_MODEL="${OLLAMA_MODEL:-}"
DASHBOARD_HOST="${FGAI_DASHBOARD_HOST:-127.0.0.1}"
DASHBOARD_PORT="${FGAI_DASHBOARD_PORT:-8088}"
LISTENER_PID_FILE="${FGAI_LISTENER_PID_FILE:-$ROOT_DIR/run/fgai-listener.pid}"
MONITOR_PID_FILE="${FGAI_MONITOR_PID_FILE:-$ROOT_DIR/run/fgai-monitor.pid}"
DASHBOARD_PID_FILE="${FGAI_DASHBOARD_PID_FILE:-$ROOT_DIR/run/fgai-dashboard.pid}"
MONITOR_LOG="${FGAI_MONITOR_LOG:-$ROOT_DIR/logs/fgai-monitor.log}"
DASHBOARD_LOG="${FGAI_DASHBOARD_LOG:-$ROOT_DIR/logs/fgai-dashboard.log}"
usage() {
printf 'Usage: %s [start|stop|restart|status|tail|analyze|install]\n' "$0"
printf '\nDefault action: start\n'
printf '\nEnvironment overrides:\n'
printf ' FGAI_SYSLOG_PORT=%s\n' "$PORT"
printf ' FGAI_SYSLOG_HOST=%s\n' "$HOST"
printf ' FGAI_SYSLOG_FILE=%s\n' "$LOG_FILE"
printf ' FGAI_LOG_ROTATE_BYTES=%s\n' "$LOG_ROTATE_BYTES"
printf ' FGAI_LOG_ROTATE_COUNT=%s\n' "$LOG_ROTATE_COUNT"
printf ' FGAI_LISTENER_LOG=%s\n' "$LISTENER_LOG"
printf ' FGAI_DASHBOARD_PORT=%s\n' "$DASHBOARD_PORT"
printf ' FGAI_LLM=%s\n' "$LLM_ENABLED"
}
activate_venv_for_script() {
# Activation here affects this script process. It cannot modify the parent shell prompt.
# Use "source .venv/bin/activate" manually if you want your interactive prompt to show it.
# shellcheck disable=SC1091
source "$VENV_DIR/bin/activate"
}
install_deps() {
if [ ! -x "$VENV_DIR/bin/python" ]; then
printf 'Creating venv: %s\n' "$VENV_DIR"
python3 -m venv "$VENV_DIR"
fi
activate_venv_for_script
printf 'Installing/updating fgAI dependencies...\n'
python -m pip install --upgrade pip
python -m pip install -e "$ROOT_DIR"
}
is_pid_running() {
[ -f "$1" ] && ps -p "$(cat "$1")" >/dev/null 2>&1
}
needs_privileged_port() {
[ "$PORT" -lt 1024 ] && [ "${EUID:-$(id -u)}" -ne 0 ]
}
listener_command() {
"$VENV_DIR/bin/fgai" listen-syslog \
--host "$HOST" \
--port "$PORT" \
--output "$LOG_FILE" \
--quiet
}
start_listener() {
install_deps
mkdir -p "$(dirname "$LOG_FILE")" "$(dirname "$LISTENER_LOG")" "$(dirname "$LISTENER_PID_FILE")" "$(dirname "$STATE_FILE")"
touch "$LOG_FILE"
if is_pid_running "$LISTENER_PID_FILE"; then
printf 'fgAI syslog listener already running, pid %s\n' "$(cat "$LISTENER_PID_FILE")"
return 0
fi
if needs_privileged_port; then
printf 'Port %s needs elevated privileges; starting listener with sudo.\n' "$PORT"
sudo nohup "$VENV_DIR/bin/fgai" listen-syslog \
--host "$HOST" \
--port "$PORT" \
--output "$LOG_FILE" \
--rotate-bytes "$LOG_ROTATE_BYTES" \
--rotate-count "$LOG_ROTATE_COUNT" \
--quiet > "$LISTENER_LOG" 2>&1 &
else
nohup "$VENV_DIR/bin/fgai" listen-syslog \
--host "$HOST" \
--port "$PORT" \
--output "$LOG_FILE" \
--rotate-bytes "$LOG_ROTATE_BYTES" \
--rotate-count "$LOG_ROTATE_COUNT" \
--quiet > "$LISTENER_LOG" 2>&1 &
fi
printf '%s\n' "$!" > "$LISTENER_PID_FILE"
printf 'Started fgAI syslog listener, pid %s\n' "$(cat "$LISTENER_PID_FILE")"
printf 'Input: udp://%s:%s\n' "$HOST" "$PORT"
printf 'Syslog file: %s\n' "$LOG_FILE"
printf 'Process log: %s\n' "$LISTENER_LOG"
}
start_monitor() {
install_deps
mkdir -p "$(dirname "$STATE_FILE")" "$(dirname "$MONITOR_LOG")" "$(dirname "$MONITOR_PID_FILE")"
if is_pid_running "$MONITOR_PID_FILE"; then
printf 'fgAI monitor already running, pid %s\n' "$(cat "$MONITOR_PID_FILE")"
return 0
fi
monitor_args=(monitor --logs "$LOG_FILE" --output "$STATE_FILE" --interval "$MONITOR_INTERVAL" --baseline-db "$BASELINE_DB")
if [ -f "$POLICY_FILE" ]; then
monitor_args+=(--policies "$POLICY_FILE")
fi
if [ "$LLM_ENABLED" = "1" ] || [ "$LLM_ENABLED" = "true" ]; then
monitor_args+=(--llm --llm-interval "$LLM_INTERVAL" --llm-timeout "$LLM_TIMEOUT")
if [ -n "$LLM_MODEL" ]; then
monitor_args+=(--model "$LLM_MODEL")
fi
fi
nohup "$VENV_DIR/bin/fgai" "${monitor_args[@]}" > "$MONITOR_LOG" 2>&1 &
printf '%s\n' "$!" > "$MONITOR_PID_FILE"
printf 'Started fgAI monitor, pid %s\n' "$(cat "$MONITOR_PID_FILE")"
printf 'Status file: %s\n' "$STATE_FILE"
}
start_dashboard() {
install_deps
mkdir -p "$(dirname "$DASHBOARD_LOG")" "$(dirname "$DASHBOARD_PID_FILE")"
if is_pid_running "$DASHBOARD_PID_FILE"; then
printf 'fgAI dashboard already running, pid %s\n' "$(cat "$DASHBOARD_PID_FILE")"
return 0
fi
nohup "$VENV_DIR/bin/fgai" dashboard \
--host "$DASHBOARD_HOST" \
--port "$DASHBOARD_PORT" \
--status-file "$STATE_FILE" \
--image-dir "$ROOT_DIR/images" > "$DASHBOARD_LOG" 2>&1 &
printf '%s\n' "$!" > "$DASHBOARD_PID_FILE"
printf 'Started fgAI dashboard, pid %s\n' "$(cat "$DASHBOARD_PID_FILE")"
printf 'Dashboard: http://%s:%s\n' "$DASHBOARD_HOST" "$DASHBOARD_PORT"
}
start_all() {
start_listener
start_monitor
start_dashboard
}
stop_pid() {
label="$1"
pid_file="$2"
if ! is_pid_running "$pid_file"; then
printf '%s is not running\n' "$label"
rm -f "$pid_file"
return 0
fi
if ! kill "$(cat "$pid_file")" 2>/dev/null; then
sudo kill "$(cat "$pid_file")"
fi
rm -f "$pid_file"
printf 'Stopped %s\n' "$label"
}
stop_all() {
stop_pid "fgAI dashboard" "$DASHBOARD_PID_FILE"
stop_pid "fgAI monitor" "$MONITOR_PID_FILE"
stop_pid "fgAI syslog listener" "$LISTENER_PID_FILE"
}
status_one() {
label="$1"
pid_file="$2"
if is_pid_running "$pid_file"; then
printf '%s running, pid %s\n' "$label" "$(cat "$pid_file")"
return 0
fi
printf '%s is not running\n' "$label"
return 1
}
status_all() {
status_one "fgAI syslog listener" "$LISTENER_PID_FILE" || true
if is_pid_running "$LISTENER_PID_FILE"; then
printf 'Input: udp://%s:%s\n' "$HOST" "$PORT"
printf 'Syslog file: %s\n' "$LOG_FILE"
if command -v ss >/dev/null 2>&1; then
ss -lunp 2>/dev/null | awk -v port=":$PORT" '$0 ~ port {print}'
fi
fi
status_one "fgAI monitor" "$MONITOR_PID_FILE" || true
status_one "fgAI dashboard" "$DASHBOARD_PID_FILE" || true
printf 'Dashboard: http://%s:%s\n' "$DASHBOARD_HOST" "$DASHBOARD_PORT"
printf 'Status file: %s\n' "$STATE_FILE"
}
command="${1:-start}"
case "$command" in
start)
start_all
;;
stop)
stop_all
;;
restart)
stop_all
start_all
;;
status)
status_all
;;
tail)
mkdir -p "$(dirname "$LOG_FILE")"
touch "$LOG_FILE"
tail -f "$LOG_FILE"
;;
analyze)
install_deps
fgai analyze-logs --logs "$LOG_FILE"
;;
install)
install_deps
;;
-h|--help|help)
usage
;;
*)
usage
exit 2
;;
esac