Warning: Permanently added '13.217.213.164' (ED25519) to the list of known hosts. You can reproduce this build on your computer by running: sudo dnf install copr-rpmbuild /usr/bin/copr-rpmbuild --verbose --drop-resultdir --srpm --task-url https://copr.fedorainfracloud.org/backend/get-srpm-build-task/11061531 Version: 1.10 PID: 11877 Logging PID: 11879 Task: {'appstream': False, 'background': False, 'build_id': 11061531, 'chroot': None, 'distributions_in_build': [], 'distributions_in_project': ['alma-kitten+epel-10', 'epel-10', 'epel-9', 'fedora-43', 'fedora-44'], 'package_name': None, 'project_dirname': 'daily', 'project_name': 'daily', 'project_owner': '@meshtastic', 'repos': [], 'sandbox': '@meshtastic/daily--vidplace7', 'source_json': {'clone_url': 'https://github.com/meshtastic/firmware.git', 'committish': 'f5940ab5dd3388c654be283089e139b64c0e46da', 'spec': '', 'srpm_build_method': 'rpkg', 'subdirectory': '', 'type': 'git'}, 'source_type': 8, 'submitter': 'vidplace7', 'task_id': '11061531'} Running: git clone https://github.com/meshtastic/firmware.git /var/lib/copr-rpmbuild/workspace/workdir-gjspp0x5/firmware --depth 500 --no-single-branch --recursive cmd: ['git', 'clone', 'https://github.com/meshtastic/firmware.git', '/var/lib/copr-rpmbuild/workspace/workdir-gjspp0x5/firmware', '--depth', '500', '--no-single-branch', '--recursive'] cwd: . rc: 0 stdout: Submodule path 'meshtestic': checked out 'dcac7e5673005f4d8a2b1f0f6e06877b689d7519' Submodule path 'protobufs': checked out '8f97d66a63ce10cfb12f94203e361691647f5ad3' stderr: Cloning into '/var/lib/copr-rpmbuild/workspace/workdir-gjspp0x5/firmware'... Submodule 'meshtestic' (https://github.com/meshtastic/meshTestic) registered for path 'meshtestic' Submodule 'protobufs' (https://github.com/meshtastic/protobufs.git) registered for path 'protobufs' Cloning into '/var/lib/copr-rpmbuild/workspace/workdir-gjspp0x5/firmware/meshtestic'... Cloning into '/var/lib/copr-rpmbuild/workspace/workdir-gjspp0x5/firmware/protobufs'... Running: git checkout f5940ab5dd3388c654be283089e139b64c0e46da -- cmd: ['git', 'checkout', 'f5940ab5dd3388c654be283089e139b64c0e46da', '--'] cwd: /var/lib/copr-rpmbuild/workspace/workdir-gjspp0x5/firmware rc: 0 stdout: stderr: Note: switching to 'f5940ab5dd3388c654be283089e139b64c0e46da'. You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by switching back to a branch. If you want to create a new branch to retain commits you create, you may do so (now or later) by using -c with the switch command. Example: git switch -c Or undo this operation with: git switch - Turn off this advice by setting config variable advice.detachedHead to false HEAD is now at f5940ab5d Meshtasticd notifications (#10216) Generated rpkg config: [rpkg] preprocess_spec = True [git] anon_clone_url = https://github.com/%(repo_path)s [lookaside] download_url = https://${git_props:remote_netloc}/repo/pkgs/%(repo_path)s/%(filename)s/%(hashtype)s/%(hash)s/%(filename)s Writing config into /var/lib/copr-rpmbuild/workspace/workdir-gjspp0x5/.config/rpkg.conf Running: rpkg srpm --outdir /var/lib/copr-rpmbuild/results --spec /var/lib/copr-rpmbuild/workspace/workdir-gjspp0x5/firmware cmd: ['rpkg', 'srpm', '--outdir', '/var/lib/copr-rpmbuild/results', '--spec', '/var/lib/copr-rpmbuild/workspace/workdir-gjspp0x5/firmware'] cwd: /var/lib/copr-rpmbuild/workspace/workdir-gjspp0x5/firmware rc: 0 stdout: Wrote: /var/lib/copr-rpmbuild/results/meshtasticd.spec setting SOURCE_DATE_EPOCH=1790812800 Wrote: /var/lib/copr-rpmbuild/results/meshtasticd-2.8.1-14673.daily.gitf5940ab5d.src.rpm RPM build warnings: stderr: git_dir_pack: archiving /var/lib/copr-rpmbuild/workspace/workdir-gjspp0x5/firmware: commit f5940ab5dd3388c654be283089e139b64c0e46da (HEAD, origin/develop, origin/HEAD, develop) Author: Jonathan Bennett Date: Thu Oct 1 17:38:14 2026 +0000 Meshtasticd notifications (#10216) * First attempt at sending message notifications on Linux * Add files via upload * Notifications: Use libnotify Use libnotify for meshtasticd desktop notifications. Add "HAS_LIBNOTIFY" macro to guard for builds where libnotify is expected. This is not included in buildroot/openwrt builds. (no libnotify except when extra repos are added). Install desktop icon in the correct location on Debian and Fedora packages. Update dependencies in packaging and dockerfiles. * Add libnotify to setup-native (GitHub Actions) * Address review feedback on the meshtasticd notification path - platformio.ini: only define HAS_LIBNOTIFY when pkg-config actually finds libnotify. The probe previously ran unguarded and the macro was defined unconditionally, so a native build without libnotify-dev both hard-failed at config time and claimed the feature was available. - Fix the sender lookup for develop's flattened NodeInfoLite: has_user/user.* are gone, replaced by nodeInfoLiteHasUser() and direct long_name/short_name. This is a silent semantic conflict - it merges cleanly but does not compile. - Move the desktop notification off the packet path. notify_notification_show() is a synchronous DBus round trip and meshtasticd's packet handling is single-threaded, so a slow or wedged notification daemon could stall the radio. handleReceived() now resolves the strings and queues them (bounded at 16); a dedicated worker owns every libnotify call. - Stop retrying forever: a failed notify_init(), or three consecutive failed shows, latches desktop notifications off instead of re-logging per message. - NodeDB: the ARCH_PORTDUINO default-enable block was nested inside #ifdef HAS_I2S, which Portduino never defines, so it never ran. Hoist it out. * fix(portduino): close input-broker guard before the libnotify block The libnotify implementation was inserted ahead of the #endif that closed #if !MESHTASTIC_EXCLUDE_INPUTBROKER, so the file's trailing #endif closed #if HAS_LIBNOTIFY instead and the input-broker conditional was never closed. Every build target failed to preprocess: src/modules/ExternalNotificationModule.cpp:630: error: unterminated #if Close the guard immediately after handleInputEvent(), as develop does, so the two conditionals stay independent and HAS_LIBNOTIFY still resolves when the input broker is excluded. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_013EZstXBJtzRyBmUD1h2FLs * fix(portduino): back off desktop notifications instead of latching them off notifyDisabled was doing two jobs: "libnotify looks unusable" and "the destructor wants the worker to exit". Because the worker's only exit path was also its only failure path, three failed shows turned notifications off for the process lifetime with no way back. The packaged daemon is exactly that case: bin/meshtasticd.service runs as User=meshtasticd and the rpm spec creates that account with /sbin/nologin, so there is no session bus and every show() fails. A stock install logged three warnings and then went silent forever, while NodeDB now enables the module by default on Portduino. Split the flag. notifyShutdown is destructor-only and remains the worker's one exit; a retry window replaces the latch. After maxNotifyFailures the worker arms a backoff (30s, doubling to a 15min cap), drops the queue rather than holding stale popups, and keeps looping. The producer refuses to queue while the window is open, so the retry is driven by the next message after it expires rather than by a timer - no idle wakeups, no probe notifications. Any success resets the backoff. notify_init() moved inside the loop so a retry can pick up a session bus that was absent at startup. The worker also no longer calls the LOG_ macros. RedirectablePrint formats into a shared static buffer that nothing guards and every other writer to it is on the main thread, so the worker now records the state change under the mutex it already holds and reportNotifyStatus() emits it from portduinoNotify() and runOnce(). Per-message failure spam becomes one line per transition: the reason and retry interval when it goes down, and a line when it comes back. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_013EZstXBJtzRyBmUD1h2FLs * fix(portduino): sanitize mesh text before it reaches libnotify The notification body was the raw decoded payload and the summary was a node name, both attacker-controlled, and neither was checked before being handed to libnotify. g_variant_new_string() rejects invalid UTF-8: a GLib CRITICAL and an "[Invalid UTF-8]" body by default, and a hard abort under G_DEBUG=fatal-criticals, so an unauthenticated mesh packet could terminate meshtasticd on any install running with that flag. An embedded NUL separately truncated the body at the first one, hiding the rest of the message. Route both strings through sanitizedMeshText(), which replaces embedded NULs and then applies the existing sanitizeUtf8() helper. TypeConversions already sanitizes names on the way into NodeDB, but an abort is too sharp an edge to leave resting on an invariant owned by another file. Escape the body for Pango markup as well. Servers advertising "body-markup" parse a markup subset there, so a message can inject formatting to dress itself up as trusted UI, and where the server also advertises body-images or body-hyperlinks it can inject tags that make the notification daemon fetch a remote URL. The escaping is unconditional rather than gated on notify_get_server_caps(): a caps query that fails, or goes stale across a daemon restart, fails open, while on a server without body-markup the only cost is entities rendering literally, and mesh text carries no markup worth preserving. The summary is not escaped - the spec gives it no markup, so escaping it would only ever show entities. Also call notify_uninit() in the destructor, after the join: the worker owns every libnotify call, so tearing down while it is still running would be a use-after-uninit. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_013EZstXBJtzRyBmUD1h2FLs * fix(portduino): gate the notification default-on behind HAS_LIBNOTIFY The default-enable block was gated on ARCH_PORTDUINO, so a native build where pkg-config could not find libnotify still shipped external_notification enabled. portduinoNotify() is not compiled into that build, and native defines EXT_NOTIFICATION_MODULE_OUTPUT as 0 with setup() guarding the pin writes behind output > 0, so nothing was driven - it only exposed a config surface that can do nothing. Gate it on the same macro that governs the code it exists to feed. HAS_LIBNOTIFY is a build flag for the whole env and is simply absent when the probe fails, so this reads as 0 on every other target, matching how ExternalNotificationModule.cpp already tests it. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_013EZstXBJtzRyBmUD1h2FLs * fix(portduino): fail closed when markup escaping returns NULL escapedNotificationBody() returned its input unescaped if g_markup_escape_text() gave back NULL, which would hand libnotify the exact attacker-controlled string the function exists to neutralize. The branch is unreachable for the input we pass - already sanitized to valid UTF-8, and GLib documents no NULL return for it - but an error path whose fallback is the unsafe action is the wrong shape for a helper on this boundary. Drop the body instead. Also note in the doc comment why running this on the caller's thread does not weaken the rule that the worker owns every libnotify call: it is a pure GLib string function that touches no libnotify or DBus state. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_013EZstXBJtzRyBmUD1h2FLs * style: format ExternalNotificationModule.h per the pinned clang-format Trunk Check Runner failed on c0d5ebff with "1 unformatted file" against this header. The deviation is a brace-spacing fix that clang-format wants on the rtttl stub's begin(): the formatter emitted it while reformatting this file for an earlier commit, and I reverted it then as unrelated churn. It was not - trunk's fmt check reports a modified file as a whole, so touching this header at all surfaces that line, and dropping the formatter's output is what turned the check red. Verified with the version trunk.yaml pins, clang-format 20.1.0, rather than the older one available locally: it is the only remaining deviation across the three files this branch touches. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_013EZstXBJtzRyBmUD1h2FLs * fix(portduino): stop the libnotify probe swallowing native-tft's link flags The docker arm64 builds failed to link native-tft: libmeshtastic-device-ui.a(CURLService.cpp.o): undefined reference to symbol 'curl_easy_reset@@CURL_GNUTLS_3' /lib/aarch64-linux-gnu/libcurl-gnutls.so.4: DSO missing from command line -lcurl was never on the link line. The libnotify probe was the last line of [native_base].build_flags, and an env extending it writes build_flags = ${native_base.build_flags} -Os -lcurl -lX11 ... so the interpolation appended those flags to the probe's own line. The result is a single shell command ending in `... && echo -D HAS_LIBNOTIFY=1 || : -Os -lcurl -lX11 -linput -lxkbcommon -ffunction-sections -fdata-sections -Wl,--gc-sections`, where the trailing flags are arguments to echo when the probe succeeds and to `:` when it fails. They never reach the compiler, and nothing reports it. Three envs lost flags this way: native-tft and native-tft-debug (-lcurl -lX11 -linput -lxkbcommon), and native-fb (-lcurl, --gc-sections). env:native appends nothing on that line, which is why the native test suite stayed green and this stayed hidden until the module compiled and something actually tried to link. Move the probe above `-I /usr/include` so a plain flag line ends the value, and record the constraint so the next flag added here does not re-break it. Verified with `pio project config --json-output`: before, the three envs carried the flags inside the probe's command string; after, all three carry them as build flags and no env still swallows any. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_013EZstXBJtzRyBmUD1h2FLs --------- Co-authored-by: Austin Lane Co-authored-by: Tom Fifield Co-authored-by: Claude git_dir_pack: Wrote: /var/lib/copr-rpmbuild/results/firmware-f5940ab5.tar.gz warning: Downloading https://github.com/meshtastic/web/releases/download/v2.6.7/build.tar to /var/lib/copr-rpmbuild/results/build.tar Downloading https://github.com/meshtastic/web/releases/download/v2.6.7/build.tar to /var/lib/copr-rpmbuild/results/build.tar Output: ['build.tar', 'meshtasticd-2.8.1-14673.daily.gitf5940ab5d.src.rpm', 'meshtasticd.spec', 'firmware-f5940ab5.tar.gz'] Running SRPMResults tool Using distributions_in_project for this build. Extracting arch-specific tags for alma-kitten+epel-10 Extracting arch-specific tags for epel-10 Extracting arch-specific tags for epel-9 Extracting arch-specific tags for fedora-43 Extracting arch-specific tags for fedora-44 Package info: { "architecture_specific_tags": { "alma-kitten+epel-10": {}, "epel-10": {}, "epel-9": {}, "fedora-43": {}, "fedora-44": {} }, "name": "meshtasticd", "epoch": null, "version": "2.8.1", "release": "14673.daily.gitf5940ab5d" } SRPMResults finished