libssh 0.2.0 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f05291298150e6683ac9b6647515440e7dafe6a7
4
- data.tar.gz: 9c174e89993c04dee954032011fdf13987947bfa
3
+ metadata.gz: 639bee53286516a176e3e46c5646a93593405f3e
4
+ data.tar.gz: e934c062774b730c8a35a4b69ea075f9389af9d1
5
5
  SHA512:
6
- metadata.gz: 4760b23e84edef76e93604c38a0903bbf04ea682652754c5df1029fb21b688cf728acf5fa211253827884002612a1a5a038d7f33ff8c10e91c8b60b181c11396
7
- data.tar.gz: 82a03d32630272ddd632527ba70e83daa87b2a84af5d35aa8cee8a8ff83947e9ed241b6ed2d7363e62f6871342a66c5041ad5b2110a0f25e17e536d1aad9b90b
6
+ metadata.gz: 6e1596553a20bb479ebb3878fc1a2dbb968bbf9eb3fb3a4c6308ec85ce2e34ddd52770584b85764a8fde72260fb4ddec5da926923ae4a5da1e137d91fe1147b2
7
+ data.tar.gz: 4ba23660ed50cb0193df4bdac73134138fcb2dfe6bef6c84c00c4b883d81db8bc2ccd2f6b8ac3bb37e9281efb33b22a0bfde564b33b8ef7ce792063d0eb9cf31
data/.gitignore CHANGED
@@ -12,3 +12,7 @@
12
12
  *.o
13
13
  *.a
14
14
  mkmf.log
15
+
16
+ spec/examples.txt
17
+ spec/known_hosts.valid
18
+ spec/known_hosts.invalid
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.rubocop.yml CHANGED
@@ -3,20 +3,19 @@ inherit_from: .rubocop_todo.yml
3
3
  AllCops:
4
4
  DisplayCopNames: true
5
5
 
6
- Metrics/AbcSize:
7
- Enabled: false
8
- Metrics/ClassLength:
9
- Enabled: false
10
- Metrics/CyclomaticComplexity:
11
- Enabled: false
12
- Metrics/LineLength:
13
- Enabled: false
14
- Metrics/MethodLength:
6
+ Style/Documentation:
7
+ Exclude:
8
+ - 'lib/libssh/key.rb' # Documented in ext
9
+ - 'spec/**'
10
+
11
+ Metrics:
15
12
  Enabled: false
16
13
 
17
14
  Style/GlobalVars:
18
15
  Exclude:
19
16
  - 'ext/libssh_ruby/extconf.rb'
17
+ Style/GuardClause:
18
+ Enabled: false
20
19
  Style/HashSyntax:
21
20
  Exclude:
22
21
  - 'Rakefile'
data/.rubocop_todo.yml CHANGED
@@ -1,7 +0,0 @@
1
- Style/Documentation:
2
- Exclude:
3
- - 'spec/**/*'
4
- - 'test/**/*'
5
- - 'lib/libssh.rb'
6
- - 'lib/libssh/key.rb'
7
- - 'lib/sshkit/backends/libssh.rb'
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## 0.3.0 (2016-02-06)
2
+ - Add wrapper methods
3
+ - `Session#fd`
4
+ - `Session#read_nonblocking`
5
+ - `Session#disconnect`
6
+ - Make `Channel#get_exit_status` and `Channel#open_session` interruptible
7
+ - More documentations
8
+ - Add integration test
9
+
1
10
  ## 0.2.0 (2016-01-30)
2
11
  - Add many wrapper methods
3
12
  - Support `upload!` and `download!` as SSHKit backend.
data/Rakefile CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'bundler/gem_tasks'
2
2
  require 'rake/extensiontask'
3
+ require 'rspec/core/rake_task'
3
4
 
4
5
  task :build => :compile
5
6
 
@@ -7,4 +8,11 @@ Rake::ExtensionTask.new('libssh_ruby') do |ext|
7
8
  ext.lib_dir = 'lib/libssh'
8
9
  end
9
10
 
10
- task :default => [:clobber, :compile]
11
+ RSpec::Core::RakeTask.new(:spec)
12
+
13
+ task :default => [:clobber, :compile, :docker, :spec]
14
+
15
+ desc 'Build docker image for integration test'
16
+ task :docker do
17
+ sh 'docker build -t libssh-ruby spec'
18
+ end
data/example/exec.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'libssh'
3
+ require 'io/wait'
3
4
 
4
5
  GC.stress = true
5
6
  puts "libssh #{LibSSH::LIBSSH_VERSION}"
@@ -38,17 +39,28 @@ end
38
39
  bufsiz = 16384
39
40
 
40
41
  channel = LibSSH::Channel.new(session)
42
+ io = IO.for_fd(session.fd, autoclose: false)
41
43
  channel.open_session do
42
44
  channel.request_exec('ps auxf')
43
45
  until channel.eof?
44
- stdout_avail = channel.poll(timeout: 1)
45
- if stdout_avail && stdout_avail > 0
46
- $stdout.write(channel.read(bufsiz))
46
+ io.wait_readable
47
+
48
+ loop do
49
+ out = channel.read_nonblocking(bufsiz)
50
+ if out && !out.empty?
51
+ $stdout.write(out)
52
+ else
53
+ break
54
+ end
47
55
  end
48
56
 
49
- stderr_avail = channel.poll(stderr: true, timeout: 1)
50
- if stderr_avail && stderr_avail > 0
51
- $stderr.write(channel.read(bufsiz, stderr: true))
57
+ loop do
58
+ err = channel.read_nonblocking(bufsiz, true)
59
+ if err && !err.empty?
60
+ $stderr.write(err)
61
+ else
62
+ break
63
+ end
52
64
  end
53
65
  end
54
66
  end
@@ -57,6 +57,12 @@ static size_t channel_memsize(RB_UNUSED_VAR(const void *arg)) {
57
57
  return sizeof(ChannelHolder);
58
58
  }
59
59
 
60
+ /* @overload initialize(session)
61
+ * Initialize a channel from the session.
62
+ * @param [Session] session
63
+ * @see http://api.libssh.org/stable/group__libssh__channel.html
64
+ * ssh_channel_new
65
+ */
60
66
  static VALUE m_initialize(VALUE self, VALUE session) {
61
67
  ChannelHolder *holder;
62
68
  SessionHolder *session_holder;
@@ -83,7 +89,6 @@ static void *nogvl_close(void *ptr) {
83
89
  /*
84
90
  * @overload close
85
91
  * Close a channel.
86
- * @since 0.1.0
87
92
  * @return [nil]
88
93
  * @see http://api.libssh.org/stable/group__libssh__channel.html
89
94
  * ssh_channel_close
@@ -100,16 +105,37 @@ static VALUE m_close(VALUE self) {
100
105
  return Qnil;
101
106
  }
102
107
 
108
+ static void select_session(ssh_session session) {
109
+ fd_set fds;
110
+ int fd;
111
+
112
+ FD_ZERO(&fds);
113
+ fd = ssh_get_fd(session);
114
+ FD_SET(fd, &fds);
115
+ select(fd + 1, &fds, NULL, NULL, NULL);
116
+ }
117
+
103
118
  static void *nogvl_open_session(void *ptr) {
104
119
  struct nogvl_channel_args *args = ptr;
105
- args->rc = ssh_channel_open_session(args->channel);
120
+ ssh_session session = ssh_channel_get_session(args->channel);
121
+ int blocking = ssh_is_blocking(session);
122
+
123
+ ssh_set_blocking(session, 0);
124
+ while (1) {
125
+ args->rc = ssh_channel_open_session(args->channel);
126
+ if (args->rc != SSH_AGAIN) {
127
+ break;
128
+ }
129
+ rb_thread_check_ints();
130
+ select_session(session);
131
+ }
132
+ ssh_set_blocking(session, blocking);
106
133
  return NULL;
107
134
  }
108
135
 
109
136
  /*
110
137
  * @overload open_session
111
138
  * Open a session channel, and close it after the block.
112
- * @since 0.1.0
113
139
  * @yieldparam [Channel] channel self
114
140
  * @return [Object] Return value of the block
115
141
  * @see http://api.libssh.org/stable/group__libssh__channel.html
@@ -120,6 +146,11 @@ static VALUE m_open_session(VALUE self) {
120
146
  struct nogvl_channel_args args;
121
147
 
122
148
  TypedData_Get_Struct(self, ChannelHolder, &channel_type, holder);
149
+ /* When ssh_channel_open_session is called before ssh_connect, libssh would
150
+ * crash :-< */
151
+ if (!ssh_is_connected(ssh_channel_get_session(holder->channel))) {
152
+ rb_raise(rb_eArgError, "Session isn't connected");
153
+ }
123
154
  args.channel = holder->channel;
124
155
  rb_thread_call_without_gvl(nogvl_open_session, &args, RUBY_UBF_IO, NULL);
125
156
  RAISE_IF_ERROR(args.rc);
@@ -147,7 +178,6 @@ static void *nogvl_request_exec(void *ptr) {
147
178
  /*
148
179
  * @overload request_exec(cmd)
149
180
  * Run a shell command without an interactive shell.
150
- * @since 0.1.0
151
181
  * @param [String] cmd The command to execute
152
182
  * @return [nil]
153
183
  * @see http://api.libssh.org/stable/group__libssh__channel.html
@@ -174,7 +204,6 @@ static void *nogvl_request_pty(void *ptr) {
174
204
  /*
175
205
  * @overload request_pty
176
206
  * Request a PTY.
177
- * @since 0.1.0
178
207
  * @return [nil]
179
208
  * @see http://api.libssh.org/stable/group__libssh__channel.html
180
209
  * ssh_channel_request_pty
@@ -209,7 +238,6 @@ static void *nogvl_read(void *ptr) {
209
238
  /*
210
239
  * @overload read(count, is_stderr: false, timeout: -1)
211
240
  * Read data from a channel.
212
- * @since 0.1.0
213
241
  * @param [Fixnum] count The count of bytes to be read.
214
242
  * @param [Boolean] is_stderr Read from the stderr flow or not.
215
243
  * @param [Fixnum] timeout A timeout in seconds. +-1+ means infinite timeout.
@@ -250,10 +278,62 @@ static VALUE m_read(int argc, VALUE *argv, VALUE self) {
250
278
  return ret;
251
279
  }
252
280
 
281
+ struct nogvl_read_nonblocking_args {
282
+ ssh_channel channel;
283
+ char *buf;
284
+ uint32_t count;
285
+ int is_stderr;
286
+ int rc;
287
+ };
288
+
289
+ static void *nogvl_read_nonblocking(void *ptr) {
290
+ struct nogvl_read_nonblocking_args *args = ptr;
291
+ args->rc = ssh_channel_read_nonblocking(args->channel, args->buf, args->count,
292
+ args->is_stderr);
293
+ return NULL;
294
+ }
295
+
296
+ /*
297
+ * @overload read_nonblocking(count, is_stderr = false)
298
+ * Do a nonblocking read on the channel.
299
+ * @param [Fixnum] count The count of bytes to be read.
300
+ * @param [Boolean] is_stderr Read from the stderr flow or not.
301
+ * @return [String, nil] Data read from the channel. +nil+ on EOF.
302
+ * @since 0.3.0
303
+ * @see http://api.libssh.org/stable/group__libssh__channel.html
304
+ * ssh_channel_read_nonblocking
305
+ */
306
+ static VALUE m_read_nonblocking(int argc, VALUE *argv, VALUE self) {
307
+ ChannelHolder *holder;
308
+ VALUE count, is_stderr;
309
+ struct nogvl_read_nonblocking_args args;
310
+ VALUE ret;
311
+
312
+ TypedData_Get_Struct(self, ChannelHolder, &channel_type, holder);
313
+ args.channel = holder->channel;
314
+ rb_scan_args(argc, argv, "11", &count, &is_stderr);
315
+ Check_Type(count, T_FIXNUM);
316
+ args.count = FIX2UINT(count);
317
+ if (is_stderr == Qundef) {
318
+ args.is_stderr = 0;
319
+ } else {
320
+ args.is_stderr = RTEST(is_stderr) ? 1 : 0;
321
+ }
322
+ args.buf = ALLOC_N(char, args.count);
323
+ rb_thread_call_without_gvl(nogvl_read_nonblocking, &args, RUBY_UBF_IO, NULL);
324
+
325
+ if (args.rc == SSH_EOF) {
326
+ ret = Qnil;
327
+ } else {
328
+ ret = rb_utf8_str_new(args.buf, args.rc);
329
+ }
330
+ ruby_xfree(args.buf);
331
+ return ret;
332
+ }
333
+
253
334
  /*
254
335
  * @overload eof?
255
336
  * Check if remote ha sent an EOF.
256
- * @since 0.1.0
257
337
  * @return [Boolean]
258
338
  * @see http://api.libssh.org/stable/group__libssh__channel.html
259
339
  * ssh_channel_is_eof
@@ -282,7 +362,6 @@ static void *nogvl_poll(void *ptr) {
282
362
  /*
283
363
  * @overload poll(is_stderr: false, timeout: -1)
284
364
  * Poll a channel for data to read.
285
- * @since 0.1.0
286
365
  * @param [Boolean] is_stderr A boolean to select the stderr stream.
287
366
  * @param [Fixnum] timeout A timeout in milliseconds. A negative value means an
288
367
  * infinite timeout.
@@ -326,14 +405,25 @@ static VALUE m_poll(int argc, VALUE *argv, VALUE self) {
326
405
 
327
406
  static void *nogvl_get_exit_status(void *ptr) {
328
407
  struct nogvl_channel_args *args = ptr;
329
- args->rc = ssh_channel_get_exit_status(args->channel);
408
+ ssh_session session = ssh_channel_get_session(args->channel);
409
+ int blocking = ssh_is_blocking(session);
410
+
411
+ ssh_set_blocking(session, 0);
412
+ while (1) {
413
+ args->rc = ssh_channel_get_exit_status(args->channel);
414
+ if (args->rc != SSH_ERROR) {
415
+ break;
416
+ }
417
+ rb_thread_check_ints();
418
+ select_session(session);
419
+ }
420
+ ssh_set_blocking(session, blocking);
330
421
  return NULL;
331
422
  }
332
423
 
333
424
  /*
334
425
  * @overload get_exit_status
335
426
  * Get the exit status of the channel.
336
- * @since 0.1.0
337
427
  * @return [Fixnum, nil] The exit status. +nil+ if no exit status has been
338
428
  * returned.
339
429
  * @see http://api.libssh.org/stable/group__libssh__channel.html
@@ -369,7 +459,6 @@ static void *nogvl_write(void *ptr) {
369
459
  /*
370
460
  * @overload write(data)
371
461
  * Write data on the channel.
372
- * @since 0.1.0
373
462
  * @param [String] data Data to write.
374
463
  * @return [Fixnum] The number of bytes written.
375
464
  * @see http://api.libssh.org/stable/group__libssh__channel.html
@@ -398,7 +487,6 @@ static void *nogvl_send_eof(void *ptr) {
398
487
  /*
399
488
  * @overload send_eof
400
489
  * Send EOF on the channel.
401
- * @since 0.1.0
402
490
  * @return [nil]
403
491
  * @see http://api.libssh.org/stable/group__libssh__channel.html
404
492
  * ssh_channel_send_eof
@@ -414,6 +502,14 @@ static VALUE m_send_eof(VALUE self) {
414
502
  return Qnil;
415
503
  }
416
504
 
505
+ /*
506
+ * Document-class: LibSSH::Channel
507
+ * Wrapper for ssh_channel struct in libssh.
508
+ *
509
+ * @since 0.1.0
510
+ * @see http://api.libssh.org/stable/group__libssh__channel.html
511
+ */
512
+
417
513
  void Init_libssh_channel(void) {
418
514
  rb_cLibSSHChannel = rb_define_class_under(rb_mLibSSH, "Channel", rb_cObject);
419
515
  rb_define_alloc_func(rb_cLibSSHChannel, channel_alloc);
@@ -428,6 +524,8 @@ void Init_libssh_channel(void) {
428
524
  rb_define_method(rb_cLibSSHChannel, "request_pty",
429
525
  RUBY_METHOD_FUNC(m_request_pty), 0);
430
526
  rb_define_method(rb_cLibSSHChannel, "read", RUBY_METHOD_FUNC(m_read), -1);
527
+ rb_define_method(rb_cLibSSHChannel, "read_nonblocking",
528
+ RUBY_METHOD_FUNC(m_read_nonblocking), -1);
431
529
  rb_define_method(rb_cLibSSHChannel, "poll", RUBY_METHOD_FUNC(m_poll), -1);
432
530
  rb_define_method(rb_cLibSSHChannel, "eof?", RUBY_METHOD_FUNC(m_eof_p), 0);
433
531
  rb_define_method(rb_cLibSSHChannel, "get_exit_status",
@@ -3,10 +3,22 @@
3
3
  VALUE rb_eLibSSHError;
4
4
  ID id_code;
5
5
 
6
+ /*
7
+ * Document-class: LibSSH::Error
8
+ * Error returned from libssh.
9
+ *
10
+ * @since 0.1.0
11
+ * @see http://api.libssh.org/stable/group__libssh__error.html
12
+ *
13
+ * @!attribute [r] code
14
+ * Error code returned from libssh.
15
+ * @return [Fixnum]
16
+ */
17
+
6
18
  void Init_libssh_error(void) {
7
19
  rb_eLibSSHError =
8
20
  rb_define_class_under(rb_mLibSSH, "Error", rb_eStandardError);
9
- id_code = rb_intern("code");
21
+ id_code = rb_intern("@code");
10
22
 
11
23
  rb_define_attr(rb_eLibSSHError, "code", 1, 0);
12
24
  }
@@ -28,6 +28,11 @@ static size_t key_memsize(RB_UNUSED_VAR(const void *arg)) {
28
28
  return sizeof(KeyHolder);
29
29
  }
30
30
 
31
+ /*
32
+ * @overload initialize
33
+ * Initialize an empty key.
34
+ * @see http://api.libssh.org/stable/group__libssh__pki.html ssh_key_new
35
+ */
31
36
  static VALUE m_initialize(VALUE self) {
32
37
  KeyHolder *holder;
33
38
  TypedData_Get_Struct(self, KeyHolder, &key_type, holder);
@@ -44,7 +49,6 @@ KeyHolder *libssh_ruby_key_holder(VALUE key) {
44
49
  /*
45
50
  * @overload sha1
46
51
  * Return the hash in SHA1 form.
47
- * @since 0.1.0
48
52
  * @return [String]
49
53
  * @see http://api.libssh.org/stable/group__libssh__pki.html
50
54
  */
@@ -65,7 +69,6 @@ static VALUE m_sha1(VALUE self) {
65
69
  /*
66
70
  * @overload type
67
71
  * Return the type of a SSH key.
68
- * @since 0.1.0
69
72
  * @return [Fixnum]
70
73
  * @see http://api.libssh.org/stable/group__libssh__pki.html ssh_key_type
71
74
  */
@@ -76,7 +79,6 @@ static VALUE m_type(VALUE self) {
76
79
  /*
77
80
  * @overload type_str
78
81
  * Return the type of a SSH key in string format.
79
- * @since 0.1.0
80
82
  * @return [String]
81
83
  * @see http://api.libssh.org/stable/group__libssh__pki.html ssh_key_type and
82
84
  * ssh_key_type_to_char
@@ -89,7 +91,6 @@ static VALUE m_type_str(VALUE self) {
89
91
  /*
90
92
  * @overload public?
91
93
  * Check if the key is a public key.
92
- * @since 0.1.0
93
94
  * @return [Boolean]
94
95
  * @see http://api.libssh.org/stable/group__libssh__pki.html ssh_key_is_public
95
96
  */
@@ -100,7 +101,6 @@ static VALUE m_public_p(VALUE self) {
100
101
  /*
101
102
  * @overload private?
102
103
  * Check if the key is a private key.
103
- * @since 0.1.0
104
104
  * @return [Boolean]
105
105
  * @see http://api.libssh.org/stable/group__libssh__pki.html ssh_key_is_private
106
106
  */
@@ -108,6 +108,14 @@ static VALUE m_private_p(VALUE self) {
108
108
  return ssh_key_is_private(libssh_ruby_key_holder(self)->key) ? Qtrue : Qfalse;
109
109
  }
110
110
 
111
+ /*
112
+ * Document-class: LibSSH::Key
113
+ * Wrapper for ssh_key struct in libssh.
114
+ *
115
+ * @since 0.1.0
116
+ * @see http://api.libssh.org/stable/group__libssh__pki.html
117
+ */
118
+
111
119
  void Init_libssh_key(void) {
112
120
  rb_cLibSSHKey = rb_define_class_under(rb_mLibSSH, "Key", rb_cObject);
113
121
  rb_define_alloc_func(rb_cLibSSHKey, key_alloc);
@@ -27,30 +27,44 @@ static VALUE m_version(int argc, VALUE *argv, RB_UNUSED_VAR(VALUE self)) {
27
27
 
28
28
  void Init_libssh_ruby(void) {
29
29
  rb_mLibSSH = rb_define_module("LibSSH");
30
- #define SERVER(name) \
31
- rb_define_const(rb_mLibSSH, "SERVER_" #name, INT2FIX(SSH_SERVER_##name))
32
- SERVER(KNOWN_OK);
33
- SERVER(KNOWN_CHANGED);
34
- SERVER(FOUND_OTHER);
35
- SERVER(NOT_KNOWN);
36
- SERVER(FILE_NOT_FOUND);
37
- #undef SERVER
38
- #define AUTH(name) \
39
- rb_define_const(rb_mLibSSH, "AUTH_" #name, INT2FIX(SSH_AUTH_##name))
40
- AUTH(DENIED);
41
- AUTH(PARTIAL);
42
- AUTH(SUCCESS);
43
- AUTH(AGAIN);
44
- #undef AUTH
45
30
 
31
+ /* @see Session#server_known */
32
+ rb_define_const(rb_mLibSSH, "SERVER_KNOWN_OK", INT2FIX(SSH_SERVER_KNOWN_OK));
33
+ /* @see Session#server_known */
34
+ rb_define_const(rb_mLibSSH, "SERVER_KNOWN_CHANGED",
35
+ INT2FIX(SSH_SERVER_KNOWN_CHANGED));
36
+ /* @see Session#server_known */
37
+ rb_define_const(rb_mLibSSH, "SERVER_FOUND_OTHER",
38
+ INT2FIX(SSH_SERVER_FOUND_OTHER));
39
+ /* @see Session#server_known */
40
+ rb_define_const(rb_mLibSSH, "SERVER_NOT_KNOWN",
41
+ INT2FIX(SSH_SERVER_NOT_KNOWN));
42
+ /* @see Session#server_known */
43
+ rb_define_const(rb_mLibSSH, "SERVER_FILE_NOT_FOUND",
44
+ INT2FIX(SSH_SERVER_FILE_NOT_FOUND));
45
+
46
+ /* Return value that indicates an authentication failure. */
47
+ rb_define_const(rb_mLibSSH, "AUTH_DENIED", INT2FIX(SSH_AUTH_DENIED));
48
+ /* Return value that indicates a partially authenticated. */
49
+ rb_define_const(rb_mLibSSH, "AUTH_PARTIAL", INT2FIX(SSH_AUTH_PARTIAL));
50
+ /* Return value that indicates a successful authentication. */
51
+ rb_define_const(rb_mLibSSH, "AUTH_SUCCESS", INT2FIX(SSH_AUTH_SUCCESS));
52
+ /* Return value that indicates EAGAIN in nonblocking mode. */
53
+ rb_define_const(rb_mLibSSH, "AUTH_AGAIN", INT2FIX(SSH_AUTH_AGAIN));
54
+
55
+ /* Major version defined in header. */
46
56
  rb_define_const(rb_mLibSSH, "LIBSSH_VERSION_MAJOR",
47
57
  INT2FIX(LIBSSH_VERSION_MAJOR));
58
+ /* Minor version defined in header. */
48
59
  rb_define_const(rb_mLibSSH, "LIBSSH_VERSION_MINOR",
49
60
  INT2FIX(LIBSSH_VERSION_MINOR));
61
+ /* Micro version defined in header. */
50
62
  rb_define_const(rb_mLibSSH, "LIBSSH_VERSION_MICRO",
51
63
  INT2FIX(LIBSSH_VERSION_MICRO));
64
+ /* Major version defined in header. */
52
65
  rb_define_const(rb_mLibSSH, "LIBSSH_VERSION_INT",
53
66
  INT2FIX(LIBSSH_VERSION_INT));
67
+ /* String version defined in header. */
54
68
  rb_define_const(rb_mLibSSH, "LIBSSH_VERSION",
55
69
  rb_str_new_cstr(SSH_STRINGIFY(LIBSSH_VERSION)));
56
70
 
@@ -54,7 +54,6 @@ static size_t scp_memsize(RB_UNUSED_VAR(const void *arg)) {
54
54
 
55
55
  /* @overload initialize(session, mode, path)
56
56
  * Create a new scp session.
57
- * @since 0.2.0
58
57
  * @param [Session] session The SSH session to use.
59
58
  * @param [Symbol] mode +:read+ or +:write+.
60
59
  * @param [String] path The directory in which write or read will be done.
@@ -99,7 +98,6 @@ static void *nogvl_close(void *ptr) {
99
98
 
100
99
  /* @overload close
101
100
  * Close the scp channel.
102
- * @since 0.2.0
103
101
  * @return [nil]
104
102
  * @see http://api.libssh.org/stable/group__libssh__scp.html ssh_scp_close
105
103
  */
@@ -123,7 +121,6 @@ static void *nogvl_init(void *ptr) {
123
121
 
124
122
  /* @overload init
125
123
  * Initialize the scp channel.
126
- * @since 0.2.0
127
124
  * @yieldparam [Scp] scp self
128
125
  * @return [Object] Return value of the block
129
126
  * @see http://api.libssh.org/stable/group__libssh__scp.html ssh_scp_init
@@ -157,7 +154,6 @@ static void *nogvl_push_file(void *ptr) {
157
154
 
158
155
  /* @overload push_file(filename, size, mode)
159
156
  * Initialize the sending of a file to a scp in sink mode.
160
- * @since 0.2.0
161
157
  * @param [String] filename The name of the file being sent.
162
158
  * @param [Integer] size Exact size in bytes of the file being sent.
163
159
  * @param [Fixnum] mode The UNIX permissions for the new file.
@@ -194,7 +190,6 @@ static void *nogvl_write(void *ptr) {
194
190
 
195
191
  /* @overload write(data)
196
192
  * Write into a remote scp file.
197
- * @since 0.2.0
198
193
  * @param [String] data The data to write.
199
194
  * @return [nil]
200
195
  * @see http://api.libssh.org/stable/group__libssh__scp.html ssh_scp_write
@@ -221,7 +216,6 @@ static void *nogvl_pull_request(void *ptr) {
221
216
 
222
217
  /* @overload pull_request
223
218
  * Wait for a scp request.
224
- * @since 0.2.0
225
219
  * @return [Fixnum]
226
220
  * REQUEST_NEWFILE: The other side is sending a file.
227
221
  * REQUEST_NEWDIR: The other side is sending a directory.
@@ -244,7 +238,6 @@ static VALUE m_pull_request(VALUE self) {
244
238
 
245
239
  /* @overload request_size
246
240
  * Get the size of the file being pushed from the other party.
247
- * @since 0.2.0
248
241
  * @return [Integer] The numeric size of the file being read.
249
242
  * @see http://api.libssh.org/stable/group__libssh__scp.html
250
243
  * ssh_scp_request_get_size64
@@ -260,7 +253,6 @@ static VALUE m_request_size(VALUE self) {
260
253
 
261
254
  /* @overload request_filename
262
255
  * Get the name of the directory or file being pushed from the other party.
263
- * @since 0.2.0
264
256
  * @return [String, nil] The filename. +nil+ on error.
265
257
  * @see http://api.libssh.org/stable/group__libssh__scp.html
266
258
  * ssh_scp_request_get_filename
@@ -281,7 +273,6 @@ static VALUE m_request_filename(VALUE self) {
281
273
  /* @overload request_permissions
282
274
  * Get the permissions of the directory or file being pushed from the other
283
275
  * party.
284
- * @since 0.2.0
285
276
  * @return [Fixnum] The UNIX permissions.
286
277
  * @see http://api.libssh.org/stable/group__libssh__scp.html
287
278
  * ssh_scp_request_get_permissions
@@ -305,7 +296,6 @@ static void *nogvl_accept_request(void *ptr) {
305
296
  /* @overload accept_request
306
297
  * Accepts transfer of a file or creation of a directory coming from the remote
307
298
  * party.
308
- * @since 0.2.0
309
299
  * @return [nil]
310
300
  * @see http://api.libssh.org/stable/group__libssh__scp.html
311
301
  * ssh_scp_accept_request
@@ -336,7 +326,6 @@ static void *nogvl_deny_request(void *ptr) {
336
326
  /* @overload deny_request
337
327
  * Deny the transfer of a file or creation of a directory coming from the
338
328
  * remote party.
339
- * @since 0.2.0
340
329
  * @return [nil]
341
330
  * @see http://api.libssh.org/stable/group__libssh__scp.html
342
331
  * ssh_scp_deny_request
@@ -368,7 +357,6 @@ static void *nogvl_read(void *ptr) {
368
357
 
369
358
  /* @overload read(size)
370
359
  * Read from a remote scp file.
371
- * @since 0.2.0
372
360
  * @param [Fixnum] The size of the buffer.
373
361
  * @return [String]
374
362
  * @see http://api.libssh.org/stable/group__libssh__scp.html ssh_scp_read
@@ -396,7 +384,6 @@ static VALUE m_read(VALUE self, VALUE size) {
396
384
 
397
385
  /* @overload request_warning
398
386
  * Get the warning string.
399
- * @since 0.2.0
400
387
  * @return [String, nil] A warning string. +nil+ on error.
401
388
  * @see http://api.libssh.org/stable/group__libssh__scp.html
402
389
  * ssh_scp_request_get_warning
@@ -414,18 +401,30 @@ static VALUE m_request_warning(VALUE self) {
414
401
  }
415
402
  }
416
403
 
404
+ /* Document-class: LibSSH::Scp
405
+ * Wrapper for ssh_scp struct in libssh.
406
+ *
407
+ * @since 0.2.0
408
+ * @see http://api.libssh.org/stable/group__libssh__scp.html
409
+ */
410
+
417
411
  void Init_libssh_scp(void) {
418
412
  rb_cLibSSHScp = rb_define_class_under(rb_mLibSSH, "Scp", rb_cObject);
419
413
  rb_define_alloc_func(rb_cLibSSHScp, scp_alloc);
420
414
 
415
+ /* @see #pull_request */
421
416
  rb_define_const(rb_cLibSSHScp, "REQUEST_NEWFILE",
422
417
  INT2FIX(SSH_SCP_REQUEST_NEWFILE));
418
+ /* @see #pull_request */
423
419
  rb_define_const(rb_cLibSSHScp, "REQUEST_NEWDIR",
424
420
  INT2FIX(SSH_SCP_REQUEST_NEWDIR));
421
+ /* @see #pull_request */
425
422
  rb_define_const(rb_cLibSSHScp, "REQUEST_ENDDIR",
426
423
  INT2FIX(SSH_SCP_REQUEST_ENDDIR));
424
+ /* @see #pull_request */
427
425
  rb_define_const(rb_cLibSSHScp, "REQUEST_WARNING",
428
426
  INT2FIX(SSH_SCP_REQUEST_WARNING));
427
+ /* @see #pull_request */
429
428
  rb_define_const(rb_cLibSSHScp, "REQUEST_EOF", INT2FIX(SSH_SCP_REQUEST_EOF));
430
429
 
431
430
  rb_define_method(rb_cLibSSHScp, "initialize", RUBY_METHOD_FUNC(m_initialize),
@@ -49,6 +49,11 @@ static size_t session_memsize(RB_UNUSED_VAR(const void *arg)) {
49
49
  return sizeof(SessionHolder);
50
50
  }
51
51
 
52
+ /*
53
+ * @overload initialize
54
+ * Create a new SSH session.
55
+ * @see http://api.libssh.org/stable/group__libssh__session.html ssh_new
56
+ */
52
57
  static VALUE m_initialize(VALUE self) {
53
58
  SessionHolder *holder;
54
59
 
@@ -60,10 +65,10 @@ static VALUE m_initialize(VALUE self) {
60
65
  /*
61
66
  * @overload log_verbosity=(verbosity)
62
67
  * Set the session logging verbosity.
63
- * @since 0.1.0
64
68
  * @param [Symbol] verbosity +:none+, +:warn+, +:info+, +:debug+, or +:trace+.
65
69
  * @return [nil]
66
- * @see http://api.libssh.org/stable/group__libssh__session.html ssh_options_set(SSH_OPTIONS_LOG_VERBOSITY)
70
+ * @see http://api.libssh.org/stable/group__libssh__session.html
71
+ * ssh_options_set(SSH_OPTIONS_LOG_VERBOSITY)
67
72
  */
68
73
  static VALUE m_set_log_verbosity(VALUE self, VALUE verbosity) {
69
74
  ID id_verbosity;
@@ -104,7 +109,6 @@ static VALUE set_string_option(VALUE self, enum ssh_options_e type, VALUE str) {
104
109
  /*
105
110
  * @overload host=(host)
106
111
  * Set the hostname or IP address to connect to.
107
- * @since 0.1.0
108
112
  * @param [String] host
109
113
  * @return [nil]
110
114
  * @see http://api.libssh.org/stable/group__libssh__session.html ssh_options_set(SSH_OPTIONS_HOST)
@@ -289,8 +293,8 @@ static VALUE m_set_hostkeys(VALUE self, VALUE hostkeys) {
289
293
  */
290
294
  static VALUE m_set_compression(VALUE self, VALUE compression) {
291
295
  SessionHolder *holder;
292
- char *val;
293
296
  if (compression == Qtrue || compression == Qfalse) {
297
+ const char *val;
294
298
  if (compression == Qtrue) {
295
299
  val = "yes";
296
300
  } else {
@@ -384,7 +388,6 @@ static VALUE m_set_gssapi_delegate_credentials(VALUE self, VALUE enable) {
384
388
  /*
385
389
  * @overload parse_config(path = nil)
386
390
  * Parse the ssh_config file.
387
- * @since 0.1.0
388
391
  * @param [String, nil] Path to ssh_config. If +nil+, the default ~/.ssh/config will be used.
389
392
  * @return [Boolean] Parsing the ssh_config was successful or not.
390
393
  * @see http://api.libssh.org/stable/group__libssh__session.html ssh_options_parse_config
@@ -413,7 +416,6 @@ static VALUE m_parse_config(int argc, VALUE *argv, VALUE self) {
413
416
  /*
414
417
  * @overload add_identity(path_format)
415
418
  * Add the identity file name format.
416
- * @since 0.1.0
417
419
  * @param [String] path_format Format string for identity file.
418
420
  * @return [nil]
419
421
  * @see http://api.libssh.org/stable/group__libssh__session.html ssh_options_set(SSH_OPTIONS_ADD_IDENTITY)
@@ -442,7 +444,6 @@ static void *nogvl_connect(void *ptr) {
442
444
  /*
443
445
  * @overload connect
444
446
  * Connect to the SSH server.
445
- * @since 0.1.0
446
447
  * @return [nil]
447
448
  * @see http://api.libssh.org/stable/group__libssh__session.html ssh_connect
448
449
  */
@@ -458,10 +459,34 @@ static VALUE m_connect(VALUE self) {
458
459
  return Qnil;
459
460
  }
460
461
 
462
+ static void *nogvl_disconnect(void *ptr) {
463
+ struct nogvl_session_args *args = ptr;
464
+ ssh_disconnect(args->session);
465
+ args->rc = 0;
466
+ return NULL;
467
+ }
468
+
469
+ /*
470
+ * @overload disconnect
471
+ * Disconnect from a session.
472
+ * @return [nil]
473
+ * @since 0.3.0
474
+ * @see http://api.libssh.org/stable/group__libssh__session.html ssh_disconnect
475
+ */
476
+ static VALUE m_disconnect(VALUE self) {
477
+ SessionHolder *holder;
478
+ struct nogvl_session_args args;
479
+
480
+ TypedData_Get_Struct(self, SessionHolder, &session_type, holder);
481
+ args.session = holder->session;
482
+ rb_thread_call_without_gvl(nogvl_disconnect, &args, RUBY_UBF_IO, NULL);
483
+
484
+ return Qnil;
485
+ }
486
+
461
487
  /*
462
488
  * @overload server_known
463
489
  * Check if the server is knonw.
464
- * @since 0.1.0
465
490
  * @return [Fixnum]
466
491
  * @see http://api.libssh.org/stable/group__libssh__session.html ssh_is_server_known
467
492
  */
@@ -475,10 +500,22 @@ static VALUE m_server_known(VALUE self) {
475
500
  return INT2FIX(rc);
476
501
  }
477
502
 
503
+ /*
504
+ * @overload fd
505
+ * Get the fd of a connection
506
+ * @return [Fixnum]
507
+ * @since 0.3.0
508
+ * @see http://api.libssh.org/stable/group__libssh__session.html ssh_get_fd
509
+ */
510
+ static VALUE m_fd(VALUE self) {
511
+ SessionHolder *holder;
512
+ TypedData_Get_Struct(self, SessionHolder, &session_type, holder);
513
+ return INT2FIX(ssh_get_fd(holder->session));
514
+ }
515
+
478
516
  /*
479
517
  * @overload userauth_none
480
518
  * Try to authenticate through then "none" method.
481
- * @since 0.1.0
482
519
  * @return [Fixnum]
483
520
  * @see http://api.libssh.org/stable/group__libssh__auth.html ssh_userauth_none
484
521
  */
@@ -495,7 +532,6 @@ static VALUE m_userauth_none(VALUE self) {
495
532
  /*
496
533
  * @overload userauth_list
497
534
  * Get available authentication methods from the server.
498
- * @since 0.1.0
499
535
  * @return [Array<Symbol>]
500
536
  * @see http://api.libssh.org/stable/group__libssh__auth.html ssh_userauth_list
501
537
  */
@@ -539,7 +575,6 @@ static void *nogvl_userauth_publickey_auto(void *ptr) {
539
575
  /*
540
576
  * @overload userauth_publickey_auto
541
577
  * Try to automatically authenticate with public key and "none".
542
- * @since 0.1.0
543
578
  * @return [Fixnum]
544
579
  * @see http://api.libssh.org/stable/group__libssh__auth.html ssh_userauth_publickey_auto
545
580
  */
@@ -557,7 +592,6 @@ static VALUE m_userauth_publickey_auto(VALUE self) {
557
592
  /*
558
593
  * @overload get_publickey
559
594
  * Get the server public key from a session.
560
- * @since 0.1.0
561
595
  * @return [Key]
562
596
  * @see http://api.libssh.org/stable/group__libssh__session.html ssh_get_publickey
563
597
  */
@@ -576,7 +610,6 @@ static VALUE m_get_publickey(VALUE self) {
576
610
  /*
577
611
  * @overload write_knownhost
578
612
  * Write the current server as known in the known_hosts file.
579
- * @since 0.1.0
580
613
  * @return [nil]
581
614
  * @see http://api.libssh.org/stable/group__libssh__session.html ssh_write_knownhost
582
615
  */
@@ -588,6 +621,14 @@ static VALUE m_write_knownhost(VALUE self) {
588
621
  return Qnil;
589
622
  }
590
623
 
624
+ /*
625
+ * Document-class: LibSSH::Session
626
+ * Wrapper for ssh_session struct in libssh.
627
+ *
628
+ * @since 0.1.0
629
+ * @see http://api.libssh.org/stable/group__libssh__session.html
630
+ */
631
+
591
632
  void Init_libssh_session() {
592
633
  rb_cLibSSHSession = rb_define_class_under(rb_mLibSSH, "Session", rb_cObject);
593
634
  rb_define_alloc_func(rb_cLibSSHSession, session_alloc);
@@ -650,8 +691,12 @@ void Init_libssh_session() {
650
691
  RUBY_METHOD_FUNC(m_add_identity), 1);
651
692
  rb_define_method(rb_cLibSSHSession, "connect", RUBY_METHOD_FUNC(m_connect),
652
693
  0);
694
+ rb_define_method(rb_cLibSSHSession, "disconnect",
695
+ RUBY_METHOD_FUNC(m_disconnect), 0);
653
696
  rb_define_method(rb_cLibSSHSession, "server_known",
654
697
  RUBY_METHOD_FUNC(m_server_known), 0);
698
+ rb_define_method(rb_cLibSSHSession, "fd", RUBY_METHOD_FUNC(m_fd), 0);
699
+
655
700
  rb_define_method(rb_cLibSSHSession, "userauth_none",
656
701
  RUBY_METHOD_FUNC(m_userauth_none), 0);
657
702
  rb_define_method(rb_cLibSSHSession, "userauth_list",
data/lib/libssh/key.rb CHANGED
@@ -2,6 +2,10 @@ require 'libssh/libssh_ruby'
2
2
 
3
3
  module LibSSH
4
4
  class Key
5
+ # Return the hash in SHA1 in hexadecimal notation.
6
+ # @return [String]
7
+ # @see #sha1
8
+ # @since 0.1.0
5
9
  def sha1_hex
6
10
  sha1.unpack('H*')[0].each_char.each_slice(2).map(&:join).join(':')
7
11
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  module LibSSH
3
- VERSION = '0.2.0'.freeze
3
+ # The version of libssh gem.
4
+ VERSION = '0.3.0'.freeze
4
5
  end
@@ -1,11 +1,31 @@
1
1
  require 'libssh'
2
+ require 'io/wait'
2
3
  require 'sshkit/backends/abstract'
3
4
  require 'sshkit/backends/connection_pool'
4
5
 
6
+ # Namespace of sshkit gem.
5
7
  module SSHKit
8
+ # Namespace of sshkit gem.
6
9
  module Backend
10
+ # SSHKit backend class for libssh.
11
+ # @since 0.1.0
12
+ # @see https://github.com/capistrano/sshkit
7
13
  class Libssh < Abstract
14
+ # Configuration class compatible with
15
+ # {SSHKit::Backend::Netssh::Configuration}.
16
+ # @since 0.1.0
8
17
  class Configuration
18
+ # @!attribute [rw] pty
19
+ # Allocate a PTY or not. Default is +false+.
20
+ # @return [Boolean]
21
+ # @!attribute [rw] connection_timeout
22
+ # Connection timeout in second. Default is +30+.
23
+ # @return [Fixnum]
24
+ # @!attribute [rw] ssh_options
25
+ # Various options for libssh. Some of them are compatible with
26
+ # {SSHKit::Backend::Netssh::Configuration#ssh_options}.
27
+ # @todo Describe supported options.
28
+ # @return [Hash]
9
29
  attr_accessor :pty, :connection_timeout, :ssh_options
10
30
 
11
31
  def initialize
@@ -19,7 +39,13 @@ module SSHKit
19
39
  BUFSIZ = 16384
20
40
  private_constant :BUFSIZ
21
41
 
22
- # @override
42
+ # Upload a local file to the remote party.
43
+ # @param [String, #read] local Path to the local file to be uploaded.
44
+ # If +local+ responds to +#read+, +local+ is treated as IO-like object.
45
+ # @param [String] remote Path to the remote file.
46
+ # @since 0.2.0
47
+ # @see SSHKit::Backend::Abstract#upload!.
48
+ # @todo Make +options+ compatible with {SSHKit::Backend::Netssh#upload!}.
23
49
  def upload!(local, remote, _options = {})
24
50
  with_session do |session|
25
51
  scp = LibSSH::Scp.new(session, :write, File.dirname(remote))
@@ -35,7 +61,13 @@ module SSHKit
35
61
  end
36
62
  end
37
63
 
38
- # @override
64
+ # Download a remote file to local.
65
+ # @param [String] remote Path to the remote file to be downloaded.
66
+ # @param [String, #write] local Path to the local file.
67
+ # If +local+ responds to +#write+, +local+ is treated as IO-like object.
68
+ # @since 0.2.0
69
+ # @see SSHKit::Backend::Abstract#download!.
70
+ # @todo Make +options+ compatible with {SSHKit::Backend::Netssh#download!}.
39
71
  def download!(remote, local, _options = {})
40
72
  with_session do |session|
41
73
  scp = LibSSH::Scp.new(session, :read, remote)
@@ -58,9 +90,13 @@ module SSHKit
58
90
  @pool = SSHKit::Backend::ConnectionPool.new
59
91
 
60
92
  class << self
93
+ # @!attribute [rw] pool
94
+ # Connection pool for libssh.
95
+ # @return [SSHKit::Backend::ConnectionPool]
61
96
  attr_accessor :pool
62
97
 
63
- # @override
98
+ # Global configuration for {SSHKit::Backend::Netssh}.
99
+ # @return [Configuration]
64
100
  def config
65
101
  @config ||= Configuration.new
66
102
  end
@@ -74,24 +110,33 @@ module SSHKit
74
110
 
75
111
  with_session do |session|
76
112
  channel = LibSSH::Channel.new(session)
113
+ io = IO.for_fd(session.fd, autoclose: false)
77
114
  channel.open_session do
78
115
  if Libssh.config.pty
79
116
  channel.request_pty
80
117
  end
81
118
  channel.request_exec(cmd.to_command)
82
119
  until channel.eof?
83
- stdout_avail = channel.poll(timeout: 1)
84
- if stdout_avail && stdout_avail > 0
85
- buf = channel.read(BUFSIZ)
86
- cmd.on_stdout(channel, buf)
87
- output.log_command_data(cmd, :stdout, buf)
120
+ io.wait_readable
121
+
122
+ loop do
123
+ buf = channel.read_nonblocking(BUFSIZ)
124
+ if buf && !buf.empty?
125
+ cmd.on_stdout(channel, buf)
126
+ output.log_command_data(cmd, :stdout, buf)
127
+ else
128
+ break
129
+ end
88
130
  end
89
131
 
90
- stderr_avail = channel.poll(stderr: true, timeout: 1)
91
- if stderr_avail && stderr_avail > 0
92
- buf = channel.read(BUFSIZ, stderr: true)
93
- cmd.on_stderr(channel, buf)
94
- output.log_command_data(cmd, :stderr, buf)
132
+ loop do
133
+ buf = channel.read_nonblocking(BUFSIZ, true)
134
+ if buf && !buf.empty?
135
+ cmd.on_stderr(channel, buf)
136
+ output.log_command_data(cmd, :stderr, buf)
137
+ else
138
+ break
139
+ end
95
140
  end
96
141
  end
97
142
 
data/libssh.gemspec CHANGED
@@ -24,6 +24,7 @@ Gem::Specification.new do |spec|
24
24
  spec.add_development_dependency 'bundler'
25
25
  spec.add_development_dependency 'rake'
26
26
  spec.add_development_dependency 'rake-compiler'
27
+ spec.add_development_dependency 'rspec', '>= 3.0.0'
27
28
  spec.add_development_dependency 'rubocop', '>= 0.36.0'
28
29
  spec.add_development_dependency 'sshkit'
29
30
  spec.add_development_dependency 'yard'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: libssh
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kohei Suzuki
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-01-30 00:00:00.000000000 Z
11
+ date: 2016-02-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 3.0.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 3.0.0
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: rubocop
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -104,6 +118,7 @@ extra_rdoc_files: []
104
118
  files:
105
119
  - ".clang-format"
106
120
  - ".gitignore"
121
+ - ".rspec"
107
122
  - ".rubocop.yml"
108
123
  - ".rubocop_todo.yml"
109
124
  - ".travis.yml"