libssh 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,457 @@
1
+ #include "libssh_ruby.h"
2
+ #include <ruby/thread.h>
3
+
4
+ #define RAISE_IF_ERROR(rc) \
5
+ if ((rc) == SSH_ERROR) \
6
+ libssh_ruby_raise(libssh_ruby_session_holder(holder->session)->session)
7
+
8
+ VALUE rb_cLibSSHScp;
9
+
10
+ static ID id_read, id_write;
11
+
12
+ static void scp_mark(void *);
13
+ static void scp_free(void *);
14
+ static size_t scp_memsize(const void *);
15
+
16
+ struct ScpHolderStruct {
17
+ ssh_scp scp;
18
+ VALUE session;
19
+ };
20
+ typedef struct ScpHolderStruct ScpHolder;
21
+
22
+ static const rb_data_type_t scp_type = {
23
+ "ssh_scp", {scp_mark, scp_free, scp_memsize, {NULL, NULL}}, NULL,
24
+ NULL, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY,
25
+ };
26
+
27
+ static VALUE scp_alloc(VALUE klass) {
28
+ ScpHolder *holder = ALLOC(ScpHolder);
29
+ holder->scp = NULL;
30
+ holder->session = Qundef;
31
+ return TypedData_Wrap_Struct(klass, &scp_type, holder);
32
+ }
33
+
34
+ static void scp_mark(void *arg) {
35
+ ScpHolder *holder = arg;
36
+ if (holder->scp != NULL) {
37
+ rb_gc_mark(holder->session);
38
+ }
39
+ }
40
+
41
+ static void scp_free(void *arg) {
42
+ ScpHolder *holder = arg;
43
+ if (holder->scp != NULL) {
44
+ /* XXX: holder->scp must be free'ed before holder->session is free'ed */
45
+ ssh_scp_free(holder->scp);
46
+ holder->scp = NULL;
47
+ }
48
+ ruby_xfree(holder);
49
+ }
50
+
51
+ static size_t scp_memsize(RB_UNUSED_VAR(const void *arg)) {
52
+ return sizeof(ScpHolder);
53
+ }
54
+
55
+ /* @overload initialize(session, mode, path)
56
+ * Create a new scp session.
57
+ * @since 0.2.0
58
+ * @param [Session] session The SSH session to use.
59
+ * @param [Symbol] mode +:read+ or +:write+.
60
+ * @param [String] path The directory in which write or read will be done.
61
+ * @return [Scp]
62
+ * @see http://api.libssh.org/stable/group__libssh__scp.html ssh_scp_new
63
+ */
64
+ static VALUE m_initialize(VALUE self, VALUE session, VALUE mode, VALUE path) {
65
+ ScpHolder *holder;
66
+ SessionHolder *session_holder;
67
+ char *c_path;
68
+ ID id_mode;
69
+ int c_mode;
70
+
71
+ Check_Type(mode, T_SYMBOL);
72
+ id_mode = SYM2ID(mode);
73
+ if (id_mode == id_read) {
74
+ c_mode = SSH_SCP_READ;
75
+ } else if (id_mode == id_write) {
76
+ c_mode = SSH_SCP_WRITE;
77
+ } else {
78
+ rb_raise(rb_eArgError, "Invalid mode value: %" PRIsVALUE, mode);
79
+ }
80
+ c_path = StringValueCStr(path);
81
+ TypedData_Get_Struct(self, ScpHolder, &scp_type, holder);
82
+ session_holder = libssh_ruby_session_holder(session);
83
+ holder->scp = ssh_scp_new(session_holder->session, c_mode, c_path);
84
+ holder->session = session;
85
+
86
+ return self;
87
+ }
88
+
89
+ struct nogvl_scp_args {
90
+ ssh_scp scp;
91
+ int rc;
92
+ };
93
+
94
+ static void *nogvl_close(void *ptr) {
95
+ struct nogvl_scp_args *args = ptr;
96
+ args->rc = ssh_scp_close(args->scp);
97
+ return NULL;
98
+ }
99
+
100
+ /* @overload close
101
+ * Close the scp channel.
102
+ * @since 0.2.0
103
+ * @return [nil]
104
+ * @see http://api.libssh.org/stable/group__libssh__scp.html ssh_scp_close
105
+ */
106
+ static VALUE m_close(VALUE self) {
107
+ ScpHolder *holder;
108
+ struct nogvl_scp_args args;
109
+
110
+ TypedData_Get_Struct(self, ScpHolder, &scp_type, holder);
111
+ args.scp = holder->scp;
112
+ rb_thread_call_without_gvl(nogvl_close, &args, RUBY_UBF_IO, NULL);
113
+ RAISE_IF_ERROR(args.rc);
114
+
115
+ return Qnil;
116
+ }
117
+
118
+ static void *nogvl_init(void *ptr) {
119
+ struct nogvl_scp_args *args = ptr;
120
+ args->rc = ssh_scp_init(args->scp);
121
+ return NULL;
122
+ }
123
+
124
+ /* @overload init
125
+ * Initialize the scp channel.
126
+ * @since 0.2.0
127
+ * @yieldparam [Scp] scp self
128
+ * @return [Object] Return value of the block
129
+ * @see http://api.libssh.org/stable/group__libssh__scp.html ssh_scp_init
130
+ */
131
+ static VALUE m_init(VALUE self) {
132
+ ScpHolder *holder;
133
+ struct nogvl_scp_args args;
134
+
135
+ TypedData_Get_Struct(self, ScpHolder, &scp_type, holder);
136
+ args.scp = holder->scp;
137
+ rb_thread_call_without_gvl(nogvl_init, &args, RUBY_UBF_IO, NULL);
138
+ RAISE_IF_ERROR(args.rc);
139
+
140
+ return rb_ensure(rb_yield, Qnil, m_close, self);
141
+ }
142
+
143
+ struct nogvl_push_file_args {
144
+ ssh_scp scp;
145
+ char *filename;
146
+ uint64_t size;
147
+ int mode;
148
+ int rc;
149
+ };
150
+
151
+ static void *nogvl_push_file(void *ptr) {
152
+ struct nogvl_push_file_args *args = ptr;
153
+ args->rc =
154
+ ssh_scp_push_file64(args->scp, args->filename, args->size, args->mode);
155
+ return NULL;
156
+ }
157
+
158
+ /* @overload push_file(filename, size, mode)
159
+ * Initialize the sending of a file to a scp in sink mode.
160
+ * @since 0.2.0
161
+ * @param [String] filename The name of the file being sent.
162
+ * @param [Integer] size Exact size in bytes of the file being sent.
163
+ * @param [Fixnum] mode The UNIX permissions for the new file.
164
+ * @return [nil]
165
+ * @see http://api.libssh.org/stable/group__libssh__scp.html
166
+ * ssh_scp_push_file64
167
+ */
168
+ static VALUE m_push_file(VALUE self, VALUE filename, VALUE size, VALUE mode) {
169
+ ScpHolder *holder;
170
+ struct nogvl_push_file_args args;
171
+
172
+ TypedData_Get_Struct(self, ScpHolder, &scp_type, holder);
173
+ args.scp = holder->scp;
174
+ args.filename = StringValueCStr(filename);
175
+ args.size = NUM2ULONG(size);
176
+ args.mode = FIX2INT(mode);
177
+ rb_thread_call_without_gvl(nogvl_push_file, &args, RUBY_UBF_IO, NULL);
178
+ RAISE_IF_ERROR(args.rc);
179
+ return Qnil;
180
+ }
181
+
182
+ struct nogvl_write_args {
183
+ ssh_scp scp;
184
+ const void *buffer;
185
+ size_t len;
186
+ int rc;
187
+ };
188
+
189
+ static void *nogvl_write(void *ptr) {
190
+ struct nogvl_write_args *args = ptr;
191
+ args->rc = ssh_scp_write(args->scp, args->buffer, args->len);
192
+ return NULL;
193
+ }
194
+
195
+ /* @overload write(data)
196
+ * Write into a remote scp file.
197
+ * @since 0.2.0
198
+ * @param [String] data The data to write.
199
+ * @return [nil]
200
+ * @see http://api.libssh.org/stable/group__libssh__scp.html ssh_scp_write
201
+ */
202
+ static VALUE m_write(VALUE self, VALUE data) {
203
+ ScpHolder *holder;
204
+ struct nogvl_write_args args;
205
+
206
+ TypedData_Get_Struct(self, ScpHolder, &scp_type, holder);
207
+ Check_Type(data, T_STRING);
208
+ args.scp = holder->scp;
209
+ args.buffer = RSTRING_PTR(data);
210
+ args.len = RSTRING_LEN(data);
211
+ rb_thread_call_without_gvl(nogvl_write, &args, RUBY_UBF_IO, NULL);
212
+ RAISE_IF_ERROR(args.rc);
213
+ return Qnil;
214
+ }
215
+
216
+ static void *nogvl_pull_request(void *ptr) {
217
+ struct nogvl_scp_args *args = ptr;
218
+ args->rc = ssh_scp_pull_request(args->scp);
219
+ return NULL;
220
+ }
221
+
222
+ /* @overload pull_request
223
+ * Wait for a scp request.
224
+ * @since 0.2.0
225
+ * @return [Fixnum]
226
+ * REQUEST_NEWFILE: The other side is sending a file.
227
+ * REQUEST_NEWDIR: The other side is sending a directory.
228
+ * REQUEST_ENDDIR: The other side has finished with the current directory.
229
+ * REQUEST_WARNING: The other side sent us a warning.
230
+ * REQUEST_EOF: The other side finished sending us files and data.
231
+ * @see http://api.libssh.org/stable/group__libssh__scp.html
232
+ * ssh_scp_pull_request
233
+ */
234
+ static VALUE m_pull_request(VALUE self) {
235
+ ScpHolder *holder;
236
+ struct nogvl_scp_args args;
237
+
238
+ TypedData_Get_Struct(self, ScpHolder, &scp_type, holder);
239
+ args.scp = holder->scp;
240
+ rb_thread_call_without_gvl(nogvl_pull_request, &args, RUBY_UBF_IO, NULL);
241
+ RAISE_IF_ERROR(args.rc);
242
+ return INT2FIX(args.rc);
243
+ }
244
+
245
+ /* @overload request_size
246
+ * Get the size of the file being pushed from the other party.
247
+ * @since 0.2.0
248
+ * @return [Integer] The numeric size of the file being read.
249
+ * @see http://api.libssh.org/stable/group__libssh__scp.html
250
+ * ssh_scp_request_get_size64
251
+ */
252
+ static VALUE m_request_size(VALUE self) {
253
+ ScpHolder *holder;
254
+ uint64_t size;
255
+
256
+ TypedData_Get_Struct(self, ScpHolder, &scp_type, holder);
257
+ size = ssh_scp_request_get_size64(holder->scp);
258
+ return ULL2NUM(size);
259
+ }
260
+
261
+ /* @overload request_filename
262
+ * Get the name of the directory or file being pushed from the other party.
263
+ * @since 0.2.0
264
+ * @return [String, nil] The filename. +nil+ on error.
265
+ * @see http://api.libssh.org/stable/group__libssh__scp.html
266
+ * ssh_scp_request_get_filename
267
+ */
268
+ static VALUE m_request_filename(VALUE self) {
269
+ ScpHolder *holder;
270
+ const char *filename;
271
+
272
+ TypedData_Get_Struct(self, ScpHolder, &scp_type, holder);
273
+ filename = ssh_scp_request_get_filename(holder->scp);
274
+ if (filename == NULL) {
275
+ return Qnil;
276
+ } else {
277
+ return rb_str_new_cstr(filename);
278
+ }
279
+ }
280
+
281
+ /* @overload request_permissions
282
+ * Get the permissions of the directory or file being pushed from the other
283
+ * party.
284
+ * @since 0.2.0
285
+ * @return [Fixnum] The UNIX permissions.
286
+ * @see http://api.libssh.org/stable/group__libssh__scp.html
287
+ * ssh_scp_request_get_permissions
288
+ */
289
+ static VALUE m_request_permissions(VALUE self) {
290
+ ScpHolder *holder;
291
+ int mode;
292
+
293
+ TypedData_Get_Struct(self, ScpHolder, &scp_type, holder);
294
+ mode = ssh_scp_request_get_permissions(holder->scp);
295
+ RAISE_IF_ERROR(mode);
296
+ return INT2FIX(mode);
297
+ }
298
+
299
+ static void *nogvl_accept_request(void *ptr) {
300
+ struct nogvl_scp_args *args = ptr;
301
+ args->rc = ssh_scp_accept_request(args->scp);
302
+ return NULL;
303
+ }
304
+
305
+ /* @overload accept_request
306
+ * Accepts transfer of a file or creation of a directory coming from the remote
307
+ * party.
308
+ * @since 0.2.0
309
+ * @return [nil]
310
+ * @see http://api.libssh.org/stable/group__libssh__scp.html
311
+ * ssh_scp_accept_request
312
+ */
313
+ static VALUE m_accept_request(VALUE self) {
314
+ ScpHolder *holder;
315
+ struct nogvl_scp_args args;
316
+
317
+ TypedData_Get_Struct(self, ScpHolder, &scp_type, holder);
318
+ args.scp = holder->scp;
319
+ rb_thread_call_without_gvl(nogvl_accept_request, &args, RUBY_UBF_IO, NULL);
320
+ RAISE_IF_ERROR(args.rc);
321
+ return Qnil;
322
+ }
323
+
324
+ struct nogvl_deny_request_args {
325
+ ssh_scp scp;
326
+ const char *reason;
327
+ int rc;
328
+ };
329
+
330
+ static void *nogvl_deny_request(void *ptr) {
331
+ struct nogvl_deny_request_args *args = ptr;
332
+ args->rc = ssh_scp_deny_request(args->scp, args->reason);
333
+ return NULL;
334
+ }
335
+
336
+ /* @overload deny_request
337
+ * Deny the transfer of a file or creation of a directory coming from the
338
+ * remote party.
339
+ * @since 0.2.0
340
+ * @return [nil]
341
+ * @see http://api.libssh.org/stable/group__libssh__scp.html
342
+ * ssh_scp_deny_request
343
+ */
344
+ static VALUE m_deny_request(VALUE self, VALUE reason) {
345
+ ScpHolder *holder;
346
+ struct nogvl_deny_request_args args;
347
+
348
+ TypedData_Get_Struct(self, ScpHolder, &scp_type, holder);
349
+ args.scp = holder->scp;
350
+ args.reason = StringValueCStr(reason);
351
+ rb_thread_call_without_gvl(nogvl_deny_request, &args, RUBY_UBF_IO, NULL);
352
+ RAISE_IF_ERROR(args.rc);
353
+ return Qnil;
354
+ }
355
+
356
+ struct nogvl_read_args {
357
+ ssh_scp scp;
358
+ void *buffer;
359
+ size_t size;
360
+ int rc;
361
+ };
362
+
363
+ static void *nogvl_read(void *ptr) {
364
+ struct nogvl_read_args *args = ptr;
365
+ args->rc = ssh_scp_read(args->scp, args->buffer, args->size);
366
+ return NULL;
367
+ }
368
+
369
+ /* @overload read(size)
370
+ * Read from a remote scp file.
371
+ * @since 0.2.0
372
+ * @param [Fixnum] The size of the buffer.
373
+ * @return [String]
374
+ * @see http://api.libssh.org/stable/group__libssh__scp.html ssh_scp_read
375
+ */
376
+ static VALUE m_read(VALUE self, VALUE size) {
377
+ ScpHolder *holder;
378
+ struct nogvl_read_args args;
379
+
380
+ TypedData_Get_Struct(self, ScpHolder, &scp_type, holder);
381
+ Check_Type(size, T_FIXNUM);
382
+ args.scp = holder->scp;
383
+ args.size = FIX2INT(size);
384
+ args.buffer = ALLOC_N(char, args.size);
385
+ rb_thread_call_without_gvl(nogvl_read, &args, RUBY_UBF_IO, NULL);
386
+ if (args.rc == SSH_ERROR) {
387
+ ruby_xfree(args.buffer);
388
+ RAISE_IF_ERROR(args.rc);
389
+ return Qnil; /* unreachable */
390
+ } else {
391
+ VALUE ret = rb_utf8_str_new(args.buffer, args.rc);
392
+ ruby_xfree(args.buffer);
393
+ return ret;
394
+ }
395
+ }
396
+
397
+ /* @overload request_warning
398
+ * Get the warning string.
399
+ * @since 0.2.0
400
+ * @return [String, nil] A warning string. +nil+ on error.
401
+ * @see http://api.libssh.org/stable/group__libssh__scp.html
402
+ * ssh_scp_request_get_warning
403
+ */
404
+ static VALUE m_request_warning(VALUE self) {
405
+ ScpHolder *holder;
406
+ const char *warning;
407
+
408
+ TypedData_Get_Struct(self, ScpHolder, &scp_type, holder);
409
+ warning = ssh_scp_request_get_warning(holder->scp);
410
+ if (warning == NULL) {
411
+ return Qnil;
412
+ } else {
413
+ return rb_str_new_cstr(warning);
414
+ }
415
+ }
416
+
417
+ void Init_libssh_scp(void) {
418
+ rb_cLibSSHScp = rb_define_class_under(rb_mLibSSH, "Scp", rb_cObject);
419
+ rb_define_alloc_func(rb_cLibSSHScp, scp_alloc);
420
+
421
+ rb_define_const(rb_cLibSSHScp, "REQUEST_NEWFILE",
422
+ INT2FIX(SSH_SCP_REQUEST_NEWFILE));
423
+ rb_define_const(rb_cLibSSHScp, "REQUEST_NEWDIR",
424
+ INT2FIX(SSH_SCP_REQUEST_NEWDIR));
425
+ rb_define_const(rb_cLibSSHScp, "REQUEST_ENDDIR",
426
+ INT2FIX(SSH_SCP_REQUEST_ENDDIR));
427
+ rb_define_const(rb_cLibSSHScp, "REQUEST_WARNING",
428
+ INT2FIX(SSH_SCP_REQUEST_WARNING));
429
+ rb_define_const(rb_cLibSSHScp, "REQUEST_EOF", INT2FIX(SSH_SCP_REQUEST_EOF));
430
+
431
+ rb_define_method(rb_cLibSSHScp, "initialize", RUBY_METHOD_FUNC(m_initialize),
432
+ 3);
433
+ rb_define_method(rb_cLibSSHScp, "init", RUBY_METHOD_FUNC(m_init), 0);
434
+ rb_define_method(rb_cLibSSHScp, "close", RUBY_METHOD_FUNC(m_close), 0);
435
+ rb_define_method(rb_cLibSSHScp, "push_file", RUBY_METHOD_FUNC(m_push_file),
436
+ 3);
437
+ rb_define_method(rb_cLibSSHScp, "write", RUBY_METHOD_FUNC(m_write), 1);
438
+
439
+ rb_define_method(rb_cLibSSHScp, "pull_request",
440
+ RUBY_METHOD_FUNC(m_pull_request), 0);
441
+ rb_define_method(rb_cLibSSHScp, "request_size",
442
+ RUBY_METHOD_FUNC(m_request_size), 0);
443
+ rb_define_method(rb_cLibSSHScp, "request_filename",
444
+ RUBY_METHOD_FUNC(m_request_filename), 0);
445
+ rb_define_method(rb_cLibSSHScp, "request_permissions",
446
+ RUBY_METHOD_FUNC(m_request_permissions), 0);
447
+ rb_define_method(rb_cLibSSHScp, "accept_request",
448
+ RUBY_METHOD_FUNC(m_accept_request), 0);
449
+ rb_define_method(rb_cLibSSHScp, "deny_request",
450
+ RUBY_METHOD_FUNC(m_deny_request), 1);
451
+ rb_define_method(rb_cLibSSHScp, "read", RUBY_METHOD_FUNC(m_read), 1);
452
+ rb_define_method(rb_cLibSSHScp, "request_warning",
453
+ RUBY_METHOD_FUNC(m_request_warning), 0);
454
+
455
+ id_read = rb_intern("read");
456
+ id_write = rb_intern("write");
457
+ }