Skip to content

Commit 57c1ce8

Browse files
committed
Merge release/0.5.11
2 parents 425ef5e + 3722a71 commit 57c1ce8

File tree

7 files changed

+112
-19
lines changed

7 files changed

+112
-19
lines changed

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ Thanks to [Ryan Stradling](http://github.com/rstradling) for these.
163163
Via [clojars](http://clojars.org) and
164164
[Leiningen](http://github.com/technomancy/leiningen).
165165

166-
:dependencies [clj-ssh "0.5.10"]
166+
:dependencies [clj-ssh "0.5.11"]
167167

168168
or your favourite maven repository aware tool.
169169

@@ -191,6 +191,10 @@ On OS X, use:
191191
ssh-add -K ~/.ssh/clj_ssh_pp
192192
```
193193

194+
## Other Libraries
195+
196+
For plain `ftp`, you might want to look at [clj-ftp](https://github.com/miner/clj-ftp).
197+
194198
## License
195199

196200
Copyright © 2012 Hugo Duncan

ReleaseNotes.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
## 0.5.11
2+
3+
- Lock known hosts file
4+
Try and prevent concurrent read and writes to the known hosts file.
5+
6+
- Fix :out :stream in the cli namespace
7+
Caller becomes responsible for closing the session.
8+
9+
Closes #29
10+
111
## 0.5.10
212

313
- Fix session? predicate

project.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
(def agentproxy-version "0.0.7")
22

3-
(defproject clj-ssh "0.5.10"
3+
(defproject clj-ssh "0.5.11"
44
:description "Library for using SSH from clojure."
55
:url "https://github.com/hugoduncan/clj-ssh"
66
:license {:name "Eclipse Public License"

src/clj_ssh/cli.clj

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,14 @@ Options are:
135135
:cmd specifies a command to exec. If no cmd is given, a shell is started
136136
and input is taken from :in.
137137
:in specifies input to the remote shell. A string or a stream.
138+
138139
:out specify :stream to obtain a an [inputstream shell]
139140
specify :bytes to obtain a byte array
140141
or specify a string with an encoding specification for a
141-
result string. In the case of :stream, the shell can
142-
be polled for connected status.
142+
result string.
143+
In the case of :stream, the shell can be polled for connected
144+
status, and the session (in the :session key of the return value)
145+
must be disconnected by the caller.
143146
:username username to use for authentication
144147
:password password to use for authentication
145148
:port port to use if no session specified
@@ -152,13 +155,17 @@ sh returns a map of
152155
(let [{:keys [cmd in out username password port ssh-agent args]
153156
:or {ssh-agent *ssh-agent*}
154157
:as options} (parse-args args)
155-
session (default-session ssh-agent hostname options)]
156-
(ssh/with-connection session
157-
(ssh/ssh
158-
session
159-
(if (seq args)
160-
(merge {:cmd (string/join " " args)} options)
161-
options)))))
158+
session (default-session ssh-agent hostname options)
159+
arg (if (seq args)
160+
(merge {:cmd (string/join " " args)} options)
161+
options)]
162+
(if (= :stream (:out options))
163+
(do
164+
(ssh/connect session)
165+
(assoc (ssh/ssh session arg)
166+
:session session))
167+
(ssh/with-connection session
168+
(ssh/ssh session arg)))))
162169

163170
(defn sftp
164171
"Execute SFTP commands.

src/clj_ssh/ssh.clj

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,11 @@
132132
(satisfies? protocols/Session x))
133133

134134
;;; Agent
135+
(def ^:private hosts-file
136+
"Something to lock to tray and prevent concurrent updates/reads to
137+
hosts file."
138+
(Object.))
139+
135140
(defn ssh-agent
136141
"Create a ssh-agent. By default a system ssh-agent is preferred."
137142
[{:keys [use-system-ssh-agent ^String known-hosts-path]
@@ -142,7 +147,8 @@
142147
(when use-system-ssh-agent
143148
(agent/connect agent))
144149
(when known-hosts-path
145-
(.setKnownHosts agent known-hosts-path))
150+
(locking hosts-file
151+
(.setKnownHosts agent known-hosts-path)))
146152
agent))
147153

148154
;;; Identities
@@ -391,9 +397,11 @@ keys. All other option key pairs will be passed as SSH config options."
391397
(defn connect
392398
"Connect a session."
393399
([session]
394-
(protocols/connect session))
400+
(locking hosts-file
401+
(protocols/connect session)))
395402
([session timeout]
396-
(protocols/connect session timeout)))
403+
(locking hosts-file
404+
(protocols/connect session timeout))))
397405

398406
(defn disconnect
399407
"Disconnect a session."
@@ -611,11 +619,10 @@ config options."
611619
(.setPty shell (boolean (opts :pty))))
612620
(when (contains? opts :agent-forwarding)
613621
(.setAgentForwarding shell (boolean (opts :agent-forwarding))))
614-
(let [resp {:channel shell
615-
:out (or out (.getInputStream shell))
616-
:in (or in (.getOutputStream shell))}]
617-
(connect-channel shell)
618-
resp)))
622+
(connect-channel shell)
623+
{:channel shell
624+
:out (or out (.getInputStream shell))
625+
:in (or in (.getOutputStream shell))}))
619626

620627
(defn ssh-shell
621628
"Run a ssh-shell."
@@ -996,6 +1003,7 @@ cmd specifies a command to exec. Valid commands are:
9961003

9971004

9981005
;; http://blogs.sun.com/janp/entry/how_the_scp_protocol_works
1006+
;; https://blogs.oracle.com/janp/entry/how_the_scp_protocol_works
9991007
(defn scp-to
10001008
"Copy local path(s) to remote path via scp.
10011009

test/clj_ssh/cli_test.clj

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,3 +176,34 @@
176176
(finally
177177
(.delete tmpfile1)
178178
(.delete tmpfile2))))))
179+
180+
181+
(deftest out-stream-test
182+
(testing "exec"
183+
(testing ":out :string"
184+
(let [proc (ssh "localhost" "ls")]
185+
(is (zero? (:exit proc)))
186+
(is (pos? (count (:out proc))) "no options")))
187+
(testing ":out :stream"
188+
(let [proc (ssh "localhost" "ls" :out :stream :pty true)]
189+
(is (clj-ssh.ssh/connected-channel? (:channel proc))
190+
":channel not connected")
191+
(is (> (count (slurp (:out-stream proc))) 1) ":out-stream")
192+
(Thread/sleep 100)
193+
(is (zero? (clj-ssh.ssh/exit-status (:channel proc)))
194+
"zero exit status")
195+
(clj-ssh.ssh/disconnect (:session proc)))))
196+
(testing "shell"
197+
(testing ":out :string"
198+
(let [proc (ssh "localhost" :in "ls")]
199+
(is (pos? (count (:out proc))) ":out has content")
200+
(is (zero? (:exit proc)) "zero exit status")))
201+
(testing ":out stream"
202+
(let [proc (ssh "localhost" :in "ls" :out :stream)]
203+
(is (clj-ssh.ssh/connected-channel? (:channel proc))
204+
":channel connected")
205+
(is (> (count (slurp (:out-stream proc))) 1) ":out-stream")
206+
(Thread/sleep 100)
207+
(is (zero? (clj-ssh.ssh/exit-status (:channel proc)))
208+
"zero exit status")
209+
(clj-ssh.ssh/disconnect (:session proc))))))

test/clj_ssh/ssh_test.clj

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,3 +602,36 @@
602602
(with-connection s
603603
(ssh-exec (the-session s) "ls" "" "" {})))
604604
"two hosts"))
605+
606+
607+
(deftest out-stream-test
608+
(let [s (session (ssh-agent {}) "localhost" {})]
609+
(with-connection s
610+
(testing "exec"
611+
(testing ":out :string"
612+
(let [proc (ssh s {:cmd "ls"})]
613+
(is (zero? (:exit proc)))
614+
(is (pos? (count (:out proc))) "no options")))
615+
(testing ":out :stream"
616+
(let [proc (ssh s {:cmd "ls" :out :stream :pty true})]
617+
(is (connected-channel? (:channel proc))
618+
":channel connected")
619+
(is (> (count (slurp (:out-stream proc))) 1) ":out-stream")
620+
(is (not (connected-channel? (:channel proc)))
621+
":channel not connected")
622+
(is (zero? (exit-status (:channel proc)))
623+
"zero exit status"))))
624+
(testing "shell"
625+
(testing ":out :string"
626+
(let [proc (ssh s {:in "ls"})]
627+
(is (pos? (count (:out proc))) ":out has content")
628+
(is (zero? (:exit proc)) "zero exit status")))
629+
(testing ":out stream"
630+
(let [proc (ssh s {:in "ls" :out :stream})]
631+
(is (connected-channel? (:channel proc))
632+
":channel connected")
633+
(is (> (count (slurp (:out-stream proc))) 1) ":out-stream")
634+
(is (not (connected-channel? (:channel proc)))
635+
":channel not connected")
636+
(is (zero? (exit-status (:channel proc)))
637+
"zero exit status")))))))

0 commit comments

Comments
 (0)