3 Commits

Author SHA1 Message Date
Xavier Roche
3426ea35ea Release 3.49.18 (#1024)
* Release 3.49.18

Source-only release so packagers can pick up the test-suite build fixes:
3.49.17-1 was FTBFS on armhf, powerpc, hppa and loong64, and the suite
failed on a Fedora build root with no ps. The engine is untouched, so
VERSION_INFO moves by revision alone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>

* Wrap the 3.49.18 changelog entry under 80 columns

lintian's debian-changelog-line-too-long fires on two lines of the new
entry, and the CI gate runs --fail-on=error,warning. The sid perl
transition that had been masking this gate has cleared, so it is live
again.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>

* Sharpen the 3.49-18 release notes

The #1021 line named the symptom and implied the remedy was a louder
failure; the fix is a /proc fallback, so the diagnostics work. Add the
internal catch-all the 3.49-17 block carries, so the cross-arch matrix
appears to the upstream audience and not only in debian/changelog.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>

---------

Signed-off-by: Xavier Roche <roche@httrack.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 21:43:50 +00:00
Xavier Roche
0274e016af Four architectures cannot build the altstack test shim, and nothing before the buildd sees it (#1023)
* Fix the four 3.49.17 buildd failures and cross-compile for those arches in CI

The LD_PRELOAD altstack shim did not build on Debian's 32-bit time64
architectures, and 183 read a frame count no unwinder there can produce.

Signed-off-by: Xavier Roche <xroche@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>

* Install the cross libc, and keep a trace-less report a failure

Review found the frame floor took a report naming no frame at all for a
weak unwinder, and the cross jobs had no target headers.

Signed-off-by: Xavier Roche <xroche@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>

* Give the cross jobs a libssl-free configure and assert what they built

No ports architecture has a cross libssl, and an empty TESTS= would have
passed having built nothing.

Signed-off-by: Xavier Roche <xroche@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>

* Add deb-src in place: a second stanza collides on the keyring spelling

Signed-off-by: Xavier Roche <xroche@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>

* Extract zlib unpatched: Debian's arch patches need Debian's rules

Signed-off-by: Xavier Roche <xroche@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>

* Turn off zlib's s390x vector CRC: the .dfsg repack drops its sources

Signed-off-by: Xavier Roche <xroche@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>

* Drop zlib's s390x vx object too, not just its define

Signed-off-by: Xavier Roche <xroche@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>

* Trim the raw_mmap comment

Signed-off-by: Xavier Roche <xroche@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>

* Keep the raw_mmap comment inside clang-format's width

Signed-off-by: Xavier Roche <xroche@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>

---------

Signed-off-by: Xavier Roche <xroche@gmail.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 18:43:51 +00:00
Xavier Roche
22fcf9ffff The suite watchdog dumps nothing on a host without ps (#1022)
* The suite watchdog dumps nothing on a host without ps

Fedora's build root ships no procps, so every process list in the hang
diagnostics came back empty: the guard still fired, but named no process
and could not signal the wedged engine for a stack. Read /proc directly
when ps is missing, and say so when neither source is available.

Closes #1021

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>

* Harden the no-ps fallback and its test

Review follow-ups: a ps that exits 0 while listing nothing (hidepid, a
locked-down container) now falls back too, whitespace inside an argv can
no longer shift the columns the consumers match on, a failed /proc open
stops leaking to stderr, and a bogus CLK_TCK can no longer abort the
shell. The test leg pins the header line, the column order and the group
filter, and drives request_engine_backtraces itself.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>

* kill -0 succeeds on a zombie, so the SIGABRT check failed in a container

The fake engine is reparented once its subshell exits, and a container's
pid 1 does not always reap it, so the signalled process stays visible to
kill -0. Read its state from /proc instead: gone or Z both prove the
signal landed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>

---------

Signed-off-by: Xavier Roche <roche@httrack.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 18:16:14 +00:00
11 changed files with 305 additions and 19 deletions

116
.github/workflows/cross-arch.yml vendored Normal file
View File

@@ -0,0 +1,116 @@
# Cross-compiles for the Debian architectures no GitHub runner exists for.
# 3.49.17 failed on four of them at once and every failure was a compile or
# assemble error that x86-64 and arm64 cannot produce, so the buildd was the
# first thing to see the code.
name: Cross-arch
on:
push:
branches: [master]
pull_request:
workflow_dispatch:
# Least privilege: the workflow only needs to read the repo.
permissions:
contents: read
# Cancel superseded runs on the same branch or PR.
concurrency:
group: cross-arch-${{ github.ref }}
cancel-in-progress: true
jobs:
cross:
name: cross (${{ matrix.arch }})
runs-on: ubuntu-24.04
# sid, not stable: the point is to predict what the sid buildds will say.
container: debian:sid
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
include:
# arch selects the hardening flags, triplet is the toolchain prefix.
# The first four are 3.49.17's failures.
- { arch: armhf, triplet: arm-linux-gnueabihf }
- { arch: powerpc, triplet: powerpc-linux-gnu }
- { arch: hppa, triplet: hppa-linux-gnu }
- { arch: loong64, triplet: loongarch64-linux-gnu }
- { arch: sh4, triplet: sh4-linux-gnu }
- { arch: m68k, triplet: m68k-linux-gnu }
- { arch: sparc64, triplet: sparc64-linux-gnu }
- { arch: riscv64, triplet: riscv64-linux-gnu }
- { arch: s390x, triplet: s390x-linux-gnu }
env:
DEB_HOST_ARCH: ${{ matrix.arch }}
TRIPLET: ${{ matrix.triplet }}
ZPREFIX: /tmp/zlib-${{ matrix.arch }}
steps:
- name: Install the toolchain
run: |
set -euo pipefail
# deb-src, so zlib below comes from the archive and not a pinned URL.
# In place: a second stanza has to repeat Signed-By, and apt rejects
# the same source carrying two spellings of the keyring.
sed -i 's/^Types: deb$/Types: deb deb-src/' \
/etc/apt/sources.list.d/debian.sources
apt-get update
apt-get install -y --no-install-recommends \
build-essential autoconf automake libtool autoconf-archive \
dpkg-dev git ca-certificates \
"gcc-$TRIPLET" "libc6-dev-$DEB_HOST_ARCH-cross"
- uses: actions/checkout@v7
with:
submodules: recursive
- name: Cross-build zlib
run: |
set -euo pipefail
# Mandatory, and no ports arch has a zlib1g-dev:<arch> to
# multiarch-install. -fPIC because libhttrack.so links it.
mkdir -p /tmp/zsrc && cd /tmp/zsrc
# Unpatched: Debian's arch-specific patches expect its own rules to
# build them (s390x's vector CRC pulls a header they add), and we only
# need something to link -lz against.
apt-get source --download-only zlib1g
dpkg-source --skip-patches -x ./*.dsc src
cd src
CHOST="$TRIPLET" CC="$TRIPLET-gcc" CFLAGS="-O2 -fPIC" \
./configure --prefix="$ZPREFIX" --static
# configure detects s390x's vector CRC, whose sources live in the
# contrib/ the .dfsg repack drops: drop the define and the object it
# would build. Nothing here needs a fast CRC.
sed -i -E 's/(-DHAVE_S390X_VX|crc32_vx\.l?o)//g' Makefile
make -j"$(nproc)"
make install
- name: Configure
run: |
set -euo pipefail
autoreconf -fi
mkdir -p /tmp/bld && cd /tmp/bld
# The buildd's own per-arch flags: -D_FILE_OFFSET_BITS=64 is the shim
# hazard, and -fstack-clash-protection is not offered everywhere.
eval "$(dpkg-buildflags --export=sh)"
# A cross AC_TRY_RUN answers "cross", which would swap in the bundled
# snprintf that no buildd ever builds. Every glibc passes these.
# --disable-https: no ports arch has a cross libssl, so the TLS paths
# are the one part of the tree this matrix does not compile.
"$GITHUB_WORKSPACE/configure" --host="$TRIPLET" \
--build="$(dpkg-architecture -qDEB_BUILD_GNU_TYPE)" --disable-https \
ac_cv_have_working_snprintf=yes ac_cv_have_working_vsnprintf=yes \
CPPFLAGS="${CPPFLAGS:-} -I$ZPREFIX/include" \
LDFLAGS="${LDFLAGS:-} -L$ZPREFIX/lib"
- name: Build
run: make -C /tmp/bld -j"$(nproc)"
- name: Build the test artifacts
# Nothing runs here, but the shims are where three of the four 3.49.17
# failures were: they only build under `check`. Asserted, because an
# empty TESTS= would otherwise pass having built nothing.
run: |
set -euo pipefail
make -C /tmp/bld -j"$(nproc)" check TESTS=
test -f /tmp/bld/tests/.libs/libaltstackprobe.so

View File

@@ -1,6 +1,6 @@
AC_PREREQ([2.71])
AC_INIT([httrack], [3.49.17], [roche+packaging@httrack.com], [httrack], [http://www.httrack.com/])
AC_INIT([httrack], [3.49.18], [roche+packaging@httrack.com], [httrack], [http://www.httrack.com/])
AC_COPYRIGHT([
HTTrack Website Copier, Offline Browser for Windows and Unix
Copyright (C) 1998-2015 Xavier Roche and other contributors
@@ -29,6 +29,7 @@ AC_CONFIG_SRCDIR(src/httrack.c)
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADERS(config.h)
AM_INIT_AUTOMAKE([subdir-objects])
# 3:10:0: revision-only bump; only the version macro moved, the engine is untouched.
# 3:9:0: revision-only bump. #991 and #1005 each added an export
# (hts_set_thread_hooks, escape_control_url); nothing changed or went away.
# 3:8:0: revision-only bump, no ABI change.
@@ -37,7 +38,7 @@ AM_INIT_AUTOMAKE([subdir-objects])
# moves nothing). Soname stays .so.3: HTTrackQt is the only consumer of the installed
# headers, so a libhttrack4 rename isn't worth it.
# (3:0:0 was the htsblk mime-buffer widening, the ABI break that moved .so.2 -> .so.3.)
VERSION_INFO="3:9:0"
VERSION_INFO="3:10:0"
AM_MAINTAINER_MODE
AC_USE_SYSTEM_EXTENSIONS

12
debian/changelog vendored
View File

@@ -1,3 +1,15 @@
httrack (3.49.18-1) unstable; urgency=medium
* New upstream release, fixing the 3.49.17-1 build failures on armhf,
powerpc, hppa and loong64: the test suite's mmap interposer did not
compile on the first three, and the alternate-stack test insisted on a
longer backtrace than loong64's unwinder produces. Nothing in the engine
changed. Upstream now cross-builds the test shims for the ports
architectures on every push, so the next such break surfaces before the
buildds do.
-- Xavier Roche <xavier@debian.org> Wed, 05 Aug 2026 23:00:24 +0200
httrack (3.49.17-1) unstable; urgency=medium
* New upstream release: security fixes in the FTP and HTTP request paths and

View File

@@ -4,6 +4,11 @@ HTTrack Website Copier release history:
This file lists all changes and fixes that have been made for HTTrack
3.49-18
+ Fixed: the 3.49.17 package build failed on four architectures: the test suite's mmap interposer did not compile on armhf, powerpc or hppa, and the alternate-stack test rejected loong64's shorter backtrace (#1023)
+ Fixed: the test suite failed on a host with no ps command, such as a Fedora build root; its hang diagnostics now read /proc directly (#1021)
+ Changed: multiple internal test and CI improvements, including a cross-compile matrix covering the Debian ports architectures
3.49-17
+ New: a pkg-config file, libhttrack.pc, ships with the development headers (#1018)
+ Fixed: a crawled page could read the WebHTTrack session id and drive the control panel; the id is now unguessable and cross-origin commands are refused (#877)

View File

@@ -51,6 +51,13 @@
<content_rating type="oars-1.1"/>
<!-- Newest first; tests/01_engine-version-macros.test enforces it. -->
<releases>
<release version="3.49.18" date="2026-08-05">
<description>
<ul>
<li>The program itself is unchanged: this release only fixes building HTTrack from source on some Linux distributions and processors</li>
</ul>
</description>
</release>
<release version="3.49.17" date="2026-08-05">
<description>
<ul>

View File

@@ -43,8 +43,8 @@ Please visit our Website: http://www.httrack.com
configure.ac, decoupled from these). VERSION is the display form, VERSIONID
the dotted numeric form, AFF_VERSION the short form shown in footers,
LIB_VERSION the data/cache format generation. */
#define HTTRACK_VERSION "3.49-17"
#define HTTRACK_VERSIONID "3.49.17"
#define HTTRACK_VERSION "3.49-18"
#define HTTRACK_VERSIONID "3.49.18"
#define HTTRACK_AFF_VERSION "3.x"
#define HTTRACK_LIB_VERSION "2.0"

View File

@@ -17,8 +17,8 @@
#endif
VS_VERSION_INFO VERSIONINFO
FILEVERSION 3, 49, 17, 0
PRODUCTVERSION 3, 49, 17, 0
FILEVERSION 3, 49, 18, 0
PRODUCTVERSION 3, 49, 18, 0
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
@@ -35,12 +35,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "Xavier Roche"
VALUE "FileDescription", VER_FILE_DESCRIPTION
VALUE "FileVersion", "3.49.17"
VALUE "FileVersion", "3.49.18"
VALUE "InternalName", VER_ORIGINAL_FILENAME
VALUE "LegalCopyright", "Copyright (C) 1998-2026 Xavier Roche and other contributors. GNU GPL v3 or later."
VALUE "OriginalFilename", VER_ORIGINAL_FILENAME
VALUE "ProductName", "HTTrack Website Copier"
VALUE "ProductVersion", "3.49-17"
VALUE "ProductVersion", "3.49-18"
END
END
BLOCK "VarFileInfo"

View File

@@ -65,7 +65,7 @@ grep -q 'slow but healthy' "$out" || fail "healthy test output lost"
# The killed tree must really be gone, or the next test inherits its ports.
sleep 1
! grep -q "$tmp/90_wedged.test" <<<"$(ps -A -o args 2>/dev/null)" ||
! grep -q "$tmp/90_wedged.test" <<<"$(ps_snapshot)" ||
fail "the wedged test survived the guard"
# --- the budget is wall clock, not a count of poll iterations ----------------
@@ -139,6 +139,72 @@ HTTRACK_TEST_TIMEOUT=600 bash "$driver" "$tmp/94_pacer.test" >"$out" 2>&1 || rc=
test "$rc" -eq 0 || fail "a 60s step under a 600s budget reported $rc, want 0"
grep -q 'the pacer let it through' "$out" || fail "the pacer skipped a test that fits"
# --- the process lists survive a host with no ps ----------------------------
# Fedora's build root ships no procps, and the guard then named nothing (#1021).
if ! is_windows && test -r /proc/self/stat; then
mkdir -p "$tmp/nops"
printf '#!/bin/sh\nexit 127\n' >"$tmp/nops/ps"
chmod +x "$tmp/nops/ps"
# Both lists match on the basename alone, so any long-lived binary will do.
ln -sf "$(command -v sleep)" "$tmp/httrack"
# Started from a subshell so ppid and pgid differ: swapping those two columns
# is invisible whenever the test shell happens to lead its own group.
(
"$tmp/httrack" 60 &
echo $! >"$tmp/fake.pid"
)
# Its own group, to prove the filter keeps an outsider out.
(
set -m
"$tmp/httrack" 60 &
echo $! >"$tmp/outside.pid"
)
fake=$(cat "$tmp/fake.pid")
outside=$(cat "$tmp/outside.pid")
trap 'set +e; kill "$fake" "$outside" 2>/dev/null; rm -rf "$tmp"' EXIT
# ppid and pgid of $1: the oracle the listing is checked against.
procfields() { awk '{ sub(/.*\) /, ""); print $2, $3 }' "/proc/$1/stat"; }
procstate() { awk '{ sub(/.*\) /, ""); print $1 }' "/proc/$1/stat" 2>/dev/null || echo gone; }
read -r fppid pgid _ <<<"$(procfields "$fake")"
read -r _ opgid _ <<<"$(procfields "$outside")"
test "$fppid" != "$pgid" || fail "the fake shares ppid and pgid ($pgid)"
test "$opgid" != "$pgid" || fail "the outsider landed in the group under test"
(
PATH="$tmp/nops:$PATH"
! ps -A >/dev/null 2>&1 || fail "the ps shim did not take"
snap=$(list_stray_processes "$pgid" group)
# Line 1 is the header every consumer drops; a row there would be lost.
case "$(head -n1 <<<"$snap")" in
[0-9]*) fail "the no-ps listing has no header: $snap" ;;
esac
! grep -qE "^$outside " <<<"$snap" ||
fail "no-ps group list reported pid $outside, in another group"
read -r _ rppid rpgid _ <<<"$(grep -E "^$fake " <<<"$snap")"
test "$rppid $rpgid" = "$fppid $pgid" ||
fail "no-ps row for $fake reads ppid/pgid '$rppid $rpgid', want '$fppid $pgid'"
engines=$(list_engine_pids "$pgid")
grep -qx "$fake" <<<"$engines" || fail "no-ps engine list lost pid $fake"
! grep -qx "$outside" <<<"$engines" ||
fail "no-ps engine list reached outside the group (pid $outside)"
# The chain, not just its input: this is what reported nothing on Fedora.
stacks=$(request_engine_backtraces "$pgid")
! grep -q 'no engine process left' <<<"$stacks" ||
fail "no-ps stack request found no engine to signal: $stacks"
# Gone, or a zombie: the fake is reparented, and a container's pid 1 does
# not always reap. kill -0 alone succeeds on a zombie and proves nothing.
case "$(procstate "$fake")" in
Z | gone) ;;
*) fail "the SIGABRT never reached pid $fake" ;;
esac
# With neither source the dump must say so, not print an empty section.
# shellcheck disable=SC2317 # reached through ps_snapshot
proc_snapshot() { return 1; }
grep -q 'no process list' <<<"$(list_stray_processes "$pgid" group)" ||
fail "a host with no ps and no /proc produced no notice"
)
kill "$fake" "$outside" 2>/dev/null || true
fi
# --- a wedged crawl yields a symbolized engine stack ------------------------
# The whole point of the dump: name the frame the engine is stuck in. Windows has
# neither half (MSYS signals do not reach a native httrack.exe, and that build has

View File

@@ -63,11 +63,24 @@ grep -q "^Caught signal 11$" <<<"$worker" || fail "-#c=threadstack: no 'Caught s
# worker's, which is what had no alternate stack.
worker_frames=$(frame_count "$worker")
plain_frames=$(frame_count "$plain")
test "$worker_frames" -ge 100 ||
fail "-#c=threadstack: $worker_frames frames, expected a runaway recursion"
test "$plain_frames" -lt 100 ||
fail "-#c=segv: $plain_frames frames, the threshold no longer discriminates"
# Naming no frame at all is the unwinder failing outright, not a weak one, and
# the floor below would take it for loong64. hts_print_backtrace() says which.
if grep -q "No stack trace available" <<<"$worker"; then
fail "-#c=threadstack: the report carries no stack trace at all"
fi
# armhf and loong64 cannot unwind past the frame that faulted on the guard page
# (180 skips on the same floor); the release trace below still judges the fix.
if [ "$worker_frames" -ge 1 ] && [ "$worker_frames" -lt 6 ]; then
echo "-#c=threadstack: $worker_frames frames, too few for this unwinder to" \
"show the recursion" >&2
elif [ "$worker_frames" -lt 100 ]; then
fail "-#c=threadstack: $worker_frames frames, expected a runaway recursion"
fi
# Everything above only proves the stack gets installed: the crashing worker
# never returns, so the release hook never runs there. -#test=threadwait spawns
# workers that do return, and the syscall trace says what each did with its

View File

@@ -14,6 +14,11 @@
#define _GNU_SOURCE
/* The largefile redirect renames this file's mmap() to mmap64, colliding with
its own mmap64(). _TIME_BITS rides on _FILE_OFFSET_BITS, so it goes too. */
#undef _FILE_OFFSET_BITS
#undef _TIME_BITS
#include <dlfcn.h>
#include <fcntl.h>
#include <signal.h>
@@ -187,6 +192,19 @@ static void *trace_mapped(void *sp) {
return sp;
}
/* The fallback when dlsym() failed, so it has to be right: 32-bit arches have
no SYS_mmap and i386's wants an argument block, so prefer SYS_mmap2, whose
offset is in 4096-byte units whatever the page size. */
static void *raw_mmap(void *addr, size_t len, int prot, int flags, int fd,
off_t off) {
#ifdef SYS_mmap2
return (void *) syscall(SYS_mmap2, addr, len, prot, flags, fd,
(unsigned long) (off / 4096));
#else
return (void *) syscall(SYS_mmap, addr, len, prot, flags, fd, off);
#endif
}
SHIM_EXPORT void *mmap(void *addr, size_t len, int prot, int flags, int fd,
off_t off);
@@ -194,10 +212,9 @@ SHIM_EXPORT void *mmap(void *addr, size_t len, int prot, int flags, int fd,
between, so the thread's last mapping is the whole of the check. */
SHIM_EXPORT void *mmap(void *addr, size_t len, int prot, int flags, int fd,
off_t off) {
return trace_mapped(
real_mmap != NULL
? real_mmap(addr, len, prot, flags, fd, off)
: (void *) syscall(SYS_mmap, addr, len, prot, flags, fd, off));
return trace_mapped(real_mmap != NULL
? real_mmap(addr, len, prot, flags, fd, off)
: raw_mmap(addr, len, prot, flags, fd, off));
}
/* glibc only, and the name that matters: _FILE_OFFSET_BITS=64 redirects the

View File

@@ -176,6 +176,56 @@ kill_tree() {
ENGINE_EXE_RE='^(lt-)?(httrack|proxytrack|htsserver|webhttrack)([.]exe)?$'
FIXTURE_SERVER_RE='^(local-server|proxy-https-server|proxy-connect-server|socks5-server|tls-stall-server)[.]py$'
# Every process as "PID PPID PGID ELAPSED S COMMAND", header first. A Fedora
# build root has no procps, and an empty list reads as "nothing running" (#1021).
ps_snapshot() {
local snap
# POSIX keywords, so the ps route holds on macOS too; a ps that lists nothing
# (hidepid, a locked-down container) is no better than an absent one.
if snap=$(ps -A -o pid,ppid,pgid,etime,state,args 2>/dev/null |
awk 'NR > 1 { rows++ } { print } END { exit rows ? 0 : 1 }'); then
printf '%s\n' "$snap"
return 0
fi
proc_snapshot && return 0
# Every consumer drops line 1, so the notice rides in the header's place.
printf 'no process list: this host has neither ps nor a readable /proc\n'
return 1
}
# The same six columns out of /proc, in bash alone; elapsed as plain seconds.
proc_snapshot() {
local d stat rest arg args comm hz uptime
local -a f
local ws # spelled out of line: bash 3.2 fails to parse $'..' inside ${v//p/r}
ws=$' \t\n\v\f\r'
test -r /proc/self/stat || return 1
hz=$(getconf CLK_TCK 2>/dev/null) || hz=
# A zero or non-numeric tick would abort the shell in the division below.
case "$hz" in '' | 0 | *[!0-9]*) hz=100 ;; esac
read -r uptime _ </proc/uptime 2>/dev/null || return 1
printf 'PID PPID PGID ELAPSED S COMMAND\n'
for d in /proc/[0-9]*; do
# Braced: a failed open reports to the caller's stderr, not the redirect.
{ read -r stat <"$d/stat"; } 2>/dev/null || continue
# comm may hold spaces and parens; every field past it is numeric.
rest=${stat##*') '}
comm=${stat#*(}
comm=${comm%)*}
read -ra f <<<"$rest" || continue
test "${#f[@]}" -ge 20 || continue # short read: the process is going away
args=
# Whitespace inside an argv would split the row or shift the columns the
# consumers match on, which ps avoids by mapping those bytes away.
{ while IFS= read -r -d '' arg; do
args="${args:+$args }${arg//["$ws"]/ }"
done <"$d/cmdline"; } 2>/dev/null || true
printf '%s %s %s %s %s %s\n' "${d#/proc/}" "${f[1]}" "${f[2]}" \
"$(((${uptime%%.*} * hz - f[19]) / hz))" \
"${f[0]}" "${args:-[$comm]}"
done
}
# List processes a hung test may have left running, one per line. $1 is the test's
# process group; $2 selects "group" (that group's members, whatever their name),
# "others" (engine and fixture processes outside it, which under "make check -j"
@@ -190,10 +240,9 @@ list_stray_processes() {
test "$mode" != others || return 0
tasklist 2>/dev/null | grep -Ei 'httrack|proxytrack|htsserver|python' || true
else
# `args` and `state` are POSIX ps keywords, so this holds on macOS too.
# Fields 6 and 7 are the command and its first argument (the interpreter
# and its script, for the Python fixtures).
ps -A -o pid,ppid,pgid,etime,state,args 2>/dev/null |
ps_snapshot |
awk -v pg="$pgid" -v mode="$mode" -v eng="$ENGINE_EXE_RE" -v srv="$FIXTURE_SERVER_RE" '
NR == 1 { print; next }
{ ingroup = (pg > 0 && $3 == pg)
@@ -239,9 +288,9 @@ reap_leftover_processes() {
list_engine_pids() {
local pgid=${1:-0}
test "$pgid" -gt 0 2>/dev/null || return 0
ps -A -o pid,pgid,args 2>/dev/null |
ps_snapshot |
awk -v pg="$pgid" -v eng="$ENGINE_EXE_RE" \
'NR > 1 && $2 == pg { c = $3; sub(/.*[\/\\]/, "", c); if (c ~ eng) print $1 }'
'NR > 1 && $3 == pg { c = $6; sub(/.*[\/\\]/, "", c); if (c ~ eng) print $1 }'
}
# Ask the wedged test's engine processes for a stack. What is obtainable differs