Compare commits

..

No commits in common. "ea55d087b5b24480749113633703e40c7246a863" and "7c5b7fe031482712b7d08648b3f6dfba38dd7e11" have entirely different histories.

3 changed files with 70 additions and 227 deletions

View file

@ -50,22 +50,22 @@ When running as a background daemon, TimeTurner provides a web interface for mon
## 🚀 Installation ## 🚀 Installation
The `setup.sh` script compiles and installs the TimeTurner application. You can run it by cloning the repository with `git` or by using the `curl` command below for a git-free installation. The `setup.sh` script is provided to compile and install the TimeTurner application and its systemd service on a Debian-based system like Raspberry Pi OS.
### Prerequisites ### Prerequisites
- **Internet Connection**: To download dependencies. - **Rust and Cargo**: The script requires the Rust programming language toolchain. If you don't have it, install it from [rustup.rs](https://rustup.rs/).
- **Curl and Unzip**: The script requires `curl` to download files and `unzip` for the git-free method. The setup script will attempt to install these if they are missing.
### Running the Installer (Recommended) ### Running the Installer
This command downloads the latest version, unpacks it, and runs the setup script. Paste it into your Raspberry Pi terminal:
1. First, clone the repository:
```bash ```bash
curl -L https://github.com/cjfranko/NTP-Timeturner/archive/refs/heads/main.zip -o NTP-Timeturner.zip && \ git clone https://github.com/cjfranko/NTP-Timeturner.git
unzip NTP-Timeturner.zip && \ cd NTP-Timeturner
cd NTP-Timeturner-main && \ ```
chmod +x setup.sh && \ 2. Make the script executable and run it. The script will use `sudo` for commands that require root privileges, so it may ask for your password.
```bash
chmod +x setup.sh
./setup.sh ./setup.sh
``` ```
@ -73,14 +73,13 @@ chmod +x setup.sh && \
The installation script automates the following steps: The installation script automates the following steps:
1. **Installs Dependencies**: Installs `git`, `curl`, `unzip`, and necessary build tools. 1. **Compiles the Binary**: Runs `cargo build --release` to create an optimised executable.
2. **Compiles the Binary**: Runs `cargo build --release` to create an optimised executable. 2. **Creates Directories**: Creates `/opt/timeturner` to store the application files.
3. **Creates Directories**: Creates `/opt/timeturner` to store the application files. 3. **Installs Files**:
4. **Installs Files**:
- The compiled binary is copied to `/opt/timeturner/timeturner`. - The compiled binary is copied to `/opt/timeturner/timeturner`.
- The web interface assets from the `static/` directory are copied to `/opt/timeturner/static`. - The web interface assets from the `static/` directory are copied to `/opt/timeturner/static`.
- A symbolic link is created from `/usr/local/bin/timeturner` to the binary, allowing it to be run from any location. - A symbolic link is created from `/usr/local/bin/timeturner` to the binary, allowing it to be run from any location.
5. **Sets up Systemd Service**: 4. **Sets up Systemd Service**:
- Copies the `timeturner.service` file to `/etc/systemd/system/`. - Copies the `timeturner.service` file to `/etc/systemd/system/`.
- Enables the service to start automatically on system boot. - Enables the service to start automatically on system boot.

View file

@ -12,8 +12,8 @@ defaultNudgeMs: 2
# Time-turning offsets. All values are added to the incoming LTC time. # Time-turning offsets. All values are added to the incoming LTC time.
# These can be positive or negative. # These can be positive or negative.
timeturnerOffset: timeturnerOffset:
hours: 0 hours: 1
minutes: 0 minutes: 2
seconds: 0 seconds: 3
frames: 0 frames: 4
milliseconds: 0 milliseconds: 5

250
setup.sh
View file

@ -21,8 +21,7 @@ echo "Detected package manager: $PKG_MANAGER"
# --- Update System Packages --- # --- Update System Packages ---
echo "Updating system packages..." echo "Updating system packages..."
if [ "$PKG_MANAGER" == "apt" ]; then if [ "$PKG_MANAGER" == "apt" ]; then
sudo apt update sudo apt update && sudo apt upgrade -y
sudo DEBIAN_FRONTEND=noninteractive apt upgrade -y -o Dpkg::Options::="--force-confold"
elif [ "$PKG_MANAGER" == "dnf" ]; then elif [ "$PKG_MANAGER" == "dnf" ]; then
sudo dnf upgrade -y sudo dnf upgrade -y
elif [ "$PKG_MANAGER" == "pacman" ]; then elif [ "$PKG_MANAGER" == "pacman" ]; then
@ -49,29 +48,14 @@ fi
echo "Installing common build dependencies..." echo "Installing common build dependencies..."
if [ "$PKG_MANAGER" == "apt" ]; then if [ "$PKG_MANAGER" == "apt" ]; then
sudo apt update sudo apt update
sudo apt install -y build-essential libudev-dev pkg-config curl wget sudo apt install -y build-essential libudev-dev pkg-config curl
elif [ "$PKG_MANAGER" == "dnf" ]; then elif [ "$PKG_MANAGER" == "dnf" ]; then
sudo dnf install -y gcc make perl-devel libudev-devel pkg-config curl wget sudo dnf install -y gcc make perl-devel libudev-devel pkg-config curl
elif [ "$PKG_MANAGER" == "pacman" ]; then elif [ "$PKG_MANAGER" == "pacman" ]; then
sudo pacman -Sy --noconfirm base-devel libudev pkg-config curl sudo pacman -Sy --noconfirm base-devel libudev pkg-config curl
fi fi
echo "Common build dependencies installed." echo "Common build dependencies installed."
# --- Install Python dependencies for testing ---
echo "🐍 Installing Python dependencies for test scripts..."
if [ "$PKG_MANAGER" == "apt" ]; then
# python3-serial is the name for pyserial in apt
sudo apt install -y python3 python3-pip python3-serial
elif [ "$PKG_MANAGER" == "dnf" ]; then
# python3-pyserial is the name for pyserial in dnf
sudo dnf install -y python3 python3-pip python3-pyserial
elif [ "$PKG_MANAGER" == "pacman" ]; then
# python-pyserial is the name for pyserial in pacman
sudo pacman -Sy --noconfirm python python-pip python-pyserial
fi
# sudo pip3 install pyserial # This is replaced by the native package manager installs above
echo "✅ Python dependencies installed."
# --- Apply custom splash screen --- # --- Apply custom splash screen ---
if [[ "$(uname)" == "Linux" ]]; then if [[ "$(uname)" == "Linux" ]]; then
echo "🖼️ Applying custom splash screen..." echo "🖼️ Applying custom splash screen..."
@ -116,10 +100,10 @@ echo "Removing NTPD (if installed) and installing Chrony, NMTUI, Adjtimex..."
if [ "$PKG_MANAGER" == "apt" ]; then if [ "$PKG_MANAGER" == "apt" ]; then
sudo apt update sudo apt update
sudo apt remove -y ntp || true # Remove ntp if it exists, ignore if not sudo apt remove -y ntp || true # Remove ntp if it exists, ignore if not
sudo apt install -y chrony network-manager adjtimex sudo apt install -y chrony nmtui adjtimex
sudo systemctl enable chrony --now sudo systemctl enable chrony --now
elif [ "$PKG_MANAGER" == "dnf" ]; then elif [ "$PKG_MANAGER" == "dnf" ]; then
sudo dnf remove -y ntp sudo dnf remove -y ntp || true
sudo dnf install -y chrony NetworkManager-tui adjtimex sudo dnf install -y chrony NetworkManager-tui adjtimex
sudo systemctl enable chronyd --now sudo systemctl enable chronyd --now
elif [ "$PKG_MANAGER" == "pacman" ]; then elif [ "$PKG_MANAGER" == "pacman" ]; then
@ -132,142 +116,29 @@ fi
echo "NTPD removed (if present). Chrony, NMTUI, and Adjtimex installed and configured." echo "NTPD removed (if present). Chrony, NMTUI, and Adjtimex installed and configured."
# --- Configure Chrony to act as a local NTP server ---
echo "⚙️ Configuring Chrony to serve local time..."
# The path to chrony.conf can vary
if [ -f /etc/chrony/chrony.conf ]; then
CHRONY_CONF="/etc/chrony/chrony.conf"
elif [ -f /etc/chrony.conf ]; then
CHRONY_CONF="/etc/chrony.conf"
else
CHRONY_CONF=""
fi
if [ -n "$CHRONY_CONF" ]; then
# Comment out any existing pool, server, or sourcedir lines to prevent syncing with external sources
echo "Disabling external NTP sources..."
sudo sed -i -E 's/^(pool|server|sourcedir)/#&/' "$CHRONY_CONF"
# Add settings to the top of the file to serve local clock
# Using a temp file to prepend is safer than multiple sed calls
TEMP_CONF=$(mktemp)
cat <<EOF > "$TEMP_CONF"
# Serve the system clock as a reference at stratum 1
server 127.127.1.0
allow 127.0.0.0/8
local stratum 1
EOF
# Append the rest of the original config file after our new lines
cat "$CHRONY_CONF" >> "$TEMP_CONF"
sudo mv "$TEMP_CONF" "$CHRONY_CONF"
# Add settings to the bottom of the file to allow LAN clients
echo "Allowing LAN clients..."
sudo tee -a "$CHRONY_CONF" > /dev/null <<EOF
# Allow LAN clients to connect
allow 0.0.0.0/0
EOF
# Restart chrony to apply changes (service name can be chrony or chronyd)
echo "Restarting Chrony service..."
sudo systemctl restart chrony || sudo systemctl restart chronyd
echo "✅ Chrony configured."
else
echo "⚠️ Warning: chrony.conf not found. Skipping Chrony configuration."
fi
# --- Install and configure WiFi hotspot and captive portal --- # --- Install and configure WiFi hotspot and captive portal ---
echo "📡 Installing and configuring WiFi hotspot and captive portal..." echo "📡 Installing and configuring WiFi hotspot and captive portal..."
if [ "$PKG_MANAGER" == "apt" ]; then if [ "$PKG_MANAGER" == "apt" ]; then
# Stop the service if it's running from a previous installation to prevent "Text file busy" error. sudo apt install -y hostapd dnsmasq nodogsplash
echo "Stopping existing nodogsplash service before installation..." sudo systemctl unmask hostapd
sudo systemctl stop nodogsplash || true sudo systemctl enable hostapd
# We will use dnsmasq for DHCP, as the compiled nodogsplash version may not support the internal DHCP server.
# sudo apt-get remove --purge -y dnsmasq || true # This line is no longer needed.
# Install dependencies for hotspot and for building nodogsplash.
sudo apt install -y hostapd dnsmasq git libmicrohttpd-dev libjson-c-dev iptables
# Force iptables-legacy for nodogsplash
echo "Setting iptables-legacy mode for nodogsplash..."
sudo update-alternatives --set iptables /usr/sbin/iptables-legacy
sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
echo "Building and installing nodogsplash from source..."
# Create a temporary directory for the build
BUILD_DIR=$(mktemp -d)
git clone https://github.com/nodogsplash/nodogsplash.git "$BUILD_DIR"
# Run the build in a subshell to isolate the directory change
(
cd "$BUILD_DIR"
make
sudo make install
# Manually install the systemd service file as 'make install' might not do it.
# This makes the script more robust.
if [ -f "debian/nodogsplash.service" ]; then
echo "Manually installing systemd service file..."
sudo cp debian/nodogsplash.service /etc/systemd/system/nodogsplash.service
# Reload systemd to recognize the new service
sudo systemctl daemon-reload
else
echo "⚠️ Warning: nodogsplash.service file not found in source. Cannot set up as a service."
fi
)
# Clean up the build directory
sudo rm -rf "$BUILD_DIR"
echo "✅ nodogsplash installed successfully."
# Disable the standalone hostapd service to let NetworkManager manage it.
sudo systemctl disable hostapd
sudo systemctl mask hostapd
sudo systemctl enable nodogsplash sudo systemctl enable nodogsplash
else
echo "This script is designed for Debian-based systems like Raspberry Pi OS."
echo "Skipping WiFi hotspot setup."
fi fi
# Stop services to configure # Stop services to configure
# Ensure services exist before trying to stop them sudo systemctl stop hostapd
sudo systemctl stop hostapd || true sudo systemctl stop dnsmasq
sudo systemctl stop dnsmasq || true sudo systemctl stop nodogsplash
# Ensure NetworkManager is managing wlan0 by removing any conflicting configurations. # Configure static IP for wlan0
# This is the critical fix for the "No suitable device" error. echo "Configuring static IP for wlan0..."
echo "Ensuring NetworkManager is managing wlan0..." sudo sed -i '/^interface wlan0/d' /etc/dhcpcd.conf
sudo rm -f /etc/NetworkManager/conf.d/99-unmanaged-wlan0.conf sudo tee -a /etc/dhcpcd.conf > /dev/null <<EOF
sudo systemctl reload NetworkManager interface wlan0
static ip_address=10.0.252.1/24
# Configure static IP for wlan0 using NetworkManager (nmcli) nohook wpa_supplicant
echo "Configuring static IP for wlan0 using NetworkManager..." EOF
# Define the connection name
CON_NAME="TimeTurner-AP"
# If a connection with this name already exists, delete it to ensure a clean slate.
if nmcli c show --active | grep -q "$CON_NAME"; then
sudo nmcli c down "$CON_NAME" || true
fi
if nmcli c show | grep -q "$CON_NAME"; then
echo "Deleting existing '$CON_NAME' connection profile..."
sudo nmcli c delete "$CON_NAME" || true
fi
# Create a new connection profile for the Access Point with a static IP.
echo "Creating new '$CON_NAME' connection profile..."
sudo nmcli c add type wifi ifname wlan0 con-name "$CON_NAME" autoconnect yes ssid "TimeTurner"
sudo nmcli c modify "$CON_NAME" 802-11-wireless.mode ap 802-11-wireless.band bg
sudo nmcli c modify "$CON_NAME" 802-11-wireless-security.key-mgmt wpa-psk
sudo nmcli c modify "$CON_NAME" 802-11-wireless-security.psk "harry-ron-hermione"
sudo nmcli c modify "$CON_NAME" ipv4.method manual ipv4.addresses 10.0.252.1/24
# Configure dnsmasq for DHCP # Configure dnsmasq for DHCP
echo "Configuring dnsmasq..." echo "Configuring dnsmasq..."
@ -277,64 +148,44 @@ dhcp-range=10.0.252.10,10.0.252.50,255.255.255.0,24h
address=/#/10.0.252.1 address=/#/10.0.252.1
EOF EOF
# Configure hostapd
echo "Configuring hostapd..."
sudo tee /etc/hostapd/hostapd.conf > /dev/null <<EOF
interface=wlan0
driver=nl80211
ssid=TimeTurner
hw_mode=g
channel=7
wmm_enabled=0
macaddr_acl=0
auth_algs=1
ignore_broadcast_ssid=0
wpa=2
wpa_passphrase=harry-ron-hermione
wpa_key_mgmt=WPA-PSK
rsn_pairwise=CCMP
EOF
sudo sed -i 's|#DAEMON_CONF=""|DAEMON_CONF="/etc/hostapd/hostapd.conf"|' /etc/default/hostapd
# Configure nodogsplash for captive portal # Configure nodogsplash for captive portal
echo "Configuring nodogsplash..." echo "Configuring nodogsplash..."
sudo tee /etc/nodogsplash/nodogsplash.conf > /dev/null <<EOF sudo tee /etc/nodogsplash/nodogsplash.conf > /dev/null <<EOF
GatewayInterface wlan0 GatewayInterface wlan0
GatewayAddress 10.0.252.1 GatewayAddress 10.0.252.1
MaxClients 250 MaxClients 50
AuthIdleTimeout 480 ClientIdleTimeout 3600
FirewallRuleSet preauthenticated-users { FirewallRuleSet preauthenticated-users {
FirewallRule allow tcp port 80 FirewallRule allow tcp port 80
FirewallRule allow tcp port 53
FirewallRule allow udp port 53
} }
RedirectURL http://10.0.252.1/static/index.html RedirectURL http://10.0.252.1/static/index.html
EOF EOF
# Restart services in the correct order and add delays to prevent race conditions # Restart services
echo "Restarting services..." sudo systemctl restart dhcpcd
# Bring up the new AP connection using nmcli
sudo nmcli c up "$CON_NAME"
# Wait for the interface to come up and get the IP address
echo "Waiting for wlan0 to be configured..."
IP_CHECK=""
# Loop for up to 30 seconds waiting for the IP
for i in {1..15}; do
# The '|| true' prevents the script from exiting if grep finds no match
IP_CHECK=$(ip -4 addr show wlan0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}' || true)
if [ "$IP_CHECK" == "10.0.252.1" ]; then
break
fi
echo "Still waiting for IP..."
sleep 2
done
# Check for the IP address before starting nodogsplash
if [ "$IP_CHECK" == "10.0.252.1" ]; then
echo "✅ wlan0 configured with IP $IP_CHECK."
sudo systemctl restart dnsmasq sudo systemctl restart dnsmasq
if command -v nodogsplash &> /dev/null; then sudo systemctl restart hostapd
echo "Attempting to start nodogsplash service..." sudo systemctl restart nodogsplash
if ! sudo systemctl restart nodogsplash; then
echo "❌ nodogsplash service failed to start. Displaying logs..."
# Give a moment for logs to be written
sleep 2
sudo journalctl -u nodogsplash.service --no-pager -n 50
echo ""
echo "To debug further, run nodogsplash in the foreground with this command:"
echo " sudo /usr/bin/nodogsplash -f -d 7"
echo ""
exit 1
fi
echo "✅ nodogsplash service started successfully."
fi
else
echo "❌ Error: wlan0 failed to get the static IP 10.0.252.1. Found: '$IP_CHECK'."
echo "Please check 'sudo nmcli c show \"$CON_NAME\"' and 'ip addr show wlan0'."
exit 1
fi
echo "✅ WiFi hotspot and captive portal configured. SSID: TimeTurner, IP: 10.0.252.1" echo "✅ WiFi hotspot and captive portal configured. SSID: TimeTurner, IP: 10.0.252.1"
echo "Clients will be redirected to http://10.0.252.1/static/index.html" echo "Clients will be redirected to http://10.0.252.1/static/index.html"
@ -361,7 +212,6 @@ sudo ln -sf $INSTALL_DIR/timeturner $BIN_DIR/timeturner
echo "✅ Binary and assets installed to $INSTALL_DIR, and binary linked to $BIN_DIR." echo "✅ Binary and assets installed to $INSTALL_DIR, and binary linked to $BIN_DIR."
# 4. Install systemd service file # 4. Install systemd service file
# Only needed for Linux systems (e.g., Raspberry Pi OS)
if [[ "$(uname)" == "Linux" ]]; then if [[ "$(uname)" == "Linux" ]]; then
echo "⚙️ Installing systemd service for Linux..." echo "⚙️ Installing systemd service for Linux..."
sudo cp timeturner.service /etc/systemd/system/ sudo cp timeturner.service /etc/systemd/system/
@ -376,20 +226,14 @@ echo ""
echo "--- Setup Complete ---" echo "--- Setup Complete ---"
echo "The TimeTurner daemon is now installed." echo "The TimeTurner daemon is now installed."
echo "The working directory is $INSTALL_DIR." echo "The working directory is $INSTALL_DIR."
# Copy default config.yml from repo if it exists echo "A default 'config.yml' will be created there on first run."
if [ -f config.yml ]; then
sudo cp config.yml $INSTALL_DIR/
echo "Default 'config.yml' installed to $INSTALL_DIR."
else
echo "⚠️ No default 'config.yml' found in repository. Please add one if needed."
fi
echo "" echo ""
if [[ "$(uname)" == "Linux" ]]; then if [[ "$(uname)" == "Linux" ]]; then
echo "To start the service, run:" echo "To start the service, run:"
echo " sudo systemctl start timeturner.service" echo " sudo systemctl start timeturner.service"
echo "" echo ""
echo "To view live logs, run:" echo "To view live logs, run:"
echo " journalctl -u tim_turner.service -f" echo " journalctl -u timeturner.service -f"
echo "" echo ""
fi fi
echo "To run the interactive TUI instead, simply run from the project directory:" echo "To run the interactive TUI instead, simply run from the project directory:"