simplessh-rb 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ require 'mkmf'
2
+
3
+ # TODO: this check's not working
4
+ abort "libssh2 is missing" unless have_library "ssh2", "libssh2_session_handshake", "libssh2.h"
5
+
6
+ create_makefile "simplessh/simplessh"
7
+ puts "running the Makefile"
8
+ puts `cd ext/simplessh; make install`
@@ -0,0 +1,360 @@
1
+ #include <stdio.h>
2
+ #include <stdlib.h>
3
+ #include <arpa/inet.h>
4
+ #include <sys/socket.h>
5
+ #include <sys/stat.h>
6
+ #include <unistd.h>
7
+ #include <netdb.h>
8
+
9
+ #include <libssh2.h>
10
+ #include <simplessh.h>
11
+
12
+ #define returnError(either, err) { \
13
+ struct simplessh_either *tmp = (either); \
14
+ tmp->side = LEFT; \
15
+ tmp->u.error = (err); \
16
+ return tmp; \
17
+ }
18
+
19
+ inline int min(int a, int b) {
20
+ return a < b ? a : b;
21
+ }
22
+
23
+ static int waitsocket(int socket_fd, LIBSSH2_SESSION *session) {
24
+ struct timeval timeout;
25
+ int rc;
26
+ fd_set fd;
27
+ fd_set *writefd = NULL;
28
+ fd_set *readfd = NULL;
29
+ int dir;
30
+
31
+ timeout.tv_sec = 10;
32
+ timeout.tv_usec = 0;
33
+
34
+ FD_ZERO(&fd);
35
+ FD_SET(socket_fd, &fd);
36
+
37
+ /* now make sure we wait in the correct direction */
38
+ dir = libssh2_session_block_directions(session);
39
+
40
+ if(dir & LIBSSH2_SESSION_BLOCK_INBOUND) readfd = &fd;
41
+ if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) writefd = &fd;
42
+
43
+ rc = select(socket_fd + 1, readfd, writefd, NULL, &timeout);
44
+ return rc;
45
+ }
46
+
47
+ inline int get_socket(const char *hostname, uint16_t port) {
48
+ struct addrinfo hints;
49
+ memset(&hints, 0, sizeof(struct addrinfo));
50
+ hints.ai_family = AF_UNSPEC;
51
+ hints.ai_socktype = SOCK_STREAM;
52
+ hints.ai_flags = AI_NUMERICSERV;
53
+ hints.ai_protocol = 0;
54
+
55
+ struct addrinfo *res = NULL;
56
+ char service[6]; // enough to contain a port number
57
+ sprintf(service, "%i", port);
58
+ int rc = getaddrinfo(hostname, service, &hints, &res);
59
+ if(rc != 0) {
60
+ if(res) freeaddrinfo(res);
61
+ return -1;
62
+ }
63
+
64
+ struct addrinfo *current;
65
+ int sock;
66
+ for(current = res; current != NULL; current = current->ai_next) {
67
+ sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
68
+ if(sock == -1) continue;
69
+
70
+ if(connect(sock, res->ai_addr, res->ai_addrlen) != -1) {
71
+ freeaddrinfo(res);
72
+ return sock;
73
+ }
74
+
75
+ close(sock);
76
+ }
77
+
78
+ freeaddrinfo(res);
79
+ return -1;
80
+ }
81
+
82
+ struct simplessh_either *simplessh_open_session(
83
+ const char *hostname,
84
+ uint16_t port,
85
+ const char *knownhosts_path) {
86
+ struct simplessh_either *either;
87
+ struct simplessh_session *session;
88
+ LIBSSH2_KNOWNHOSTS *knownhosts;
89
+ char *hostkey;
90
+ int hostkey_type, rc;
91
+ size_t hostkey_len;
92
+
93
+ #define returnLocalErrorSP(err) { \
94
+ simplessh_close_session(session); \
95
+ returnError(either, (err)); \
96
+ }
97
+
98
+ // Empty simplessh_session
99
+ session = malloc(sizeof(struct simplessh_session));
100
+ session->lsession = NULL;
101
+
102
+ // Empty simplessh_either
103
+ either = malloc(sizeof(struct simplessh_either));
104
+ either->side = RIGHT;
105
+ either->u.value = session;
106
+
107
+ // Connection initialisation
108
+ session->sock = get_socket(hostname, port);
109
+ if(session->sock == -1) returnError(either, CONNECT);
110
+
111
+ session->lsession = libssh2_session_init();
112
+ if(!session) returnLocalErrorSP(INIT);
113
+
114
+ libssh2_session_set_blocking(session->lsession, 0);
115
+
116
+ while((rc = libssh2_session_handshake(session->lsession, session->sock)) == LIBSSH2_ERROR_EAGAIN);
117
+ if(rc) returnLocalErrorSP(HANDSHAKE);
118
+
119
+ // Check host in the knownhosts
120
+ knownhosts = libssh2_knownhost_init(session->lsession);
121
+ if(!knownhosts) returnLocalErrorSP(KNOWNHOSTS_INIT);
122
+
123
+ libssh2_knownhost_readfile(knownhosts, knownhosts_path, LIBSSH2_KNOWNHOST_FILE_OPENSSH);
124
+
125
+ hostkey = (char*)libssh2_session_hostkey(session->lsession, &hostkey_len, &hostkey_type);
126
+ if(hostkey) {
127
+ struct libssh2_knownhost *host;
128
+ int check = libssh2_knownhost_check(knownhosts, hostname, hostkey, hostkey_len,
129
+ LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW,
130
+ &host);
131
+
132
+ if(check != 0) returnLocalErrorSP(KNOWNHOSTS_CHECK);
133
+ libssh2_knownhost_free(knownhosts);
134
+ } else {
135
+ libssh2_knownhost_free(knownhosts);
136
+ returnLocalErrorSP(KNOWNHOSTS_HOSTKEY);
137
+ }
138
+ // End of the knownhosts checking
139
+
140
+ return either;
141
+ }
142
+
143
+ struct simplessh_either *simplessh_authenticate_password(
144
+ struct simplessh_session *session,
145
+ const char *username,
146
+ const char *password) {
147
+ int rc;
148
+ struct simplessh_either *either = malloc(sizeof(struct simplessh_either));
149
+
150
+ while((rc = libssh2_userauth_password(session->lsession, username, password)) == LIBSSH2_ERROR_EAGAIN);
151
+ if(rc) {
152
+ either->side = LEFT;
153
+ either->u.error = AUTHENTICATION;
154
+ } else {
155
+ either->side = RIGHT;
156
+ either->u.value = session;
157
+ }
158
+
159
+ return either;
160
+ }
161
+
162
+ struct simplessh_either *simplessh_authenticate_key(
163
+ struct simplessh_session *session,
164
+ const char *username,
165
+ const char *public_key_path,
166
+ const char *private_key_path,
167
+ const char *passphrase) {
168
+ int rc;
169
+ struct simplessh_either *either = malloc(sizeof(struct simplessh_either));
170
+
171
+ while((rc = libssh2_userauth_publickey_fromfile(session->lsession, username, public_key_path, private_key_path, passphrase)) == LIBSSH2_ERROR_EAGAIN);
172
+ if(rc) {
173
+ either->side = LEFT;
174
+ either->u.error = AUTHENTICATION;
175
+ } else {
176
+ either->side = RIGHT;
177
+ either->u.value = session;
178
+ }
179
+
180
+ return either;
181
+ }
182
+
183
+ struct simplessh_either *simplessh_exec_command(
184
+ struct simplessh_session *session,
185
+ const char *command) {
186
+ struct simplessh_either *either;
187
+ struct simplessh_result *result;
188
+ LIBSSH2_CHANNEL *channel;
189
+ int rc, rc2;
190
+ char *out = NULL;
191
+ char *err = NULL;
192
+
193
+ // Empty result
194
+ result = malloc(sizeof(struct simplessh_result));
195
+ result->out = NULL;
196
+ result->err = NULL;
197
+ result->exit_code = 127;
198
+ result->exit_signal = NULL;
199
+
200
+ // Empty either
201
+ either = malloc(sizeof(struct simplessh_either));
202
+ either->side = RIGHT;
203
+ either->u.value = result;
204
+
205
+ #define returnLocalErrorC(error) { \
206
+ if(out != NULL) free(out); \
207
+ if(err != NULL) free(err); \
208
+ free(result); \
209
+ returnError(either, (error)); \
210
+ }
211
+
212
+ while((channel = libssh2_channel_open_session(session->lsession)) == NULL) {
213
+ if(libssh2_session_last_errno(session->lsession) == LIBSSH2_ERROR_EAGAIN)
214
+ waitsocket(session->sock, session->lsession);
215
+ else
216
+ returnLocalErrorC(CHANNEL_OPEN);
217
+ }
218
+
219
+ // Send the command
220
+ while((rc = libssh2_channel_exec(channel, command)) != 0) {
221
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
222
+ waitsocket(session->sock, session->lsession);
223
+ } else {
224
+ returnLocalErrorC(CHANNEL_EXEC);
225
+ }
226
+ }
227
+
228
+ // Read result
229
+ int out_size = 128, out_position = 0, err_size = 128, err_position = 0;
230
+ out = malloc(out_size);
231
+ err = malloc(err_size);
232
+
233
+ for(;;) {
234
+ rc = libssh2_channel_read(channel,
235
+ out + out_position,
236
+ out_size - out_position - 1);
237
+ rc2 = libssh2_channel_read_stderr(channel,
238
+ err + err_position,
239
+ err_size - err_position - 1);
240
+
241
+ if(rc == 0 && rc2 == 0) {
242
+ break;
243
+ } else if(rc == LIBSSH2_ERROR_EAGAIN && rc2 == LIBSSH2_ERROR_EAGAIN) {
244
+ waitsocket(session->sock, session->lsession);
245
+ } else if((rc < 0 && rc != LIBSSH2_ERROR_EAGAIN) ||
246
+ (rc2 < 0 && rc2 != LIBSSH2_ERROR_EAGAIN)) {
247
+ returnLocalErrorC(READ);
248
+ } else {
249
+ if(rc > 0) {
250
+ out_position += rc;
251
+ if(out_size - out_position < 1024) {
252
+ out_size = min(out_size * 2, out_size + 65536);
253
+ out = realloc(out, out_size);
254
+ }
255
+ }
256
+ if(rc2 > 0) {
257
+ err_position += rc2;
258
+ if(err_size - err_position < 1024) {
259
+ err_size = min(err_size * 2, err_size + 65536);
260
+ err = realloc(err, err_size);
261
+ }
262
+ }
263
+ }
264
+ }
265
+ out[out_position] = '\0';
266
+ out = realloc(out, out_position + 1);
267
+ result->out = out;
268
+
269
+ err[err_position] = '\0';
270
+ err = realloc(err, err_position + 1);
271
+ result->err = err;
272
+
273
+ while((rc = libssh2_channel_close(channel)) == LIBSSH2_ERROR_EAGAIN)
274
+ waitsocket(session->sock, session->lsession);
275
+
276
+ if(rc == 0) {
277
+ result->exit_code = libssh2_channel_get_exit_status(channel);
278
+ libssh2_channel_get_exit_signal(channel,
279
+ &result->exit_signal, NULL,
280
+ NULL, NULL,
281
+ NULL, NULL);
282
+ }
283
+
284
+
285
+ libssh2_channel_free(channel);
286
+
287
+ return either;
288
+ }
289
+
290
+ struct simplessh_either *simplessh_send_file(
291
+ struct simplessh_session *session,
292
+ int mode,
293
+ const char *local_path,
294
+ const char *destination_path) {
295
+ struct simplessh_either *either;
296
+ LIBSSH2_CHANNEL *channel = NULL;
297
+ int rc;
298
+ int *transferred = malloc(sizeof(int));
299
+ *transferred = 0;
300
+
301
+ // Empty either
302
+ either = malloc(sizeof(struct simplessh_either));
303
+ either->side = RIGHT;
304
+ either->u.value = transferred;
305
+
306
+ #define returnLocalErrorS(err) { \
307
+ if(f) fclose(f); \
308
+ if(channel) libssh2_channel_free(channel); \
309
+ either->side = LEFT; \
310
+ either->u.error = err; \
311
+ return either; \
312
+ }
313
+
314
+ FILE *f = fopen(local_path, "r");
315
+ if(f == NULL) returnLocalErrorS(FILEOPEN);
316
+
317
+ size_t size;
318
+ struct stat *fileinfo = malloc(sizeof(struct stat));
319
+ stat(local_path, fileinfo);
320
+ size = fileinfo->st_size;
321
+ free(fileinfo);
322
+
323
+ while((channel = libssh2_scp_send(session->lsession, destination_path, mode & 0777, size)) == NULL) {
324
+ if(libssh2_session_last_errno(session->lsession) == LIBSSH2_ERROR_EAGAIN) {
325
+ waitsocket(session->sock, session->lsession);
326
+ } else {
327
+ returnLocalErrorS(CHANNEL_OPEN);
328
+ }
329
+ }
330
+
331
+ char buf[1024];
332
+ char *current;
333
+ while(!feof(f)) {
334
+ size_t n = fread(buf, 1, sizeof(buf), f);
335
+ current = buf;
336
+ // Ready to write n bytes to the channel
337
+ while(n > 0) {
338
+ rc = libssh2_channel_write(channel, current, n);
339
+ if(rc < 0) returnLocalErrorS(WRITE);
340
+ n -= rc;
341
+ current += rc;
342
+ *transferred += rc;
343
+ }
344
+ }
345
+
346
+ libssh2_channel_send_eof(channel);
347
+ libssh2_channel_wait_eof(channel);
348
+ libssh2_channel_wait_closed(channel);
349
+ libssh2_channel_free(channel);
350
+ fclose(f);
351
+ return either;
352
+ }
353
+
354
+ void simplessh_close_session(struct simplessh_session *session) {
355
+ libssh2_session_disconnect(session->lsession, "simplessh_close_session");
356
+ libssh2_session_free(session->lsession);
357
+ close(session->sock);
358
+ free(session);
359
+ libssh2_exit();
360
+ }
@@ -0,0 +1,37 @@
1
+ #ifndef __simplessh_header
2
+ #define __simplessh_header 1
3
+
4
+ #include <stdint.h>
5
+
6
+ #include <simplessh_types.h>
7
+
8
+ struct simplessh_either *simplessh_open_session(
9
+ const char*,
10
+ uint16_t,
11
+ const char*);
12
+
13
+ struct simplessh_either *simplessh_authenticate_password(
14
+ struct simplessh_session*,
15
+ const char *username,
16
+ const char *password);
17
+
18
+ struct simplessh_either *simplessh_authenticate_key(
19
+ struct simplessh_session*,
20
+ const char*,
21
+ const char*,
22
+ const char*,
23
+ const char*);
24
+
25
+ struct simplessh_either *simplessh_exec_command(
26
+ struct simplessh_session*,
27
+ const char *);
28
+
29
+ struct simplessh_either *simplessh_send_file(
30
+ struct simplessh_session*,
31
+ int,
32
+ const char*,
33
+ const char*);
34
+
35
+ void simplessh_close_session(struct simplessh_session*);
36
+
37
+ #endif
@@ -0,0 +1,68 @@
1
+ #include <stdlib.h>
2
+ #include <stdio.h>
3
+
4
+ #include <simplessh_types.h>
5
+
6
+ int simplessh_is_left(struct simplessh_either *either) {
7
+ return either->side == LEFT;
8
+ }
9
+
10
+ int simplessh_get_error(struct simplessh_either *either) {
11
+ if(either->side != LEFT) {
12
+ fprintf(stderr, "Error: simplessh_get_error: trying to get the error of a Right element\n");
13
+ abort();
14
+ }
15
+ return either->u.error;
16
+ }
17
+
18
+ void *simplessh_get_value(struct simplessh_either *either) {
19
+ if(either->side != RIGHT) {
20
+ fprintf(stderr, "Error: simplessh_get_value: trying to get the value of a Left element\n");
21
+ abort();
22
+ } else if(either->u.value == NULL) {
23
+ fprintf(stderr, "Error: simplessh_get_value: element is Right but value has not been set\n");
24
+ abort();
25
+ }
26
+ return either->u.value;
27
+ }
28
+
29
+ void simplessh_free_either_result(struct simplessh_either *either) {
30
+ struct simplessh_result *result = either->u.value;
31
+ if(either->side == RIGHT && result != NULL) {
32
+ if(result->out != NULL) free(result->out);
33
+ if(result->err != NULL) free(result->err);
34
+ if(result->exit_signal != NULL) free(result->exit_signal);
35
+ free(result);
36
+ }
37
+ free(either);
38
+ }
39
+
40
+ void simplessh_free_either_count(struct simplessh_either *either) {
41
+ if(either->side == RIGHT && either->u.value != NULL) free(either->u.value);
42
+ free(either);
43
+ }
44
+
45
+ char *simplessh_get_out(struct simplessh_result *result) {
46
+ return result->out;
47
+ }
48
+
49
+ char *simplessh_get_err(struct simplessh_result *result) {
50
+ return result->err;
51
+ }
52
+
53
+ int simplessh_get_exit_code(struct simplessh_result *result) {
54
+ return result->exit_code;
55
+ }
56
+
57
+ char *simplessh_get_exit_signal(struct simplessh_result *result) {
58
+ return result->exit_signal;
59
+ }
60
+
61
+ int simplessh_get_count(int *ptr) {
62
+ return *ptr;
63
+ }
64
+
65
+ // Just to make FFI shut up
66
+ void simplessh_free(void *ptr) {
67
+ free(ptr);
68
+ }
@@ -0,0 +1,62 @@
1
+ #ifndef __SIMPLESSH_TYPES_HEADER
2
+ #define __SIMPLESSH_TYPES_HEADER 1
3
+
4
+ #include <libssh2.h>
5
+
6
+ enum simplessh_left_right {
7
+ LEFT,
8
+ RIGHT
9
+ };
10
+
11
+ enum simplessh_error {
12
+ CONNECT = 1,
13
+ INIT = 2,
14
+ HANDSHAKE = 3,
15
+ KNOWNHOSTS_INIT = 4,
16
+ KNOWNHOSTS_HOSTKEY = 5,
17
+ KNOWNHOSTS_CHECK = 6,
18
+ AUTHENTICATION = 7,
19
+ CHANNEL_OPEN = 8,
20
+ CHANNEL_EXEC = 9,
21
+ READ = 10,
22
+ FILEOPEN = 11,
23
+ WRITE = 12
24
+ };
25
+
26
+ struct simplessh_either {
27
+ enum simplessh_left_right side;
28
+ union {
29
+ enum simplessh_error error;
30
+ void *value;
31
+ } u;
32
+ };
33
+
34
+ struct simplessh_session {
35
+ LIBSSH2_SESSION *lsession;
36
+ int sock;
37
+ };
38
+
39
+ struct simplessh_result {
40
+ char *out;
41
+ char *err;
42
+ int exit_code;
43
+ char *exit_signal;
44
+ };
45
+
46
+ int simplessh_is_left(struct simplessh_either*);
47
+ int simplessh_get_error(struct simplessh_either*);
48
+ void *simplessh_get_value(struct simplessh_either*);
49
+
50
+ void simplessh_free_either_result(struct simplessh_either*);
51
+ void simplessh_free_either_count(struct simplessh_either*);
52
+
53
+ char *simplessh_get_out(struct simplessh_result*);
54
+ char *simplessh_get_err(struct simplessh_result*);
55
+ int simplessh_get_exit_code(struct simplessh_result*);
56
+ char *simplessh_get_exit_signal(struct simplessh_result*);
57
+
58
+ int simplessh_get_count(int*);
59
+
60
+ void simplessh_free(void*);
61
+
62
+ #endif
@@ -0,0 +1,81 @@
1
+ module SimpleSSH
2
+ class Either
3
+ attr_reader :val
4
+
5
+ def self.return(val)
6
+ Right.new val
7
+ end
8
+
9
+ def initialize(val)
10
+ @val = val
11
+ end
12
+
13
+ def self.from_pointer(pointer, &block)
14
+ if SimpleSSH::Foreign.is_left(pointer) == 0
15
+ value_pointer = SimpleSSH::Foreign.value pointer
16
+ value = block.call(value_pointer)
17
+ Right.new value
18
+ else
19
+ error_code = SimpleSSH::Foreign.error(pointer)
20
+ Left.from_code(error_code)
21
+ end
22
+ end
23
+
24
+ def self.from_pointer!(pointer, &block)
25
+ result = self.from_pointer(pointer, &block)
26
+ SimpleSSH::Foreign.free pointer
27
+ result
28
+ end
29
+
30
+ def flat_map(&block)
31
+ raise NotImplementedError
32
+ end
33
+
34
+ def left?
35
+ raise NotImplementedError
36
+ end
37
+
38
+ def right?
39
+ not left?
40
+ end
41
+
42
+ class Left < Either
43
+ def self.from_code(code)
44
+ err = case code
45
+ when 1 then :connect
46
+ when 2 then :libssh2_initialisation
47
+ when 3 then :handshake
48
+ when 4 then :known_hosts_initialisation
49
+ when 5 then :known_hosts_hostkey
50
+ when 6 then :known_hosts_check
51
+ when 7 then :authentication
52
+ when 8 then :channel_open
53
+ when 9 then :channel_execution
54
+ when 10 then :read
55
+ when 11 then :file_open
56
+ when 12 then :write
57
+ else :unknown_error
58
+ end
59
+ self.new err
60
+ end
61
+
62
+ def flat_map
63
+ self
64
+ end
65
+
66
+ def left?
67
+ true
68
+ end
69
+ end
70
+
71
+ class Right < Either
72
+ def flat_map(&block)
73
+ block.call val
74
+ end
75
+
76
+ def left?
77
+ false
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,30 @@
1
+ require 'ffi'
2
+
3
+ module SimpleSSH
4
+ module Foreign
5
+ extend FFI::Library
6
+ extension = FFI::Platform.mac? ? "bundle" : "so"
7
+ ffi_lib File.join(File.dirname(__FILE__), "..", "..", "ext", "simplessh", "simplessh.#{extension}")
8
+ ffi_convention :stdcall
9
+
10
+ attach_function :open_session, :simplessh_open_session, [:string, :int, :string], :pointer
11
+ attach_function :authenticate_with_password, :simplessh_authenticate_password, [:pointer, :string, :string], :pointer
12
+ attach_function :authenticate_with_key, :simplessh_authenticate_key, [:pointer, :string, :string, :string, :string], :pointer
13
+ attach_function :exec_command, :simplessh_exec_command, [:pointer, :string], :pointer
14
+ attach_function :send_file, :simplessh_send_file, [:pointer, :int, :string, :string], :pointer
15
+ attach_function :close_session, :simplessh_close_session, [:pointer], :void
16
+
17
+ attach_function :is_left, :simplessh_is_left, [:pointer], :int
18
+ attach_function :error, :simplessh_get_error, [:pointer], :int
19
+ attach_function :value, :simplessh_get_value, [:pointer], :pointer
20
+ attach_function :free_either_result, :simplessh_free_either_result, [:pointer], :void
21
+ attach_function :free_either_count, :simplessh_free_either_count, [:pointer], :void
22
+ attach_function :out, :simplessh_get_out, [:pointer], :string
23
+ attach_function :err, :simplessh_get_err, [:pointer], :string
24
+ attach_function :exit_code, :simplessh_get_exit_code, [:pointer], :int
25
+ attach_function :exit_signal, :simplessh_get_exit_signal, [:pointer], :string
26
+ attach_function :count, :simplessh_get_count, [:pointer], :int
27
+
28
+ attach_function :free, :simplessh_free, [:pointer], :void
29
+ end
30
+ end
@@ -0,0 +1,7 @@
1
+ module SimpleSSH
2
+ module Helpers
3
+ def file_mode(path)
4
+ File.stat(path).mode % 01000
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,36 @@
1
+ module SimpleSSH
2
+ class Result
3
+ # The pointer is freed after this call in SimpleSSH::Session#exec_command
4
+ def self.from_pointer(pointer)
5
+ exit_code = SimpleSSH::Foreign.exit_code pointer
6
+ exit_signal = SimpleSSH::Foreign.exit_signal pointer
7
+ out = SimpleSSH::Foreign.out pointer
8
+ err = SimpleSSH::Foreign.err pointer
9
+ self.new(exit_code, exit_signal, out, err)
10
+ end
11
+
12
+ def initialize(code, sig, out, err)
13
+ @code, @sig, @out, @err = code, sig, out, err
14
+ end
15
+
16
+ def success?
17
+ not signal? && @exit_code == 0
18
+ end
19
+
20
+ def signal?
21
+ not @sig.nil?
22
+ end
23
+
24
+ def stdout
25
+ @out
26
+ end
27
+
28
+ def stderr
29
+ @err
30
+ end
31
+
32
+ def exit
33
+ signal? ? @sig : @code
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,71 @@
1
+ module SimpleSSH
2
+ class Session
3
+ include SimpleSSH::Helpers
4
+
5
+ def self.from_pointer(pointer)
6
+ self.new(pointer)
7
+ end
8
+
9
+ def initialize(pointer)
10
+ @session_pointer = pointer
11
+ end
12
+
13
+ def authenticate_with_password(username, password)
14
+ either_pointer = SimpleSSH::Foreign.authenticate_with_password(@session_pointer, username, password)
15
+ SimpleSSH::Either.from_pointer!(either_pointer) do |session_pointer|
16
+ SimpleSSH::Session.from_pointer(session_pointer)
17
+ end
18
+ end
19
+
20
+ def authenticate_with_key(username, public_key, private_key, passphrase)
21
+ either_pointer = SimpleSSH::Foreign.authenticate_with_key(@session_pointer, username, public_key, private_key, passphrase)
22
+ SimpleSSH::Either.from_pointer!(either_pointer) do |session_pointer|
23
+ SimpleSSH::Session.from_pointer(session_pointer)
24
+ end
25
+ end
26
+
27
+ def exec_command(cmd)
28
+ either_pointer = SimpleSSH::Foreign.exec_command(@session_pointer, cmd)
29
+ either_result = SimpleSSH::Either.from_pointer(either_pointer) do |result_pointer|
30
+ SimpleSSH::Result.from_pointer(result_pointer)
31
+ end
32
+ SimpleSSH::Foreign.free_either_result(either_pointer)
33
+ either_result
34
+ end
35
+
36
+ def send_file(mode, source, target)
37
+ either_pointer = SimpleSSH::Foreign.send_file(@session_pointer, mode, source, target)
38
+ either_count = SimpleSSH::Either.from_pointer(either_pointer) do |count_pointer|
39
+ SimpleSSH::Foreign.count(count_pointer)
40
+ end
41
+ SimpleSSH::Foreign.free_either_count(either_pointer)
42
+ either_count
43
+ end
44
+
45
+ def send_directory(source, target)
46
+ exec_command("mkdir -m #{file_mode(source).to_s(8)} #{target}").flat_map do |dir_creation_res|
47
+ if dir_creation_res.success?
48
+ Dir.open(source).reject { |p| p == '.' || p == '..' }.reduce(SimpleSSH::Either.return(nil)) do |prev, name|
49
+ local_path = File.join(source, name)
50
+ remote_path = "#{target}/#{name}"
51
+ if File.file? local_path
52
+ prev.flat_map { |_| send_file(file_mode(local_path), local_path, remote_path) }
53
+ else
54
+ prev.flat_map { |_| send_directory(local_path, remote_path) }
55
+ end
56
+ end
57
+ else
58
+ SimpleSSH::Either::Left.new :could_not_create_directory
59
+ end
60
+ end.flat_map { |_| SimpleSSH::Either.return nil }
61
+ end
62
+
63
+ def close
64
+ SimpleSSH::Foreign.close_session @session_pointer
65
+ end
66
+
67
+ def finalize
68
+ close
69
+ end
70
+ end
71
+ end
data/lib/simplessh.rb ADDED
@@ -0,0 +1,50 @@
1
+ require 'simplessh/helpers'
2
+ require 'simplessh/foreign'
3
+ require 'simplessh/either'
4
+ require 'simplessh/session'
5
+ require 'simplessh/result'
6
+
7
+ module SimpleSSH
8
+ def self.open_session(hostname, port, known_hosts)
9
+ either_pointer = SimpleSSH::Foreign.open_session(hostname, port, known_hosts)
10
+
11
+ SimpleSSH::Either.from_pointer!(either_pointer) do |sess_pointer|
12
+ SimpleSSH::Session.from_pointer(sess_pointer)
13
+ end
14
+ end
15
+
16
+ def self.with_session_password(hostname, username, password, opts = {}, &block)
17
+ port = opts[:port] || 22
18
+ known_hosts = opts[:known_hosts] || File.join(Dir.home, ".ssh", "known_hosts")
19
+
20
+ sess = self.open_session(hostname, port, known_hosts).flat_map do |s|
21
+ s.authenticate_with_password(username, password)
22
+ end
23
+
24
+ res = sess.flat_map do |s|
25
+ block.call s
26
+ end
27
+
28
+ sess.flat_map { |s| s.close }
29
+ res
30
+ end
31
+
32
+ def self.with_session_key(hostname, username, opts = {}, &block)
33
+ port = opts[:port] || 22
34
+ passphrase = opts[:passphrase] || ""
35
+ public_key = opts[:public_key] || File.join(Dir.home, ".ssh", "id_rsa.pub")
36
+ private_key = opts[:private_key] || File.join(Dir.home, ".ssh", "id_rsa")
37
+ known_hosts = opts[:known_hosts] || File.join(Dir.home, ".ssh", "known_hosts")
38
+
39
+ sess = self.open_session(hostname, port, known_hosts).flat_map do |s|
40
+ s.authenticate_with_key(username, public_key, private_key, passphrase)
41
+ end
42
+
43
+ res = sess.flat_map do |s|
44
+ block.call s
45
+ end
46
+
47
+ sess.flat_map { |s| s.close }
48
+ res
49
+ end
50
+ end
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: simplessh-rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Tom Feron
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-11-20 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: ffi
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ description: Simple wrapper around libssh2 allowing to send commands and files, supports
31
+ stdout/stderr, exit codes and signals.
32
+ email: tho.feron@gmail.com
33
+ executables: []
34
+ extensions:
35
+ - ext/simplessh/extconf.rb
36
+ extra_rdoc_files: []
37
+ files:
38
+ - lib/simplessh.rb
39
+ - lib/simplessh/foreign.rb
40
+ - lib/simplessh/session.rb
41
+ - lib/simplessh/either.rb
42
+ - lib/simplessh/result.rb
43
+ - lib/simplessh/helpers.rb
44
+ - ext/simplessh/simplessh.c
45
+ - ext/simplessh/simplessh.h
46
+ - ext/simplessh/simplessh_types.c
47
+ - ext/simplessh/simplessh_types.h
48
+ - ext/simplessh/extconf.rb
49
+ homepage: https://github.com/thoferon/simplessh-rb
50
+ licenses: []
51
+ post_install_message:
52
+ rdoc_options: []
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ! '>='
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ requirements: []
68
+ rubyforge_project:
69
+ rubygems_version: 1.8.23
70
+ signing_key:
71
+ specification_version: 3
72
+ summary: Ruby version of SimpleSSH
73
+ test_files: []