For some time running:

nohup some-job &

causes ssh to hang. It didn't used to when I used telnet
instead. Some research confirmed that ssh (openSSH in
particular) will not close the session if any processes
are still connected to the tty. Although patches to
ssh are posted all over the internet, the maintainers,
correctly in my opinion, claim it was telnetd that didn't
work correctly and the current behavior is correct. They
think we should run jobs this way:

nohup some-job exit

Which will not cause the session to hang.

Thinking about this, I think that the POSIX nohup
definition is based on the assumption of the old
telnetd behavior, which closed the session when the
shell exited, regardless of what other processes
may still be connected.

I read somplace that POSIX is currently being revised
for the next version of the standard. It seems to me
that a simple change to nohup to close stdin, or
redirected from /dev/null would solve the problem.
(A flag/environment variable could be added to restore
the original behavior.)

In fact I have made such a change to the GNU version of
nohup, and it works great. I think this would be a
proper change; either nohup shouldn't handle redirection
at all or it should do it right.

Does Solaris implementation of sshd have this problem?
Is there some standard mailing list or wiki or someplace
more appropriate to discuss this? Does anyone care?


----------------modified GNU nohup.c-------------------

/* nohup -- run a command immume to hangups, with output to a non-tty
Copyright (C) 2003, 2004 Free Software Foundation, Inc.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */

/* Written by Jim Meyering */

/* Modified 5/2005 by Wayne Pollock. The change
is to redirect stdin (if a tty) from /dev/null. Rational: OpenSSH
and others work differently than old telnetd did, in that when the
shell closes the session is closed even if some process still has
the connection open for I/O. Dispite requests and posted patches
SSH keeps the session open if any process is attached. This is
proper but it does mean utilities such as nohup are broken, in that
they don't completely redirect terminal input and output.
Future work: get POSIX to update all nohup, and add a flag and/or
environment variable to restore old behavior. */


#include "system.h"

#include "error.h"
#include "long-options.h"
#include "path-concat.h"
#include "quote.h"
#include "cloexec.h"

#define PROGRAM_NAME "nohup"

#define AUTHORS "Jim Meyering"

/* Exit statuses. */
/* `nohup' itself failed. */

char *program_name;

usage (int status)
if (status != EXIT_SUCCESS)
fprintf (stderr, _("Try `%s --help' for more information.\n"),
printf (_("\
Usage: %s COMMAND [ARG]...\n\
or: %s OPTION\n\
program_name, program_name);

fputs (_("\
Run COMMAND, ignoring hangup signals.\n\
"), stdout);
printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
exit (status);

main (int argc, char **argv)
int fd;
int saved_stderr_fd = -1;
int stderr_isatty;

initialize_main (&argc, &argv);
program_name = argv[0];
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);

initialize_exit_failure (NOHUP_FAILURE);
atexit (close_stdout);

parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
usage, AUTHORS, (char const *) NULL);

/* The above handles --help and --version.
Now, handle `--'. */
if (argc > 1 && STREQ (argv[1], "--"))

if (argc <= 1)
error (0, 0, _("too few arguments"));

/* If standard output is a tty, redirect it (appending) to a file.
First try nohup.out, then $HOME/nohup.out. */
if (isatty (STDOUT_FILENO))
char *in_home = NULL;
char const *file = "nohup.out";
int flags = O_CREAT | O_WRONLY | O_APPEND;
mode_t mode = S_IRUSR | S_IWUSR;
int saved_errno;

fd = open (file, flags, mode);
if (fd == -1)
saved_errno = errno;
in_home = path_concat (getenv ("HOME"), file, NULL);
fd = open (in_home, flags, mode);
if (fd == -1)
int saved_errno2 = errno;
error (0, saved_errno, _("failed to open %s"), quote (file));
error (0, saved_errno2, _("failed to open %s"), quote (in_home));
file = in_home;

/* Redirect standard output to the file. */
if (dup2 (fd, STDOUT_FILENO) == -1)
error (NOHUP_FAILURE, errno, _("failed to redirect standard output"));

error (0, 0, _("appending output to %s"), quote (file));
if (in_home)
free (in_home);

/* If stderr is on a tty, redirect it to stdout. */
if ((stderr_isatty = isatty (STDERR_FILENO)))
/* Save a copy of stderr before redirecting, so we can use the original
if execve fails. It's no big deal if this dup fails. It might
not change anything, and at worst, it'll lead to suppression of
the post-failed-execve diagnostic. */
saved_stderr_fd = dup (STDERR_FILENO);

if (saved_stderr_fd != -1
&& ! set_cloexec_flag (saved_stderr_fd, true))
error (NOHUP_FAILURE, errno,
_("failed to set the copy of stderr to close on exec"));

if (dup2 (fd, STDERR_FILENO) == -1)
error (NOHUP_FAILURE, errno, _("failed to redirect standard error"));

/* modified code starts here: */
/* if stdin is on a tty, redirect it from /dev/null. */
if ( isatty( STDIN_FILENO ) )
char const *file = "/dev/null";
int flags = O_RDONLY;

fd = open (file, flags);
if (fd == -1)
int saved_errno = errno;
error (0, saved_errno, _("failed to open %s"), quote (file));

if (dup2 (fd, STDIN_FILENO) == -1)
error (NOHUP_FAILURE, errno, _("failed to redirect standard input"));
/* end of modification */

/* Ignore hang-up signals. */
struct sigaction sigact;
sigact.sa_handler = SIG_IGN;
sigemptyset (&sigact.sa_mask);
sigact.sa_flags = 0;
sigaction (SIGHUP, &sigact, NULL);
signal (SIGHUP, SIG_IGN);

int exit_status;
int saved_errno;
char **cmd = argv + 1;

execvp (*cmd, cmd);
exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
saved_errno = errno;

/* The execve failed. Output a diagnostic to stderr only if:
- stderr was initially redirected to a non-tty, or
- stderr was initially directed to a tty, and we've
just dup2'd it to point back to that same tty.
In other words, output the diagnostic if possible, but not if
it'd go to nohup.out. */
if ( ! stderr_isatty
|| (saved_stderr_fd != -1
&& dup2 (saved_stderr_fd, STDERR_FILENO) != -1))
error (0, saved_errno, _("cannot run command %s"), quote (*cmd));

exit (exit_status);