src/Pure/General/ssh.scala
changeset 76147 75f0fc965539
parent 76145 a6bdf4b889ca
child 76148 769ebb139a32
equal deleted inserted replaced
76146:a64f3496d93a 76147:75f0fc965539
    52       (if (user.nonEmpty) List(entry("User", user)) else Nil) :::
    52       (if (user.nonEmpty) List(entry("User", user)) else Nil) :::
    53       (if (control_master) List("ControlMaster=yes", "ControlPersist=yes") else Nil) :::
    53       (if (control_master) List("ControlMaster=yes", "ControlPersist=yes") else Nil) :::
    54       (if (control_path.nonEmpty) List("ControlPath=" + control_path) else Nil)
    54       (if (control_path.nonEmpty) List("ControlPath=" + control_path) else Nil)
    55     }
    55     }
    56 
    56 
    57     def make_command(command: String, config: List[String]): String =
    57     def option(entry: String): String = "-o " + Bash.string(entry)
    58       Bash.string(command) + " " + config.map(entry => "-o " + Bash.string(entry)).mkString(" ")
    58     def option(x: String, y: String): String = option(entry(x, y))
       
    59     def option(x: String, y: Int): String = option(entry(x, y))
       
    60     def option(x: String, y: Boolean): String = option(entry(x, y))
       
    61 
       
    62     def command(command: String, config: List[String]): String =
       
    63       Bash.string(command) + config.map(entry => " " + option(entry)).mkString
    59   }
    64   }
    60 
    65 
    61   def sftp_string(str: String): String = {
    66   def sftp_string(str: String): String = {
    62     val special = Set(' ', '*', '?', '{', '}')
    67     val special = Set(' ', '*', '?', '{', '}')
    63     if (str.exists(special)) {
    68     if (str.exists(special)) {
    82     multiplex: Boolean = !Platform.is_windows
    87     multiplex: Boolean = !Platform.is_windows
    83   ): Session = {
    88   ): Session = {
    84     val (control_master, control_path) =
    89     val (control_master, control_path) =
    85       if (multiplex) (true, Isabelle_System.tmp_file("ssh_socket", initialized = false).getPath)
    90       if (multiplex) (true, Isabelle_System.tmp_file("ssh_socket", initialized = false).getPath)
    86       else (false, "")
    91       else (false, "")
    87 
       
    88     new Session(options, host, port, user, control_master, control_path)
    92     new Session(options, host, port, user, control_master, control_path)
    89   }
    93   }
    90 
    94 
    91   class Session private[SSH](
    95   class Session private[SSH](
    92     val options: Options,
    96     val options: Options,
   104     override def toString: String = user_prefix + host + port_suffix
   108     override def toString: String = user_prefix + host + port_suffix
   105     override def hg_url: String = "ssh://" + toString + "/"
   109     override def hg_url: String = "ssh://" + toString + "/"
   106     override def rsync_prefix: String = user_prefix + host + ":"
   110     override def rsync_prefix: String = user_prefix + host + ":"
   107 
   111 
   108 
   112 
   109     /* ssh commands */
   113     /* local ssh commands */
   110 
   114 
   111     def run_command(command: String,
   115     def run_command(command: String,
   112       master: Boolean = false,
   116       master: Boolean = false,
   113       opts: String = "",
   117       opts: String = "",
   114       args: String = "",
   118       args: String = "",
   118     ): Process_Result = {
   122     ): Process_Result = {
   119       val config =
   123       val config =
   120         Config.make(options, port = port, user = user,
   124         Config.make(options, port = port, user = user,
   121           control_master = master, control_path = control_path)
   125           control_master = master, control_path = control_path)
   122       val cmd =
   126       val cmd =
   123         Config.make_command(command, config) +
   127         Config.command(command, config) +
   124         (if (opts.nonEmpty) " " + opts else "") +
   128         (if (opts.nonEmpty) " " + opts else "") +
   125         (if (args.nonEmpty) " -- " + args else "")
   129         (if (args.nonEmpty) " -- " + args else "")
   126       Isabelle_System.bash(cmd, progress_stdout = progress_stdout,
   130       Isabelle_System.bash(cmd, progress_stdout = progress_stdout,
   127         progress_stderr = progress_stderr, strict = strict)
   131         progress_stderr = progress_stderr, strict = strict)
   128     }
   132     }
   249       remote_host: String = "localhost",
   253       remote_host: String = "localhost",
   250       local_port: Int = 0,
   254       local_port: Int = 0,
   251       local_host: String = "localhost",
   255       local_host: String = "localhost",
   252       ssh_close: Boolean = false
   256       ssh_close: Boolean = false
   253     ): Port_Forwarding = {
   257     ): Port_Forwarding = {
   254       if (control_path.isEmpty) error("SSH port forwarding requires multiplexing")
       
   255 
       
   256       val port = if (local_port > 0) local_port else Isabelle_System.local_port()
   258       val port = if (local_port > 0) local_port else Isabelle_System.local_port()
   257 
   259 
   258       val string = List(local_host, port, remote_host, remote_port).mkString(":")
   260       val forward = List(local_host, port, remote_host, remote_port).mkString(":")
   259       run_ssh(opts = "-L " + Bash.string(string) + " -O forward").check
   261       val forward_option = "-L " + Bash.string(forward)
       
   262 
       
   263       val cancel: () => Unit =
       
   264         if (control_path.nonEmpty) {
       
   265           run_ssh(opts = forward_option + " -O forward").check
       
   266           () => run_ssh(opts = forward_option + " -O cancel")  // permissive
       
   267         }
       
   268         else error("SSH port forwarding requires multiplexing")
       
   269 
       
   270       val shutdown_hook =
       
   271         Isabelle_System.create_shutdown_hook { cancel() }
   260 
   272 
   261       new Port_Forwarding(host, port, remote_host, remote_port) {
   273       new Port_Forwarding(host, port, remote_host, remote_port) {
   262         override def toString: String = string
   274         override def toString: String = forward
   263         override def close(): Unit = {
   275         override def close(): Unit = {
   264           run_ssh(opts = "-L " + Bash.string(string) + " -O cancel").check
   276           cancel()
       
   277           Isabelle_System.remove_shutdown_hook(shutdown_hook)
   265           if (ssh_close) ssh.close()
   278           if (ssh_close) ssh.close()
   266         }
   279         }
   267       }
   280       }
   268     }
   281     }
   269   }
   282   }