Subject: elogind: SwayWM fails to launch from vTTY: Could not take device: No such file or directory

From: Nathan Schulte <[email-masked]>
Date: Fri, 23 Jan 2026 13:24:02 -0600

Source: elogind
Version: 255.17-4
Severity: normal
X-Debbugs-Cc: [email-masked]

Hello.

Upon upgrading to elogind packages version 255.17-4, SwayWM
fell/"crashed" back to vTTY, and is no longer able to be started,
producing the log attached. The log is produced with the command:

> sway --debug --verbose &> sway-$(date -u -Im).log

elogind service is running with this upgrade, and the system is
practically virgin (a fresh install). However, the only way I've managed
to launch Sway is by downgrading elogind packages to version 255.17-3.
Reboots, both warm and cold, do not resolve the issue, nor does any
amount of waiting.

Thank you for your attention.

-- System Information:
Distributor ID:	Devuan
Description:	Devuan GNU/Linux 7 (freia/ceres)
Release:	7
Codename:	freia ceres
Architecture: x86_64

Kernel: Linux 6.18.5+deb14-amd64 (SMP w/16 CPU threads; PREEMPT)
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8), LANGUAGE not set
Shell: /bin/sh linked to /usr/bin/dash
Init: sysvinit (via /sbin/init)
LSM: AppArmor: enabled

-- no debconf information

---

From: Andrew Bower <[email-masked]>
Date: Sat, 24 Jan 2026 16:19:13 +0000

Hi Nathan,

> Reboots, both warm and cold, do not resolve the issue, nor does any

The change between these two versions of elogind is a new initscript,
for two purposes:

1) To simplify the script using init-d-script(5).

2) To enable use of the sd_notify(3) protocol during elogind startup so
that the service is not deemed to have started until it is actually
ready to service requests. This is to prevent problems with other
services assuming elogind is ready when it is not just because those
services had expressed a startup dependency on elogind.

The scenario that (2) is trying to fix is:

  LSB header for service A expresses dependency on elogind

  T
  |
  + elogind service started
  |
  + service A started
  |
  + service A tries unsuccessfully to use elogind functionality
  |
  + elogind is ready to service clients

The new ordering should be:

  T
  |
  + elogind service started
  |
  + elogind notifies start-stop-daemon that it is ready
  |
  + service A started
  |
  + service A successfully uses elogind functionality

So in theory this should be an improvement, but it will change timings
and could have adverse side effects if not everything else is correct.

Take this situation:

  LSB header for service A expresses dependency on elogind
  Service B relies on service A but does not express this dependency.

  T
  |
  + elogind service started
  |
  + service A started
  |
  + service B started
  |
  + elogind is ready to service clients
  |
  + service A successfully uses elogind functionality
  | 
  + service B successfully uses service A functionality

  ... everything is fine - by chance.

But with the new behaviour, timings could change things to be like this:

  T
  |
  + elogind service started
  |
  + service B started
  |
  + service B tries unsuccessfully to use service A functionality
  |
  + elogind is ready to service clients
  |
  + service A started
  |
  + service A is ready to service clients

Now it is far from clear that this is what is actually happening but it
is a possible cause.

Would you mind sharing the file /etc/init.d/.depend.start, please, so we
can see what the dependency chain is on your system? That might reveal
if there is an issue similar to the Service A/Service B interactions
I've described above.

Another possibility other than that is that a service gets in which sets
some system features up incorrectly as a fallback which elogind would
have set up correctly. We sometimes see that sort of thing with cgroups
mounts, for example.

Another user has also suggested something to eliminate (1) as a cause of
this regression. You could reinstall version -4 of the package and edit
the following variable as such:

  DAEMON_ARGS="-D"
  START_ARGS=""

i.e. putting the behaviour back how it was but with the structure of the
new version of the initscript.

> 00:00:00.000 [INFO] [sway/main.c:321] Sway version 1.11
> 00:00:00.000 [INFO] [sway/main.c:322] wlroots version 0.19.1
> 00:00:00.001 [INFO] [sway/main.c:78] Linux desmas-l-fa617xt 6.18.5+deb14-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.18.5-1 (2026-01-16) x86_64 GNU/Linux
> 00:00:00.001 [INFO] [sway/main.c:94] Contents of /etc/os-release:
> 00:00:00.001 [INFO] [sway/main.c:78] PRETTY_NAME="Devuan GNU/Linux 7 (freia/ceres)"
> 00:00:00.001 [INFO] [sway/main.c:78] NAME="Devuan GNU/Linux"
> 00:00:00.001 [INFO] [sway/main.c:78] VERSION_ID="7"
> 00:00:00.001 [INFO] [sway/main.c:78] VERSION="7 (freia/ceres)"
> 00:00:00.001 [INFO] [sway/main.c:78] VERSION_CODENAME="freia ceres"
> 00:00:00.001 [INFO] [sway/main.c:78] ID=devuan
> 00:00:00.001 [INFO] [sway/main.c:78] ID_LIKE=debian
> 00:00:00.001 [INFO] [sway/main.c:78] HOME_URL="https://www.devuan.org/"
> 00:00:00.001 [INFO] [sway/main.c:78] SUPPORT_URL="https://devuan.org/os/community"
> 00:00:00.001 [INFO] [sway/main.c:78] BUG_REPORT_URL="https://bugs.devuan.org/"
> 00:00:00.001 [INFO] [sway/main.c:94] Contents of /etc/debian_version:
> 00:00:00.001 [INFO] [sway/main.c:78] forky/sid
> 00:00:00.001 [INFO] [sway/main.c:66] LD_LIBRARY_PATH=
> 00:00:00.001 [INFO] [sway/main.c:66] LD_PRELOAD=
> 00:00:00.001 [INFO] [sway/main.c:66] PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
> 00:00:00.001 [INFO] [sway/main.c:66] SWAYSOCK=
> 00:00:00.001 [INFO] [sway/main.c:351] Starting sway version 1.11
> 00:00:00.001 [DEBUG] [sway/server.c:236] Initializing Wayland server
> 00:00:00.001 [INFO] [wlr] [libseat] [libseat/backend/seatd.c:64] Could not connect to socket /run/seatd.sock: No such file or directory
> 00:00:00.001 [INFO] [wlr] [libseat] [libseat/libseat.c:76] Backend 'seatd' failed to open seat, skipping
> 00:00:00.004 [INFO] [wlr] [libseat] [libseat/libseat.c:73] Seat opened with backend 'logind'

elogind does seem to be operational here and has opened a session or
whatever.

> 00:00:00.004 [INFO] [wlr] [backend/session/session.c:108] Successfully loaded libseat session
> 00:00:02.439 [ERROR] [wlr] [libseat] [libseat/backend/logind.c:124] Could not take device: No such file or directory
> 00:00:02.439 [ERROR] [wlr] [backend/session/session.c:331] Failed to open device: '/dev/dri/card0': Resource temporarily unavailable

This is the failing call:

ret = sd_bus_call_method(session->bus, "org.freedesktop.login1", session->path,
                         "org.freedesktop.login1.Session", "TakeDevice", &error, &msg, "uu",
                         major(st.st_rdev), minor(st.st_rdev));

> 00:00:02.444 [ERROR] [wlr] [libseat] [libseat/backend/logind.c:124] Could not take device: No such file or directory
> 00:00:02.445 [ERROR] [wlr] [backend/session/session.c:331] Failed to open device: '/dev/dri/card1': Resource temporarily unavailable
> 00:00:02.446 [ERROR] [wlr] [backend/backend.c:245] Found 0 GPUs, cannot create backend
> 00:00:02.446 [ERROR] [wlr] [backend/backend.c:420] Failed to open any DRM device
> 00:00:02.460 [ERROR] [sway/server.c:247] Unable to create backend

Thanks!

Andrew

---

From: [email-masked]
Date: Sat, 24 Jan 2026 18:14:06 +0000

On Saturday, January 24th, 2026 at 10:19, Andrew Bower <[email-masked]> wrote:
> ... snip ...

Thank you for that detailed explanation Andrew. Attached is /etc/init.d/.depend.start from the system.

> new version of the initscript.

I have tested this, and indeed this allows Sway to launch, successfully "Found 2 GPUs" and "Initializing DRM backend for /dev/dri/card...".

> major(st.st_rdev), minor(st.st_rdev));

I have attached also the D-Bus system message output, from both 255.17-4 without initscript daemon/startup argument changes (255.17-4-unedited.dbus.log), and also with (255.17-4-edited.dbus.log).

--
Nathan

---

From: Andrew Bower <[email-masked]>
Date: Sat, 24 Jan 2026 18:22:53 +0000

> Thank you for that detailed explanation Andrew. Attached is /etc/init.d/.depend.start from the system.

Thanks for this!

It looks like a very simple setup and I can't see anything that might
resemble the A-B service combinations I was imagining.

> I have attached also the D-Bus system message output, from both 255.17-4 without initscript daemon/startup argument changes (255.17-4-unedited.dbus.log), and also with (255.17-4-edited.dbus.log).

I am afraid I know nothing about dbus so I hope someone else will be
able to learn something from these to help.

Andrew

---

From: Andrew Bower <[email-masked]>
Date: Sat, 24 Jan 2026 23:34:49 +0000

One small thing to try, could you add '--verbose --no-close' to
START_ARGS?

---

From: [email-masked]
Date: Sun, 25 Jan 2026 00:30:07 +0000

> One small thing to try, could you add '--verbose --no-close' to

It appears log messages from elogind would go to /var/log/auth.log; is this correct? I see nothing different with --verbose and --no-close options.

Looking at the elogind source, it appears I may need to rebuild it with ENABLE_DEBUG_ELOGIND and/or LOG_TRACE:

- https://git.devuan.org/devuan/elogind/src/branch/master/src/basic/log.h#L235
- https://git.devuan.org/devuan/elogind/src/branch/master/src/basic/log.h#L241
- https://github.com/elogind/elogind/blob/88c346cdc5ef231465552de9c55332fd9d2c408b/meson.build#L1248

It's also not clear what --verbose should impact, looking briefly at the source.

kennylevinsen, via [email-masked], has told:

> 14:56 < kennylevinsen> well the main things to look for: 1. is elogind still running as root? it has to, 2. is the test being run after the GPU is available (if you're starting sway itself from an init script you can be too fast)
> 14:59 < kennylevinsen> worst case you could edit the init script to strace elogind
> 15:00 < kennylevinsen> but no I don't quite know what that init system does and why it might have broken it

1. elogind does run as root
2. I assume the GPU is still available; I have not altered anything but the init script due to upgrade, and flopping back/forth to daemon/notify in the same boot functions as expected.

I think to rebuild elogind with debug logging before wading through strace logs.

--
Nathan

---

From: Andrew Bower <[email-masked]>
Date: Sun, 25 Jan 2026 09:01:46 +0000

Hi Nathan,

> It appears log messages from elogind would go to /var/log/auth.log; is this correct? I see nothing different with --verbose and --no-close options.

Sorry I didn't explain what I was thinking: I was trying to eliminate
differences between how elogind daemonizes itself and how
start-stop-daemon (s-s-d) daemonizes services. One is that the latter
redirects stdio to /dev/null so --no-close was to stop that as I don't
have much else to try!

--verbose was just a bonus while making another change, and is for
diagnostics from s-s-d itself showing the readiness notification
protocol.

> It's also not clear what --verbose should impact, looking briefly at the source.

See above, this wasn't an option for elogind.

> 1. elogind does run as root

It does run as root but I wonder if some privilege has somehow got lost
in the different daemonization process. This might give us some clues:

  cat /proc/$(cat /run/elogind.pid)/status

Anyone could check that between the two modes - doesn't need to be in
your system where the failure is occurring.

> I think to rebuild elogind with debug logging before wading through strace logs.

That indeed might help.

Mark,

Now we have two users badly and reliably affected with different
scenarios, no smoking gun and I don't really have time today to debug,
do you think we should revert the startup semantics and reopen #935?

I still think #935 is worth doing to get robust setups all round that
aren't susceptible to random timing issues, but I think we need to
reproduce Nathan's failure, root cause and fix.

We can keep the new init-d-script as that's been proven not to be the
problem, we just need to have:

  DAEMON_ARGS='-D'
  START_ARGS=''

It may be that when root-caused, the fix needs to go in s-s-d. I'll also
keep reviewing that implementation by inspection and comparing with
elogind.

Andrew

---

From: [email-masked]
Date: Sun, 25 Jan 2026 09:57:02 +0000

> cat /proc/$(cat /run/elogind.pid)/status

The only difference I see here is that SIGINT is not ignored by elogind (with --no-close etc. START_ARGS; vs elogind-daemon).

--
Nathan

---

From: Andrew Bower <[email-masked]>
Date: Sun, 25 Jan 2026 11:11:41 +0000

> The only difference I see here is that SIGINT is not ignored by elogind (with --no-close etc. START_ARGS; vs elogind-daemon).

How very strange this all is.

Well there is one other thing we could try - could it all be about
timings in the end after all? We could do this combination:

  DAEMON_ARGS=''
  START_ARGS='--verbose --background'

I.e. just remove '--notify-await' but keep s-s-d doing the
backgrounding.

And yet we didn't find anything in your startup sequence that was
waiting for elogind.

---

From: [email-masked]
Date: Sun, 25 Jan 2026 17:35:16 +0000

> And yet we didn't find anything in your startup sequence that was

This results in being able to launch SwayWM.

Also, I tried launching sway as root just to see; it makes no difference (in any scenario; the results shared as UID=1000, match).

--
Nathan

---

From: [email-masked]
Date: Sun, 25 Jan 2026 18:21:25 +0000

I've built elogind packages with log-trace: true and debug-extra: ['elogind'], and it provides more detail, e.g.:

> elogind[13734]: Got message type=method_call sender=:1.30 destination=org.freedesktop.login1 path=/org/freedesktop/login1/session/_38 interface=org.freedesktop.login1.Session member=TakeDevice  cookie=10 reply_cookie=0 signature=uu error-name=n/a error-message=n/a
> elogind[13734]: Failed to send notify message to '/run/.s-s-d-notify.49UVq6/notify': No such file or directory
> elogind[13734]: Sent message type=signal sender=n/a destination=:1.30 path=/org/freedesktop/login1/session/_38 interface=org.freedesktop.login1.Session member=PauseDevice cookie=76 reply_cookie=0 signature=uus error-name=n/a error-message=n/a
> elogind[13734]: Sent message type=error sender=n/a destination=:1.30 path=n/a interface=n/a member=n/a cookie=77 reply_cookie=10 signature=s error-name=org.freedesktop.DBus.Error.FileNotFound error-message=No such file or directory
> elogind[13734]: Failed to process message type=method_call sender=:1.30 destination=org.freedesktop.login1 path=/org/freedesktop/login1/session/_38 interface=org.freedesktop.login1.Session member=TakeDevice cookie=10 reply_cookie=0 signature=uu error-name=n/a error-message=n/a: No such file or directory

--
Nathan

---

From: Andrew Bower <[email-masked]>
Date: Sun, 25 Jan 2026 18:44:34 +0000

> > elogind[13734]: Failed to process message type=method_call sender=:1.30 destination=org.freedesktop.login1 path=/org/freedesktop/login1/session/_38 interface=org.freedesktop.login1.Session member…

Oh this is interesting.

Since the initial readiness exchange seems to work OK, I wonder if
what's happening is elogind continues to send information over this
channel through its lifetime despite the fact it is now closed, and I
wonder if it then takes some wrong code paths which prevents it from
performing its main function properly.

It might be worth it looking at the places that elogind tries to signal
a notification and make sure failure to signal on a closed channel is
ignored and doesn't cause a broader error.

---

From: Andrew Bower <[email-masked]>
Date: Sun, 25 Jan 2026 18:51:11 +0000

> Since the initial readiness exchange seems to work OK, I wonder if

Just to confirm this with you - when you added '--verbose' to
START_ARGS, did the output confirm to you:

 -> Notification => ready for service.

? If you only relied on reboot then 'service elogind start' should show
this.

---

From: [email-masked]
Date: Sun, 25 Jan 2026 19:03:54 +0000

> ? If you only relied on reboot then 'service elogind start' should show

This is correct; with --verbose in START_ARGS, I do see this "ready for service" message upon `service elogind restart`.

Also, man sd_notify has interesting notes about $NOTIFY_SOCKET w/re: return values of calls re: status messages and supporting the various schemes of service managers, but I don't have enough knowledge to know if it's pertinent.

I'm happy to test any branch or patch made available, but drumming one up myself is unlikely right now.

Thanks!

--
Nathan

---

From: Andrew Bower <[email-masked]>
Date: Sun, 25 Jan 2026 19:06:44 +0000

> I'm happy to test any branch or patch made available, but drumming one up myself is unlikely right now.

I have a hunch this will do it...

The error is indeed coming from a call that will cause failure. Setting
this argument to 'true' will deconfigure use of the notify socket once
we've signalled readiness.

$ git diff
diff --git a/src/shared/daemon-util.h b/src/shared/daemon-util.h
index 711885bba..6823cc31d 100644
--- a/src/shared/daemon-util.h
+++ b/src/shared/daemon-util.h
@@ -12,7 +12,7 @@
 
 static inline const char *notify_start(const char *start, const char *stop) {
         if (start)
-                (void) sd_notify(false, start);
+                (void) sd_notify(true, start);
 
         return stop;
 }

---

From: [email-masked]
Date: Sun, 25 Jan 2026 19:14:58 +0000

> +++ b/src/shared/daemon-util.h

Winner, winner!  This seems to do the trick, with an unmolested elogind initscript (255.17-4).

Kudos!

--
Nathan

---

From: Andrew Bower <[email-masked]>
Date: Sun, 25 Jan 2026 19:21:45 +0000

> Winner, winner!  This seems to do the trick, with an unmolested elogind initscript (255.17-4).

Exellent!

Now I don't reckon this is a good enough production patch because
someone could write a service manager (that isn't systemd!) that wanted
to use the sd readiness protocol for the whole life of a service.

I think we need to change pid_notify_with_fds_internal() in
src/shared/daemon-util.h so that if the send fails with ENOENT, the call
returns successfully and unsets NOTIFY_SOCKET from the environment:

  n = sendmsg(fd, &msghdr, MSG_NOSIGNAL);
  if (n < 0) {
          if (!send_ucred)
                  return log_debug_errno(errno, "Failed to send notify message to '%s': %m", e);

I'm not 100% sure this is the right thing always to do so a carefully
constructed comment might be appropriate to warn future developers of
why we did it. A safe thing to do would be to return success but not
clear the environment but it would be nice to stop repeated pointless
behaviour.

> Kudos!

Thanks for being prepared to try an untested patch!

---

From: Andrew Bower <[email-masked]>
Date: Sun, 25 Jan 2026 19:40:56 +0000

> clear the environment but it would be nice to stop repeated pointless

Nah, changed my mind. We shouldn't really modify the implementation
under sd_notify() which inherits loads of stuff from libsystemd which
could be substituted one day with a simpler version.

Instead we should find out exactly what is calling sd_notify() at these
later points and make it handle the -errno style error code
appropriately. I can't see where this is getting called.

We could hack s-s-d to keep showing us or hack elogind to print them
out. One quick way might be to hack src/shared/daemon-util.h
NOTIFY_READY definition to misspell 'READY' as 'REAxDY' and whack a big
--notify-timout into START_ARGS; then s-s-d will keep waiting and
printing everything it gets from elogind and we can trace it that way.

---

From: [email-masked]
Date: Sun, 25 Jan 2026 20:49:31 +0000

> printing everything it gets from elogind and we can trace it that way.

Not sure there's enough detail with this method; attached elogind-reaxdy.log; double line breaks are entered by me (marking before sway, w/in sway, exiting sway); sd_notify(true, ...) reverted.  Sway launches successfully ...

START_ARGS='--background --notify-await --notify-timeout 120 --verbose --no-close'

--
Nathan

---

From: Andrew Bower <[email-masked]>
Date: Sun, 25 Jan 2026 21:10:39 +0000

> Not sure there's enough detail with this method; attached elogind-reaxdy.log; double line breaks are entered by me (marking before sway, w/in sway, exiting sway); sd_notify(true, ...) reverted.  Sw…

Oh no this is perfect, thanks!

Looks like there's a bunch of nonsense about passing fds to PID1 that we
are not interested in and should fail beningly or not be done.

Here's another possible patch. There's probably a multitude of ways we
can solve this.

diff --git a/src/shared/daemon-util.c b/src/shared/daemon-util.c
index 32180a14b..c641799c8 100644
--- a/src/shared/daemon-util.c
+++ b/src/shared/daemon-util.c
@@ -46,6 +46,7 @@ int close_and_notify_warn(int fd, const char *name) {
 
 static int notify_push_fd(int fd, const char *name) {
         _cleanup_free_ char *state = NULL;
+        int r;
 
         assert(fd >= 0);
         assert(name);
@@ -55,7 +56,14 @@ static int notify_push_fd(int fd, const char *name) {
         if (!state)
                 return -ENOMEM;
 
-        return sd_pid_notify_with_fds(0, /* unset_environment = */ false, state, &fd, 1);
+        r = sd_pid_notify_with_fds(0, /* unset_environment = */ false, state, &fd, 1);
+        if (r == -ENOENT) {
+                 log_warning_errno(r,
+                                   "Supervisor notification channel vanished. Cannot store file descriptor \"%s\", ignoring: %m",
+                                   name);
+                 return 0;
+        }
+        return r;
 }
 
 int notify_push_fdf(int fd, const char *format, ...) {