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 } |