Ubuntu PPA für chr

Es gibt nun auch ein PPA für chr und chr-tiny:

sudo add-apt-repository ppa:chr-istoph/chr
sudo apt update
sudo apt install chr

Quelle: PPA: chr-istoph/chr

Gitlab CI mit QEMU für FreeBSD, OpenBSD oder NetBSD oder alternative Architekturen

Für das Tuiwidgets und Termpaint Projekt benötigen wir auch CI Build in BSD. Anhand von FreeBSD dokumentiere ich hier einmal, wie wir mit GitLab und QEMU dies in der CI zum Laufen gebracht haben.

Zunächst haben wir ein Image erstellt, das alle für unseren Zweck notwendigen Pakete enthält.

ISO herunterladen:

wget http://ftp-archive.freebsd.org/pub/FreeBSD/releases/amd64/amd64/ISO-IMAGES/13.2/FreeBSD-13.2-RELEASE-amd64-bootonly.iso

Raw Device erstellen. Hier wird ein leeres Image erzeugt, in dem das Betriebssystem installiert wird:

qemu-img create -f raw FreeBSD-VM10G.raw 10G

Dann wird das Image installiert, hierfür wird ein x11 fähiger Zugriff auf den Träger benötigt:

qemu-system-x86_64 -m 4G -cpu host -enable-kvm -smp cpus=4 -drive file=~/FreeBSD-VM10G.raw,if=virtio -nographic -cdrom ~/FreeBSD-13.2-RELEASE-amd64-bootonly.iso -net nic,model=e1000

Für unsere Zwecke wird ein SSH schlüssel hinterlegt und der Zugriff via com console freigeschaltet:

echo 'console="comconsole"' >> /boot/loader.conf
echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config
mkdir /root/.ssh
echo 'ssh-ed25519 xxx root@qemu-host-key' >> /root/.ssh/authorized_keys
chmod 0600 /root/.ssh/authorized_keys
chmod 0600 /root/.ssh

in der Gitlab config.toml wird nur eine neuer Runner angelegt, in der custom Sektion werden 3 zusätzlich Scripte angelegt:

[[runners]]
  name = xxx
...
  [runners.custom]
    prepare_exec = "/root/gitlab-qemu/prepare"
    run_exec = "/root/gitlab-qemu/run"
    cleanup_exec = "/root/gitlab-qemu/cleanup"

prepare

#!/bin/bash

VM_ID="runner-$CUSTOM_ENV_CI_RUNNER_ID-project-$CUSTOM_ENV_CI_PROJECT_ID-concurrent-$CUSTOM_ENV_CI_CONCURRENT_PROJECT_ID-job-$CUSTOM_ENV_CI_JOB_ID"

read LOWERPORT UPPERPORT < /proc/sys/net/ipv4/ip_local_port_range
while :
do
        PORT="`shuf -i $LOWERPORT-$UPPERPORT -n 1`"
        ss -lpn | grep -q ":$PORT " || break
done
echo -n $PORT > ~/${VM_ID}.port

echo "Preparing VM $VM_ID"


case $CUSTOM_ENV_CI_JOB_IMAGE in
    amd64-freebsd*)
        screen -S $VM_ID -Logfile ~/log/${VM_ID}.screen -L -d -m qemu-system-x86_64 -cpu host -enable-kvm -m 4G -smp 4 -drive file=~/FreeBSD-VM10G.raw,if=virtio -nographic -net nic,model=e1000 -net user,hostfwd=tcp::${PORT}-:22 -snapshot -pidfile ~/${VM_ID}.pid
        ;;
    amd64-netbsd*)
        screen -S $VM_ID -Logfile ~/log/${VM_ID}.screen -L -d -m qemu-system-x86_64 -cpu host -enable-kvm -m 4G -smp 4 -drive file=~/netbsd-10.0-2023-11-30.qcow2,if=virtio -nographic -net nic,model=e1000 -net user,hostfwd=tcp::${PORT}-:22 -snapshot -pidfile ~/${VM_ID}.pid
        ;;
    amd64-openbsd*)
        screen -S $VM_ID -Logfile ~/log/${VM_ID}.screen -L -d -m qemu-system-x86_64 -cpu host -enable-kvm -m 4G -smp 4 -drive file=~/openbsd-7.3-2023-04-22.qcow2,if=virtio -nographic -net nic,model=e1000 -net user,hostfwd=tcp::${PORT}-:22 -snapshot -pidfile ~/${VM_ID}.pid
        ;;
    *)
        echo "Unknown arch"
        exit "$SYSTEM_FAILURE_EXIT_CODE"
        ;;
esac

ssh-keygen -f "~/.ssh/known_hosts" -R "[localhost]:${PORT}" || true

# Wait for ssh to become available
echo "Waiting for sshd to be available [localhost]:${PORT}"
for i in $(seq 1 120); do
    if timeout 5 ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i ~/ci-access.key -p${PORT} root@localhost true >/dev/null 2>/dev/null; then
        break
    fi

    if [ "$i" == "120" ]; then
        echo 'Waited 120 seconds for sshd to start, exiting...'
        # Inform GitLab Runner that this is a system failure, so it
        # should be retried.
        exit "$SYSTEM_FAILURE_EXIT_CODE"
    fi

    sleep 1s
done

Mit der -snapshot Option wird das Image Read Only gemountet. Somit können wir sicherstellen, dass immer wieder ein sauberes Environment zur Verfügung steht.

run

#!/bin/bash

VM_ID="runner-$CUSTOM_ENV_CI_RUNNER_ID-project-$CUSTOM_ENV_CI_PROJECT_ID-concurrent-$CUSTOM_ENV_CI_CONCURRENT_PROJECT_ID-job-$CUSTOM_ENV_CI_JOB_ID"

ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i ~/ci-access.key -p$(cat ~/${VM_ID}.port) root@localhost bash < "$1"

cleanup

#!/bin/bash

VM_ID="runner-$CUSTOM_ENV_CI_RUNNER_ID-project-$CUSTOM_ENV_CI_PROJECT_ID-concurrent-$CUSTOM_ENV_CI_CONCURRENT_PROJECT_ID-job-$CUSTOM_ENV_CI_JOB_ID"

kill $(cat ~/${VM_ID}.pid)
rm ~/${VM_ID}.pid
rm ~/${VM_ID}.port


In der .gitlab-ci.yml geben wir nun folgenden Dinge an:
build:freebsd:
  stage: build
  when: manual
  tags:
    - qemu
  image: amd64-freebsd-13
  script:
    - uname -a

Howto debug PHP segfault

Es gibt hier 2 Möglichkeiten segfaults mit gdb anzuschauen. Zum debuggen benötigt man erstmal gdb und die debugs-symbole des Endsprechenden Modules. In meinem aktiven Fall war das, das soap Modul:

dnf install -y gdb
debuginfo-install php74-php-soap

Für das debugen eines coredumps muss man in der /etc/opt/remi/php82/php-fpm.d/www.conf folgendes einstellen:

rlimit_core = 0

Reload nicht vergessen: systemctl reload php74-php-fpm.service

Dem Kernel muss ein Pfad angegeben werden, der auch von php-fpm geschrieben werden kann. Achtung, im cordump können auch sensible Informationen stehen. Daher sollte ein Pfad gewählt werden, der nicht vom Webserver ausgeliefert wird:

echo '/tmp/coredump-%e.%p' > /proc/sys/kernel/core_pattern

Dann kann man sich den coredump anschauen, in dem man das Programm und den Dump angibt:

gdb /opt/remi/php82/root/usr/sbin/php-fpm coredump-php-fpm.852720

Dan gibt es noch die Möglichkeit des live Debuggings. Dafür habe ich die Parameter in php-fpm angepasst, dass nur noch 1 Prozess gestartet wird. So habe ich die Möglichkeit die segfaults sofort mitzubekommen (ob dies ein gangbarer Weg im produktiv Umgebungen ist, müsst ihr selbst Endscheiden):

/etc/opt/remi/php82/php-fpm.d/www.conf

pm.max_children = 1
pm.start_servers = 1
pm.min_spare_servers = 1
pm.max_spare_servers = 1

Reload nicht vergessen: systemctl reload php74-php-fpm.service

Mit `ps` kann man sehen das jetzt nur noch 2 Prozesse vorhanden sind. Wir hängen uns an den chaild:

ps axfu 
...
root      171156  0.0  0.0 493916 11688 ?        Ss   10:46   0:00 php-fpm: master process (/etc/opt/remi/php82/php-fpm.conf)
apache    189306  0.0  0.1 497068 26096 ?        S    10:53   0:00  \_ php-fpm: pool www

Dann kann ich mich mit gdb an den chaild Prozess hängen:

gdb -p 189306
...
(gdb) continue 
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x00007f60ce1f1c75 in __strlen_avx2 () from /lib64/libc.so.6

Backtrace, gibt einen den Output des laufenden Prozesses:

(gdb) bt
#0  0x00007f60ce1f1c75 in __strlen_avx2 () from /lib64/libc.so.6
#1  0x00007f60c1cdeb6d in get_param (function=function@entry=0x7f60cc2720c0, 
    param_name=param_name@entry=0x18 , index=, response=response@entry=1)
    at /usr/src/debug/php82-php-8.2.0~rc3-16.el8.remi.x86_64/ext/soap/soap.c:3978
#2  0x00007f60c1cdfe54 in serialize_response_call2 (body=body@entry=0x557c1ae411d0, function=function@entry=0x7f60cc2720c0, 
    function_name=function_name@entry=0x7f60cc256078 "openSessionResponse", uri=uri@entry=0x7f60cc2660c0 "urn:soapService", 
    ret=ret@entry=0x7ffcc3881630, version=version@entry=1, main=1, node=0x0)
    at /usr/src/debug/php82-php-8.2.0~rc3-16.el8.remi.x86_64/ext/soap/soap.c:3292
#3  0x00007f60c1ce509b in serialize_response_call (function=0x7f60cc2720c0, function_name=0x7f60cc256078 "openSessionResponse", 
    uri=0x7f60cc2660c0 "urn:soapService", ret=0x7ffcc3881630, headers=0x0, version=1)
    at /usr/src/debug/php82-php-8.2.0~rc3-16.el8.remi.x86_64/ext/soap/soap.c:3660
#4  0x00007f60c1ced386 in zim_SoapServer_handle (execute_data=0x7f60cc213090, return_value=)
    at /usr/src/debug/php82-php-8.2.0~rc3-16.el8.remi.x86_64/ext/soap/soap.c:1484
#5  0x00007f60cbcbe4f5 in xdebug_execute_internal () from /opt/remi/php82/root/usr/lib64/php/modules/xdebug.so
#6  0x0000557c196a02c8 in ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER ()
    at /usr/src/debug/php82-php-8.2.0~rc3-16.el8.remi.x86_64/Zend/zend_vm_execute.h:1844
#7  execute_ex (ex=0x18) at /usr/src/debug/php82-php-8.2.0~rc3-16.el8.remi.x86_64/Zend/zend_vm_execute.h:56047
#8  0x00007f60cbcbda4c in xdebug_execute_ex () from /opt/remi/php82/root/usr/lib64/php/modules/xdebug.so
#9  0x0000557c196a1932 in zend_execute (op_array=0x7f60cc280000, return_value=0x0)
    at /usr/src/debug/php82-php-8.2.0~rc3-16.el8.remi.x86_64/Zend/zend_vm_execute.h:60379
#10 0x0000557c1962ed15 in zend_execute_scripts (type=type@entry=8, retval=retval@entry=0x0, file_count=file_count@entry=3)
    at /usr/src/debug/php82-php-8.2.0~rc3-16.el8.remi.x86_64/Zend/zend.c:1780
#11 0x0000557c195c849a in php_execute_script (primary_file=) at /usr/src/debug/php82-php-8.2.0~rc3-16.el8.remi.x86_64/main/main.c:2537
#12 0x0000557c1946e662 in main (argc=, argv=)
    at /usr/src/debug/php82-php-8.2.0~rc3-16.el8.remi.x86_64/sapi/fpm/fpm/fpm_main.c:1891

Die jeweiligen stop’s bekommt man mit frame:

(gdb) frame 1
#1  0x00007f60c1cdeb6d in get_param (function=function@entry=0x7f60cc2720c0, 
    param_name=param_name@entry=0x18 , index=, response=response@entry=1)
    at /usr/src/debug/php82-php-8.2.0~rc3-16.el8.remi.x86_64/ext/soap/soap.c:3978
3978			if ((tmp = zend_hash_str_find_ptr(ht, param_name, strlen(param_name))) != NULL) {

Eine liste des Sourcecodes bekommt man mit list, Voraussetzung sind hier die installierten debug symbole:

(gdb) list
3973		if (ht == NULL) {
3974		  return NULL;
3975		}
3976	
3977		if (param_name != NULL) {
3978			if ((tmp = zend_hash_str_find_ptr(ht, param_name, strlen(param_name))) != NULL) {
3979				return tmp;
3980			} else {
3981				ZEND_HASH_FOREACH_PTR(ht, tmp) {
3982					if (tmp->paramName && strcmp(param_name, tmp->paramName) == 0) {

Die Ausgabe der im frame gespeicherten variablen:

(gdb) info locals 
tmp = 
ht = 0x7f60cc260428

(gdb) print param_name
$1 = 0x18 

Tui Widgeds Video

Unser Vortag auf der #FrOSCon17 ist Online:

Launch of Tui Widgets

Wir waren fleißig und haben am vergangenen Wochenende, endlich nach 5 Jahren Arbeit, Tui Widgets veröffentlicht.

Demo Tui Widgets

Für unseren Vortag: Tui Widgets: Ein Baukasten für Terminal-Anwendungen auf der #FrOSCon17 erstellen wir gerade die Präsentation und Doku.