title: How do I properly open a SSH tunnel in the background?
body: <p>I wish to tunnel a port over SSH from a remote host. I wish to implement this as an <a href="https://oclif.io/" rel="noreferrer"><code>oclif</code></a> plugin; I want the user experience to look like this:</p>
  <pre><code>laptop$ give-jupyter
  http://localhost:4040/
  laptop$ kill-jupyter
  laptop$
  </code></pre>
  <p>...and that should be relatively straightforward; I just* need to maintain a pidfile, right? Something like:</p>
  <pre class="lang-js prettyprint-override"><code>import child_process from 'child_process';
  const childProcess = child_process.spawn('ssh', [/* flags */], {detached: true, stdio: 'ignore'});
  childProcess.unref();
  writeToSomePidfile({childProcess.pid);
  </code></pre>
  <p>However, all of this is basically besides the point. The problem is I have to figure those flags out! Well, okay; this works:</p>
  <pre class="lang-sh prettyprint-override"><code>laptop$ ssh machine -L 4040:localhost:4040
  machine$ 
  </code></pre>
  <p>...however, it also opens a shell on the remote end. No problem, <code>man ssh</code> says that's what <code>-L</code> is for:</p>
  <pre class="lang-sh prettyprint-override"><code>laptop$ ssh machine -LN 4040:localhost:4040
  
  </code></pre>
  <p>That's great, but it's now taking my user's shell hostage. Fine, let's just send the process to the background:</p>
  <pre class="lang-sh prettyprint-override"><code>laptop$ ssh machine -LN 4040:localhost:4040 &amp;
  laptop$ f
  f: file not found
  laptop$ fg
  ^C
  </code></pre>
  <p>The background version of <code>ssh</code> enters a race condition with the shell over <code>STDIN</code>, and everything is positively terrible. Fine, <code>man ssh</code> says that's what <code>-n</code> is for:</p>
  <pre class="lang-sh prettyprint-override"><code>laptop$ ssh machine -nLN 4040:localhost:4040 &amp;
  laptop$Job 1, 'ssh machine -nNL 40…' has ended
  </code></pre>
  <p>...well, that's just great: <code>ssh</code> now quits immediately, and so does the tunnel.</p>
  <p>SSH mentions <code>-f</code> should enable some sort of background mode, but <code>ssh -fnN</code> doesn't do it either; <code>ssh</code> quits immediately still.</p>
  <p>If I can't have nice things, maybe I can approximate them with a command that will run forever even with no STDIN. Server Fault <a href="https://serverfault.com/questions/358866/create-unix-named-socket-from-the-command-line">suggests</a>:</p>
  <pre class="lang-sh prettyprint-override"><code>laptop$ ssh machine -nL 4040:localhost:4040 tail -f /dev/null &amp;
  laptop$
  </code></pre>
  <p>Still no good?! Fine:</p>
  <pre class="lang-sh prettyprint-override"><code>laptop$ ssh machine -nL 4040:localhost:4040 sleep infinity &amp;
  laptop$
  </code></pre>
  <p>That seems to work at the low, low cost of one tiny process, a far sight better than other iterations I had tried while writing this questions, mostly involving <code>yes</code>...</p>
  <p>Is there however a... less kludgy way to run an SSH tunnel in the background? Bonus points: I need this to work on OSX laptops too...</p>