Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 17e0f6b53a | |||
| e5911c09c2 |
6 changed files with 145 additions and 12 deletions
|
|
@ -41,7 +41,7 @@ import Imsd;
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// ---- static config (env-overridable, same knobs as imsd.py) ---------------
|
// ---- static config (env-overridable, same knobs as imsd.py) ---------------
|
||||||
constexpr const char* Version = "0.3.0";
|
constexpr const char* Version = "0.3.1";
|
||||||
constexpr const char* BusName = "net.catcrafts.IMS1";
|
constexpr const char* BusName = "net.catcrafts.IMS1";
|
||||||
constexpr const char* ObjPath = "/net/catcrafts/IMS1";
|
constexpr const char* ObjPath = "/net/catcrafts/IMS1";
|
||||||
constexpr const char* Iface = "net.catcrafts.IMS1";
|
constexpr const char* Iface = "net.catcrafts.IMS1";
|
||||||
|
|
|
||||||
1
packaging/aport/80-imsd.preset
Normal file
1
packaging/aport/80-imsd.preset
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
enable imsd.service
|
||||||
81
packaging/aport/APKBUILD
Normal file
81
packaging/aport/APKBUILD
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
# SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||||
|
# The canonical apk aport for imsd, owned by this repository since
|
||||||
|
# 2026-09-01 (transferred from fp6-img, which now consumes this directory:
|
||||||
|
# its build.sh copies packaging/aport/ into the pmaports overlay, pins
|
||||||
|
# _commit to a reviewed commit, and generates the source tarball with
|
||||||
|
# git-archive — the Forgejo instance serves no source archives).
|
||||||
|
# packaging/APKBUILD{,.binary} are the older standalone variants.
|
||||||
|
maintainer="Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>"
|
||||||
|
pkgname=imsd
|
||||||
|
pkgver=0.3.1
|
||||||
|
pkgrel=0
|
||||||
|
pkgdesc="Userspace IMS/VoLTE daemon for mainline Linux phones"
|
||||||
|
url="https://forgejo.catcrafts.net/Catcrafts/imsd"
|
||||||
|
# other arches: nothing wrong known, just never run there
|
||||||
|
arch="aarch64 x86_64"
|
||||||
|
license="GPL-3.0-only"
|
||||||
|
# the media leg dlopen's the AMR-WB codecs; pw-record/pw-play drive PipeWire —
|
||||||
|
# none of which abuild's .so auto-scan can see
|
||||||
|
depends="modemmanager opencore-amr vo-amrwbenc pipewire-tools"
|
||||||
|
# clang/libc++ C++26-modules build (upstream Makefile); llvm-runtimes ships
|
||||||
|
# the libc++ std module sources the build precompiles
|
||||||
|
makedepends="clang lld libc++-dev llvm-libunwind-dev llvm-runtimes glib-dev pkgconf"
|
||||||
|
# the versioned provides both satisfies soc-qcom-modem's 81voltd dependency
|
||||||
|
# and excludes the real package: 81voltd serves the modem firmware's own
|
||||||
|
# ims-PDN requests, which races imsd for the PDN and flaps it (a new prefix
|
||||||
|
# every ~2.5 min) — two IMS stacks cannot share one PDN. Installing imsd is
|
||||||
|
# an explicit choice to hand the IMS PDN to userspace.
|
||||||
|
provides="81voltd=$pkgver-r$pkgrel"
|
||||||
|
# no OpenRC service yet: the unit's PDN-bring-up/env-file sequencing is only
|
||||||
|
# tested under systemd; an initd is welcome once someone can verify one
|
||||||
|
subpackages="$pkgname-systemd"
|
||||||
|
# _commit is pinned by the consuming CI, which also drops the git-archive
|
||||||
|
# tarball (prefix imsd/) next to this APKBUILD. The skel override hides
|
||||||
|
# kde-telephony's modem daemon autostart for the account created at install —
|
||||||
|
# imsd-dialerd owns those session D-Bus names instead.
|
||||||
|
_commit="REPLACED_BY_CI"
|
||||||
|
source="
|
||||||
|
imsd-$_commit.tar.gz
|
||||||
|
org.kde.modem.daemon.desktop
|
||||||
|
80-imsd.preset
|
||||||
|
"
|
||||||
|
builddir="$srcdir/$pkgname"
|
||||||
|
|
||||||
|
build() {
|
||||||
|
make
|
||||||
|
}
|
||||||
|
|
||||||
|
check() {
|
||||||
|
make check
|
||||||
|
}
|
||||||
|
|
||||||
|
package() {
|
||||||
|
make install DESTDIR="$pkgdir"
|
||||||
|
install -Dm644 "$srcdir"/org.kde.modem.daemon.desktop \
|
||||||
|
"$pkgdir"/etc/skel/.config/autostart/org.kde.modem.daemon.desktop
|
||||||
|
# enabled by preset: the unit is a no-op until /etc/imsd.env exists, and
|
||||||
|
# VoLTE surviving reboots must not depend on a manual systemctl enable
|
||||||
|
install -Dm644 "$srcdir"/80-imsd.preset \
|
||||||
|
"$pkgdir"/usr/lib/systemd/system-preset/80-imsd.preset
|
||||||
|
mkdir -p "$pkgdir"/etc/systemd/system/multi-user.target.wants
|
||||||
|
ln -s /usr/lib/systemd/system/imsd.service \
|
||||||
|
"$pkgdir"/etc/systemd/system/multi-user.target.wants/imsd.service
|
||||||
|
# ...but only actually start once the carrier config exists, so
|
||||||
|
# unconfigured systems don't boot into a failing unit
|
||||||
|
mkdir -p "$pkgdir"/usr/lib/systemd/system/imsd.service.d
|
||||||
|
printf '[Unit]\nConditionPathExists=/etc/imsd.env\n' \
|
||||||
|
> "$pkgdir"/usr/lib/systemd/system/imsd.service.d/10-require-config.conf
|
||||||
|
}
|
||||||
|
|
||||||
|
systemd() {
|
||||||
|
install_if="$pkgname=$pkgver-r$pkgrel systemd"
|
||||||
|
|
||||||
|
amove usr/lib/systemd/system
|
||||||
|
}
|
||||||
|
|
||||||
|
sha512sums="
|
||||||
|
REPLACED_BY_CI imsd-REPLACED_BY_CI.tar.gz
|
||||||
|
REPLACED_BY_CI org.kde.modem.daemon.desktop
|
||||||
|
REPLACED_BY_CI 80-imsd.preset
|
||||||
|
"
|
||||||
2
packaging/aport/org.kde.modem.daemon.desktop
Normal file
2
packaging/aport/org.kde.modem.daemon.desktop
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
[Desktop Entry]
|
||||||
|
Hidden=true
|
||||||
|
|
@ -1,22 +1,42 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# SPDX-License-Identifier: GPL-3.0-only
|
# SPDX-License-Identifier: GPL-3.0-only
|
||||||
# SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
# SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||||
# ims-pdn-up.sh — boot bring-up for the IPv6 `ims` PDN (journal/ims.md s45).
|
# ims-pdn-up.sh — boot bring-up for the `ims` PDN (journal/ims.md s45).
|
||||||
#
|
#
|
||||||
# Finds-or-creates the ims ipv6 bearer via ModemManager, connects it, and
|
# Finds-or-creates the ims bearer via ModemManager, connects it, and
|
||||||
# configures the muxed netdev with the MM-assigned address (the s34 recipe,
|
# configures the muxed netdev with the MM-assigned address (the s34 recipe,
|
||||||
# automated). Idempotent — safe to run when the PDN is already up. Runs as
|
# automated). Idempotent — safe to run when the PDN is already up. Runs as
|
||||||
# ExecStartPre of imsd.service, so imsd only starts once the PDN exists; a
|
# ExecStartPre of imsd.service, so imsd only starts once the PDN exists; a
|
||||||
# nonzero exit fails the unit and systemd retries per Restart/RestartSec.
|
# nonzero exit fails the unit and systemd retries per Restart/RestartSec.
|
||||||
|
#
|
||||||
|
# Configuration, via the unit's EnvironmentFile /etc/imsd.env (also read
|
||||||
|
# directly so manual runs behave the same):
|
||||||
|
# IMS_IP_TYPE bearer ip-type (default ipv6)
|
||||||
|
# IMS_REG_TIMEOUT max seconds to wait for network registration before
|
||||||
|
# connect attempts start counting anyway (default 300)
|
||||||
|
|
||||||
log() { echo "ims-pdn-up: $*"; }
|
log() { echo "ims-pdn-up: $*"; }
|
||||||
|
|
||||||
kv() { mmcli "$@" -K 2>/dev/null; }
|
kv() { mmcli "$@" -K 2>/dev/null; }
|
||||||
|
|
||||||
|
# mmcli's multi-line error text as one journal-friendly line
|
||||||
|
squash() { printf '%s' "$1" | tr '\n' ' ' | sed 's/ */ /g'; }
|
||||||
|
|
||||||
|
modem_state() { kv -m "$MODEM" | sed -n 's/^modem\.generic\.state *: *//p'; }
|
||||||
|
packet_state() { kv -m "$MODEM" | sed -n 's/^modem\.3gpp\.packet-service-state *: *//p'; }
|
||||||
|
|
||||||
bearer_paths() {
|
bearer_paths() {
|
||||||
kv -m "$MODEM" | sed -n 's/^modem\.generic\.bearers\.value\[[0-9]*\] *: *//p'
|
kv -m "$MODEM" | sed -n 's/^modem\.generic\.bearers\.value\[[0-9]*\] *: *//p'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
envval() { # $1 = key — for manual runs; under systemd the vars are inherited
|
||||||
|
sed -n "s/^$1=//p" /etc/imsd.env 2>/dev/null | tail -n1 | tr -d '"'
|
||||||
|
}
|
||||||
|
IP_TYPE=${IMS_IP_TYPE:-$(envval IMS_IP_TYPE)}
|
||||||
|
IP_TYPE=${IP_TYPE:-ipv6}
|
||||||
|
REG_TIMEOUT=${IMS_REG_TIMEOUT:-$(envval IMS_REG_TIMEOUT)}
|
||||||
|
REG_TIMEOUT=${REG_TIMEOUT:-300}
|
||||||
|
|
||||||
# ---- wait for a modem (MM + modem firmware take a while after boot)
|
# ---- wait for a modem (MM + modem firmware take a while after boot)
|
||||||
n=0
|
n=0
|
||||||
while :; do
|
while :; do
|
||||||
|
|
@ -29,11 +49,11 @@ done
|
||||||
log "modem $MODEM"
|
log "modem $MODEM"
|
||||||
|
|
||||||
# ---- find a connected ims bearer; else find-or-create one and connect it
|
# ---- find a connected ims bearer; else find-or-create one and connect it
|
||||||
# (LTE attach can lag boot, so connect attempts retry)
|
|
||||||
find_ims_bearer() { # $1 = required bearer.status.connected value
|
find_ims_bearer() { # $1 = required bearer.status.connected value
|
||||||
for B in $(bearer_paths); do
|
for B in $(bearer_paths); do
|
||||||
INFO=$(kv -b "$B") || continue
|
INFO=$(kv -b "$B") || continue
|
||||||
echo "$INFO" | grep -q '^bearer\.properties\.apn *: *ims$' || continue
|
echo "$INFO" | grep -q '^bearer\.properties\.apn *: *ims$' || continue
|
||||||
|
echo "$INFO" | grep -q "^bearer\.properties\.ip-type *: *$IP_TYPE\$" || continue
|
||||||
echo "$INFO" | grep -q "^bearer\.status\.connected *: *$1\$" || continue
|
echo "$INFO" | grep -q "^bearer\.status\.connected *: *$1\$" || continue
|
||||||
echo "$B"
|
echo "$B"
|
||||||
return 0
|
return 0
|
||||||
|
|
@ -42,20 +62,48 @@ find_ims_bearer() { # $1 = required bearer.status.connected value
|
||||||
}
|
}
|
||||||
|
|
||||||
BEARER=$(find_ims_bearer yes)
|
BEARER=$(find_ims_bearer yes)
|
||||||
|
|
||||||
|
# ---- gate the connect attempts on network registration: the 10x10 s window
|
||||||
|
# below is shorter than some carriers' post-boot attach (measured ~2 min), so
|
||||||
|
# without this every attempt can fail on no-service and the retries end
|
||||||
|
# before the network is even attached
|
||||||
|
if [ -z "$BEARER" ]; then
|
||||||
|
waited=0 last=
|
||||||
|
while :; do
|
||||||
|
STATE=$(modem_state)
|
||||||
|
case "$STATE" in registered|connecting|connected) break ;; esac
|
||||||
|
[ "$STATE" != "$last" ] && log "waiting for registration (state: ${STATE:-unknown})"
|
||||||
|
last=$STATE
|
||||||
|
if [ "$waited" -ge "$REG_TIMEOUT" ]; then
|
||||||
|
log "not registered after $REG_TIMEOUT s — attempting anyway"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 5
|
||||||
|
waited=$((waited + 5))
|
||||||
|
done
|
||||||
|
log "modem state: $(modem_state), packet service: $(packet_state)"
|
||||||
|
fi
|
||||||
|
|
||||||
n=0
|
n=0
|
||||||
while [ -z "$BEARER" ]; do
|
while [ -z "$BEARER" ]; do
|
||||||
n=$((n + 1))
|
n=$((n + 1))
|
||||||
[ "$n" -gt 10 ] && { log "bearer connect failed after 10 attempts"; exit 1; }
|
[ "$n" -gt 10 ] && { log "bearer connect failed after 10 attempts"; exit 1; }
|
||||||
B=$(find_ims_bearer no) # reuse a stale disconnected ims bearer
|
B=$(find_ims_bearer no) # reuse a stale disconnected ims bearer
|
||||||
if [ -z "$B" ]; then
|
if [ -z "$B" ]; then
|
||||||
B=$(mmcli -m "$MODEM" --create-bearer='apn=ims,ip-type=ipv6' 2>/dev/null |
|
OUT=$(mmcli -m "$MODEM" --create-bearer="apn=ims,ip-type=$IP_TYPE" 2>&1)
|
||||||
sed -n 's,.*\(/org/freedesktop/ModemManager1/Bearer/[0-9]*\).*,\1,p')
|
B=$(printf '%s' "$OUT" | sed -n 's,.*\(/org/freedesktop/ModemManager1/Bearer/[0-9]*\).*,\1,p')
|
||||||
[ -n "$B" ] && log "created bearer $B"
|
if [ -n "$B" ]; then
|
||||||
|
log "created bearer $B (ip-type=$IP_TYPE)"
|
||||||
|
else
|
||||||
|
log "create-bearer attempt $n failed: $(squash "$OUT"); retrying in 10 s"
|
||||||
|
sleep 10
|
||||||
|
continue
|
||||||
fi
|
fi
|
||||||
if [ -n "$B" ] && mmcli -b "$B" --connect >/dev/null 2>&1; then
|
fi
|
||||||
|
if OUT=$(mmcli -b "$B" --connect 2>&1); then
|
||||||
BEARER=$B
|
BEARER=$B
|
||||||
else
|
else
|
||||||
log "connect attempt $n failed; retrying in 10 s"
|
log "connect attempt $n failed (state: $(modem_state)): $(squash "$OUT"); retrying in 10 s"
|
||||||
sleep 10
|
sleep 10
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,11 @@ Wants=ModemManager.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
# bring up the ims PDN first; waits for modem + LTE attach, idempotent.
|
# bring up the ims PDN first; waits for modem + network registration,
|
||||||
# worst case ~4 min (modem wait + connect retries), hence TimeoutStartSec
|
# idempotent. worst case ~14 min (2 min modem wait + 5 min registration
|
||||||
|
# gate + connect retries), hence TimeoutStartSec
|
||||||
ExecStartPre=/usr/libexec/ims-pdn-up.sh
|
ExecStartPre=/usr/libexec/ims-pdn-up.sh
|
||||||
TimeoutStartSec=600
|
TimeoutStartSec=900
|
||||||
# /run/imsd.env: written by ims-pdn-up.sh (DEV= the connected ims netdev).
|
# /run/imsd.env: written by ims-pdn-up.sh (DEV= the connected ims netdev).
|
||||||
# /etc/imsd.env: admin configuration — PCSCF= is REQUIRED (the carrier's
|
# /etc/imsd.env: admin configuration — PCSCF= is REQUIRED (the carrier's
|
||||||
# P-CSCF address; see the README's Configuration section) and wins over the
|
# P-CSCF address; see the README's Configuration section) and wins over the
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue