# Makefile - Pelote
#
# Copyright (C) 2026 SliTaz GNU/Linux - BSD License, see LICENSE
#
# Cook exports its own CFLAGS and LDFLAGS: everything pelote needs to
# build lives in the := variables below, so it cannot be dropped.
#
# Building against a sysroot (dependencies unpacked, not installed):
#   make SYSROOT=/path/to/fs check

VERSION  := 0.1

PREFIX   ?= /usr
DESTDIR  ?=

CC       ?= cc
CFLAGS   ?= -O2
WERROR   ?= -Werror
# A daemon facing the network and a setuid helper: an overflow must
# abort, not run. HARDEN= turns it off (a -O0 debug build, for one).
HARDEN    ?= -D_FORTIFY_SOURCE=2 -fstack-protector-strong -fPIE
RELRO_LD  ?= -Wl,-z,relro,-z,now
HARDEN_LD ?= -pie $(RELRO_LD)
# noise-c's static libraries are not built PIC yet: what links them
# cannot be a PIE. Set DAEMON_PIE=-pie once noise-c-dev is rebuilt.
DAEMON_PIE ?=

PL_CFLAGS := -std=c99 -D_GNU_SOURCE -Wall -Wextra $(WERROR) $(HARDEN) -Ilib \
             -MMD -MP -DPELOTE_VERSION='"$(VERSION)"' \
             -DPELOTE_HELPER='"$(PREFIX)/libexec/pelote-helper"'
PL_LDFLAGS := $(HARDEN_LD)
# The same without -pie, for what links noise-c
NOISE_LDFLAGS := $(DAEMON_PIE) $(RELRO_LD)

ifneq ($(SYSROOT),)
PL_CFLAGS  += -I$(SYSROOT)/usr/include
PL_LDFLAGS += -L$(SYSROOT)/usr/lib
NOISE_LDFLAGS += -L$(SYSROOT)/usr/lib
RUNENV     := LD_LIBRARY_PATH=$(SYSROOT)/usr/lib
endif

LIB_OBJS := lib/msg.o lib/env.o lib/ws.o

TESTS := tests/test_msg tests/test_env tests/test_ws tests/test_fuzz tests/test_relay \
         tests/test_daemon

all: relay/pelote-relay daemon/pelote daemon/pelote-helper

%.o: %.c
	$(CC) $(CFLAGS) $(PL_CFLAGS) -c -o $@ $<

tests/test_msg: tests/test_msg.o lib/msg.o
	$(CC) $(LDFLAGS) $(PL_LDFLAGS) -o $@ $^ -lcjson -lm

tests/test_env: tests/test_env.o lib/env.o lib/msg.o
	$(CC) $(LDFLAGS) $(PL_LDFLAGS) -o $@ $^ -lcjson -lm

tests/test_ws: tests/test_ws.o lib/ws.o
	$(CC) $(LDFLAGS) $(PL_LDFLAGS) -o $@ $^ -lcrypto

tests/test_relay: tests/test_relay.o lib/ws.o lib/env.o lib/msg.o relay/pelote-relay
	$(CC) $(LDFLAGS) $(PL_LDFLAGS) -o $@ $(filter %.o,$^) -lcjson -lcrypto -lm

relay/pelote-relay: relay/relay.o lib/ws.o lib/env.o lib/msg.o
	$(CC) $(LDFLAGS) $(PL_LDFLAGS) -o $@ $^ -lcjson -lcrypto -lm

# noise-c only ships static libraries, built on libsodium
DAEMON_OBJS := daemon/main.o daemon/daemon.o daemon/conf.o daemon/keys.o \
               daemon/action.o daemon/monitor.o daemon/link.o daemon/ctl.o \
               daemon/qr.o lib/ws.o lib/env.o lib/msg.o
DAEMON_LIBS := -lnoiseprotocol -lsodium -lqrencode -lcjson -lssl -lcrypto \
               -lm -pthread

daemon/pelote: $(DAEMON_OBJS)
	$(CC) $(LDFLAGS) $(NOISE_LDFLAGS) -o $@ $^ $(DAEMON_LIBS)

daemon/pelote-helper: daemon/helper.o daemon/action.o lib/msg.o
	$(CC) $(LDFLAGS) $(PL_LDFLAGS) -o $@ $^ -lcjson -lm

tests/test_daemon: tests/test_daemon.o lib/ws.o lib/env.o lib/msg.o \
                   relay/pelote-relay daemon/pelote
	$(CC) $(LDFLAGS) $(NOISE_LDFLAGS) -o $@ $(filter %.o,$^) $(DAEMON_LIBS)

# A command line stand-in for the Android app, for testing by hand
tests/fakeapp: tests/fakeapp.o daemon/link.o lib/ws.o lib/env.o lib/msg.o
	$(CC) $(LDFLAGS) $(NOISE_LDFLAGS) -o $@ $^ $(DAEMON_LIBS)

tests/test_fuzz: tests/test_fuzz.o lib/ws.o lib/env.o lib/msg.o
	$(CC) $(LDFLAGS) $(PL_LDFLAGS) -o $@ $^ -lcjson -lcrypto -lm

check: $(TESTS)
	@for t in $(TESTS); do $(RUNENV) ./$$t || exit 1; done

# Everything rebuilt with undefined behaviour checks that trap (no
# runtime library needed: gcc 6 on SliTaz has no libubsan nor libasan)
# and run with glibc's heap checks; the fuzzer goes for 100000 rounds.
SANITIZE := -O1 -g -fsanitize=undefined -fsanitize-undefined-trap-on-error
check-sanitize:
	$(MAKE) clean
	$(MAKE) CFLAGS="$(SANITIZE)" $(TESTS)
	@for t in $(TESTS); do $(RUNENV) MALLOC_CHECK_=3 MALLOC_PERTURB_=165 \
		FUZZ_ITER=100000 ./$$t || exit 1; done
	$(MAKE) clean

# Everything again under valgrind. Not on SliTaz 5.1: its ld.so is
# stripped and valgrind needs its symbols. The unit tests and the fuzzer
# directly, the relay and the daemon through a wrapper the integration
# tests start instead of the real binaries. Any error fails the target.
VALGRIND := valgrind -q --error-exitcode=99 --leak-check=full \
            --errors-for-leak-kinds=definite
check-valgrind: $(TESTS) tests/valgrind-wrap
	@rm -f tests/valgrind-*.log
	@for t in tests/test_msg tests/test_env tests/test_ws tests/test_fuzz; do \
		$(RUNENV) FUZZ_ITER=3000 $(VALGRIND) ./$$t || exit 1; done
	@$(RUNENV) ./tests/test_relay tests/valgrind-wrap
	@$(RUNENV) ./tests/test_daemon tests/valgrind-wrap tests/valgrind-wrap
	@if find tests -name 'valgrind-*.log' -size +0 | grep .; then \
		echo "valgrind: errors, see the logs above"; exit 1; fi
	@echo "valgrind: no error in $$(ls tests/valgrind-*.log | wc -l) relay and daemon runs"

tests/valgrind-wrap: tests/valgrind-wrap.sh
	cp $< $@ && chmod 755 $@

# The helper goes in at 0755: setuid root and group pelote are set by the
# package once the pelote user exists (chgrp pelote; chmod 4750).
install: all
	install -D -m 755 relay/pelote-relay $(DESTDIR)$(PREFIX)/bin/pelote-relay
	install -D -m 755 relay/pelote-relay.init \
		$(DESTDIR)/etc/init.d/pelote-relay
	install -D -m 755 daemon/pelote $(DESTDIR)$(PREFIX)/bin/pelote
	install -D -m 755 daemon/pelote-helper \
		$(DESTDIR)$(PREFIX)/libexec/pelote-helper
	install -D -m 755 daemon/pelote.init $(DESTDIR)/etc/init.d/pelote
	install -D -m 644 daemon/pelote.conf $(DESTDIR)/etc/pelote/pelote.conf
	install -d -m 755 $(DESTDIR)/etc/pelote/actions.d \
		$(DESTDIR)/etc/pelote/devices
	install -m 644 actions/*.sh $(DESTDIR)/etc/pelote/actions.d

clean:
	rm -f lib/*.[od] relay/*.[od] daemon/*.[od] tests/*.[od] $(TESTS) \
		relay/pelote-relay daemon/pelote daemon/pelote-helper tests/fakeapp \
		tests/valgrind-wrap tests/valgrind-*.log

.PHONY: all check check-sanitize check-valgrind install clean

-include $(wildcard lib/*.d relay/*.d daemon/*.d tests/*.d)
