101 def promptYesNo(msg: String): Boolean = false |
101 def promptYesNo(msg: String): Boolean = false |
102 def showMessage(msg: String): Unit = Output.writeln(msg) |
102 def showMessage(msg: String): Unit = Output.writeln(msg) |
103 } |
103 } |
104 |
104 |
105 |
105 |
106 /* channel */ |
|
107 |
|
108 class Channel[C <: JSch_Channel] private[SSH]( |
|
109 val session: Session, val kind: String, val channel: C) |
|
110 { |
|
111 override def toString: String = kind + " " + session.toString |
|
112 |
|
113 def close() { channel.disconnect } |
|
114 } |
|
115 |
|
116 |
|
117 /* Sftp channel */ |
106 /* Sftp channel */ |
118 |
107 |
119 type Attrs = SftpATTRS |
108 type Attrs = SftpATTRS |
120 |
109 |
121 sealed case class Dir_Entry(name: Path, attrs: Attrs) |
110 sealed case class Dir_Entry(name: Path, attrs: Attrs) |
122 { |
111 { |
123 def is_file: Boolean = attrs.isReg |
112 def is_file: Boolean = attrs.isReg |
124 def is_dir: Boolean = attrs.isDir |
113 def is_dir: Boolean = attrs.isDir |
125 } |
114 } |
126 |
115 |
127 class Sftp private[SSH](session: Session, kind: String, channel: ChannelSftp) |
|
128 extends Channel[ChannelSftp](session, kind, channel) |
|
129 { |
|
130 channel.connect(connect_timeout(session.options)) |
|
131 |
|
132 val settings: Map[String, String] = |
|
133 { |
|
134 val home = channel.getHome |
|
135 Map("HOME" -> home, "USER_HOME" -> home) |
|
136 } |
|
137 def expand_path(path: Path): Path = path.expand_env(settings) |
|
138 def remote_path(path: Path): String = expand_path(path).implode |
|
139 |
|
140 def chmod(permissions: Int, path: Path): Unit = channel.chmod(permissions, remote_path(path)) |
|
141 def mv(path1: Path, path2: Path): Unit = channel.rename(remote_path(path1), remote_path(path2)) |
|
142 def rm(path: Path): Unit = channel.rm(remote_path(path)) |
|
143 def mkdir(path: Path): Unit = channel.mkdir(remote_path(path)) |
|
144 def rmdir(path: Path): Unit = channel.rmdir(remote_path(path)) |
|
145 |
|
146 def stat(path: Path): Option[Dir_Entry] = |
|
147 try { Some(Dir_Entry(expand_path(path), channel.stat(remote_path(path)))) } |
|
148 catch { case _: SftpException => None } |
|
149 |
|
150 def is_file(path: Path): Boolean = stat(path).map(_.is_file) getOrElse false |
|
151 def is_dir(path: Path): Boolean = stat(path).map(_.is_dir) getOrElse false |
|
152 |
|
153 def mkdirs(path: Path): Unit = |
|
154 if (!is_dir(path)) { |
|
155 session.execute( |
|
156 "perl -e \"use File::Path make_path; make_path('" + remote_path(path) + "');\"") |
|
157 if (!is_dir(path)) error("Failed to create directory: " + quote(remote_path(path))) |
|
158 } |
|
159 |
|
160 def read_dir(path: Path): List[Dir_Entry] = |
|
161 { |
|
162 val dir = channel.ls(remote_path(path)) |
|
163 (for { |
|
164 i <- (0 until dir.size).iterator |
|
165 a = dir.get(i).asInstanceOf[AnyRef] |
|
166 name = Untyped.get[String](a, "filename") |
|
167 attrs = Untyped.get[Attrs](a, "attrs") |
|
168 if name != "." && name != ".." |
|
169 } yield Dir_Entry(Path.basic(name), attrs)).toList |
|
170 } |
|
171 |
|
172 def find_files(root: Path, pred: Dir_Entry => Boolean = _ => true): List[Dir_Entry] = |
|
173 { |
|
174 def find(dir: Path): List[Dir_Entry] = |
|
175 read_dir(dir).flatMap(entry => |
|
176 { |
|
177 val file = dir + entry.name |
|
178 if (entry.is_dir) find(file) |
|
179 else if (pred(entry)) List(entry.copy(name = file)) |
|
180 else Nil |
|
181 }) |
|
182 find(root) |
|
183 } |
|
184 |
|
185 def open_input(path: Path): InputStream = channel.get(remote_path(path)) |
|
186 def open_output(path: Path): OutputStream = channel.put(remote_path(path)) |
|
187 |
|
188 def read_file(path: Path, local_path: Path): Unit = |
|
189 channel.get(remote_path(path), File.platform_path(local_path)) |
|
190 def read_bytes(path: Path): Bytes = using(open_input(path))(Bytes.read_stream(_)) |
|
191 def read(path: Path): String = using(open_input(path))(File.read_stream(_)) |
|
192 |
|
193 def write_file(path: Path, local_path: Path): Unit = |
|
194 channel.put(File.platform_path(local_path), remote_path(path)) |
|
195 def write_bytes(path: Path, bytes: Bytes): Unit = |
|
196 using(open_output(path))(bytes.write_stream(_)) |
|
197 def write(path: Path, text: String): Unit = |
|
198 using(open_output(path))(stream => Bytes(text).write_stream(stream)) |
|
199 } |
|
200 |
|
201 |
116 |
202 /* exec channel */ |
117 /* exec channel */ |
203 |
118 |
204 private val exec_wait_delay = Time.seconds(0.3) |
119 private val exec_wait_delay = Time.seconds(0.3) |
205 |
120 |
206 class Exec private[SSH](session: Session, kind: String, channel: ChannelExec) |
121 class Exec private[SSH](session: Session, channel: ChannelExec) |
207 extends Channel[ChannelExec](session, kind, channel) |
122 { |
208 { |
123 override def toString: String = "exec " + session.toString |
|
124 |
|
125 def close() { channel.disconnect } |
|
126 |
209 def kill(signal: String) { channel.sendSignal(signal) } |
127 def kill(signal: String) { channel.sendSignal(signal) } |
210 |
128 |
211 val exit_status: Future[Int] = |
129 val exit_status: Future[Int] = |
212 Future.thread("ssh_wait") { |
130 Future.thread("ssh_wait") { |
213 while (!channel.isClosed) Thread.sleep(exec_wait_delay.ms) |
131 while (!channel.isClosed) Thread.sleep(exec_wait_delay.ms) |
288 (if (session.getUserName == null) "" else session.getUserName + "@") + |
206 (if (session.getUserName == null) "" else session.getUserName + "@") + |
289 (if (session.getHost == null) "" else session.getHost) + |
207 (if (session.getHost == null) "" else session.getHost) + |
290 (if (session.getPort == default_port) "" else ":" + session.getPort) + |
208 (if (session.getPort == default_port) "" else ":" + session.getPort) + |
291 (if (session.isConnected) "" else " (disconnected)") |
209 (if (session.isConnected) "" else " (disconnected)") |
292 |
210 |
293 def close() { session.disconnect } |
211 |
294 |
212 /* sftp channel */ |
295 def sftp(): Sftp = |
213 |
296 { |
214 val sftp: ChannelSftp = session.openChannel("sftp").asInstanceOf[ChannelSftp] |
297 val kind = "sftp" |
215 sftp.connect(connect_timeout(options)) |
298 val channel = session.openChannel(kind).asInstanceOf[ChannelSftp] |
216 |
299 new Sftp(this, kind, channel) |
217 def close() { sftp.disconnect; session.disconnect } |
300 } |
218 |
|
219 val settings: Map[String, String] = |
|
220 { |
|
221 val home = sftp.getHome |
|
222 Map("HOME" -> home, "USER_HOME" -> home) |
|
223 } |
|
224 def expand_path(path: Path): Path = path.expand_env(settings) |
|
225 def remote_path(path: Path): String = expand_path(path).implode |
|
226 |
|
227 def chmod(permissions: Int, path: Path): Unit = sftp.chmod(permissions, remote_path(path)) |
|
228 def mv(path1: Path, path2: Path): Unit = sftp.rename(remote_path(path1), remote_path(path2)) |
|
229 def rm(path: Path): Unit = sftp.rm(remote_path(path)) |
|
230 def mkdir(path: Path): Unit = sftp.mkdir(remote_path(path)) |
|
231 def rmdir(path: Path): Unit = sftp.rmdir(remote_path(path)) |
|
232 |
|
233 def stat(path: Path): Option[Dir_Entry] = |
|
234 try { Some(Dir_Entry(expand_path(path), sftp.stat(remote_path(path)))) } |
|
235 catch { case _: SftpException => None } |
|
236 |
|
237 def is_file(path: Path): Boolean = stat(path).map(_.is_file) getOrElse false |
|
238 def is_dir(path: Path): Boolean = stat(path).map(_.is_dir) getOrElse false |
|
239 |
|
240 def mkdirs(path: Path): Unit = |
|
241 if (!is_dir(path)) { |
|
242 execute( |
|
243 "perl -e \"use File::Path make_path; make_path('" + remote_path(path) + "');\"") |
|
244 if (!is_dir(path)) error("Failed to create directory: " + quote(remote_path(path))) |
|
245 } |
|
246 |
|
247 def read_dir(path: Path): List[Dir_Entry] = |
|
248 { |
|
249 val dir = sftp.ls(remote_path(path)) |
|
250 (for { |
|
251 i <- (0 until dir.size).iterator |
|
252 a = dir.get(i).asInstanceOf[AnyRef] |
|
253 name = Untyped.get[String](a, "filename") |
|
254 attrs = Untyped.get[Attrs](a, "attrs") |
|
255 if name != "." && name != ".." |
|
256 } yield Dir_Entry(Path.basic(name), attrs)).toList |
|
257 } |
|
258 |
|
259 def find_files(root: Path, pred: Dir_Entry => Boolean = _ => true): List[Dir_Entry] = |
|
260 { |
|
261 def find(dir: Path): List[Dir_Entry] = |
|
262 read_dir(dir).flatMap(entry => |
|
263 { |
|
264 val file = dir + entry.name |
|
265 if (entry.is_dir) find(file) |
|
266 else if (pred(entry)) List(entry.copy(name = file)) |
|
267 else Nil |
|
268 }) |
|
269 find(root) |
|
270 } |
|
271 |
|
272 def open_input(path: Path): InputStream = sftp.get(remote_path(path)) |
|
273 def open_output(path: Path): OutputStream = sftp.put(remote_path(path)) |
|
274 |
|
275 def read_file(path: Path, local_path: Path): Unit = |
|
276 sftp.get(remote_path(path), File.platform_path(local_path)) |
|
277 def read_bytes(path: Path): Bytes = using(open_input(path))(Bytes.read_stream(_)) |
|
278 def read(path: Path): String = using(open_input(path))(File.read_stream(_)) |
|
279 |
|
280 def write_file(path: Path, local_path: Path): Unit = |
|
281 sftp.put(File.platform_path(local_path), remote_path(path)) |
|
282 def write_bytes(path: Path, bytes: Bytes): Unit = |
|
283 using(open_output(path))(bytes.write_stream(_)) |
|
284 def write(path: Path, text: String): Unit = |
|
285 using(open_output(path))(stream => Bytes(text).write_stream(stream)) |
|
286 |
|
287 |
|
288 /* exec channel */ |
301 |
289 |
302 def exec(command: String): Exec = |
290 def exec(command: String): Exec = |
303 { |
291 { |
304 val kind = "exec" |
292 val channel = session.openChannel("exec").asInstanceOf[ChannelExec] |
305 val channel = session.openChannel(kind).asInstanceOf[ChannelExec] |
|
306 channel.setCommand("export USER_HOME=\"$HOME\"\n" + command) |
293 channel.setCommand("export USER_HOME=\"$HOME\"\n" + command) |
307 new Exec(this, kind, channel) |
294 new Exec(this, channel) |
308 } |
295 } |
309 |
296 |
310 def execute(command: String, |
297 def execute(command: String, |
311 progress_stdout: String => Unit = (_: String) => (), |
298 progress_stdout: String => Unit = (_: String) => (), |
312 progress_stderr: String => Unit = (_: String) => (), |
299 progress_stderr: String => Unit = (_: String) => (), |