#!/usr/bin/env bash set -euo pipefail script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" repo_root="$(cd "${script_dir}/.." && pwd)" target_dir="${repo_root}/qortal/data" target_file="" legacy_default_args='-XX:MaxRAMPercentage=25 -XX:+UseG1GC -Xss1024k' legacy_autosized_args='-XX:MaxRAMPercentage=66 -XX:+UseG1GC -Xss1024k' gib_bytes=$((1024 * 1024 * 1024)) env_file="" seed_args="${QORTAL_JVM_MEMORY_ARGS:-}" managed_state_file="" resolve_path() { local raw="${1:-}" if [[ -z "${raw}" ]]; then printf '%s' "" return 0 fi if [[ "${raw}" == /* ]]; then printf '%s' "${raw}" return 0 fi printf '%s' "${repo_root}/${raw}" } ensure_qortal_data_permissions() { local data_dir="${1:-}" if [[ "${EUID:-$(id -u)}" -ne 0 ]]; then return 0 fi if [[ -z "${data_dir}" || ! -d "${data_dir}" ]]; then return 0 fi chown -R 1000:100 "${data_dir}" >/dev/null 2>&1 || true chmod -R u+rwX,g+rwX "${data_dir}" >/dev/null 2>&1 || true } read_host_memory_bytes() { if [[ -r /proc/meminfo ]]; then awk '/^MemTotal:/ { print $2 * 1024; exit }' /proc/meminfo 2>/dev/null || true fi } read_cgroup_memory_bytes() { local cgroup_file="" local value="" for cgroup_file in /sys/fs/cgroup/memory.max /sys/fs/cgroup/memory/memory.limit_in_bytes; do [[ -r "${cgroup_file}" ]] || continue value="$(tr -d '\r\n' < "${cgroup_file}" 2>/dev/null || true)" case "${value}" in ""|"max") continue ;; esac if [[ "${value}" =~ ^[0-9]+$ ]] && [[ "${value}" -gt 0 ]]; then printf '%s' "${value}" return 0 fi done return 1 } resolve_available_memory_bytes() { local host_bytes="" local cgroup_bytes="" host_bytes="$(read_host_memory_bytes)" cgroup_bytes="$(read_cgroup_memory_bytes || true)" if [[ -n "${host_bytes}" && "${host_bytes}" -gt 0 ]]; then if [[ -n "${cgroup_bytes}" && "${cgroup_bytes}" -gt 0 && "${cgroup_bytes}" -lt "${host_bytes}" ]]; then printf '%s' "${cgroup_bytes}" return 0 fi printf '%s' "${host_bytes}" return 0 fi if [[ -n "${cgroup_bytes}" && "${cgroup_bytes}" -gt 0 ]]; then printf '%s' "${cgroup_bytes}" return 0 fi return 1 } compute_auto_qortal_args() { local total_bytes="" local target_bytes="" local percentage="" total_bytes="$(resolve_available_memory_bytes || true)" if [[ -z "${total_bytes}" || "${total_bytes}" -le 0 ]]; then printf '%s' "${legacy_autosized_args}" return 0 fi target_bytes=$(( total_bytes * 66 / 100 )) if [[ "${total_bytes}" -ge $((6 * gib_bytes)) ]]; then if [[ "${target_bytes}" -lt $((4 * gib_bytes)) ]]; then target_bytes=$((4 * gib_bytes)) fi if [[ "${target_bytes}" -gt $((8 * gib_bytes)) ]]; then target_bytes=$((8 * gib_bytes)) fi fi percentage="$(awk -v target="${target_bytes}" -v total="${total_bytes}" 'BEGIN { if (total <= 0) { print "66"; exit } printf "%.2f", (target / total) * 100 }')" printf '%s' "-XX:MaxRAMPercentage=${percentage} -XX:+UseG1GC -Xss1024k" } while [[ $# -gt 0 ]]; do case "$1" in --target-dir) target_dir="${2:-}" shift 2 ;; --target-dir=*) target_dir="${1#*=}" shift ;; --target-file) target_file="${2:-}" shift 2 ;; --target-file=*) target_file="${1#*=}" shift ;; -h|--help) cat < Start args directory (default: ${repo_root}/qortal/data) --target-file Start args file path override env-file Optional env file whose QORTAL_JVM_MEMORY_ARGS value overrides auto-sizing -h, --help Show this help USAGE exit 0 ;; *) if [[ -z "${env_file}" ]]; then env_file="${1}" shift else echo "Unknown argument: $1" >&2 exit 1 fi ;; esac done target_dir="$(resolve_path "${target_dir}")" if [[ -z "${target_file}" ]]; then target_file="${target_dir%/}/start-arguments.txt" else target_file="$(resolve_path "${target_file}")" target_dir="$(dirname "${target_file}")" fi managed_state_file="${target_file}.mode" if [[ -n "${env_file}" && -f "${env_file}" ]]; then if [[ "${env_file}" != /* ]]; then env_file="$(resolve_path "${env_file}")" fi value_from_env_file="$(grep -E '^QORTAL_JVM_MEMORY_ARGS=' "${env_file}" | tail -n1 | cut -d= -f2- || true)" if [[ -n "${value_from_env_file}" ]]; then seed_args="${value_from_env_file}" fi fi mkdir -p "${target_dir}" ensure_qortal_data_permissions "${target_dir}" auto_args="$(compute_auto_qortal_args)" existing_args="" existing_mode="" if [[ -f "${target_file}" && -s "${target_file}" ]]; then existing_args="$(tr -d '\r' < "${target_file}" | sed 's/[[:space:]]*$//')" fi if [[ -f "${managed_state_file}" && -s "${managed_state_file}" ]]; then existing_mode="$(tr -d '\r' < "${managed_state_file}" | sed 's/[[:space:]]*$//')" fi if [[ -n "${seed_args}" ]]; then printf '%s\n' "${seed_args}" > "${target_file}" printf '%s\n' "manual" > "${managed_state_file}" if [[ "${existing_args}" == "${seed_args}" ]]; then echo "Using Qortal JVM start args from env: ${target_file}" else echo "Updated Qortal JVM start args from env: ${target_file}" fi exit 0 fi if [[ -f "${target_file}" && -s "${target_file}" ]]; then if [[ "${existing_mode}" == "auto" || "${existing_args}" == "${legacy_default_args}" || "${existing_args}" == "${legacy_autosized_args}" ]]; then printf '%s\n' "${auto_args}" > "${target_file}" printf '%s\n' "auto" > "${managed_state_file}" echo "Updated auto-sized Qortal JVM start args: ${target_file}" exit 0 fi echo "Using existing Qortal JVM start args: ${target_file}" exit 0 fi printf '%s\n' "${auto_args}" > "${target_file}" printf '%s\n' "auto" > "${managed_state_file}" echo "Initialized auto-sized Qortal JVM start args: ${target_file}"