simplessh-rb 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: []