libssh 0.1.0 → 0.2.0

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