src/Pure/General/ssh.scala
changeset 64256 c3197aeae90b
parent 64254 b1aef25ce8df
child 64257 9d51ac055cec
equal deleted inserted replaced
64255:a9f540881611 64256:c3197aeae90b
   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) => (),