Repository: xonsh/xonsh

Issue:
xonsh builtin - nohup & disown
Currently, when I want to run something in background with `nohup` like:
```
$ nohup py myserver.py -p 8080 &
```
After this, the server should still be running after `exit`. But we have:
```
$ exit
xonsh: there is an unfinished job
-----
[1]+ running: nohup py myserver.py .... (35438)
-----
Type "exit" or press "ctrl-d" again to force quit.
```

A "force quit" would kill the `myserver` process.

The server stay normal if I forcely close the terminal Tab, but it will be great if xonsh recognizes `nohup` "natively".


Discussion:
What we really need is a disown.
I never use disown. Seems similar things: http://unix.stackexchange.com/questions/3886/difference-between-nohup-disown-and
`nohup` does a bunch of piping/signal stuff and is more about POSIX. `disown` does job control stuff and is more about the shell. Together (`nohup mybackground thing & disown`), they allow you to spin off processes.
When using `nohup something &`, an extra `disown` would not be needed I guess.
Strictly it would, since `nohup something` would still be a job to the shell. It's still part of the process group, and most shells track the job until all the processes in the group finish or the user tells it to ignore the job.
Practically, it's not :) At least for bash, nohup made the process (or process group) detached from the controlling terminal, and though it remains in jobs, there's not effects from the shell's exit, since SIGHUP is suppressed by nohup. The link I put above explains all of these details.
Oh, bash no longer blocks exit when there's uncompleted jobs. It used to.

zsh does warn but exits anyway.

Both of them list `nohup` processes as jobs.
Worth mentioning #1246, I guess.
I don't know anything about xonsh after the big subprocess rewrite.  In older versions, this was pretty close to trivial (see [this patch](https://notabug.org/adqm/tako/commit/31fd57b331cd228c5949a7731ee66e363bbcf2ed)), so maybe there is a similarly simple patch here.

I tried just using that patch, and it failed because of some weird Thread-handling thing.  I didn't pursue it any farther than that, but that might be a place to start if someone is interested in putting this in to xonsh.
Old thread, but I just came across xonsh and missed "disown" quite a bit. I have a basic working implementation for this feature which will just require some cleanup and more thorough testing before I submit a PR: https://github.com/ion201/xonsh/commit/d0262cc0189c6215fd1357f76a114eb36d316d91
PR #4090 is only for disown.

For nohup compatibility, it might be as simple as changing this function to send SIGHUP instead of SIGKILL though I don't usually use nohup so have not put much thought into it: https://github.com/xonsh/xonsh/blob/7fe7242/xonsh/jobs.py#L361-L367

Resolving patch:
diff --git a/xonsh/jobs.py b/xonsh/jobs.py
--- a/xonsh/jobs.py
+++ b/xonsh/jobs.py
@@ -76,6 +76,8 @@ def _kill(job):
             stderr=subprocess.STDOUT,
         )
 
+    _hup = _kill  # there is no equivalent of SIGHUP on Windows
+
     def ignore_sigtstp():
         pass
 
@@ -114,6 +116,9 @@ def _continue(job):
     def _kill(job):
         _send_signal(job, signal.SIGKILL)
 
+    def _hup(job):
+        _send_signal(job, signal.SIGHUP)
+
     def ignore_sigtstp():
         signal.signal(signal.SIGTSTP, signal.SIG_IGN)
 
@@ -313,7 +318,7 @@ def add_job(info):
 def clean_jobs():
     """Clean up jobs for exiting shell
 
-    In non-interactive mode, kill all jobs.
+    In non-interactive mode, send SIGHUP to all jobs.
 
     In interactive mode, check for suspended or background jobs, print a
     warning if any exist, and return False. Otherwise, return True.
@@ -335,7 +340,7 @@ def clean_jobs():
                 # part of the last command and is now being called again
                 # immediately. Kill jobs and exit without reminder about
                 # unfinished jobs in this case.
-                kill_all_jobs()
+                hup_all_jobs()
             else:
                 if len(XSH.all_jobs) > 1:
                     msg = "there are unfinished jobs"
@@ -357,18 +362,18 @@ def clean_jobs():
                 jobs_clean = False
                 _last_exit_time = time.time()
     else:
-        kill_all_jobs()
+        hup_all_jobs()
 
     return jobs_clean
 
 
-def kill_all_jobs():
+def hup_all_jobs():
     """
-    Send SIGKILL to all child processes (called when exiting xonsh).
+    Send SIGHUP to all child processes (called when exiting xonsh).
     """
     _clear_dead_jobs()
     for job in XSH.all_jobs.values():
-        _kill(job)
+        _hup(job)
 
 
 def jobs(args, stdin=None, stdout=sys.stdout, stderr=None):


Test patch: