From ae2899baae0c871873a99dffcf2533fc87a82152 Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Sat, 27 Jun 2020 10:34:52 -0700 Subject: [PATCH] Notify initiating client before shutdown When a remote client sent the command `shutdown` through the shell, the client would log an error and exit with a nonzero exit code because before shutting down, the server would notify the client that it was disconnecting it due to shutdown. In this scenario, we actually do not want the client to log an error since they initiated the shutdown, so before doing the full shutdown, we shutdown the client that inititated the shutdown with the flag that tells the client not to log the shutdown or return a nonzero exit code. --- .../scala/sbt/internal/client/NetworkClient.scala | 2 ++ .../src/main/scala/sbt/internal/CommandExchange.scala | 11 ++++++++--- .../scala/sbt/internal/server/NetworkChannel.scala | 9 +++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala b/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala index 233ca0526..ea42ec597 100644 --- a/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala +++ b/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala @@ -166,6 +166,8 @@ class NetworkClient( console.appendLog(Level.Error, "sbt server disconnected") exitClean.set(false) } + } else { + console.appendLog(Level.Info, "sbt server disconnected") } stdinBytes.offer(-1) Option(inputThread.get).foreach(_.close()) diff --git a/main/src/main/scala/sbt/internal/CommandExchange.scala b/main/src/main/scala/sbt/internal/CommandExchange.scala index a4e048edc..aa35b0cf6 100644 --- a/main/src/main/scala/sbt/internal/CommandExchange.scala +++ b/main/src/main/scala/sbt/internal/CommandExchange.scala @@ -422,9 +422,14 @@ private[sbt] final class CommandExchange { case _ => mt.channel.prompt(ConsolePromptEvent(lastState.get)) } commandQueue.add(Exec(t, None, None)) - case "exit" => exit(mt) - case "shutdown" => shutdown(mt.channel.name) - case _ => + case "exit" => exit(mt) + case "shutdown" => + channels.find(_.name == mt.channel.name) match { + case Some(c: NetworkChannel) => c.shutdown(false) + case _ => + } + shutdown(mt.channel.name) + case _ => } } if (!isStopped.get) impl() diff --git a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala index 7259478ef..4fa5a37a5 100644 --- a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala +++ b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala @@ -549,6 +549,15 @@ final class NetworkChannel( shutdown(true) } import sjsonnew.BasicJsonProtocol.BooleanJsonFormat + + /** + * Closes down the channel. Before closing the socket, it sends a notification to + * the client to shutdown. If the client initiated the shutdown, we don't want the + * client to display an error or return a non-zero exit code so we send it a + * notification that tells it whether or not to log the shutdown. This can't + * easily be done client side because when the client is in interactive session, + * it doesn't know commands it has sent to the server. + */ override def shutdown(logShutdown: Boolean): Unit = { terminal.close() StandardMain.exchange.removeChannel(this)