I'm trying to start awesome windows manager as a subprocess of ssh-agent. It worked when I used startx (ssh-agent startx). But now I'm trying to make it work under lightdm.

lightdm starts /usr/bin/xinitrcsession-helper:

exec $HOME/.xinitrc


ssh-agent awesome

And what I get is:

  509     1   lightdm         /usr/bin/lightdm
  526   509     Xorg            /usr/lib/Xorg :0 -seat seat0 -auth /run/lightdm/root/:0 -nolisten tcp vt7 -novtswitch
  877   509     lightdm         lightdm --session-child 14 21
 1003   877       xinitrcse       /bin/bash /usr/bin/xinitrcsession-helper
 1028  1003         awesome         awesome
 1029  1028           ssh-a           ssh-agent awesome

And set | grep SSH returns nothing.

Then I start another xterm (ssh-agent xterm) and it works:

 1636     1   xterm           xterm
 1638  1636     bash            bash
 1651  1638       vim             vim
 9435  1651         xterm           xterm
 9447  9435           ssh-a           ssh-agent xterm
 9449  9435           bash            bash
10464  9449             ps              ps -eHo pid,ppid,comm,args
10465  9449             les             less

The strange thing here is that ssh-agent is a child of a program it starts. Can you explain that?

And how do I run awesome so that the programs I start after that could see ssh-agent?

UPD Regarding ssh-agent being a child to the command it runs. That is made to be able to replace command with ssh-agent command. So, ssh-agent forks, and parent execs the command.

UPD My bad, I was using xbindkeys to start xterm, and the former happened to be started before ssh-agent. Like in, xbindkeys && ssh-agent awesome. So, it didn't have SSH_* variables to pass to xterm. Or so is my most probable explanation. When using awesome's builtin facilities to start xterm, environment variables are passed down all right.


In your update you mentioned that you start xterm from xbindkeys and since you run

xbindkeys && ssh-agent awesome

bindkeys will not have the SSH-related environment, and as a consequence of that, xterm won't either.

To solve this, I would suggest

eval "$(ssh-agent)"
xbindkeys && awesome

Now, this would set the variables for both xbindkeys and awesome (which may well be what you need and want), but it would not automatically kill the ssh-agent process when you log out.

For that, you could use (with bash),

eval "$(ssh-agent)"
trap 'eval "$(ssh-agent -k)"' EXIT
xbindkeys && awesome

or something similar. This would call ssh-agent -k which would kill the agent, as soon as that shell exited or was terminated by TERM, HUP or INT.

Running eval on the output of ssh-agent -k would just unset the SSH-variables, and it may not be needed (since the script is about to exit anyway), so the trap may be set up to run just ssh-agent -k >/dev/null instead.

The thing about ssh-agent being a child process of the command that it starts just looks strange.

ssh-agent forks off the actual agent process, and then replaces the original process with that of the command it's supposed to run (using exec()). The result is that the original process (xterm in your second process tree) is the parent of the agent:

 * Fork, and have the parent execute the command, if any, or present
 * the socket data.  The child continues as the authentication agent.
if (D_flag || d_flag) {
            SYSLOG_FACILITY_AUTH, 1);
        format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n";
        printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name,
        printf("echo Agent pid %ld;\n", (long)parent_pid);
        goto skip;
pid = fork();
if (pid == -1) {
if (pid != 0) {         /* Parent - execute the given command. */
        snprintf(pidstrbuf, sizeof pidstrbuf, "%ld", (long)pid);
        if (ac == 0) {
                format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n";
                printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name,
                printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf,
                printf("echo Agent pid %ld;\n", (long)pid);
        if (setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1) == -1 ||
            setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1) == -1) {
        execvp(av[0], av);

(the child process then continues executing the rest of the code)

This allows you to kill the agent without much consequence to the command that you'd want to run, for example.

  • Actually, I've found answers to all my questions. But you've got not a bad answer there. Although I've got my 2 cents to share. I'd still prefer the wrapper solution. Where you put xbindkeys && exec awesome into awesome.sh, and then ssh-agent awesome.sh. And no need for all the trickery with traps, and everything. Regarding traps, from my experience with recent releases of bash, EXIT is enough. – x-yuri Mar 14 '19 at 0:08
  • ...As for ssh-agent ending up being a child, it's unexpected no matter how you look at it. The docs says that ssh-agent executes the command if given. And the most straightforward way is to run it as a subprecess. Also, I've failed to state it clearly, and I've got no authoritative proof link, but most likely the reason for ssh-agent making itself running as a subprocess is to be able to transparently replace command with ssh-agent command. – x-yuri Mar 14 '19 at 0:09
  • 1
    @x-yuri I missed the fact that you were using bash. Other shells may not execute the EXIT trap when terminating due to a signal. I've also added the actual forking code from ssh-agent. – Kusalananda Mar 14 '19 at 6:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.