Skip to content

systemd Service

The C daemon ships with a systemd service unit and environment file for production deployment on Linux systems.

Installation

cd daemon
make
sudo make install

This installs:

File Destination
timenow-daemon /usr/local/bin/timenow-daemon
timenow-daemon.service /lib/systemd/system/timenow-daemon.service
timenow-daemon.conf /etc/timenow/timenow-daemon.conf

Configuration

Edit the environment file with your location:

sudo nano /etc/timenow/timenow-daemon.conf
LATITUDE=48.2082
LONGITUDE=16.3738
INTERVAL=1
Variable Description Example
LATITUDE Decimal degrees (-90 to 90) 48.2082 (Vienna)
LONGITUDE Decimal degrees (-180 to 180) 16.3738 (Vienna)
INTERVAL Update interval in seconds 1

Service Management

# Enable and start
sudo systemctl enable --now timenow-daemon

# Check status
sudo systemctl status timenow-daemon

# View logs
journalctl -u timenow-daemon -f

# Restart after config change
sudo systemctl restart timenow-daemon

# Stop
sudo systemctl stop timenow-daemon

Unit File

The service unit (timenow-daemon.service) is configured as follows:

[Unit]
Description=TimeNow Solar Time Daemon
After=network.target gpsd.service
Wants=gpsd.service

[Service]
Type=forking
EnvironmentFile=/etc/timenow/timenow-daemon.conf
ExecStart=/usr/local/bin/timenow-daemon --lat=${LATITUDE} --lng=${LONGITUDE} --interval=${INTERVAL}
PIDFile=/run/timenow/timenow.pid
RuntimeDirectory=timenow
Restart=on-failure
RestartSec=5

# Security hardening
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/run/timenow /dev/shm
PrivateTmp=yes

[Install]
WantedBy=multi-user.target

Security Hardening

The service unit includes several systemd security features:

Directive Effect
NoNewPrivileges=yes Prevents privilege escalation
ProtectSystem=strict Mounts /usr, /boot, /efi read-only
ProtectHome=yes Makes /home, /root, /run/user inaccessible
ReadWritePaths=/run/timenow /dev/shm Only these paths are writable
PrivateTmp=yes Isolates /tmp and /var/tmp
RuntimeDirectory=timenow Creates /run/timenow with correct ownership

Additional Hardening (Optional)

For maximum isolation, add these directives to the [Service] section:

ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictNamespaces=yes
RestrictRealtime=yes
MemoryDenyWriteExecute=yes
SystemCallFilter=@system-service

Rust Daemon with systemd

The Rust daemon runs in the foreground and does not include a service file. Create one manually:

sudo nano /lib/systemd/system/timenow-daemon-rs.service
[Unit]
Description=TimeNow Solar Time Daemon (Rust)
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/timenow-daemon-rs --lat=48.2082 --lng=16.3738 --interval=60
RuntimeDirectory=timenow
Restart=on-failure
RestartSec=5

NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/run/timenow /dev/shm
PrivateTmp=yes

[Install]
WantedBy=multi-user.target

Note

The Rust daemon uses Type=simple (not forking) because it runs in the foreground.

Then enable it:

sudo systemctl daemon-reload
sudo systemctl enable --now timenow-daemon-rs

Logs

C Daemon

The C daemon is silent by default when running as a service. Use --foreground during development to see output.

Rust Daemon

The Rust daemon always logs to stderr, which systemd captures in the journal:

journalctl -u timenow-daemon-rs -f

Example output:

[timenow] 2025-01-15 12:30:00 UTC | elev=18.45° az=192.34° offset=-1054.23s
[timenow] 2025-01-15 12:31:00 UTC | elev=18.52° az=192.78° offset=-1050.12s

Troubleshooting

Service fails to start

# Check for errors
journalctl -u timenow-daemon -e --no-pager

# Verify config file
cat /etc/timenow/timenow-daemon.conf

# Test manually
/usr/local/bin/timenow-daemon --lat=48.2 --lng=16.37 --foreground

Permission denied on /run/timenow

Ensure the RuntimeDirectory=timenow directive is present in the service file. This tells systemd to create /run/timenow with the correct permissions before starting the daemon.

GPS Integration

The service includes Wants=gpsd.service and After=gpsd.service so it starts after GPS is available. Future versions may read coordinates directly from gpsd.