Subject: Herd `fport_write: Broken pipe` error when running `guix home reconfigure`

From: Richard Sent <[email-masked]>
Date: Thu, 16 Nov 2023 15:09:13 -0500

Did some more testing and I was able to find a workaround for my version 
of this issue, although I don't know if it'll solve it for others. TL;DR 
is I changed my login shell from fish back to bash.

The system configuration for the nonworking machine was using fish 
`(user-account (shell (file-append fish "/bin/fish") ...)`. There seems 
to be some unintended behavior with executing the `on-first-login` 
script in `.profile` using fish.

The stock `config.fish` sources $HOME/.profile, which executes the 
.guix-home/on-first-login script via fenv source $HOME/.profile. Herd 
seems to start up nicely, but somehow stdout for herd winds assigned to 
a pipe without a reader between initialization and spawning gpg-agent.

initialization writes to the logfile just fine regardless of if you're 
using fish or bash:
--8<---------------cut here---------------start------------->8---
2023-11-16 14:17:15 Starting service root...
2023-11-16 14:17:15 Service root started.
2023-11-16 14:17:15 Service root running with value #t.
2023-11-16 14:17:15 Service root has been started.
2023-11-16 14:17:15 Daemonizing...
2023-11-16 14:17:15 Restarting signal handler.
2023-11-16 14:17:15 Now running as process 936.
2023-11-16 14:17:15 Starting services...
2023-11-16 14:17:15 Configuration successfully loaded from 
'/gnu/store/kzh1x4y030drw0jrdk08nynvvyasm3c1-shepherd.conf'.
2023-11-16 14:17:15 Starting service gpg-agent...
2023-11-16 14:17:15 Service gpg-agent has been started.
2023-11-16 14:17:15 Service gpg-agent started.
2023-11-16 14:17:15 Service gpg-agent running with value (("ssh" . 
#<input-output: socket 16>) ("browser" . #<input-output: socket 18>) 
("extra" . #<input-output: socket 19>) ("std" . #<input-output: socket 
20>)).
2023-11-16 14:17:15 Successfully started 2 services in the background.
--8<---------------cut here---------------end--------------->8---

When using bash instead of fish, stdout (file descriptor 1) points to 
/dev/null so the write does not fail. Another write is for 
$HOME/.local/state/shepherd.log.

bash fds
--8<---------------cut here---------------start------------->8---
$ ls -l /proc/<HERD_PID>/fd)
lr-x------   1 richard        users    64 2023-11-16 14:17 0 -> 
/dev/null
l-wx------   1 richard        users    64 2023-11-16 14:17 1 -> 
/dev/null
l-wx------   1 richard        users    64 2023-11-16 15:04 2 -> 
/home/richard/.local/share/sddm/xorg-session.log
l-wx------   1 richard        users    64 2023-11-16 14:17 6 -> 
/home/richard/.local/state/log/shepherd.log
// ...
--8<---------------cut here---------------end--------------->8---

bash login shell strace of herd, right after running $ gpg 
--card-status:
--8<---------------cut here---------------start------------->8---
write(1, "Spawning systemd-style service 
/gnu/store/2zgdxhbnkz8fgsb1l4xydm3nbvj66mih-gnupg-2.2.39/bin/gpg-agent.\n", 
103) = 103
newfstatat(AT_FDCWD, "/etc/localtime", {st_mode=S_IFREG|0444, 
st_size=3536, ...}, 0) = 0
write(6, "2023-11-16 14:36:29 Spawning systemd-style service 
/gnu/store/2zgdxhbnkz8fgsb1l4xydm3nbvj66mih-gnupg-2.2.39/bin/gpg-agent.\n", 
123) = 123
// happy success messages
--8<---------------cut here---------------end--------------->8---

However, when fish is in charge of executing on-first-login, stdout and 
stderr for the user shepherd get directed to a pipe instead of 
/dev/null.

fish fds, both stdout and stderr point to a pipe:
--8<---------------cut here---------------start------------->8---
$ ls -l /proc/<HERD_PID>/fd)
lr-x------   1 richard        users    64 2023-11-16 14:47 0 -> 
/dev/null
l-wx------   1 richard        users    64 2023-11-16 14:47 1 -> 
pipe:[41039]
l-wx------   1 richard        users    64 2023-11-16 14:47 2 -> 
pipe:[41039]
l-wx------   1 richard        users    64 2023-11-16 14:47 6 -> 
/home/richard/.local/state/log/shepherd.log
// ...
--8<---------------cut here---------------end--------------->8---

fish login shell strace of herd:
--8<---------------cut here---------------start------------->8---
21:46:43 write(1, "Spawning systemd-style service 
/gnu/store/2zgdxhbnkz8fgsb1l4xydm3nbvj66mih-gnupg-2.2.39/bin/gpg-agent.\n", 
103) = -1 EPIPE (Broken pipe)
--8<---------------cut here---------------end--------------->8---

Details of what processes are using the pipe: (i.e. nothing else is 
using the pipe. Which makes sense for a broken pipe error at least...)
--8<---------------cut here---------------start------------->8---
$ lsof -n | grep -w 41039
(standard input):shepherd   991                   richard    1w     FIFO 
               0,14       0t0     41039 pipe
(standard input):shepherd   991                   richard    2w     FIFO 
               0,14       0t0     41039 pipe
--8<---------------cut here---------------end--------------->8---

What confuses me about this situation is judging from that bash behavior 
pipes shouldn't be involved at all for stdin/stdout/stderr. There's also 
an discrepancy in that stderr for bash points to xorg-session.log while 
for fish it points to the same pipe as stdout. It would be interesting 
to know if other users who are experiencing broken pipe errors are also 
using fish as their user shell.