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.
- data/ext/simplessh/extconf.rb +8 -0
- data/ext/simplessh/simplessh.c +360 -0
- data/ext/simplessh/simplessh.h +37 -0
- data/ext/simplessh/simplessh_types.c +68 -0
- data/ext/simplessh/simplessh_types.h +62 -0
- data/lib/simplessh/either.rb +81 -0
- data/lib/simplessh/foreign.rb +30 -0
- data/lib/simplessh/helpers.rb +7 -0
- data/lib/simplessh/result.rb +36 -0
- data/lib/simplessh/session.rb +71 -0
- data/lib/simplessh.rb +50 -0
- metadata +73 -0
@@ -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,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: []
|