diff --git a/contrib/agrarian-build-menu.sh b/contrib/agrarian-build-menu.sh index b561acf1..db4acd0f 100755 --- a/contrib/agrarian-build-menu.sh +++ b/contrib/agrarian-build-menu.sh @@ -7,6 +7,10 @@ HOST_WIN64="${HOST_WIN64:-x86_64-w64-mingw32}" MENU_CHOICE="${AGRARIAN_MENU_CHOICE:-}" ROOT="" +DAEMON_WAS_RUNNING=0 +DAEMON_RESTART_MODE="" +DAEMON_RESTART_CMD=() +DAEMON_SYSTEMD_SERVICE="" detect_script_branch() { local script_dir @@ -91,6 +95,192 @@ confirm() { [[ "$answer" == "y" || "$answer" == "Y" || "$answer" == "yes" || "$answer" == "YES" ]] } +process_pids() { + local name="$1" + pgrep -u "$(id -u)" -x "$name" 2>/dev/null || true +} + +all_process_pids() { + local name="$1" + pgrep -x "$name" 2>/dev/null || true +} + +other_user_process_pids() { + local name="$1" + local pid owner current_user + current_user="$(id -un)" + + while IFS= read -r pid; do + [[ -n "$pid" ]] || continue + owner="$(ps -o user= -p "$pid" 2>/dev/null | awk '{print $1}')" + [[ -n "$owner" && "$owner" != "$current_user" ]] && printf '%s %s\n' "$pid" "$owner" + done < <(all_process_pids "$name") +} + +process_cmdline() { + local pid="$1" + tr '\0' '\n' < "/proc/$pid/cmdline" 2>/dev/null || true +} + +daemon_cli_path() { + if [[ -x "$ROOT/src/agrarian-cli" ]]; then + echo "$ROOT/src/agrarian-cli" + elif has_cmd agrarian-cli; then + command -v agrarian-cli + else + return 1 + fi +} + +daemon_args_from_cmdline() { + local pid="$1" + local arg + + while IFS= read -r arg; do + case "$arg" in + -conf=*|-datadir=*|-testnet|-regtest) + printf '%s\n' "$arg" + ;; + esac + done < <(process_cmdline "$pid") +} + +remember_daemon_restart() { + local pid="$1" + local arg exe + + DAEMON_RESTART_MODE="command" + DAEMON_RESTART_CMD=() + + exe="$(readlink -f "/proc/$pid/exe" 2>/dev/null || true)" + [[ -n "$exe" ]] || exe="$ROOT/src/agrariand" + + DAEMON_RESTART_CMD+=("$exe") + while IFS= read -r arg; do + DAEMON_RESTART_CMD+=("$arg") + done < <(daemon_args_from_cmdline "$pid") + + case " ${DAEMON_RESTART_CMD[*]} " in + *" -daemon "*) ;; + *) DAEMON_RESTART_CMD+=("-daemon") ;; + esac +} + +active_user_daemon_service() { + has_cmd systemctl || return 1 + systemctl --user is-active --quiet agrariand.service 2>/dev/null || return 1 + echo "agrariand.service" +} + +stop_running_daemon() { + local pids pid cli args=() service + + pids="$(process_pids agrariand)" + [[ -n "$pids" ]] || return 0 + + DAEMON_WAS_RUNNING=1 + service="$(active_user_daemon_service || true)" + if [[ -n "$service" ]]; then + DAEMON_RESTART_MODE="systemd" + DAEMON_SYSTEMD_SERVICE="$service" + echo "Detected running user service: $service" + else + pid="$(printf '%s\n' "$pids" | head -n 1)" + remember_daemon_restart "$pid" + echo "Detected running agrariand process: $pid" + fi + + if ! confirm "Stop agrariand gracefully before building?"; then + fail "Refusing to build while agrariand is running." + fi + + if [[ "$DAEMON_RESTART_MODE" == "systemd" ]]; then + systemctl --user stop "$DAEMON_SYSTEMD_SERVICE" + else + pid="$(printf '%s\n' "$pids" | head -n 1)" + cli="$(daemon_cli_path)" || fail "agrarian-cli is required to stop the running daemon gracefully." + mapfile -t args < <(daemon_args_from_cmdline "$pid") + "$cli" "${args[@]}" stop + fi + + local waited=0 + while [[ -n "$(process_pids agrariand)" && "$waited" -lt 120 ]]; do + sleep 1 + waited=$((waited + 1)) + done + + [[ -z "$(process_pids agrariand)" ]] || fail "agrariand did not stop within 120 seconds. Build was not started." +} + +stop_running_qt_wallet() { + local pids + + pids="$(process_pids agrarian-qt)" + [[ -n "$pids" ]] || return 0 + + cat >&2 < MASTERNODE_SYNC_SPORKS) return; + // Small networks can have every connected peer marked fulfilled before an + // empty masternode stage reaches its per-peer timeout check. Advance empty + // stages here so staking is not blocked forever on networks with no + // masternodes, winners, or budgets. + if (GetTime() - nAssetSyncStarted > MASTERNODE_SYNC_TIMEOUT * 5) { + if (RequestedMasternodeAssets == MASTERNODE_SYNC_SPORKS) { + GetNextAsset(); + return; + } + + if (RequestedMasternodeAssets == MASTERNODE_SYNC_LIST && lastMasternodeList == 0) { + if (IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT)) { + LogPrintf("CMasternodeSync::Process - ERROR - Sync has failed, will retry later\n"); + RequestedMasternodeAssets = MASTERNODE_SYNC_FAILED; + RequestedMasternodeAttempt = 0; + lastFailure = GetTime(); + nCountFailures++; + } else { + GetNextAsset(); + } + return; + } + + if (RequestedMasternodeAssets == MASTERNODE_SYNC_MNW && lastMasternodeWinner == 0) { + if (IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT)) { + LogPrintf("CMasternodeSync::Process - ERROR - Sync has failed, will retry later\n"); + RequestedMasternodeAssets = MASTERNODE_SYNC_FAILED; + RequestedMasternodeAttempt = 0; + lastFailure = GetTime(); + nCountFailures++; + } else { + GetNextAsset(); + } + return; + } + + if (RequestedMasternodeAssets == MASTERNODE_SYNC_BUDGET && lastBudgetItem == 0) { + GetNextAsset(); + activeMasternode.ManageStatus(); + return; + } + } + TRY_LOCK(cs_vNodes, lockRecv); if (!lockRecv) return; @@ -284,11 +327,15 @@ void CMasternodeSync::Process() //set to synced if (RequestedMasternodeAssets == MASTERNODE_SYNC_SPORKS) { + if (RequestedMasternodeAttempt >= MASTERNODE_SYNC_THRESHOLD) { + GetNextAsset(); + return; + } + if (pnode->HasFulfilledRequest("getspork")) continue; pnode->FulfilledRequest("getspork"); pnode->PushMessage("getsporks"); //get current network sporks - if (RequestedMasternodeAttempt >= 2) GetNextAsset(); RequestedMasternodeAttempt++; return;