6 |
6 |
7 package isabelle |
7 package isabelle |
8 |
8 |
9 |
9 |
10 import com.jcraft.jsch.{JSch, Logger => JSch_Logger, Session => JSch_Session, |
10 import com.jcraft.jsch.{JSch, Logger => JSch_Logger, Session => JSch_Session, |
11 OpenSSHConfig, UserInfo, ChannelExec, ChannelSftp} |
11 OpenSSHConfig, UserInfo, Channel => JSch_Channel, ChannelExec, ChannelSftp} |
12 |
12 |
13 |
13 |
14 object SSH |
14 object SSH |
15 { |
15 { |
16 /* init */ |
16 /* init */ |
17 |
17 |
18 def init(config_dir: Path = Path.explode("~/.ssh"), |
18 def init(options: Options): SSH = |
19 config_file: Path = Path.explode("~/.ssh/config"), |
|
20 identity_files: List[Path] = |
|
21 List("~/.ssh/id_dsa", "~/.ssh/id_ecdsa", "~/.ssh/id_rsa").map(Path.explode(_))): SSH = |
|
22 { |
19 { |
|
20 val config_dir = Path.explode(options.string("ssh_config_dir")) |
23 if (!config_dir.is_dir) error("Bad ssh config directory: " + config_dir) |
21 if (!config_dir.is_dir) error("Bad ssh config directory: " + config_dir) |
24 |
22 |
25 val jsch = new JSch |
23 val jsch = new JSch |
26 |
24 |
|
25 val config_file = Path.explode(options.string("ssh_config_file")) |
27 if (config_file.is_file) |
26 if (config_file.is_file) |
28 jsch.setConfigRepository(OpenSSHConfig.parseFile(File.platform_path(config_file))) |
27 jsch.setConfigRepository(OpenSSHConfig.parseFile(File.platform_path(config_file))) |
29 |
28 |
30 val known_hosts = config_dir + Path.explode("known_hosts") |
29 val known_hosts = config_dir + Path.explode("known_hosts") |
31 if (!known_hosts.is_file) known_hosts.file.createNewFile |
30 if (!known_hosts.is_file) known_hosts.file.createNewFile |
32 jsch.setKnownHosts(File.platform_path(known_hosts)) |
31 jsch.setKnownHosts(File.platform_path(known_hosts)) |
33 |
32 |
|
33 val identity_files = |
|
34 Library.space_explode(':', options.string("ssh_identity_files")).map(Path.explode(_)) |
34 for (identity_file <- identity_files if identity_file.is_file) |
35 for (identity_file <- identity_files if identity_file.is_file) |
35 jsch.addIdentity(File.platform_path(identity_file)) |
36 jsch.addIdentity(File.platform_path(identity_file)) |
36 |
37 |
37 new SSH(jsch) |
38 new SSH(options, jsch) |
38 } |
39 } |
|
40 |
|
41 def connect_timeout(options: Options): Int = |
|
42 options.seconds("ssh_connect_timeout").ms.toInt |
39 |
43 |
40 |
44 |
41 /* logging */ |
45 /* logging */ |
42 |
46 |
43 def logging(verbose: Boolean = true, debug: Boolean = false) |
47 def logging(verbose: Boolean = true, debug: Boolean = false) |
71 def promptYesNo(msg: String): Boolean = false |
75 def promptYesNo(msg: String): Boolean = false |
72 def showMessage(msg: String): Unit = Output.writeln(msg) |
76 def showMessage(msg: String): Unit = Output.writeln(msg) |
73 } |
77 } |
74 |
78 |
75 |
79 |
76 /* remote command execution */ |
80 /* channel: exec, sftp etc. */ |
77 |
81 |
78 class Exec private [SSH](val session: Session, val channel: ChannelExec) |
82 class Channel[C <: JSch_Channel] private[SSH](val session: Session, |
|
83 val kind: String, val channel_options: Options, val channel: C) |
79 { |
84 { |
80 override def toString: String = "exec " + session.toString |
85 override def toString: String = kind + " " + session.toString |
81 |
86 |
82 def close { channel.disconnect } |
87 def close { channel.disconnect } |
83 } |
88 } |
84 |
89 |
85 |
90 |
86 /* session */ |
91 /* session */ |
87 |
92 |
88 class Session private[SSH](val session: JSch_Session) |
93 class Session private[SSH](val session_options: Options, val session: JSch_Session) |
89 { |
94 { |
90 override def toString: String = |
95 override def toString: String = |
91 (if (session.getUserName == null) "" else session.getUserName + "@") + |
96 (if (session.getUserName == null) "" else session.getUserName + "@") + |
92 (if (session.getHost == null) "" else session.getHost) + |
97 (if (session.getHost == null) "" else session.getHost) + |
93 (if (session.getPort == 22) "" else ":" + session.getPort) + |
98 (if (session.getPort == 22) "" else ":" + session.getPort) + |
94 (if (session.isConnected) "" else " (disconnected)") |
99 (if (session.isConnected) "" else " (disconnected)") |
95 |
100 |
96 def close { session.disconnect } |
101 def close { session.disconnect } |
97 |
102 |
98 def exec(command: String, connect_timeout: Time = Time.seconds(60)): Exec = |
103 def exec(command: String, options: Options = session_options): Channel[ChannelExec] = |
99 { |
104 { |
100 val channel = session.openChannel("exec").asInstanceOf[ChannelExec] |
105 val kind = "exec" |
|
106 val channel = session.openChannel(kind).asInstanceOf[ChannelExec] |
101 channel.setCommand(command) |
107 channel.setCommand(command) |
102 channel.connect(connect_timeout.ms.toInt) |
|
103 |
108 |
104 new Exec(this, channel) |
109 channel.connect(connect_timeout(options)) |
|
110 new Channel(this, kind, options, channel) |
105 } |
111 } |
106 |
112 |
107 def channel_sftp: ChannelSftp = |
113 def sftp(options: Options = session_options): Channel[ChannelSftp] = |
108 session.openChannel("sftp").asInstanceOf[ChannelSftp] |
114 { |
|
115 val kind = "sftp" |
|
116 val channel = session.openChannel(kind).asInstanceOf[ChannelSftp] |
|
117 |
|
118 channel.connect(connect_timeout(options)) |
|
119 new Channel(this, kind, options, channel) |
|
120 } |
109 } |
121 } |
110 } |
122 } |
111 |
123 |
112 class SSH private(val jsch: JSch) |
124 class SSH private(val options: Options, val jsch: JSch) |
113 { |
125 { |
114 def open_session(host: String, port: Int = 22, user: String = null, |
126 def open_session(host: String, port: Int = 22, user: String = null): SSH.Session = |
115 connect_timeout: Time = Time.seconds(60), |
|
116 compression: Boolean = true): SSH.Session = |
|
117 { |
127 { |
118 val session = jsch.getSession(user, host, port) |
128 val session = jsch.getSession(user, host, port) |
119 |
129 |
120 session.setUserInfo(SSH.No_User_Info) |
130 session.setUserInfo(SSH.No_User_Info) |
121 session.setConfig("MaxAuthTries", "3") |
131 session.setConfig("MaxAuthTries", "3") |
122 |
132 |
123 if (compression) { |
133 if (options.bool("ssh_compression")) { |
124 session.setConfig("compression.s2c", "zlib@openssh.com,zlib,none") |
134 session.setConfig("compression.s2c", "zlib@openssh.com,zlib,none") |
125 session.setConfig("compression.c2s", "zlib@openssh.com,zlib,none") |
135 session.setConfig("compression.c2s", "zlib@openssh.com,zlib,none") |
126 session.setConfig("compression_level", "9") |
136 session.setConfig("compression_level", "9") |
127 } |
137 } |
128 |
138 |
129 session.connect(connect_timeout.ms.toInt) |
139 session.connect(SSH.connect_timeout(options)) |
130 |
140 new SSH.Session(options, session) |
131 new SSH.Session(session) |
|
132 } |
141 } |
133 } |
142 } |