diff --git a/lib/jekyll/commands/serve.rb b/lib/jekyll/commands/serve.rb index 4600130f..e54542ff 100644 --- a/lib/jekyll/commands/serve.rb +++ b/lib/jekyll/commands/serve.rb @@ -274,12 +274,19 @@ module Jekyll def boot_or_detach(server, opts) if opts["detach"] pid = Process.fork do + # Detach the process from controlling terminal + $stdin.reopen("/dev/null", "r") + $stdout.reopen("/dev/null", "w") + $stderr.reopen("/dev/null", "w") server.start end Process.detach(pid) Jekyll.logger.info "Server detached with pid '#{pid}'.", "Run `pkill -f jekyll' or `kill -9 #{pid}' to stop the server." + + # Exit without running `at_exit` inherited by the forked process. + Process.exit! 0 else t = Thread.new { server.start } trap("INT") { server.shutdown } diff --git a/test/test_commands_serve.rb b/test/test_commands_serve.rb index fe9d972c..b7828b01 100644 --- a/test/test_commands_serve.rb +++ b/test/test_commands_serve.rb @@ -313,4 +313,36 @@ class TestCommandsServe < JekyllUnitTest @merc.execute(:serve, "watch" => false) end end + + context "using --detach" do + setup do + skip_if_windows "fork is not supported on Windows" + skip("fork is not supported by JRuby") if jruby? + + @temp_dir = Dir.mktmpdir("jekyll_serve_detach_test") + @destination = File.join(@temp_dir, "_site") + Dir.mkdir(@destination) || flunk("Could not make directory #{@destination}") + end + + teardown do + FileUtils.remove_entry_secure(@temp_dir, true) + end + + should "fork into daemon process" do + process, output = Jekyll::Utils::Exec.run("jekyll", "serve", + "--source", @temp_dir, + "--dest", @destination, + "--detach") + + re = %r!Server detached with pid '(?\d+)'! + + assert_match re, output + assert_equal 0, process.exitstatus + + pid = re.match(output)["pid"].to_i + assert pid > 1 + + Process.kill 9, pid + end + end end