packaging: restart imsd and imsd-ofonod on upgrade
An upgrade left the old imsd and imsd-ofonod running until they were next restarted, while a changed bus policy is live the moment apk writes it: dbus-broker-launch reloads on its own. A policy that relies on a check inside the daemons would face the old daemons in between. Mark imsd.service and imsd-ofonod.service needs-restart in post-upgrade scripts; the systemd trigger restarts them once the transaction is done (enqueue-marked). A call in progress ends. The same scripts run as post-install: `apk del` removes a package's files and leaves its daemon running, so a later `apk add` of the package is this upgrade for that daemon — and apk runs the post-install script, not post-upgrade, for a package it adds. GNOME Calls 50.0 dials nothing after imsd-ofonod restarts under it until it restarts too, so imsd-ofonod's ExecStartPost restarts the Calls daemon of each user running one — not at the unit's first start since boot, when a Calls that runs already is left alone. A daemon older than 0.3.10 kept its bring-up throttle wait in memory only, so a restart lost it. imsd's post-upgrade records that daemon's invocation, and imsd-handover.sh, an ExecStartPre after ims-pdn-up.sh, reads the invocation's journal and, while the fresh-SA throttle's wait it finds there still runs (`spi-s=0`; the old daemon's other deferrals are not handed over), writes what is left of it to the holds file when this start's LOCAL is the address the wait was earned on, or when that address cannot be read; when LOCAL is another address, only the verdict that the old registration is gone. Tested on the dev phone (KPN) in builds of this series; not every path has run there.
This commit is contained in:
parent
0a91050635
commit
2fe991a14e
17 changed files with 275 additions and 27 deletions
7
Makefile
7
Makefile
|
|
@ -11,7 +11,8 @@
|
||||||
# make check -> build + run the 7 unit-test suites
|
# make check -> build + run the 7 unit-test suites
|
||||||
# make install -> DESTDIR/PREFIX staged install (binaries + systemd
|
# make install -> DESTDIR/PREFIX staged install (binaries + systemd
|
||||||
# units, D-Bus policies, dialerd autostart, the GNOME
|
# units, D-Bus policies, dialerd autostart, the GNOME
|
||||||
# Calls plugin override, ims-pdn-up.sh)
|
# Calls plugin override, the imsd-ofono preset, and the libexec scripts
|
||||||
|
# ims-pdn-up.sh, imsd-handover.sh, imsd-ofono-restart-calls.sh)
|
||||||
#
|
#
|
||||||
# Requires clang++ with libc++ and the libc++ std module sources
|
# Requires clang++ with libc++ and the libc++ std module sources
|
||||||
# (std.cppm — Alpine: llvm-runtimes, Arch: libc++). Override STD_CPPM if
|
# (std.cppm — Alpine: llvm-runtimes, Arch: libc++). Override STD_CPPM if
|
||||||
|
|
@ -133,6 +134,8 @@ install: all
|
||||||
install -Dm755 $(O)/imsd-media $(DESTDIR)$(PREFIX)/libexec/imsd-media
|
install -Dm755 $(O)/imsd-media $(DESTDIR)$(PREFIX)/libexec/imsd-media
|
||||||
install -Dm755 packaging/ims-pdn-up.sh \
|
install -Dm755 packaging/ims-pdn-up.sh \
|
||||||
$(DESTDIR)$(PREFIX)/libexec/ims-pdn-up.sh
|
$(DESTDIR)$(PREFIX)/libexec/ims-pdn-up.sh
|
||||||
|
install -Dm755 packaging/imsd-handover.sh \
|
||||||
|
$(DESTDIR)$(PREFIX)/libexec/imsd-handover.sh
|
||||||
install -Dm644 packaging/imsd.service \
|
install -Dm644 packaging/imsd.service \
|
||||||
$(DESTDIR)$(PREFIX)/lib/systemd/system/imsd.service
|
$(DESTDIR)$(PREFIX)/lib/systemd/system/imsd.service
|
||||||
install -Dm644 packaging/imsd-dialerd.desktop \
|
install -Dm644 packaging/imsd-dialerd.desktop \
|
||||||
|
|
@ -141,6 +144,8 @@ install: all
|
||||||
$(DESTDIR)$(PREFIX)/share/dbus-1/system.d/net.catcrafts.IMS1.conf
|
$(DESTDIR)$(PREFIX)/share/dbus-1/system.d/net.catcrafts.IMS1.conf
|
||||||
install -Dm644 packaging/imsd-ofonod.service \
|
install -Dm644 packaging/imsd-ofonod.service \
|
||||||
$(DESTDIR)$(PREFIX)/lib/systemd/system/imsd-ofonod.service
|
$(DESTDIR)$(PREFIX)/lib/systemd/system/imsd-ofonod.service
|
||||||
|
install -Dm755 packaging/imsd-ofono-restart-calls.sh \
|
||||||
|
$(DESTDIR)$(PREFIX)/libexec/imsd-ofono-restart-calls.sh
|
||||||
install -Dm644 packaging/org.ofono.conf \
|
install -Dm644 packaging/org.ofono.conf \
|
||||||
$(DESTDIR)$(PREFIX)/share/dbus-1/system.d/imsd-ofono.conf
|
$(DESTDIR)$(PREFIX)/share/dbus-1/system.d/imsd-ofono.conf
|
||||||
install -Dm644 packaging/90_imsd-ofono.gschema.override \
|
install -Dm644 packaging/90_imsd-ofono.gschema.override \
|
||||||
|
|
|
||||||
29
README.md
29
README.md
|
|
@ -149,7 +149,11 @@ On a phone the daemon runs as root on the system bus (policy in
|
||||||
account `user` access; adjust it if your session account is named
|
account `user` access; adjust it if your session account is named
|
||||||
differently), started by `packaging/imsd.service`
|
differently), started by `packaging/imsd.service`
|
||||||
after `packaging/ims-pdn-up.sh` has brought up the ims PDN through
|
after `packaging/ims-pdn-up.sh` has brought up the ims PDN through
|
||||||
ModemManager. The bring-up never lets the ims PDN become the phone's IPv6
|
ModemManager. An apk upgrade restarts `imsd` and `imsd-ofonod` once the
|
||||||
|
transaction is done (a call in progress ends), as does adding a package
|
||||||
|
again over the daemon an `apk del` left running; the restart resumes the
|
||||||
|
registration over its existing SAs, or goes on waiting out a hold the
|
||||||
|
network had set. The bring-up never lets the ims PDN become the phone's IPv6
|
||||||
default route: the PDN's router advertisement is not accepted as a default
|
default route: the PDN's router advertisement is not accepted as a default
|
||||||
router (`accept_ra_defrtr=0` on the netdev; its prefix and MTU still apply)
|
router (`accept_ra_defrtr=0` on the netdev; its prefix and MTU still apply)
|
||||||
and the P-CSCF gets a host route instead, so on IPv4-only wifi the phone's
|
and the P-CSCF gets a host route instead, so on IPv4-only wifi the phone's
|
||||||
|
|
@ -227,7 +231,7 @@ Known gaps, all inherited rather than introduced:
|
||||||
normal dialpad is unaffected — that reaches imsd's own classifier.
|
normal dialpad is unaffected — that reaches imsd's own classifier.
|
||||||
- DTMF is forwarded to imsd, where `SendDtmf` is still a stub (RFC 4733 is not
|
- DTMF is forwarded to imsd, where `SendDtmf` is still a stub (RFC 4733 is not
|
||||||
implemented yet) — the same limitation Plasma has.
|
implemented yet) — the same limitation Plasma has.
|
||||||
- **Restart GNOME Calls after restarting `imsd-ofonod` itself.** When the
|
- **Restarting `imsd-ofonod` restarts GNOME Calls with it.** When the
|
||||||
modem's `VoiceCallManager` interface goes away and comes back, Calls 50.0
|
modem's `VoiceCallManager` interface goes away and comes back, Calls 50.0
|
||||||
adds a new origin but leaves the old origin's `CallAdded` handler connected:
|
adds a new origin but leaves the old origin's `CallAdded` handler connected:
|
||||||
after one such cycle every call is added twice, after a few the stale handler
|
after one such cycle every call is added twice, after a few the stale handler
|
||||||
|
|
@ -237,7 +241,10 @@ Known gaps, all inherited rather than introduced:
|
||||||
restarts and registration drops from triggering it, `imsd-ofonod` lists the
|
restarts and registration drops from triggering it, `imsd-ofonod` lists the
|
||||||
interface permanently: `Online` tracks registration, and `Dial` fails with
|
interface permanently: `Online` tracks registration, and `Dial` fails with
|
||||||
"imsd is not registered" / "imsd is not running" instead. Only `org.ofono`
|
"imsd is not registered" / "imsd is not running" instead. Only `org.ofono`
|
||||||
vanishing — this daemon restarting — still cycles the origin.
|
vanishing — this daemon restarting — still cycles the origin, and after
|
||||||
|
that Calls dials nothing until it restarts, so the unit's `ExecStartPost`
|
||||||
|
restarts the Calls daemon of every user running one. Its first start
|
||||||
|
since boot leaves a running Calls alone.
|
||||||
|
|
||||||
Development, without a phone or root:
|
Development, without a phone or root:
|
||||||
|
|
||||||
|
|
@ -311,12 +318,24 @@ refused or throttled fresh attempt); imsd-ofonod refuses a `Dial` then, and
|
||||||
imsd-dialerd lists no modem. A bring-up the network deferred waits the same
|
imsd-dialerd lists no modem. A bring-up the network deferred waits the same
|
||||||
schedule out with nothing to reconnect over (the resume just found the
|
schedule out with nothing to reconnect over (the resume just found the
|
||||||
stored SAs unusable), then tries again. The network's holds outlive a
|
stored SAs unusable), then tries again. The network's holds outlive a
|
||||||
restart of imsd: they are kept in
|
restart of imsd (an upgrade restarts it): they are kept in
|
||||||
`/run/imsd.holds` for the PDN they were set on, with the schedule's place
|
`/run/imsd.holds` for the PDN they were set on, with the schedule's place
|
||||||
(a restart does not start it over at 21 min), and waited out at the next
|
(a restart does not start it over at 21 min), and waited out at the next
|
||||||
start, before the resume (a refresh's `Retry-After`) or before the fresh
|
start, before the resume (a refresh's `Retry-After`) or before the fresh
|
||||||
registration (a fresh attempt's schedule or `Retry-After`, a terminated
|
registration (a fresh attempt's schedule or `Retry-After`, a terminated
|
||||||
binding's wait); a reboot or another PDN address starts clean.
|
binding's wait); a reboot or another PDN address starts clean. An upgrade from a daemon
|
||||||
|
older than 0.3.10, which kept its bring-up throttle wait in memory only,
|
||||||
|
hands that wait over the same way when this start registers on the address
|
||||||
|
it was earned on (the fresh-SA throttle's wait, `spi-s=0`; the old daemon's
|
||||||
|
other deferrals, after a 401 it could not use, are not handed over): the
|
||||||
|
upgrade records that daemon's invocation, and the next
|
||||||
|
start reads its journal lines once the daemon has stopped — a wait that
|
||||||
|
ended meanwhile is read as it ended; on another address (the normal hop:
|
||||||
|
0.3.9 sat on the kernel's temporary address) only the verdict that the old
|
||||||
|
registration is gone is kept and the start registers fresh at once. When
|
||||||
|
the old address cannot be read the wait is handed over anyway; when the wait
|
||||||
|
cannot be timed, or this start has no `LOCAL`, the journal says it was not
|
||||||
|
handed over.
|
||||||
The journal says which (`…; next: fresh registration in N s`, `registering
|
The journal says which (`…; next: fresh registration in N s`, `registering
|
||||||
fresh (recovery attempt N)`, `fresh registration deferred/refused/failed:
|
fresh (recovery attempt N)`, `fresh registration deferred/refused/failed:
|
||||||
…`, `bring-up deferred: …`). One case leaves through a real restart
|
…`, `bring-up deferred: …`). One case leaves through a real restart
|
||||||
|
|
|
||||||
|
|
@ -735,14 +735,14 @@ std::int64_t NowEpoch() {
|
||||||
|
|
||||||
// The recovery policy's holds, kept for the next process: the network's
|
// The recovery policy's holds, kept for the next process: the network's
|
||||||
// word (a Retry-After, the schedule after a refused or throttled fresh
|
// word (a Retry-After, the schedule after a refused or throttled fresh
|
||||||
// attempt, a terminated binding's wait) outlives a restart of imsd. They
|
// attempt, a terminated binding's wait) outlives a restart of imsd, and
|
||||||
// are filed under the PDN's address — another one, or a reboot, starts
|
// an upgrade restarts it. They are filed under the PDN's address —
|
||||||
// clean — so the file names that address and the boot, and
|
// another one, or a reboot, starts clean — so the file names that
|
||||||
// lives in /run. Seconds left at `written_at`, which is on the boot
|
// address and the boot, and lives in /run. Seconds left at
|
||||||
// clock: no clock step moves it, and it runs through a suspend as the
|
// `written_at`, which is on the boot clock: no clock step moves it, and
|
||||||
// network's timers do. Whether the old registration is gone travels with
|
// it runs through a suspend as the network's timers do. Whether the old
|
||||||
// them: a restart inside a hold must not resume over SAs the network has
|
// registration is gone travels with them: a restart inside a hold must
|
||||||
// written off.
|
// not resume over SAs the network has written off.
|
||||||
std::int64_t BootSeconds() {
|
std::int64_t BootSeconds() {
|
||||||
timespec ts{};
|
timespec ts{};
|
||||||
clock_gettime(CLOCK_BOOTTIME, &ts);
|
clock_gettime(CLOCK_BOOTTIME, &ts);
|
||||||
|
|
@ -1232,13 +1232,14 @@ private:
|
||||||
auto sa = imsd::ipsec::ParseExistingSa(state.out, policy.out, pcscf_, local_);
|
auto sa = imsd::ipsec::ParseExistingSa(state.out, policy.out, pcscf_, local_);
|
||||||
// The registration may sit on another address of the link: before
|
// The registration may sit on another address of the link: before
|
||||||
// 0.3.10 imsd registered on the kernel's temporary one (ims ledger
|
// 0.3.10 imsd registered on the kernel's temporary one (ims ledger
|
||||||
// F18). Resume there while it is still up — a restart keeps the
|
// F18). Resume there while it is still up — a restart, an upgrade
|
||||||
// registration for the price of a re-REGISTER, where a fresh one is
|
// among them, keeps the registration for the price of a re-REGISTER,
|
||||||
// an AKA run, a new SA pair and, when KPN refuses it, the old
|
// where a fresh one is an AKA run, a new SA pair and, when KPN
|
||||||
// registration and a 21-minute wait (KPN refuses a fresh pair on an
|
// refuses it, the old registration and a 21-minute wait (KPN
|
||||||
// address whose registration is live and has accepted one on another
|
// refuses a fresh pair on an address whose registration is live and
|
||||||
// address of the link meanwhile; other carriers' rules are unknown) —
|
// has accepted one on another address of the link meanwhile; other
|
||||||
// and move at the next fresh registration.
|
// carriers' rules are unknown) — and move at the next fresh
|
||||||
|
// registration.
|
||||||
if (!sa) {
|
if (!sa) {
|
||||||
for (const std::string& other : imsd::ipsec::SaLocals(state.out, pcscf_)) {
|
for (const std::string& other : imsd::ipsec::SaLocals(state.out, pcscf_)) {
|
||||||
if (other == local_ || !OnLink(dev_, other)) continue;
|
if (other == local_ || !OnLink(dev_, other)) continue;
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ provides="81voltd=$pkgver-r$pkgrel"
|
||||||
# Calls at the ofono provider. A Plasma Mobile phone wants imsd-dialerd, which
|
# Calls at the ofono provider. A Plasma Mobile phone wants imsd-dialerd, which
|
||||||
# is in the main package, and must NOT get that override.
|
# is in the main package, and must NOT get that override.
|
||||||
subpackages="$pkgname-ofono:ofono"
|
subpackages="$pkgname-ofono:ofono"
|
||||||
|
# restart the running daemons onto an upgrade (systemd's enqueue-marked)
|
||||||
|
install="$pkgname.post-upgrade $pkgname.post-install $pkgname-ofono.post-upgrade $pkgname-ofono.post-install"
|
||||||
source="$pkgname-$pkgver.tar.gz::$url/archive/v$pkgver.tar.gz"
|
source="$pkgname-$pkgver.tar.gz::$url/archive/v$pkgver.tar.gz"
|
||||||
builddir="$srcdir/$pkgname"
|
builddir="$srcdir/$pkgname"
|
||||||
|
|
||||||
|
|
@ -43,6 +45,7 @@ ofono() {
|
||||||
pkgdesc="GNOME Calls (Phosh, GNOME Mobile) backend for imsd"
|
pkgdesc="GNOME Calls (Phosh, GNOME Mobile) backend for imsd"
|
||||||
depends="$pkgname=$pkgver-r$pkgrel calls"
|
depends="$pkgname=$pkgver-r$pkgrel calls"
|
||||||
amove usr/bin/imsd-ofonod
|
amove usr/bin/imsd-ofonod
|
||||||
|
amove usr/libexec/imsd-ofono-restart-calls.sh
|
||||||
amove usr/lib/systemd/system/imsd-ofonod.service
|
amove usr/lib/systemd/system/imsd-ofonod.service
|
||||||
amove usr/share/dbus-1/system.d/imsd-ofono.conf
|
amove usr/share/dbus-1/system.d/imsd-ofono.conf
|
||||||
amove usr/share/glib-2.0/schemas/90_imsd-ofono.gschema.override
|
amove usr/share/glib-2.0/schemas/90_imsd-ofono.gschema.override
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,9 @@
|
||||||
# README's "Cross-compiling" section into a proper apk — used by this repo's
|
# README's "Cross-compiling" section into a proper apk — used by this repo's
|
||||||
# package CI (packaging/build-package.sh, which also seds pkgver from
|
# package CI (packaging/build-package.sh, which also seds pkgver from
|
||||||
# implementations/main.cpp) and runnable by hand. The source tarball is
|
# implementations/main.cpp) and runnable by hand. The source tarball is
|
||||||
# produced by packaging/make-bin-tarball.sh; the desktop/preset files live
|
# produced by packaging/make-bin-tarball.sh; the desktop/preset files and
|
||||||
# next to it in packaging/ (copy them beside this file). Since 2026-09-02
|
# the install scripts (*.post-upgrade, *.post-install) live next to it in
|
||||||
|
# packaging/ (copy them beside this file). Since 2026-09-02
|
||||||
# this is the only imsd packaging: fp6-img images install the apk this
|
# this is the only imsd packaging: fp6-img images install the apk this
|
||||||
# builds from the registry rather than building their own.
|
# builds from the registry rather than building their own.
|
||||||
pkgname=imsd
|
pkgname=imsd
|
||||||
|
|
@ -29,6 +30,9 @@ provides="81voltd=$pkgver-r$pkgrel"
|
||||||
# (ofono before systemd: systemd() amoves all of usr/lib/systemd/system,
|
# (ofono before systemd: systemd() amoves all of usr/lib/systemd/system,
|
||||||
# so ofono() must take imsd-ofonod.service first)
|
# so ofono() must take imsd-ofonod.service first)
|
||||||
subpackages="$pkgname-ofono:ofono $pkgname-systemd"
|
subpackages="$pkgname-ofono:ofono $pkgname-systemd"
|
||||||
|
# restart the running daemons onto an upgrade (systemd's enqueue-marked); the
|
||||||
|
# systemd subpackage marks imsd.service too, since it brings the unit file
|
||||||
|
install="$pkgname.post-upgrade $pkgname.post-install $pkgname-ofono.post-upgrade $pkgname-ofono.post-install $pkgname-systemd.post-upgrade $pkgname-systemd.post-install"
|
||||||
options="!check !tracedeps"
|
options="!check !tracedeps"
|
||||||
source="
|
source="
|
||||||
imsd-$pkgver.tar.gz
|
imsd-$pkgver.tar.gz
|
||||||
|
|
@ -42,6 +46,7 @@ package() {
|
||||||
install -Dm755 imsd-media "$pkgdir"/usr/libexec/imsd-media
|
install -Dm755 imsd-media "$pkgdir"/usr/libexec/imsd-media
|
||||||
install -Dm755 imsd-dialerd "$pkgdir"/usr/bin/imsd-dialerd
|
install -Dm755 imsd-dialerd "$pkgdir"/usr/bin/imsd-dialerd
|
||||||
install -Dm755 ims-pdn-up.sh "$pkgdir"/usr/libexec/ims-pdn-up.sh
|
install -Dm755 ims-pdn-up.sh "$pkgdir"/usr/libexec/ims-pdn-up.sh
|
||||||
|
install -Dm755 imsd-handover.sh "$pkgdir"/usr/libexec/imsd-handover.sh
|
||||||
install -Dm644 imsd.service "$pkgdir"/usr/lib/systemd/system/imsd.service
|
install -Dm644 imsd.service "$pkgdir"/usr/lib/systemd/system/imsd.service
|
||||||
install -Dm644 imsd-dialerd.desktop \
|
install -Dm644 imsd-dialerd.desktop \
|
||||||
"$pkgdir"/etc/xdg/autostart/imsd-dialerd.desktop
|
"$pkgdir"/etc/xdg/autostart/imsd-dialerd.desktop
|
||||||
|
|
@ -64,6 +69,8 @@ package() {
|
||||||
printf '[Unit]\nConditionPathExists=/etc/imsd.env\n' \
|
printf '[Unit]\nConditionPathExists=/etc/imsd.env\n' \
|
||||||
> "$pkgdir"/usr/lib/systemd/system/imsd.service.d/10-require-config.conf
|
> "$pkgdir"/usr/lib/systemd/system/imsd.service.d/10-require-config.conf
|
||||||
install -Dm755 imsd-ofonod "$pkgdir"/usr/bin/imsd-ofonod
|
install -Dm755 imsd-ofonod "$pkgdir"/usr/bin/imsd-ofonod
|
||||||
|
install -Dm755 imsd-ofono-restart-calls.sh \
|
||||||
|
"$pkgdir"/usr/libexec/imsd-ofono-restart-calls.sh
|
||||||
install -Dm644 imsd-ofonod.service \
|
install -Dm644 imsd-ofonod.service \
|
||||||
"$pkgdir"/usr/lib/systemd/system/imsd-ofonod.service
|
"$pkgdir"/usr/lib/systemd/system/imsd-ofonod.service
|
||||||
install -Dm644 org.ofono.conf \
|
install -Dm644 org.ofono.conf \
|
||||||
|
|
@ -78,6 +85,7 @@ ofono() {
|
||||||
pkgdesc="GNOME Calls (Phosh, GNOME Mobile) backend for imsd"
|
pkgdesc="GNOME Calls (Phosh, GNOME Mobile) backend for imsd"
|
||||||
depends="$pkgname=$pkgver-r$pkgrel calls"
|
depends="$pkgname=$pkgver-r$pkgrel calls"
|
||||||
amove usr/bin/imsd-ofonod
|
amove usr/bin/imsd-ofonod
|
||||||
|
amove usr/libexec/imsd-ofono-restart-calls.sh
|
||||||
amove usr/lib/systemd/system/imsd-ofonod.service
|
amove usr/lib/systemd/system/imsd-ofonod.service
|
||||||
amove usr/share/dbus-1/system.d/imsd-ofono.conf
|
amove usr/share/dbus-1/system.d/imsd-ofono.conf
|
||||||
amove usr/share/glib-2.0/schemas/90_imsd-ofono.gschema.override
|
amove usr/share/glib-2.0/schemas/90_imsd-ofono.gschema.override
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ rm -rf "$PKG"
|
||||||
mkdir -p "$PKG"
|
mkdir -p "$PKG"
|
||||||
cp "$SRC/packaging/APKBUILD.binary" "$PKG/APKBUILD"
|
cp "$SRC/packaging/APKBUILD.binary" "$PKG/APKBUILD"
|
||||||
cp "$SRC/packaging/org.kde.modem.daemon.desktop" \
|
cp "$SRC/packaging/org.kde.modem.daemon.desktop" \
|
||||||
"$SRC/packaging/80-imsd.preset" "$PKG/"
|
"$SRC/packaging/80-imsd.preset" "$SRC"/packaging/*.post-upgrade "$SRC"/packaging/*.post-install "$PKG/"
|
||||||
mv "imsd-$VER.tar.gz" "$PKG/"
|
mv "imsd-$VER.tar.gz" "$PKG/"
|
||||||
sed -i "s/^pkgver=.*/pkgver=$VER/" "$PKG/APKBUILD"
|
sed -i "s/^pkgver=.*/pkgver=$VER/" "$PKG/APKBUILD"
|
||||||
# a throwaway signing key: phones trust the registry-signed APKINDEX, not
|
# a throwaway signing key: phones trust the registry-signed APKINDEX, not
|
||||||
|
|
|
||||||
83
packaging/imsd-handover.sh
Executable file
83
packaging/imsd-handover.sh
Executable file
|
|
@ -0,0 +1,83 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
# SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||||
|
#
|
||||||
|
# ExecStartPre of imsd.service, after ims-pdn-up.sh: hand a daemon older
|
||||||
|
# than 0.3.10 its bring-up throttle wait over. Such a daemon kept the wait
|
||||||
|
# in memory only, so the restart an upgrade makes registered fresh at once
|
||||||
|
# instead of waiting the schedule out. imsd.post-upgrade
|
||||||
|
# records the running daemon's invocation in /run/imsd.handover; here, with
|
||||||
|
# that daemon stopped and its journal final, the invocation's last bring-up
|
||||||
|
# line is read and, when it is the old throttle's `bring-up deferred:
|
||||||
|
# fresh-SA throttle active (spi-s=0) — … next fresh registration in
|
||||||
|
# N min`, what is left of the N min is written to
|
||||||
|
# the holds file (/run/imsd.holds, or HOLDS_FILE) the way 0.3.10 keeps
|
||||||
|
# it — with the verdict that the old registration is gone: that line
|
||||||
|
# follows a throttled initial REGISTER, at which KPN drops the
|
||||||
|
# registration, so the new start does not resume over the written-off SAs.
|
||||||
|
# The old daemon deferred the same way after a 401 it could not use (no
|
||||||
|
# ipsec-3gpp mechanism, an unsupported algorithm); nothing says the
|
||||||
|
# network dropped the registration then, and that wait is not handed over.
|
||||||
|
# The wait itself is handed over only for the address it was earned on
|
||||||
|
# (the old daemon logged its LOCAL once at start): KPN's refusal is the
|
||||||
|
# address's — a fresh pair is refused where a registration is live or has
|
||||||
|
# just gone — and on the normal hop the old daemon sat on the kernel's
|
||||||
|
# temporary address while this start registers on the PDN's own, which
|
||||||
|
# holds none: then only the verdict is written and the new start registers
|
||||||
|
# fresh at once (should the network refuse it there, the daemon's own
|
||||||
|
# schedule takes over from its start: the old daemon's place in it was
|
||||||
|
# the other address's).
|
||||||
|
# When the old address cannot be read the wait is handed over anyway.
|
||||||
|
# Reading at the start, not at the upgrade, means a wait that
|
||||||
|
# ended between the two — the old daemon making another attempt — is
|
||||||
|
# read as it ended.
|
||||||
|
set -u
|
||||||
|
marker=/run/imsd.handover
|
||||||
|
[ -r "$marker" ] || exit 0
|
||||||
|
inv=$(cat "$marker"); rm -f "$marker"
|
||||||
|
[ -n "$inv" ] || exit 0
|
||||||
|
holds=${HOLDS_FILE:-/run/imsd.holds} # the holds file as imsd reads it (HoldsPath)
|
||||||
|
[ -e "$holds" ] && exit 0 # a 0.3.10 or later daemon kept its own holds
|
||||||
|
line=$(journalctl -u imsd.service "_SYSTEMD_INVOCATION_ID=$inv" -o json --no-pager 2>/dev/null \
|
||||||
|
| grep -E '"MESSAGE" *: *"imsd: (bring-up deferred: |REGISTERED|FRESH registration|a hold the network)' | tail -n 1)
|
||||||
|
case "$line" in *'imsd: bring-up deferred: fresh-SA throttle active (spi-s=0)'*) ;; *) exit 0 ;; esac
|
||||||
|
mins=$(printf '%s\n' "$line" | sed -n 's/.*next fresh registration in \([0-9][0-9]*\) min.*/\1/p')
|
||||||
|
# the line's age on the monotonic clock (journald stamps every entry with it;
|
||||||
|
# /proc/timer_list reads it now), which no clock step moves — this phone's
|
||||||
|
# wall clock reads 1970 until its first NTP sync. It stops in a suspend, so
|
||||||
|
# the age can only come out short and the wait handed over long, never
|
||||||
|
# short of the network's window.
|
||||||
|
at=$(printf '%s\n' "$line" | sed -n 's/.*"__MONOTONIC_TIMESTAMP" *: *"\([0-9][0-9]*\)".*/\1/p')
|
||||||
|
now=$(sed -n 's/^now at \([0-9][0-9]*\) nsecs$/\1/p' /proc/timer_list 2>/dev/null | head -n 1)
|
||||||
|
if [ -z "$mins" ] || [ -z "$at" ] || [ -z "$now" ]; then
|
||||||
|
echo "imsd-handover: the previous daemon waited for a fresh registration, but its wait could not be timed: NOT handed over"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
left=$((mins * 60 - (now / 1000000000 - at / 1000000)))
|
||||||
|
[ "$left" -gt 0 ] || exit 0
|
||||||
|
# the address this start registers on, as systemd hands it to imsd: the unit
|
||||||
|
# reads its EnvironmentFile= lines anew for every command it spawns, so LOCAL
|
||||||
|
# here is the /etc/imsd.env pin when there is one, else the one ims-pdn-up.sh
|
||||||
|
# just wrote to /run/imsd.env — unquoted by systemd's parser, the same bytes
|
||||||
|
# imsd compares the holds file's pdn with. The file read is for a run outside
|
||||||
|
# the unit.
|
||||||
|
pdn=${LOCAL:-$(sed -n 's/^LOCAL=//p' /run/imsd.env 2>/dev/null | tail -n 1)}
|
||||||
|
if [ -z "$pdn" ]; then
|
||||||
|
echo "imsd-handover: the previous daemon waited $mins min for a fresh registration, but this start has no LOCAL: NOT handed over"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
# the address the wait was earned on: the old daemon's own start line
|
||||||
|
old=$(journalctl -u imsd.service "_SYSTEMD_INVOCATION_ID=$inv" -o cat --no-pager 2>/dev/null \
|
||||||
|
| sed -n 's/^imsd: LOCAL=\([^ ]*\) P-CSCF=.*/\1/p' | tail -n 1)
|
||||||
|
boot=$(cat /proc/sys/kernel/random/boot_id); now_s=$(cut -d. -f1 /proc/uptime)
|
||||||
|
if [ -n "$old" ] && [ "$old" != "$pdn" ]; then
|
||||||
|
printf '{"pdn": "%s", "boot": "%s", "written_at": %s, "hold": 0, "fresh_hold": 0, "old_gone": true}\n' \
|
||||||
|
"$pdn" "$boot" "$now_s" > "$holds" \
|
||||||
|
&& echo "imsd-handover: the previous daemon's fresh-registration wait ($left s of $mins min left) was earned on $old, not this start's $pdn: only the verdict that its registration is gone is handed over"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
tm=$((mins * 3 / 2)); [ "$tm" -gt 60 ] && tm=60
|
||||||
|
printf '{"pdn": "%s", "boot": "%s", "written_at": %s, "hold": 0, "fresh_hold": %s, "throttle_minutes": %s, "old_gone": true}\n' \
|
||||||
|
"$pdn" "$boot" "$now_s" "$left" "$tm" > "$holds" \
|
||||||
|
&& echo "imsd-handover: the previous daemon's fresh-registration wait ($left s of $mins min left) handed over for $pdn$([ -n "$old" ] && echo " (its own address)" || echo "; its address could not be read")"
|
||||||
|
exit 0
|
||||||
24
packaging/imsd-ofono-restart-calls.sh
Executable file
24
packaging/imsd-ofono-restart-calls.sh
Executable file
|
|
@ -0,0 +1,24 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
# SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||||
|
#
|
||||||
|
# ExecStartPost of imsd-ofonod.service. GNOME Calls 50.0 keeps a stale modem
|
||||||
|
# when org.ofono vanishes and dials nothing until it is restarted (README,
|
||||||
|
# Known gaps), so every start of imsd-ofonod after org.ofono went away
|
||||||
|
# restarts the Calls daemon of each user running one. Its first start since
|
||||||
|
# boot is not one: no Calls has seen org.ofono go, and a Calls that runs
|
||||||
|
# already (a login during the bring-up) is left alone. The unit's own
|
||||||
|
# timestamp tells the first start from a restart (an upgrade from 0.3.9 is
|
||||||
|
# a restart); the marker covers a stop and a later start.
|
||||||
|
marker=/run/imsd-ofonod.started
|
||||||
|
if [ ! -e "$marker" ] && [ "$(systemctl show -P ActiveExitTimestampMonotonic imsd-ofonod.service)" = 0 ]; then
|
||||||
|
: > "$marker"
|
||||||
|
echo "imsd-ofono-restart-calls: first start since boot, Calls left alone"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
: > "$marker"
|
||||||
|
for uid in $(systemctl list-units 'user@*.service' --legend=no --plain | sed -n 's/^user@\([0-9]*\)\.service.*/\1/p'); do
|
||||||
|
echo "imsd-ofono-restart-calls: restarting the Calls daemon of uid $uid, if it runs"
|
||||||
|
systemctl --user -M "$uid@" try-restart --no-block calls-daemon.service || :
|
||||||
|
done
|
||||||
|
exit 0
|
||||||
1
packaging/imsd-ofono.post-install
Symbolic link
1
packaging/imsd-ofono.post-install
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
imsd-ofono.post-upgrade
|
||||||
21
packaging/imsd-ofono.post-upgrade
Executable file
21
packaging/imsd-ofono.post-upgrade
Executable file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
# SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||||
|
#
|
||||||
|
# As imsd.post-upgrade, for imsd-ofonod: restarted once apk is done, and
|
||||||
|
# its ExecStartPost restarts GNOME Calls with it. Also installed as
|
||||||
|
# imsd-ofono.post-install, for a package added again over the daemon an
|
||||||
|
# `apk del` left running.
|
||||||
|
|
||||||
|
# A unit whose file an apk del removed is not-found to systemd until a
|
||||||
|
# reload and refuses properties ("failed to load properly"); its file is
|
||||||
|
# back on disk by the time post-install runs, so reload and mark again.
|
||||||
|
mark() {
|
||||||
|
systemctl set-property "$1" Markers=+needs-restart 2>/dev/null && return 0
|
||||||
|
systemctl daemon-reload && systemctl set-property "$1" Markers=+needs-restart || :
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -d /run/systemd/system ] && systemctl is-active -q imsd-ofonod.service; then
|
||||||
|
mark imsd-ofonod.service
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
|
@ -3,8 +3,9 @@
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=oFono-compatible GNOME Calls backend for imsd
|
Description=oFono-compatible GNOME Calls backend for imsd
|
||||||
Documentation=https://forgejo.catcrafts.net/Catcrafts/imsd
|
Documentation=https://forgejo.catcrafts.net/Catcrafts/imsd
|
||||||
# org.ofono is a system-bus name, so this is a system service; it needs no
|
# org.ofono is a system-bus name, so this is a system service, and it runs
|
||||||
# privileges beyond owning that name. Only enable it on a GNOME/Phosh session
|
# as root: its start hook restarts GNOME Calls in the users' own managers.
|
||||||
|
# Only enable it on a GNOME/Phosh session
|
||||||
# — Plasma Mobile uses imsd-dialerd instead. Both may run at once (different
|
# — Plasma Mobile uses imsd-dialerd instead. Both may run at once (different
|
||||||
# buses, different consumers), but a real ofonod must not.
|
# buses, different consumers), but a real ofonod must not.
|
||||||
Conflicts=ofono.service
|
Conflicts=ofono.service
|
||||||
|
|
@ -17,6 +18,9 @@ Type=simple
|
||||||
# matches imsd's configuration; imsd itself owns emergency classification.
|
# matches imsd's configuration; imsd itself owns emergency classification.
|
||||||
EnvironmentFile=-/etc/imsd.env
|
EnvironmentFile=-/etc/imsd.env
|
||||||
ExecStart=/usr/bin/imsd-ofonod
|
ExecStart=/usr/bin/imsd-ofonod
|
||||||
|
# GNOME Calls keeps a stale modem when org.ofono restarts and dials nothing
|
||||||
|
# until it restarts too: restart the Calls daemon of each user running one
|
||||||
|
ExecStartPost=-/usr/libexec/imsd-ofono-restart-calls.sh
|
||||||
# it holds no call state of its own — imsd is re-queried on every reconnect
|
# it holds no call state of its own — imsd is re-queried on every reconnect
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
RestartSec=5
|
RestartSec=5
|
||||||
|
|
|
||||||
1
packaging/imsd-systemd.post-install
Symbolic link
1
packaging/imsd-systemd.post-install
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
imsd-systemd.post-upgrade
|
||||||
23
packaging/imsd-systemd.post-upgrade
Executable file
23
packaging/imsd-systemd.post-upgrade
Executable file
|
|
@ -0,0 +1,23 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
# SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||||
|
#
|
||||||
|
# imsd.service ships in this subpackage, which apk installs after imsd.
|
||||||
|
# When imsd is added back over a daemon an `apk del` left running,
|
||||||
|
# imsd.post-install runs before this unit file is on disk and cannot mark
|
||||||
|
# the unit (not-found to systemd), so the package that brings the file
|
||||||
|
# marks it too; on an upgrade both mark it, which is one marker.
|
||||||
|
# Also installed as imsd-systemd.post-install.
|
||||||
|
|
||||||
|
# A unit whose file an apk del removed is not-found to systemd until a
|
||||||
|
# reload and refuses properties ("failed to load properly"); the file is
|
||||||
|
# on disk by now, so reload and mark again.
|
||||||
|
mark() {
|
||||||
|
systemctl set-property "$1" Markers=+needs-restart 2>/dev/null && return 0
|
||||||
|
systemctl daemon-reload && systemctl set-property "$1" Markers=+needs-restart || :
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -d /run/systemd/system ] && systemctl is-active -q imsd.service; then
|
||||||
|
mark imsd.service
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
1
packaging/imsd.post-install
Symbolic link
1
packaging/imsd.post-install
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
imsd.post-upgrade
|
||||||
48
packaging/imsd.post-upgrade
Executable file
48
packaging/imsd.post-upgrade
Executable file
|
|
@ -0,0 +1,48 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
# SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||||
|
#
|
||||||
|
# Restart imsd onto the upgraded binary once apk is done: the systemd
|
||||||
|
# trigger restarts every unit marked needs-restart (enqueue-marked). A
|
||||||
|
# changed bus policy is live the moment apk writes it (dbus-broker-launch
|
||||||
|
# reloads on its own), so it must not be left facing the old daemon. The
|
||||||
|
# restart resumes the registration over its existing SAs, or goes on
|
||||||
|
# waiting when the network had imsd wait; a call in progress ends.
|
||||||
|
#
|
||||||
|
# Also installed as imsd.post-install: `apk del imsd` removes the files
|
||||||
|
# and leaves the daemon running, so a later `apk add` of the package is
|
||||||
|
# this upgrade for that daemon, and apk runs post-install for it. A first
|
||||||
|
# install finds no unit active and marks nothing. On the binary route
|
||||||
|
# (APKBUILD.binary) the unit file ships in imsd-systemd, installed after
|
||||||
|
# this package; its imsd-systemd.post-upgrade marks the unit then.
|
||||||
|
#
|
||||||
|
# A daemon older than 0.3.10 kept its bring-up throttle wait in memory
|
||||||
|
# only, so a restart inside it registered fresh at once instead of
|
||||||
|
# waiting the schedule out. Before marking, record the running
|
||||||
|
# invocation in /run/imsd.handover: the next start (imsd-handover.sh, an
|
||||||
|
# ExecStartPre after ims-pdn-up.sh) reads that invocation's last bring-up
|
||||||
|
# line once the daemon has stopped and hands what is left of its wait over
|
||||||
|
# as imsd's holds file (on another address only the verdict that its
|
||||||
|
# registration is gone). Read then, not here: a wait that ends between this
|
||||||
|
# script and the restart at the end of the transaction — the old daemon
|
||||||
|
# making another attempt — is read as it ended.
|
||||||
|
|
||||||
|
# A unit whose file an apk del removed is not-found to systemd until a
|
||||||
|
# reload and refuses properties ("failed to load properly"); its file is
|
||||||
|
# back on disk by the time post-install runs, so reload and mark again.
|
||||||
|
mark() {
|
||||||
|
systemctl set-property "$1" Markers=+needs-restart 2>/dev/null && return 0
|
||||||
|
systemctl daemon-reload && systemctl set-property "$1" Markers=+needs-restart || :
|
||||||
|
}
|
||||||
|
|
||||||
|
record_old_invocation() {
|
||||||
|
[ -e /run/imsd.holds ] && return 0 # a 0.3.10 or later daemon keeps its own holds
|
||||||
|
inv=$(systemctl show -P InvocationID imsd.service 2>/dev/null)
|
||||||
|
[ -n "$inv" ] && echo "$inv" > /run/imsd.handover
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -d /run/systemd/system ] && systemctl is-active -q imsd.service; then
|
||||||
|
record_old_invocation
|
||||||
|
mark imsd.service
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
|
@ -13,6 +13,11 @@ Type=simple
|
||||||
# idempotent. worst case ~14 min (2 min modem wait + 5 min registration
|
# idempotent. worst case ~14 min (2 min modem wait + 5 min registration
|
||||||
# gate + connect retries), hence TimeoutStartSec
|
# gate + connect retries), hence TimeoutStartSec
|
||||||
ExecStartPre=/usr/libexec/ims-pdn-up.sh
|
ExecStartPre=/usr/libexec/ims-pdn-up.sh
|
||||||
|
# a daemon older than 0.3.10 that an upgrade restarted inside its bring-up
|
||||||
|
# throttle wait: the wait — on another address only the verdict that its
|
||||||
|
# registration is gone — is handed over from its journal now that it has
|
||||||
|
# stopped (imsd.post-upgrade recorded the invocation in /run/imsd.handover)
|
||||||
|
ExecStartPre=-/usr/libexec/imsd-handover.sh
|
||||||
TimeoutStartSec=900
|
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,
|
||||||
# LOCAL= the PDN address ModemManager assigned to it).
|
# LOCAL= the PDN address ModemManager assigned to it).
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,9 @@ cp "$BIN" "$stage/imsd-$VER/imsd"
|
||||||
cp "$MEDIA" "$stage/imsd-$VER/imsd-media"
|
cp "$MEDIA" "$stage/imsd-$VER/imsd-media"
|
||||||
cp "$DIALERD" "$stage/imsd-$VER/imsd-dialerd"
|
cp "$DIALERD" "$stage/imsd-$VER/imsd-dialerd"
|
||||||
cp "$OFONOD" "$stage/imsd-$VER/imsd-ofonod"
|
cp "$OFONOD" "$stage/imsd-$VER/imsd-ofonod"
|
||||||
cp packaging/ims-pdn-up.sh packaging/imsd.service \
|
cp packaging/ims-pdn-up.sh packaging/imsd-handover.sh packaging/imsd.service \
|
||||||
packaging/imsd-dialerd.desktop packaging/imsd-ofonod.service \
|
packaging/imsd-dialerd.desktop packaging/imsd-ofonod.service \
|
||||||
|
packaging/imsd-ofono-restart-calls.sh \
|
||||||
packaging/org.ofono.conf packaging/90_imsd-ofono.gschema.override \
|
packaging/org.ofono.conf packaging/90_imsd-ofono.gschema.override \
|
||||||
packaging/80-imsd-ofono.preset \
|
packaging/80-imsd-ofono.preset \
|
||||||
packaging/net.catcrafts.IMS1.conf "$stage/imsd-$VER/"
|
packaging/net.catcrafts.IMS1.conf "$stage/imsd-$VER/"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue