libssh 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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"