#!/usr/bin/make -f
# Build the self-contained Site24x7 agent bundle. See PACKAGING-NOTES.md for
# the design; the layout (/opt/site24x7/{monagent,python,venv}), the wheel-
# install loop, the venv relocation and the SyntaxWarning fix all come from
# there and mirror the RPM spec one-to-one.

export DH_VERBOSE = 1
export DEB_BUILD_MAINT_OPTIONS = hardening=+all

INSTALL_PREFIX = /opt/site24x7
AGENT_HOME     = $(INSTALL_PREFIX)/monagent
PYTHON_HOME    = $(INSTALL_PREFIX)/python
VENV_HOME      = $(INSTALL_PREFIX)/venv
CUSTOMER_ID    = us_839f5a7fe9686d925fd5e9e58013b9e5
PYTHON_VERS    = 3.12.1
PYTHON_XY      = 3.12

# Staging dir dh_auto_install installs into; everything below ends up in the
# binary package.
BUILDROOT = $(CURDIR)/debian/python3-site24x7-agent

# Extra plugin wheels vendored alongside the vendor's pypi/pip_packages bundle.
# Listed once here and reused in the install step.
EXTRA_WHEELS = \
    cryptography-41.0.7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl \
    psycopg2_binary-2.9.12-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl \
    pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl \
    redis-8.0.0-py3-none-any.whl

%:
	dh $@

# Top-level source tree has no configure script; our build is driven from
# override_dh_auto_build below.
override_dh_auto_configure:

override_dh_auto_build:
	# Extract the vendor's installer payload. The .install file is a bash
	# header followed by an appended .tar.gz; the header's INSTALL_FILE_LENGTH
	# variable (declared near the top of the script) holds the line number at
	# which the payload starts. Read it from the file rather than hardcoding,
	# so a vendor version bump doesn't require touching this recipe.
	set -e; \
	hdr=$$(awk -F= '/^INSTALL_FILE_LENGTH=/ { print $$2; exit }' \
	    Site24x7MonitoringAgent.install); \
	test -n "$$hdr" || { \
	    echo "ERROR: INSTALL_FILE_LENGTH not found in Site24x7MonitoringAgent.install" >&2; \
	    exit 1; \
	}; \
	tail -n +"$$hdr" Site24x7MonitoringAgent.install | tar xzf -
	# Vendor SyntaxWarning fix under Python 3.12: AgentConstants.py /
	# DatabaseConstants.py write the defunct-process filter as "\[sh] <defunct>"
	# in plain (non-raw) literals where `\[` is an invalid escape. Doubling the
	# backslash is value-identical and idempotent; anchoring on the leading
	# quote keeps it safe to re-run.
	find monagent -type f -name '*.py' -exec sed -i 's/"\\[[]sh]/"\\\\[sh]/g' {} +
	# Bundled CPython source.
	tar xf Python-$(PYTHON_VERS).tar.xz
	# Disable stdlib modules the monitoring agent doesn't need and which break
	# the build on newer toolchains:
	#   _crypt     - On Ubuntu 26.04 (resolute) glibc no longer ships crypt();
	#                it lives in libxcrypt. Python 3.12.1's link line for
	#                _cryptmodule.o doesn't add -lcrypt, so the .so builds but
	#                dlopens with `undefined symbol: crypt`. The build's
	#                check_extension_modules.py then unlinks the broken file,
	#                but SHAREDMODS still lists it - sharedinstall errors with
	#                `install: No such file or directory`. _crypt is removed
	#                entirely in 3.13 and the agent never calls crypt(); just
	#                drop it.
	#   nis        - deprecated in 3.12, removed in 3.13. Depends on libnsl /
	#                libtirpc, both of which have been moving around since
	#                glibc 2.34 removed nis from libc.
	#   audioop    - deprecated in 3.12, removed in 3.13; never used.
	#   ossaudiodev- Open Sound System headers, never used.
	# makesetup gives Setup.local higher precedence than Setup.stdlib, so the
	# *disabled* list here wins regardless of what configure auto-detected.
	printf '%s\n' '*disabled*' _crypt nis audioop ossaudiodev \
	    > Python-$(PYTHON_VERS)/Modules/Setup.local
	# Prefix MUST equal the runtime install path (CPython bakes sysconfig
	# paths at build time). RPATH is essential so the bundled interpreter
	# finds libpython3.12.so.1.0 under PYTHON_HOME/lib without needing
	# LD_LIBRARY_PATH on the target host.
	cd Python-$(PYTHON_VERS) && ./configure \
	    --prefix=$(PYTHON_HOME) \
	    --enable-shared \
	    --with-ensurepip=install \
	    --without-static-libpython \
	    LDFLAGS="-Wl,-rpath,$(PYTHON_HOME)/lib"
	$(MAKE) -C Python-$(PYTHON_VERS)

override_dh_auto_test:
	# Nothing to test at build time; the agent talks to Site24x7's cloud.

override_dh_auto_install:
	# Install CPython under $(BUILDROOT)$(PYTHON_HOME).
	$(MAKE) -C Python-$(PYTHON_VERS) install DESTDIR=$(BUILDROOT)
	# Lay agent files at $(BUILDROOT)$(AGENT_HOME).
	mkdir -p $(BUILDROOT)$(INSTALL_PREFIX)
	cp -a monagent $(BUILDROOT)$(INSTALL_PREFIX)/
	# The vendor's bin/monagent and bin/monagentwatchdog are shell wrappers that
	# fork a Python process; systemd invokes the Python entry point directly and
	# handles restarts via Restart=on-failure, so the wrappers (and their
	# support scripts) just create surface area for admins to accidentally
	# start an unmanaged agent. Remove them. .product_profile is kept (read by
	# the oracledb code); .profile / .profile.env are bash-only and only
	# consumed by the wrapper we just removed, so they're omitted entirely.
	rm -f $(BUILDROOT)$(AGENT_HOME)/bin/monagent \
	      $(BUILDROOT)$(AGENT_HOME)/bin/monagentwatchdog \
	      $(BUILDROOT)$(AGENT_HOME)/bin/monagentservice \
	      $(BUILDROOT)$(AGENT_HOME)/bin/monagentdocker \
	      $(BUILDROOT)$(AGENT_HOME)/bin/monagentsyslog \
	      $(BUILDROOT)$(AGENT_HOME)/bin/monagentupgrade \
	      $(BUILDROOT)$(AGENT_HOME)/bin/shell_monagent \
	      $(BUILDROOT)$(AGENT_HOME)/bin/shell_monagentwatchdog \
	      $(BUILDROOT)$(AGENT_HOME)/bin/setmonagentuser.sh \
	      $(BUILDROOT)$(AGENT_HOME)/bin/upgrade.sh \
	      $(BUILDROOT)$(AGENT_HOME)/bin/venv_upgrade.sh \
	      $(BUILDROOT)$(AGENT_HOME)/bin/hybrid_upgrade \
	      $(BUILDROOT)$(AGENT_HOME)/bin/hybrid_monagent_upgrade \
	      $(BUILDROOT)$(AGENT_HOME)/bin/uninstall \
	      $(BUILDROOT)$(AGENT_HOME)/bin/profile.sh \
	      $(BUILDROOT)$(AGENT_HOME)/bin/profile.env.sh \
	      $(BUILDROOT)$(AGENT_HOME)/bin/freebsd_monagentservice \
	      $(BUILDROOT)$(AGENT_HOME)/bin/osx_monagentservice \
	      $(BUILDROOT)$(AGENT_HOME)/bin/osx_monagentwatchdogservice \
	      $(BUILDROOT)$(AGENT_HOME)/bin/sysd_monagent.service \
	      $(BUILDROOT)$(AGENT_HOME)/bin/sysd_monagent-shutdown.service \
	      $(BUILDROOT)$(AGENT_HOME)/bin/sysd_monagent-reboot.service \
	      $(BUILDROOT)$(AGENT_HOME)/bin/sunos_monagent_service.xml \
	      $(BUILDROOT)$(AGENT_HOME)/bin/hadoop_rc
	# Drop in our pre-baked .product_profile (oracledb's LD_LIBRARY_PATH
	# handler reads it; other vars are harmless leftovers from the vendor's
	# runtime environment).
	install -m 0644 debian/product_profile $(BUILDROOT)$(AGENT_HOME)/.product_profile
	# Drop in our pre-baked monagent.cfg (replaces vendor default). Tenant
	# customer_id is injected here so the .cfg in the source tree stays
	# vendor-neutral.
	install -m 0644 monagent.cfg $(BUILDROOT)$(AGENT_HOME)/conf/monagent.cfg
	sed -i "s/^customer_id.*$$/customer_id = $(CUSTOMER_ID)/" \
	    $(BUILDROOT)$(AGENT_HOME)/conf/monagent.cfg
	# Add the extra wheels (cryptography fallback + plugin deps) so the venv
	# install loop below picks them up. The cryptography fallback covers
	# glibc < 2.34 (bullseye); newer releases get the bundled 44.0.2 wheel.
	install -m 0644 $(addprefix $(CURDIR)/,$(EXTRA_WHEELS)) \
	    $(BUILDROOT)$(AGENT_HOME)/pypi/pip_packages/
	# Restore exec bit on shell scripts (defensive; the vendor tarball
	# already has them executable).
	find $(BUILDROOT)$(AGENT_HOME) -type f -iname '*.sh' -exec chmod 0755 {} +
	# Pre-mangle ambiguous python shebangs across CPython stdlib + vendor tree.
	# Not strictly required on Debian (dpkg doesn't reject them) but matches
	# what the agent expects on EL hosts and keeps lintian quiet.
	find $(BUILDROOT)$(INSTALL_PREFIX) -type f -name '*.py' -executable -print0 \
	    | xargs -0 -r sed -i -E \
	        -e '1s|^#!/usr/bin/env python$$|#!/usr/bin/env python3|' \
	        -e '1s|^#!/usr/bin/python$$|#!/usr/bin/python3|'
	# Remove the rogue python shebang from a vendor license text file.
	if [ -f $(BUILDROOT)$(AGENT_HOME)/lib/lib/com/manageengine/monagent/thirdPartyFile/pyinotify-0_9_5_License.txt ]; then \
	    sed -i '1{/^#!/d}' $(BUILDROOT)$(AGENT_HOME)/lib/lib/com/manageengine/monagent/thirdPartyFile/pyinotify-0_9_5_License.txt; \
	fi
	# Clean scratch dirs that may carry stale temp files.
	rm -rf $(BUILDROOT)$(AGENT_HOME)/scripts/tmp/*
	# Writable runtime dirs. The agent runs as site24x7-agent but monagent/
	# installs root-owned and the agent can't mkdir these itself, so we
	# pre-create them here. Ownership is fixed up in postinst (dpkg has no
	# per-file owner metadata for non-root users).
	mkdir -p $(BUILDROOT)$(AGENT_HOME)/logs/details \
	         $(BUILDROOT)$(AGENT_HOME)/plugins \
	         $(BUILDROOT)$(AGENT_HOME)/temp \
	         $(BUILDROOT)$(AGENT_HOME)/conf/backup \
	         $(BUILDROOT)$(AGENT_HOME)/upload \
	         $(BUILDROOT)$(AGENT_HOME)/data \
	         $(BUILDROOT)$(AGENT_HOME)/upgrade
	# Native ifdetails / libnic.so helper.
	$(MAKE) -C $(BUILDROOT)$(AGENT_HOME)/lib/devops/native/c \
	    BIN_DIR=$(BUILDROOT)$(AGENT_HOME)/bin \
	    LIB_DIR=$(BUILDROOT)$(AGENT_HOME)/lib \
	    OBJ_DIR=$(BUILDROOT)$(AGENT_HOME)/lib/devops/native/c/obj \
	    ifdetails
	# Create the venv and populate it from bundled + extra wheels. The
	# interpreter lives under $(BUILDROOT) at build time so it needs
	# LD_LIBRARY_PATH (to find libpython) and PYTHONHOME (to find its stdlib).
	# --symlinks makes venv/bin/python* internal symlinks rather than copies
	# (shrinks the package and avoids duplicate-binary lint). We replace the
	# outer python* with relative symlinks to the bundled interpreter below.
	# Install each wheel individually and tolerate failures: the vendor bundle
	# contains wheels that don't match the bundled Python's ABI (e.g.
	# pkcs7-0.1.2-cp33 is Python-3.3-only); the agent lazy-imports those and
	# runs without them. We deliberately skip requirements.txt (lists packages
	# like `docker` that aren't shipped as wheels) and the .tar.gz sdists
	# (stale duplicates that would need Python.h).
	set -e; \
	export LD_LIBRARY_PATH=$(BUILDROOT)$(PYTHON_HOME)/lib; \
	export PYTHONHOME=$(BUILDROOT)$(PYTHON_HOME); \
	$(BUILDROOT)$(PYTHON_HOME)/bin/python3 -m venv --symlinks $(BUILDROOT)$(VENV_HOME); \
	for whl in $(BUILDROOT)$(AGENT_HOME)/pypi/pip_packages/*.whl; do \
	    $(BUILDROOT)$(VENV_HOME)/bin/pip install \
	        --no-index --no-cache-dir --no-deps \
	        --find-links $(BUILDROOT)$(AGENT_HOME)/pypi/pip_packages \
	        "$$whl" \
	    || echo "WARN: skipping incompatible wheel $$(basename "$$whl")"; \
	done
	# Replace the venv's outer python/python3/python3.12 (copies by default)
	# with relative symlinks to the bundled interpreter. Absolute symlinks
	# into $(BUILDROOT) would break the package; relative survive verbatim.
	# dpkg preserves symlinks during packaging.
	set -e; \
	for p in python python3 python$(PYTHON_XY); do \
	    f=$(BUILDROOT)$(VENV_HOME)/bin/$$p; \
	    rm -f "$$f"; \
	    ln -s ../../python/bin/python$(PYTHON_XY) "$$f"; \
	done
	# Rewrite $(BUILDROOT)-rooted paths so the venv works at the runtime
	# prefix: pyvenv.cfg (home = ...), bin/activate* (VIRTUAL_ENV), and
	# bin/<shell shebangs> (pip wrappers). pip also writes direct_url.json
	# under *.dist-info/ with file:// URLs referencing $(BUILDROOT); those
	# records aren't consulted at runtime, so delete rather than rewrite.
	sed -i "s|$(BUILDROOT)||g" $(BUILDROOT)$(VENV_HOME)/pyvenv.cfg
	set -e; \
	for f in $(BUILDROOT)$(VENV_HOME)/bin/*; do \
	    [ -f "$$f" ] && [ ! -L "$$f" ] || continue; \
	    sed -i "s|$(BUILDROOT)||g" "$$f"; \
	done
	find $(BUILDROOT)$(VENV_HOME)/lib -name direct_url.json -delete

override_dh_auto_clean:
	rm -rf Python-$(PYTHON_VERS) monagent
	dh_auto_clean

# Do NOT run dh_python3. It would try to compute Python deps and byte-compile
# /opt/site24x7 with the system Python - exactly what we forbid. A bare `dh $@`
# already skips it, but override defensively in case the addon gets pulled in.
override_dh_python3:

# Install + enable + start the systemd unit. dh_installsystemd only
# auto-detects debian/<pkg>.service (and <pkg>@.service); it does NOT scan the
# debian/<pkg>.<name>.service form on its own, so without this override the
# `dh` no-op detector sees no matching default-named unit and skips the helper
# entirely - the unit never gets installed and no enable/start/stop snippets
# are emitted into the maintainer scripts. An explicit override target is never
# skipped by that detector, and --name=site24x7-agent makes the helper pick up
# debian/python3-site24x7-agent.site24x7-agent.service and install it as
# site24x7-agent.service, wiring enable/start (postinst), stop (prerm) and
# disable (postrm) via the #DEBHELPER# tokens.
override_dh_installsystemd:
	dh_installsystemd --name=site24x7-agent

# dpkg-shlibdeps needs to see the bundled libpython3.12.so.1.0 to resolve
# internal NEEDED entries; -l adds it to the search path. --ignore-missing-info
# accepts that the private libpython has no providing package - real system
# deps (libssl, libffi, libc6, ...) still resolve and end up in
# ${shlibs:Depends}. -X excludes the venv's site-packages tree: manylinux
# wheels (psycopg2-binary, cryptography, ...) bundle their own dependent libs
# under <wheel>.libs/ with hash-suffixed filenames (libkrb5-fcafa220.so.3.3
# etc.) that auditwheel rewrites RPATHs to find at runtime via $ORIGIN -
# dpkg-shlibdeps can't resolve those mangled names against any Debian package
# and errors out. The bundled python3.12 interpreter is still scanned, so the
# real system deps of the bundle (libssl/libcrypto/libffi/libsqlite3/
# libreadline/libuuid/libbz2/liblzma/libz/libncurses) are picked up there.
override_dh_shlibdeps:
	dh_shlibdeps -Xsite-packages -l$(BUILDROOT)$(PYTHON_HOME)/lib -- --ignore-missing-info

# Bundled CPython + venv binaries don't need split dbgsym packages.
override_dh_strip:
	dh_strip --no-automatic-dbgsym

# dwz chokes on the bundled non-Debian ELF (different build IDs etc.); skip it.
override_dh_dwz:

# We don't ship public shared libraries (libpython is bundled-private).
override_dh_makeshlibs:
