|
1 | 1 | (ns clj-libssh2.session
|
2 | 2 | "Functions for creating and managing sessions."
|
3 |
| - (:require [clj-libssh2.libssh2 :as libssh2] |
| 3 | + (:require [clojure.set :as set] |
| 4 | + [clj-libssh2.libssh2 :as libssh2] |
4 | 5 | [clj-libssh2.libssh2.session :as libssh2-session]
|
5 | 6 | [clj-libssh2.authentication :refer [authenticate]]
|
6 | 7 | [clj-libssh2.error :refer [handle-errors with-timeout]]
|
7 | 8 | [clj-libssh2.known-hosts :as known-hosts]
|
8 | 9 | [clj-libssh2.socket :as socket]))
|
9 | 10 |
|
10 | 11 | (def sessions
|
11 |
| - "A pool of currently running sessions. This is an atomic map where the keys |
12 |
| - are session IDs and the values are running session objects." |
13 |
| - (atom {})) |
| 12 | + "An atomic set of currently active sessions. This is used to trigger calls to |
| 13 | + libssh2/init and libssh2/exit at appropriate times. It's also used to |
| 14 | + protect against attempting to close sessions twice." |
| 15 | + (atom #{})) |
14 | 16 |
|
15 | 17 | (def default-opts
|
16 | 18 | "The default options for a session. These are not only the defaults, but an
|
|
23 | 25 | :read-timeout 60000
|
24 | 26 | :write-chunk-size (* 1024 1024)})
|
25 | 27 |
|
26 |
| -(defrecord Session [id session socket host port options]) |
| 28 | +(defrecord Session [session socket host port options]) |
27 | 29 |
|
28 | 30 | (defn- create-session
|
29 | 31 | "Make a native libssh2 session object.
|
|
96 | 98 | (with-timeout :session
|
97 | 99 | (libssh2-session/handshake session socket))))
|
98 | 100 |
|
99 |
| -(defn- session-id |
100 |
| - "Generate the session ID that will be used to pool sessions. |
101 |
| -
|
102 |
| - Arguments: |
103 |
| -
|
104 |
| - host The hostname or IP of the remote host. |
105 |
| - port The port we're connecting to. |
106 |
| - credentials The credentials to be used to authenticate the session. |
107 |
| - opts The session options. |
108 |
| -
|
109 |
| - Return: |
110 |
| -
|
111 |
| - A string that will uniquely identify a session." |
112 |
| - [host port credentials opts] |
113 |
| - (format "%s@%s:%d-%d-%d" |
114 |
| - (:username credentials) |
115 |
| - host |
116 |
| - port |
117 |
| - (.hashCode credentials) |
118 |
| - (.hashCode opts))) |
119 |
| - |
120 | 101 | (defn close
|
121 | 102 | "Disconnect an SSH session and discard the session.
|
122 | 103 |
|
|
127 | 108 | Return:
|
128 | 109 |
|
129 | 110 | nil."
|
130 |
| - [{id :id}] |
131 |
| - (when-let [session (get @sessions id)] |
| 111 | + [session] |
| 112 | + (when (contains? @sessions session) |
132 | 113 | (destroy-session session)
|
133 | 114 | (socket/close (:socket session))
|
134 |
| - (swap! sessions dissoc id) |
| 115 | + (swap! sessions set/difference #{session}) |
135 | 116 | (when (empty? @sessions)
|
136 | 117 | (libssh2/exit))))
|
137 | 118 |
|
|
149 | 130 | Return:
|
150 | 131 |
|
151 | 132 | A Session object for the connected and authenticated session."
|
152 |
| - ([host port credentials] |
153 |
| - (open host port credentials {})) |
154 |
| - ([host port credentials opts] |
155 |
| - (when (empty? @sessions) |
156 |
| - (handle-errors nil (libssh2/init 0))) |
157 |
| - (let [session-options (create-session-options opts) |
158 |
| - id (session-id host port credentials session-options)] |
159 |
| - (if (contains? @sessions id) |
160 |
| - (get @sessions id) |
161 |
| - (let [session (Session. id |
162 |
| - (create-session) |
163 |
| - (socket/connect host port) |
164 |
| - host |
165 |
| - port |
166 |
| - session-options)] |
167 |
| - (when (> 0 (:socket session)) |
168 |
| - (destroy-session session "Shutting down due to bad socket." true)) |
169 |
| - (try |
170 |
| - (libssh2-session/set-blocking (:session session) 0) |
171 |
| - (handshake session) |
172 |
| - (known-hosts/check session) |
173 |
| - (authenticate session credentials) |
174 |
| - (swap! sessions assoc (:id session) session) |
175 |
| - (get @sessions (:id session)) |
176 |
| - (catch Exception e |
177 |
| - (close session) |
178 |
| - (throw e)))))))) |
| 133 | + [host port credentials opts] |
| 134 | + (when (empty? @sessions) |
| 135 | + (handle-errors nil (libssh2/init 0))) |
| 136 | + (let [session (Session. (create-session) |
| 137 | + (socket/connect host port) |
| 138 | + host |
| 139 | + port |
| 140 | + (create-session-options opts))] |
| 141 | + (when (> 0 (:socket session)) |
| 142 | + (destroy-session session "Shutting down due to bad socket." true)) |
| 143 | + (try |
| 144 | + (libssh2-session/set-blocking (:session session) 0) |
| 145 | + (handshake session) |
| 146 | + (known-hosts/check session) |
| 147 | + (authenticate session credentials) |
| 148 | + (swap! sessions conj session) |
| 149 | + session |
| 150 | + (catch Exception e |
| 151 | + (close session) |
| 152 | + (throw e))))) |
179 | 153 |
|
180 | 154 | (defmacro with-session
|
181 | 155 | "A convenience macro for running some code with a particular session.
|
|
0 commit comments