Repository navigation
SSH in Mac OS: launchctl commands return "Load failed: 5: Input/output error" #504
Description
Activity
Well, I don't have these plists installed but I think I can still answer the question (and possibly confuse you even more too).
From the main README:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>gnupg.gpg-agent</string> <key>RunAtLoad</key> <true/> <key>KeepAlive</key> <false/> <key>ProgramArguments</key> <array> <string>/usr/local/MacGPG2/bin/gpg-connect-agent</string> <string>/bye</string> </array> </dict> </plist>The meat of that is:
/usr/local/MacGPG2/bin/gpg-connect-agentNow, on a Mac (irrespective of Intel or Apple silicon):
$ ls -l /usr/local/MacGPG2/bin/gpg-connect-agent ls: /usr/local/MacGPG2/bin/gpg-connect-agent: No such file or directorySo that's the basic problem and is why you're seeing the "Load failed: 5: Input/output error".
If I run:
$ which gpg-connect-agentthen:
-
on an Apple Silicon Mac:
/opt/homebrew/bin/gpg-connect-agent -
on an Intel Mac:
/usr/local/bin/gpg-connect-agent
And that's the fix. You go back to the basic plist and replace:
/usr/local/MacGPG2/bin/gpg-connect-agentwith the relevant path from your Apple or Intel Mac.
Next, if I install the modified content at the path:
$HOME/Library/LaunchAgents/gnupg.gpg-agent.plistthen there is no need to "load" it. That's because current macOS senses the addition of a plist dynamically. You should get a notification telling you that a background item has been added to the "Allow in the Background" list in the "Login Items" panel in "System Settings".
However, if you want to remove such an item then I believe that the recommended approach is to unload it before removing:
$ launchctl unload ~/Library/LaunchAgents/gnupg.gpg-agent.plist $ rm ~/Library/LaunchAgents/gnupg.gpg-agent.plistOn the topic of
sudo, don't do that. This is a user launch agent, not a system launch agent.Last thing. As far as I know,
kickstarthas replacedbootstrapfor most situations. It's a bit tricky with user launch agents but, for this situation, the appropriate command would be:$ launchctl kickstart -k gui/$(id -u)/gnupg.gpg-agentThe
id -ureturns your numeric user ID, and the tail end of the path is theLabelfield in the plist.Although I did install
gnupg.gpg-agent.plistwhile I was writing the above, I didn't try the "symlink.plist". I studied it for a while but it gives me the heebies.This is the bit that troubles me:
/bin/ln -sf $HOME/.gnupg/S.gpg-agent.ssh $SSH_AUTH_SOCKFirstly, to the best of my knowledge, the first command argument
S.gpg-agent.sshis a socket that will only exist if the YubiKey has been inserted, unlocked and used. Certainly, none of the socket files is present on my system after a reboot until first use of the YubiKey. That implies any launch agent which assumes the socket always exists will fail when it doesn't.However, let's assume the socket exists:
$ ls -l $HOME/.gnupg/S.gpg-agent.ssh srwx------ 1 user group 0 May 20 16:34 $HOME/.gnupg/S.gpg-agent.sshSecondly, I do not see how a Launch Agent can "see" the
SSH_AUTH_SOCKenvironment variable so, at face value, it should be blank so the command would reduce to:$ /bin/ln -sf $HOME/.gnupg/S.gpg-agent.sshExecuting that results in:
$ ls -l $HOME/.gnupg/S.gpg-agent.ssh lrwxr-xr-x 1 user group 37 May 20 17:17 /$HOME/.gnupg/S.gpg-agent.ssh -> $HOME/.gnupg/S.gpg-agent.sshSo now it's a symlink pointing to itself and the socket is either masked or destroyed (I don't know which). What I do know is that it doesn't work:
$ file $HOME/.gnupg/S.gpg-agent.ssh $HOME/.gnupg/S.gpg-agent.ssh: broken symbolic link to $HOME/.gnupg/S.gpg-agent.sshComputer says no! The only "fix" I've been able to find is to remove that file, reboot, and make the YubiKey do something.
It gets weirder. Let's assume the launch agent can somehow see
SSH_AUTH_SOCK. What should its value be? Hunting around the README:export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)Let's run the meat of that:
$ gpgconf --list-dirs agent-ssh-socket $HOME/.gnupg/S.gpg-agent.sshIn short, as far as I can tell, if we assume the Launch Agent can see
SSH_AUTH_SOCKthe command would become:ln -sf $HOME/.gnupg/S.gpg-agent.ssh $HOME/.gnupg/S.gpg-agent.sshwhich is still a symlink pointing to itself. Thus, irrespective of whether a launch agent can or can't see
SSH_AUTH_SOCK, we still wind up with a masked socket and a broken symlink.See why it gives me the heebies?
Not on my system! Well, not until someone more knowledgeable than me can explain why any of this makes sense.
Hope this helps.
Reacted by drduhReacted by drduh-
Well, I don't have these plists installed but I think I can still answer the question (and possibly confuse you even more too).
From the main README:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>gnupg.gpg-agent</string> <key>RunAtLoad</key> <true/> <key>KeepAlive</key> <false/> <key>ProgramArguments</key> <array> <string>/usr/local/MacGPG2/bin/gpg-connect-agent</string> <string>/bye</string> </array> </dict> </plist>The meat of that is:
/usr/local/MacGPG2/bin/gpg-connect-agentNow, on a Mac (irrespective of Intel or Apple silicon):
$ ls -l /usr/local/MacGPG2/bin/gpg-connect-agent ls: /usr/local/MacGPG2/bin/gpg-connect-agent: No such file or directorySo that's the basic problem and is why you're seeing the "Load failed: 5: Input/output error".
If I run:
$ which gpg-connect-agentthen:
- on an Apple Silicon Mac:
/opt/homebrew/bin/gpg-connect-agent - on an Intel Mac:
/usr/local/bin/gpg-connect-agent
And that's the fix. You go back to the basic plist and replace:
/usr/local/MacGPG2/bin/gpg-connect-agentwith the relevant path from your Apple or Intel Mac.
I understand all of this part ^^^^^^^^^^, but in my case I am using GPG Suite on my mac, which is the recommended way on the Dr. Duh Yubikey guide. With GPG Suite installed, the paths in that file are correct, as you can see below:
ls -l /usr/local/MacGPG2/bin/gpg-connect-agent -rwxr-xr-x 1 root wheel 360064 Sep 26 2024 /usr/local/MacGPG2/bin/gpg-connect-agentwhich gpg-connect-agent /usr/local/MacGPG2/bin/gpg-connect-agentSo I don't think I need to replace anything, since GPG Suite has installed gpg-connect-agent in the same paths according to the plist file.
As for this text below, I do not understand it much. What do you mean the heebies? Are you implying its dangerous, or a security concern? Does this apply to me even if GPG Suite is installed with those paths listed in the plist file?
Next, if I install the modified content at the path:
$HOME/Library/LaunchAgents/gnupg.gpg-agent.plistthen there is no need to "load" it. That's because current macOS senses the addition of a plist dynamically. You should get a notification telling you that a background item has been added to the "Allow in the Background" list in the "Login Items" panel in "System Settings".
However, if you want to remove such an item then I believe that the recommended approach is to unload it before removing:
$ launchctl unload ~/Library/LaunchAgents/gnupg.gpg-agent.plist $ rm ~/Library/LaunchAgents/gnupg.gpg-agent.plistOn the topic of
sudo, don't do that. This is a user launch agent, not a system launch agent.Last thing. As far as I know,
kickstarthas replacedbootstrapfor most situations. It's a bit tricky with user launch agents but, for this situation, the appropriate command would be:$ launchctl kickstart -k gui/$(id -u)/gnupg.gpg-agentThe
id -ureturns your numeric user ID, and the tail end of the path is theLabelfield in the plist.Although I did install
gnupg.gpg-agent.plistwhile I was writing the above, I didn't try the "symlink.plist". I studied it for a while but it gives me the heebies.This is the bit that troubles me:
/bin/ln -sf $HOME/.gnupg/S.gpg-agent.ssh $SSH_AUTH_SOCKFirstly, to the best of my knowledge, the first command argument
S.gpg-agent.sshis a socket that will only exist if the YubiKey has been inserted, unlocked and used. Certainly, none of the socket files is present on my system after a reboot until first use of the YubiKey. That implies any launch agent which assumes the socket always exists will fail when it doesn't.However, let's assume the socket exists:
$ ls -l $HOME/.gnupg/S.gpg-agent.ssh srwx------ 1 user group 0 May 20 16:34 $HOME/.gnupg/S.gpg-agent.sshSecondly, I do not see how a Launch Agent can "see" the
SSH_AUTH_SOCKenvironment variable so, at face value, it should be blank so the command would reduce to:$ /bin/ln -sf $HOME/.gnupg/S.gpg-agent.sshExecuting that results in:
$ ls -l $HOME/.gnupg/S.gpg-agent.ssh lrwxr-xr-x 1 user group 37 May 20 17:17 /$HOME/.gnupg/S.gpg-agent.ssh -> $HOME/.gnupg/S.gpg-agent.sshSo now it's a symlink pointing to itself and the socket is either masked or destroyed (I don't know which). What I do know is that it doesn't work:
$ file $HOME/.gnupg/S.gpg-agent.ssh $HOME/.gnupg/S.gpg-agent.ssh: broken symbolic link to $HOME/.gnupg/S.gpg-agent.sshComputer says no! The only "fix" I've been able to find is to remove that file, reboot, and make the YubiKey do something.
It gets weirder. Let's assume the launch agent can somehow see
SSH_AUTH_SOCK. What should its value be? Hunting around the README:export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)Let's run the meat of that:
$ gpgconf --list-dirs agent-ssh-socket $HOME/.gnupg/S.gpg-agent.sshIn short, as far as I can tell, if we assume the Launch Agent can see
SSH_AUTH_SOCKthe command would become:ln -sf $HOME/.gnupg/S.gpg-agent.ssh $HOME/.gnupg/S.gpg-agent.sshwhich is still a symlink pointing to itself. Thus, irrespective of whether a launch agent can or can't see
SSH_AUTH_SOCK, we still wind up with a masked socket and a broken symlink.See why it gives me the heebies?
Not on my system! Well, not until someone more knowledgeable than me can explain why any of this makes sense.
Hope this helps.
Thanks for any help!
- on an Apple Silicon Mac:
Doh!
According to what is in my GnuPG keychain, I set up my YubiKeys in late 2019. I'm pretty sure the notes I made at the time where what I followed when setting up new computers since then. By that I mean I did not go back to the DrDuh guide and start from scratch, so that may well explain why you have a path with
MacGPG2in the middle while my computers havehomebrew. That'll teach me to make unwarranted MyMac=YourMac assumptions and draw the wrong conclusions! My bad.That said, I could not actually replicate your
Load failed: 5: Input/output errorwhen I did the copy/paste to create the property list and installed it in the LaunchAgents directory, so I started going line by line to see what jumped out, which was the MacGPG2 issue.I've just realised that if I had read the lines before the plist more carefully, I might've twigged that they were relevant to both the
gpg-agent.confand the plist. My bad again.One of the problems with
launchdthings is that, compared withcron, I've never found its debugging facilities to be useful. Withcronon the other hand, any spurious output (which generally means errors) winds up in a local mail message. In theory you can instrument launch agents to redirectstdoutandstderrto files. Although the files get created, I've yet to see anything get written to them, which is exactly what happened when I tried the plist withMacGPG2in situ - nada. Every assistance short of actual help.In retracing my steps, it occurred to me to try to make a deliberate mistake with the copy/paste so that the property list was mal-formed. Just by deleting the last
>in the file and trying aloadI get:Load failed: 5: Input/output error Try running `launchctl bootstrap` as root for richer errors.I can get confirmation of the mal-formed file by running:
$ plutil -p gnupg.gpg-agent.plist gnupg.gpg-agent.plist: Encountered unexpected EOFSo maybe your situation was (and perhaps still is) as simple as that: a typo in the plist.
You included
sudoin the "text below".The
sudocommand means "become root and then do the command". The process of becoming root changes a lot of things about your process environment and doing things as root when it isn't necessary is the cause of many problems. Try this:$ env | sort >user.env ; sudo env | sort >root.env ; diff -y user.env root.envYou'll notice that an awful lot of the environment goes away when you "become root".
If you have a Linux box handy, also try running the same commands there. One of the things that can trap script-writers is
$HOMEdoesn't change on macOS whereas it does change on Linux. It's a tiny difference but quite significant.In the context of a Launch Agent, it actually makes no sense to use
sudo. The idea of a Launch Agent is that it is running on behalf of, and with the privileges of, the user, not root. If something needs root privileges then you need a Launch Daemon.More to come...
Now I'll move to the "heebies" bit.
You know that lawyer's adage about never asking a question unless you already know the answer? That's more or less how I approach computers. I like to have a fair idea of how something is going to behave before I start using it.
When I see a command like this buried in a Launch Agent, I want to know what it does before I consider using it:
/bin/ln -sf $HOME/.gnupg/S.gpg-agent.ssh $SSH_AUTH_SOCKFirst off, Launch Agent plists can't substitute variables. That's OK in this case because the whole command is an argument to
sh -cwhich meansshwill be doing the variable substitution. When I was writing my original reply I ran the command:$ echo $SSH_AUTH_SOCKand got nothing, which is why I concluded that the Launch Agent wouldn't see anything.
I've just run that same command again and now I'm getting:
/private/tmp/com.apple.launchd.zJWr9wHH0R/ListenersThere are two plausible explanations for this. The first is a dumb-head typo by me which I didn't notice when I was writing my earlier reply. The other is that I was running macOS Sonoma 14.7.1 at the time, but have since upgraded to Sequoia 15.5 so maybe that upgrade "fixed" something that I didn't even know was broken. This is a 2019 iMac I'm working on and it's accumulated a lot of software mileage.
Whatever the explanation, I'll expand on what I was thinking then and what I think now.
One bit of context is something I mentioned before:
-
at least on my system,
S.gpg-agent.sshdoesn't exist after a reboot until I "do something" withgpgthat involves the YubiKey (agpg -Kseems to be enough and I infer that's because the private key stubs in the local keychain point to the YubiKey). My assumption is that that's the purpose of this in the first plist:gpg-connect-agent /bye
Anyway, when I was writing my earlier reply, it was in the belief that
SSH_AUTH_SOCKwas null, so the possibilities were:S.gpg-agent.sshdoesn't exist; orS.gpg-agent.sshexists (as a socket).
In each case, the
f(force) inln -sfwill result inS.gpg-agent.sshas a symlink pointing to itself. If the socket existed (case 2), it will be destroyed. If the socket didn't exist (case 1) then whengpgtries to create the socket, it will be blocked. If you attempt to use your YubiKey in these situations, you get a barrage of error messages, none of which actually points you to the true cause. You just have to figure it out.It's one thing to be experimenting and breaking things where you know what you just did and can undo the damage; quite another if this is happening behind the scenes and all you know is your YubiKey doesn't work.
That's what I meant by it giving me the heebies. I didn't see it as dangerous in the sense it could cause permanent damage, or a security concern in the sense that it could lead to a breach of some kind. I meant it in the sense of stuff going wrong where there's very little evidence to lead you towards a solution.
The above was then, so let's move forward to now.
Now that I'm seeing values for
SSH_AUTH_SOCKI can probably assume that the second argument toln -sfwill be non-null. We still really only have two possibilities:S.gpg-agent.ssheither exists (as a socket) or it doesn't.
In either case, the symlink will be created at the
SSH_AUTH_SOCKpath which implicitly destroys the original socket set up by macOS. IfS.gpg-agent.sshexists then any reference to theSSH_AUTH_SOCKpath will find the socket atS.gpg-agent.sshand everything will be sweet.However, if
S.gpg-agent.sshdoes not exist then what was a socket atSSH_AUTH_SOCKwill be a symlink pointing nowhere. My guess is the assumption underlying the two plists is that it doesn't really matter which order they run in (my understanding is the launch order is undefined), it won't take long before the symlink atSSH_AUTH_SOCKwill be pointing toS.gpg-agent.sshwhich, hopefully, is a socket.Unless there is some (small) time window during which the symlink at
SSH_AUTH_SOCKhas been created and whenS.gpg-agent.sshis created, and some key process happens to run in that window that will fail if a socket isn't there, the strategy is probably fairly safe. So not giving me quite so many heebies.That said, if I had been writing something like these plists I would likely have done it as a single Launch Agent pointing to a script supplied via this Dr Duh repo to be installed somewhere like
~/.gnupg. That script would have done its own search forgpg-connect-agent(so nobody had to know Intel vs Apple Silicon), plus had all the necessary error-checking to make sure it didn't do anything risky like create a symlink where the source and target didn't exist. In other words, saying it doesn't give me quite so many heebies is not the same as saying heebee-free.One final observation. I've never had either of these plists or anything remotely like them installed on any Mac. My YubiKeys have worked just fine for over 5 years. And that's because my
$HOME/.gnupg/gpg-agent.confcontains:- for Intel
pinentry-program /usr/local/bin/pinentry-tty - for Apple Silicon
pinentry-program /opt/homebrew/bin/pinentry-tty
The excellent Dr Duh Guide says:
pinentry-tty set as the pinentry program (in gpg-agent.conf) is reported to cause problems with Mutt TUI, because it uses curses; use pinentry-curses or other graphic pinentry program instead.
I don't know what Mutt TUI is so that caution doesn't trouble me. The guide also says (with an implicit contradiction when it comes to
pinentry-curses):Any pinentry program except pinentry-tty or pinentry-curses may be used. This is because local gpg-agent may start headlessly (by systemd without $GPG_TTY set locally telling which tty it is on), thus failed to obtain the pin. Errors on the remote may be misleading saying that there is IO Error. (Yes, internally there is actually an IO Error since it happens when writing to/reading from tty while finding no tty to use, but for end users this is not friendly.)
I've never had a problem like this. As far as I'm concerned, the
ttyPIN entry is perfect. In Terminal I'm running some CLI command which involves the YubiKey and the prompt to unlock the card appears inline exactly where my attention is already focused:Please unlock the card Number: 99 999 999 Holder: My Name Here Counter: 999 PIN:In other words, I don't have some random dialog appearing elsewhere on screen where I have to change focus to the dialog, then change back to the Terminal window.
I know
pinentry-macfollows the Apple "security dialog" norm. It irks me when Apple forces this change of focus on me too.More to the point,
pinentry-ttydoesn't depend on those two plists being installed so none of these issues arise.Still, as I always say: your system, your rules.
Hope this helps.
-
Please see also:
- PR 2025-06-18 gpg-agent.conf better default pinentry-program behaviour #510; and
- Paraphraser/set-gpg-pinentry-program.
I've been mulling over the earlier discussion in this issue. The
bashscript at the above repo is an attempt to deal with the problem of the two launch agents that give me the heebies. With the proposed script, ifpinentry-macis being selected, the result is a single launch agent which invokes a delegate which, in turn, performs basic sanity checking (like "isSSH_AUTH_SOCKdefined?" and "isS.gpg-agent.ssha socket or has it been futzed with in some way?") before just ploughing ahead, and writes log messages where it encounters problems (as opposed to the current launch agents which do zero checking and emit no logs).While solving those problems, I realised that it would be useful to automate the switching of
pinentryprograms and, from that, an appreciation that the existing hardened config didn't have a good list the candidates likely to be encountered by most users - if they were hand-editing the file. I also realised that the default setting was only going to work on Linux. Solving all that is what #510 is geared towards.I considered proposing a separate PR to add this script to the DrDuh scripts folder but it seemed to me it would be better to present it separately for comment and additional testing. Integrating it into DrDuh would involve several changes to the README and I'd rather first get some agreement on the approach, and (hopefully) that it doesn't throw up any situations I haven't foreseen on platforms I'm unable to test on.
In DR.DUH_GUIDE --> SSH --> macOS
I am having some issues with the launchctl commands.
I have created the two files
$HOME/Library/LaunchAgents/gnupg.gpg-agent.plistand$HOME/Library/LaunchAgents/gnupg.gpg-agent-symlink.plist. Then I run the following commands which produce input/output errors:as normal user (no sudo)
With sudo:
Any reason why I'm getting these errors? What can I do to fix this?
Thanks.