mirror of
https://github.com/xroche/httrack.git
synced 2026-08-06 07:46:50 +03:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3426ea35ea | ||
|
|
0274e016af | ||
|
|
22fcf9ffff |
116
.github/workflows/cross-arch.yml
vendored
Normal file
116
.github/workflows/cross-arch.yml
vendored
Normal 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
|
||||
@@ -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
12
debian/changelog
vendored
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user