mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-11-01 20:02:30 +00:00
* WIP: Haskell impl of resizeImageToStrSize * WIP: add FFI export wrapper * WIP: switch ios resizeImageToStrSizeSync to backend * WIP: fix resizeImageToStrSizeSync * WIP: add JNI wrappers * Add non-base64 resize and cWriteImage * WIP: add chat_write_image * fix resizeToStr * fixes * fix syntax, raise jpeg quality back * add chatWriteImage * Fix cfArgs in tests * Use jpeg-turbo encoder * add nix pins * bump jpeg-turbo * add CI deps * add encoding funs to android lib * bump jpeg-turbo for 20.04 fix * jpeg-turbo fix for 8.10 * android fixes * Bump jpeg-turbo * set static flag for jpeg-turbo * Fix nix for aarch64/android * Fix android C api * Fix resize in android * Bump hs-libjpeg-turbo * target correct override * override android lib from android pkgs * add macos build tweaks * bump hs-jpeg-turbo for 8.10 fixes * more build fixes * make ios compile * Always use pkgconfig for libjpeg-turbo * WIP: debug libjpeg in ubuntu pkg-config CI * Add pkg-config to dockerfiles * Add libturbojpeg to dockerfiles * Use -dev version of libturbojpeg in dockerfiles * Install pkg-config in windows CI * Disable flag for custom libturbojpeg linking options Workarounds relocation error on x86_64-linux with gcc: /usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/libturbojpeg.a(turbojpeg.c.o): relocation R_X86_64_TPOFF32 against `errStr' can not be used when making a shared object; recompile with -fPIC /usr/bin/ld: failed to set dynamic section sizes: bad value * WIP: debug libjpeg in windows pkg-config CI * WIP: debug libjpeg package in windows CI * core: image resize module and buffer FFI * ci: link libjpeg-turbo statically on all platforms * ui: resize images in the core * core: rename image resize functions to chat_resize_image and chat_resize_image_uri * ci: restore whitespace in build.yml * ci: build libjpeg-turbo 3.1.4.1 from a submodule * core: hs-jpeg-turbo from github.com/simplex-chat * nodejs: resize images * python: resize images * core: hs-jpeg-turbo from gitlab.com, the GitHub import lacks LFS objects * core: generate /img previews * ci: link libjpeg-turbo on Windows without pkg-config * ci: build libjpeg-turbo on Windows with the GHC toolchain * docs: plan for image resizing in the core * docs: update image resizing plan * ios: prevent crash when wallpaper image cannot be resized * android, desktop: raise an error when an image cannot be resized * docs: update spec references for image resizing * docker: build the CLI image from a clone without submodules * docs: apply review fixes to image resizing plan * docker: build the CLI from a git worktree with the submodule present * docs: apply second review to image resizing plan * docs: point image resizing plan at the rewritten commit * core: choose image format by transparency in chat_resize_image * nodejs: resize images to the app sizes * python: resize images to the app sizes * nodejs, python: describe the core modules as used for image resizing * docs: add license notices of the image libraries * ci: describe the avatar method without attributing it to the apps * ci, ios, nix: remove the libjpeg-turbo build outside cabal * python: name resize_image in the core module text * nodejs, python: describe resizing to the app image sizes * core: refuse images larger than the supported size * core: resize to JPEG images with a side above 65500 pixels * docs: record image resizing decisions for the bundled libjpeg-turbo * core, docs: build libjpeg-turbo and stb from the fork submodules * docs: record the fork pins in the image resizing plan * core: resize images to at most 4320 pixels per side, refuse above 16384 * docs: record the image side limits in the image resizing plan * core: pin the rebased hs-jpeg-turbo fork * core: pin the hs-jpeg-turbo fork without package.yaml * docs: plan scaled JPEG decoding and EXIF orientation of images * docs: copy the JuicyPixels-stbir licence of the pinned fork * core: pin the hs-jpeg-turbo fork with scaled JPEG decoding * core: decode JPEG at the display size, up to 65500 pixels per side * core: rotate JPEG images by their EXIF orientation * docs: refer the image plan to the JPEG decoding code * docs: record the approved image processing decisions * core: limit JPEG decoding memory to 1024 MB outside iOS and Android * docs: name JPEG decoding in the libjpeg-turbo notice * docs: plan image resizing from files and the decisions of 26 Oct * core: scale images in one Catmull-Rom pass with sharpening * docs: plan image sizes at a fixed quality * core: resize images at a fixed quality to fill the size limit * core: resize images from a file path or a file descriptor * ios, android, desktop: resize picked images from their files * core: encode resized images with mozjpeg * tests: check descriptor closing by the file it refers to * core: raise JPEG quality only for a full-size image below 97% of the limit * ci: name the mozjpeg submodule in the Windows cleanup comments * docs: refer the image plan to the current code * docs: refer the image plan to the code merged with master * core: export every image resizing function from each library * core: close the file descriptor when its handle cannot be created * docs: count the C sources of mozjpeg with the arithmetic decoder * core: use the reviewed hs-jpeg-turbo and JuicyPixels-stbir forks * ci: build the armv7a Android core library on request * core: round image heights half up in the size search * ios: describe the file image resizing functions in SimpleX.h * ci: pick, preview and send images in the Claude app tests * core: decode PNG images with libspng and refuse other formats * core: encode PNG with libspng, keeping the smaller filter choice * ci: pick, preview and send images in the scripted app smoke tests * flake: build the PNG decoder package in nix builds * ios: remove the copy of a picked image after sending it * core: refuse image files larger than the decode memory limit * core: scale a JPEG image to its largest size once per resize * core: bisect JPEG quality without a step function * core: reduce the memory used to sharpen and to decode PNG images * android, desktop: pass the image format to the core as an integer * android, desktop: fuse the preview conditions of picked media * android, desktop, ci: check that the core resized the smoke test images * python: remove the docstrings of the explicit image resizing functions * docs: add the second review fixes to the image plan * ci: tap the iOS attachment button by its accessibility label * core: keep the colour profiles of resized images * docs: add the colour profiles to the image plan * ci: wait up to 30 s for the new group screens in the iOS smoke test * ci: pick the Android smoke test images from their own photo folders * ci: tap the iOS photo picker by position, as its tree is not shown * ci: pick the test card and the logo by their iOS picker positions * core: update JuicyPixels-stbir with the stb_image_resize licence terms * core: write a fixed Display P3 profile for Display P3 images only * core: do not keep the input image in memory for its colour profile * core: read the JPEG segments once for the orientation and the profile * core: read an image file into one buffer of its size * docs: add the third review fixes to the image plan * core: resize images with Display P3 or no profile, not profile bytes * ci: check the pixel sizes of the images sent in the iOS smoke test * core: update hs-jpeg-turbo with PNG chunk limits and encoder fixes * docs: list Compact ICC Profiles, libspng and miniz in the dependencies * android, desktop: reword the picked media preview comment * docs: refer the image plan to the code merged with master * ci: render the iOS snapshots before the images case * ci: dismiss the snapshot tests' camera alert before the iOS images case * core: update hs-jpeg-turbo with tests and the JuicyPixels-spng synopsis * core: pin hs-jpeg-turbo and JuicyPixels-stbir in simplex-chat repositories * update ios core library * core, ui: accept only JPEG and PNG image data * core: update JuicyPixels-stbir * docs: plan for the iOS copy of picked images --------- Co-authored-by: IC Rainbow <aenor.realm@gmail.com> Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com> Co-authored-by: Alexandre Esteves <alexfmpe@proton.me>
351 lines
14 KiB
Bash
Executable File
351 lines
14 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Test backend on the app runner: a chat relay and a directory service on the preset servers, with one group
|
|
# and one channel registered and approved by a CLI peer, and another CLI peer that must find both by sending the
|
|
# directory the search text. Both have profile images as large as the apps make them.
|
|
# For SimpleX names, an SMP server on 127.0.0.1 resolves .testing names through a local resolver, and a CLI peer
|
|
# with an address on it claims the name seeded.testing.
|
|
# Usage:
|
|
# scripts/ci/desktop-linux/backend.sh start <backend .tar.gz> <work dir> writes the directory's short address to <work dir>/directory-link
|
|
# and the names server's address to <work dir>/names-server
|
|
# scripts/ci/desktop-linux/backend.sh stop <work dir> stops the servers, prints their logs
|
|
# The relay, the directory, the SMP server and the resolver keep running between start and stop.
|
|
|
|
set -euo pipefail
|
|
|
|
here=$(cd "$(dirname "$0")" && pwd)
|
|
directory_name="SimpleX Directory"
|
|
admin=ci_admin
|
|
search_text="ci seed"
|
|
# the longest profile image data URI the apps make and the core accepts (maxProfileImageSize)
|
|
max_image_size=12500
|
|
name_label=seeded
|
|
resolver_port=8090
|
|
|
|
# Runs one chat command in a CLI client that stays online for <secs> after it; the output is in $work/out.
|
|
chat() {
|
|
local db=$1 secs=$2 cmd=$3
|
|
shift 3
|
|
local status=0
|
|
"$bin/simplex-chat" -d "$work/$db" -e "$cmd" -t "$secs" --execute-log all "$@" < /dev/null > "$work/out" 2>&1 || status=$?
|
|
cat "$work/out" >> "$work/$db-cli.log"
|
|
# commands with images are thousands of characters long
|
|
echo "::group::$db: ${cmd:0:200}"
|
|
cat "$work/out"
|
|
echo "::endgroup::"
|
|
return "$status"
|
|
}
|
|
|
|
admin() {
|
|
chat "$admin" "$1" "$2" --user-display-name "$admin"
|
|
}
|
|
|
|
expect_out() {
|
|
if ! grep -qE "$1" "$work/out"; then
|
|
echo "error: \"$1\" not in the output"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Each run keeps the client online for a few seconds, which also lets the handshakes it waits for progress.
|
|
until_shown() {
|
|
local client=$1 pattern=$2 secs=$3 cmd=$4
|
|
local deadline=$((SECONDS + secs))
|
|
until "$client" 5 "$cmd" && grep -qE "$pattern" "$work/out"; do
|
|
if [ "$SECONDS" -ge "$deadline" ]; then
|
|
echo "error: \"$pattern\" not shown by \"$cmd\" after $secs s"
|
|
return 1
|
|
fi
|
|
done
|
|
}
|
|
|
|
admin_until() {
|
|
until_shown admin "$@"
|
|
}
|
|
|
|
db_query() {
|
|
sqlite3 "$work/$1_chat.db" "$2"
|
|
}
|
|
|
|
# Prints a profile image data URI: JPEG at quality 85, scaled down by the square root
|
|
# of its excess, at most halved per step, until it is at most max_image_size characters. The picture fills the
|
|
# whole square, a gradient between two shades of one colour with a large white letter; noise keeps JPEG from
|
|
# compressing it much, so the image ends up near the limit.
|
|
avatar() {
|
|
local top=$1 bottom=$2 letter=$3 source=$work/avatar-source.png file=$work/avatar.jpg size=512 uri font
|
|
font=$(fc-match -f '%{file}' 'DejaVu Sans:bold')
|
|
convert -size 512x512 gradient:"$top-$bottom" -seed 1 -attenuate 0.5 +noise Gaussian \
|
|
-font "$font" -pointsize 320 -fill white -gravity center -annotate +0+0 "$letter" "$source"
|
|
while :; do
|
|
convert "$source" -resize "${size}x${size}" -quality 85 "$file"
|
|
uri="data:image/jpg;base64,$(base64 -w 0 "$file")"
|
|
[ "${#uri}" -gt "$max_image_size" ] || break
|
|
size=$(awk -v s="$size" -v n="${#uri}" -v m="$max_image_size" 'BEGIN { r = sqrt(n / m); if (r > 2) r = 2; print int(s / r) }')
|
|
done
|
|
echo "avatar \"$letter\" over $top to $bottom: ${size}x${size} px, ${#uri} characters, mean colour $(convert "$file" -resize '1x1!' -format '%[pixel:p{0,0}]' info:)" >&2
|
|
echo "$uri"
|
|
}
|
|
|
|
start_relay() {
|
|
nohup "$bin/simplex-chat" -d "$work/relay" --relay --headless --user-display-name ci_relay < /dev/null > "$work/relay.log" 2>&1 &
|
|
echo $! > "$work/relay.pid"
|
|
local deadline=$((SECONDS + 120))
|
|
until grep -qE '^https://[^ ]+/r#' "$work/relay.log"; do
|
|
if ! kill -0 "$(cat "$work/relay.pid")" 2> /dev/null || [ "$SECONDS" -ge "$deadline" ]; then
|
|
echo "error: the relay did not create its address"
|
|
cat "$work/relay.log"
|
|
return 1
|
|
fi
|
|
sleep 1
|
|
done
|
|
relay_link=$(grep -m 1 -oE '^https://[^ ]+/r#[^ ]+' "$work/relay.log")
|
|
echo "relay address: $relay_link"
|
|
}
|
|
|
|
# The address is created here, like the directory tests do, with auto-accept on before the service subscribes to it.
|
|
create_directory() {
|
|
chat directory 1 "/ad pq_ratchet=on" --create-bot-display-name "$directory_name"
|
|
directory_link=$(awk '/Your new chat address is created!/ {getline; getline; print; exit}' "$work/out")
|
|
case "$directory_link" in
|
|
https://*/a#*) ;;
|
|
*)
|
|
echo "error: no short directory address in the output"
|
|
return 1
|
|
;;
|
|
esac
|
|
chat directory 1 "/auto_accept on"
|
|
expect_out "^auto_accept on"
|
|
echo "$directory_link" > "$work/directory-link"
|
|
echo "directory address: $directory_link"
|
|
}
|
|
|
|
start_directory() {
|
|
local pid deadline=$((SECONDS + 60))
|
|
# -y: the service adds its own tables to the database the CLI created, and would otherwise ask on stdin
|
|
nohup "$bin/simplex-directory-service" -d "$work/directory" -y --super-users "$1" < /dev/null >> "$work/directory.log" 2>&1 &
|
|
pid=$!
|
|
echo "$pid" > "$work/directory.pid"
|
|
# its output is block-buffered, so it shows that its chat has started by connecting to its servers
|
|
until grep -q "pid=$pid," <<< "$(ss -Htnp state established)"; do
|
|
if ! kill -0 "$pid" 2> /dev/null || [ "$SECONDS" -ge "$deadline" ]; then
|
|
echo "error: the directory service did not start"
|
|
cat "$work/directory.log"
|
|
return 1
|
|
fi
|
|
sleep 1
|
|
done
|
|
}
|
|
|
|
# Waits for the process to exit, which completes its output, and kills it after 30 s.
|
|
stop_process() {
|
|
local pid deadline
|
|
pid=$(cat "$work/$1.pid")
|
|
kill "-$2" "$pid" 2> /dev/null || true
|
|
deadline=$((SECONDS + 30))
|
|
while kill -0 "$pid" 2> /dev/null; do
|
|
if [ "$SECONDS" -ge "$deadline" ]; then
|
|
kill -KILL "$pid" 2> /dev/null || true
|
|
fi
|
|
sleep 1
|
|
done
|
|
}
|
|
|
|
# A super user is known by contact ID, which exists only after the admin connects, so the service starts
|
|
# with nobody and is restarted with the admin.
|
|
connect_admin() {
|
|
admin 10 "/c $directory_link"
|
|
expect_out "connection request sent!"
|
|
admin_until "Welcome to $directory_name" 180 "/tail @'$directory_name' 10"
|
|
local id
|
|
id=$(db_query directory "SELECT contact_id FROM contacts WHERE local_display_name = '$admin'")
|
|
stop_process directory INT
|
|
start_directory "$id:$admin"
|
|
echo "directory restarted with super user $id:$admin"
|
|
}
|
|
|
|
# The directory sends each registration to super users as a message with the exact /approve command.
|
|
approve() {
|
|
local name=$1 reply=$2
|
|
admin_until "/approve [0-9]+:$name [0-9]+" 300 "/tail @'$directory_name' 20"
|
|
local cmd
|
|
cmd=$(grep -oE "/approve [0-9]+:$name [0-9]+" "$work/out" | tail -n 1)
|
|
admin 5 "@'$directory_name' $cmd"
|
|
admin_until "$reply" 120 "/tail @'$directory_name' 20"
|
|
}
|
|
|
|
# The CLI's group commands can't set an image, so groups are created with the API commands, with the profiles
|
|
# that "/g" and "/public group" make plus the image.
|
|
group_json() {
|
|
local name=$1 descr=$2 image=$3 prefs=$4
|
|
echo "{\"displayName\":\"$name\",\"fullName\":\"\",\"shortDescr\":\"$descr\",\"image\":\"$image\",\"groupPreferences\":{\"directMessages\":{\"enable\":\"on\"},\"history\":{\"enable\":\"on\"}$prefs}}"
|
|
}
|
|
|
|
seed_group() {
|
|
local user_id
|
|
user_id=$(db_query "$admin" "SELECT user_id FROM users WHERE local_display_name = '$admin'")
|
|
group_image=$(avatar "rgb(235,80,70)" "rgb(150,25,30)" G)
|
|
admin 1 "/_group $user_id $(group_json ci_group "CI seeded group" "$group_image" "")"
|
|
expect_out "group #ci_group \(CI seeded group\) is created"
|
|
admin 5 "/a ci_group '$directory_name' admin"
|
|
expect_out "invitation to join the group #ci_group sent to"
|
|
approve ci_group "Group approved!"
|
|
}
|
|
|
|
seed_channel() {
|
|
admin 1 "/relays name=ci_relay $relay_link"
|
|
expect_out "^ok$"
|
|
local user_id relay_id
|
|
user_id=$(db_query "$admin" "SELECT user_id FROM users WHERE local_display_name = '$admin'")
|
|
relay_id=$(db_query "$admin" "SELECT chat_relay_id FROM chat_relays WHERE display_name = 'ci_relay' AND deleted = 0")
|
|
channel_image=$(avatar "rgb(80,200,90)" "rgb(25,120,45)" C)
|
|
admin 10 "/_public group $user_id $relay_id $(group_json ci_channel "CI seeded channel" "$channel_image" ",\"support\":{\"enable\":\"off\"}")"
|
|
expect_out "group #ci_channel \(CI seeded channel\) is created"
|
|
local group_id
|
|
group_id=$(db_query "$admin" "SELECT group_id FROM groups WHERE local_display_name = 'ci_channel'")
|
|
admin_until "relay id [0-9]+: active" 180 "/_get relays #$group_id"
|
|
admin 5 "/share chat #ci_channel @'$directory_name'"
|
|
expect_out "link to join channel #ci_channel"
|
|
approve ci_channel "Channel approved!"
|
|
}
|
|
|
|
# An SMP server on 127.0.0.1 whose [NAMES] section asks the local resolver, which answers from names.json.
|
|
start_names_server() {
|
|
local cfg=$work/smp/etc logs=$work/smp/var ini deadline
|
|
echo '{}' > "$work/names.json"
|
|
nohup python3 "$here/names-resolver.py" "$resolver_port" "$work/names.json" < /dev/null > "$work/resolver.log" 2>&1 &
|
|
echo $! > "$work/resolver.pid"
|
|
SMP_SERVER_CFG_PATH=$cfg SMP_SERVER_LOG_PATH=$logs "$bin/smp-server" init -y --ip 127.0.0.1 --no-password --disable-web
|
|
ini=$cfg/smp-server.ini
|
|
# only 5223: 443 needs root
|
|
sed -i -e 's/^port = 5223,443$/port = 5223/' \
|
|
-e '/^\[NAMES\]/,/^\[/{s/^enable: off$/enable: on/;s|^# resolver_endpoint: http://127.0.0.1:8000$|resolver_endpoint: http://127.0.0.1:'"$resolver_port"'|}' "$ini"
|
|
if ! grep -qx 'port = 5223' "$ini" ||
|
|
! sed -n '/^\[NAMES\]/,/^\[/p' "$ini" | grep -qx 'enable: on' ||
|
|
! sed -n '/^\[NAMES\]/,/^\[/p' "$ini" | grep -qx "resolver_endpoint: http://127.0.0.1:$resolver_port"; then
|
|
echo "error: $ini has a different port or [NAMES] than expected:"
|
|
grep -E '^port|^\[NAMES\]|enable|resolver' "$ini"
|
|
return 1
|
|
fi
|
|
SMP_SERVER_CFG_PATH=$cfg SMP_SERVER_LOG_PATH=$logs nohup "$bin/smp-server" start < /dev/null > "$work/smp-server.log" 2>&1 &
|
|
echo $! > "$work/smp-server.pid"
|
|
deadline=$((SECONDS + 60))
|
|
until (exec 3<> /dev/tcp/127.0.0.1/5223) 2> /dev/null; do
|
|
if ! kill -0 "$(cat "$work/smp-server.pid")" 2> /dev/null || [ "$SECONDS" -ge "$deadline" ]; then
|
|
echo "error: the SMP server is not listening on 5223"
|
|
cat "$work/smp-server.log"
|
|
return 1
|
|
fi
|
|
sleep 1
|
|
done
|
|
names_server="smp://$(cat "$cfg/fingerprint")@127.0.0.1"
|
|
echo "$names_server" > "$work/names-server"
|
|
echo "names server: $names_server"
|
|
}
|
|
|
|
# A CLI client started with -s uses only that server and, once its user exists, gives it every role, names included.
|
|
names_client() {
|
|
local db=$1
|
|
shift
|
|
chat "$db" "$@" --user-display-name "$db" -s "$names_server"
|
|
}
|
|
|
|
# ci_contact's address is on the names server; the resolver maps seeded.testing to it, and ci_contact claims the name.
|
|
seed_name() {
|
|
local image link
|
|
image=$(avatar "rgb(70,130,240)" "rgb(30,50,170)" N)
|
|
names_client ci_contact 1 "/_profile 1 {\"displayName\":\"ci_contact\",\"fullName\":\"\",\"shortDescr\":\"CI seeded contact\",\"image\":\"$image\"}"
|
|
names_client ci_contact 3 "/ad"
|
|
link=$(awk '/Your new chat address is created!/ {getline; getline; print; exit}' "$work/out")
|
|
case "$link" in
|
|
https://*/a#*) ;;
|
|
*)
|
|
echo "error: no short address for ci_contact in the output"
|
|
return 1
|
|
;;
|
|
esac
|
|
jq -n --arg n "$name_label" --arg l "$link" '{($n): {simplexContact: [$l]}}' > "$work/names.json"
|
|
names_client ci_contact 5 "/_set domain 1 $name_label.testing"
|
|
# what the app does on the Search key, from a client that knows nothing but the names server; it finds the
|
|
# address only if ci_contact's claim is in the address data
|
|
names_client name_probe 1 "/user"
|
|
names_client name_probe 5 "/_connect plan 1 @$name_label.testing"
|
|
expect_out "contact address: ok to connect"
|
|
echo "@$name_label.testing resolves to ci_contact's address"
|
|
}
|
|
|
|
searcher() {
|
|
chat searcher "$1" "$2" --user-display-name ci_searcher
|
|
}
|
|
|
|
# A new user connects to the directory and sends the search text; it replies with the count, then one message per
|
|
# group or channel found, with its image.
|
|
search_by_message() {
|
|
searcher 10 "/c $directory_link"
|
|
expect_out "connection request sent!"
|
|
until_shown searcher "Welcome to $directory_name" 180 "/tail @'$directory_name' 10"
|
|
searcher 5 "@'$directory_name' $search_text"
|
|
until_shown searcher "Found 2 group\(s\)" 120 "/tail @'$directory_name' 10"
|
|
until_shown searcher "ci_group" 60 "/tail @'$directory_name' 10"
|
|
until_shown searcher "ci_channel" 60 "/tail @'$directory_name' 10"
|
|
echo "search for \"$search_text\" by message found ci_group and ci_channel"
|
|
}
|
|
|
|
dump_directory() {
|
|
echo "::group::directory registrations"
|
|
db_query directory "SELECT group_id, group_reg_status FROM sx_directory_group_regs" || true
|
|
echo "::endgroup::"
|
|
}
|
|
|
|
start() {
|
|
bin=$work/bin
|
|
mkdir -p "$bin"
|
|
tar -C "$bin" -xzf "$1"
|
|
"$bin/simplex-chat" --version
|
|
"$bin/simplex-directory-service" --version
|
|
|
|
start_relay
|
|
create_directory
|
|
start_directory "0:nobody"
|
|
trap dump_directory EXIT
|
|
connect_admin
|
|
seed_group
|
|
seed_channel
|
|
search_by_message
|
|
start_names_server
|
|
seed_name
|
|
}
|
|
|
|
stop() {
|
|
local p
|
|
for p in directory relay smp-server; do
|
|
if [ -f "$work/$p.pid" ]; then
|
|
stop_process "$p" INT
|
|
fi
|
|
done
|
|
# Python, started in the background by a script, ignores SIGINT
|
|
if [ -f "$work/resolver.pid" ]; then
|
|
stop_process resolver TERM
|
|
fi
|
|
for p in directory relay smp-server resolver; do
|
|
echo "::group::$p log"
|
|
cat "$work/$p.log" 2> /dev/null || true
|
|
echo "::endgroup::"
|
|
done
|
|
}
|
|
|
|
case "${1:-}" in
|
|
start)
|
|
work=$3
|
|
mkdir -p "$work"
|
|
start "$2"
|
|
;;
|
|
stop)
|
|
work=$2
|
|
stop
|
|
;;
|
|
*)
|
|
echo "usage: $0 start <backend .tar.gz> <work dir> | stop <work dir>"
|
|
exit 1
|
|
;;
|
|
esac
|