mallcop 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Carl Lerche, Aaron Patterson
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,48 @@
1
+ = mall_cop
2
+
3
+ * FIX (url)
4
+
5
+ == DESCRIPTION:
6
+
7
+ FIX (describe your package)
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ * FIX (list of features or problems)
12
+
13
+ == SYNOPSIS:
14
+
15
+ FIX (code sample of usage)
16
+
17
+ == REQUIREMENTS:
18
+
19
+ * FIX (list of requirements)
20
+
21
+ == INSTALL:
22
+
23
+ * FIX (sudo gem install, anything else)
24
+
25
+ == LICENSE:
26
+
27
+ (The MIT License)
28
+
29
+ Copyright (c) 2010 FIX
30
+
31
+ Permission is hereby granted, free of charge, to any person obtaining
32
+ a copy of this software and associated documentation files (the
33
+ 'Software'), to deal in the Software without restriction, including
34
+ without limitation the rights to use, copy, modify, merge, publish,
35
+ distribute, sublicense, and/or sell copies of the Software, and to
36
+ permit persons to whom the Software is furnished to do so, subject to
37
+ the following conditions:
38
+
39
+ The above copyright notice and this permission notice shall be
40
+ included in all copies or substantial portions of the Software.
41
+
42
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
43
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
44
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
45
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
46
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
47
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
48
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,222 @@
1
+ #include <mallcop.h>
2
+
3
+ VALUE rb_cMallCopChannel;
4
+
5
+ static void dealloc(MallCopChannel *m_channel)
6
+ {
7
+ BLOCK(libssh2_channel_free( m_channel->libssh2_channel ));
8
+ mallcop_session_release( m_channel->m_session );
9
+ free( m_channel );
10
+ }
11
+
12
+ static VALUE allocate(VALUE klass)
13
+ {
14
+ MallCopChannel *m_channel = calloc( 1, sizeof(MallCopChannel) );
15
+ return Data_Wrap_Struct(klass, 0, dealloc, m_channel);
16
+ }
17
+
18
+ static VALUE initialize(VALUE self)
19
+ {
20
+ MallCopChannel *m_channel;
21
+ MallCopSession *m_session;
22
+ VALUE session;
23
+
24
+ Data_Get_Struct(self, MallCopChannel, m_channel);
25
+
26
+ if ( m_channel->libssh2_channel ) {
27
+ rb_raise(rb_eRuntimeError, "the libssh2 channel is already initialized");
28
+ return Qnil;
29
+ }
30
+
31
+ session = rb_iv_get(self, "@session");
32
+
33
+ if ( NIL_P(session) ) {
34
+ rb_raise(rb_eRuntimeError, "@session is nil");
35
+ return Qfalse;
36
+ }
37
+
38
+ Data_Get_Struct(session, MallCopSession, m_session);
39
+
40
+ m_channel->libssh2_channel = libssh2_channel_open_session(m_session->libssh2_session);
41
+
42
+ if ( !m_channel->libssh2_channel ) {
43
+ if ( libssh2_session_last_errno(m_session->libssh2_session) == LIBSSH2_ERROR_EAGAIN ) {
44
+ return INT2FIX(LIBSSH2_ERROR_EAGAIN);
45
+ }
46
+
47
+ mallcop_raise_last_error(m_session, rb_eRuntimeError);
48
+ return Qnil;
49
+ }
50
+
51
+ // Initializing the libssh2 channel is successful, so we can associate
52
+ // the session object with the channel object.
53
+ m_channel->m_session = m_session;
54
+ mallcop_session_retain(m_session);
55
+
56
+ return Qtrue;
57
+ }
58
+
59
+ static VALUE request_pty(VALUE self, VALUE term)
60
+ {
61
+ MallCopChannel *m_channel;
62
+ int ret;
63
+
64
+ Data_Get_Struct(self, MallCopChannel, m_channel);
65
+
66
+ ret = libssh2_channel_request_pty(m_channel->libssh2_channel, StringValuePtr(term));
67
+
68
+ if ( ret == 0 || ret == LIBSSH2_ERROR_EAGAIN ) {
69
+ return INT2FIX(ret);
70
+ }
71
+
72
+ mallcop_raise_last_error(m_channel->m_session, rb_eRuntimeError);
73
+ return Qnil;
74
+ }
75
+
76
+ static VALUE shell(VALUE self)
77
+ {
78
+ MallCopChannel *m_channel;
79
+ int ret;
80
+
81
+ Data_Get_Struct(self, MallCopChannel, m_channel);
82
+
83
+ ret = libssh2_channel_shell(m_channel->libssh2_channel);
84
+
85
+ if ( ret == 0 || ret == LIBSSH2_ERROR_EAGAIN ) {
86
+ return INT2FIX(ret);
87
+ }
88
+
89
+ mallcop_raise_last_error(m_channel->m_session, rb_eRuntimeError);
90
+ return Qnil;
91
+ }
92
+
93
+ static VALUE channel_exec(VALUE self, VALUE command)
94
+ {
95
+ MallCopChannel *m_channel;
96
+ int ret;
97
+
98
+ Data_Get_Struct(self, MallCopChannel, m_channel);
99
+
100
+ ret = libssh2_channel_exec(m_channel->libssh2_channel, StringValuePtr(command));
101
+
102
+ if ( ret == 0 || ret == LIBSSH2_ERROR_EAGAIN ) {
103
+ return INT2FIX(ret);
104
+ }
105
+
106
+ mallcop_raise_last_error(m_channel->m_session, rb_eRuntimeError);
107
+ return Qnil;
108
+ }
109
+
110
+ static VALUE channel_read(VALUE self)
111
+ {
112
+ MallCopChannel *m_channel;
113
+ char buffer[0x4000];
114
+ int count;
115
+
116
+ Data_Get_Struct(self, MallCopChannel, m_channel);
117
+
118
+ count = libssh2_channel_read(m_channel->libssh2_channel, buffer, sizeof(buffer));
119
+
120
+ if ( count >= 0 ) {
121
+ return rb_str_new((const char *) &buffer, count);
122
+ } else if ( count == LIBSSH2_ERROR_EAGAIN ) {
123
+ return INT2FIX(LIBSSH2_ERROR_EAGAIN);
124
+ }
125
+
126
+ mallcop_raise_last_error(m_channel->m_session, rb_eRuntimeError);
127
+ return Qnil;
128
+ }
129
+
130
+ static VALUE channel_write(VALUE self, VALUE string)
131
+ {
132
+ MallCopChannel *m_channel;
133
+ ssize_t ret;
134
+
135
+ Data_Get_Struct(self, MallCopChannel, m_channel);
136
+
137
+ ret = libssh2_channel_write(m_channel->libssh2_channel, StringValuePtr(string), RSTRING_LEN(string));
138
+
139
+ if ( ret >= 0 || ret == LIBSSH2_ERROR_EAGAIN ) {
140
+ return INT2FIX(ret);
141
+ }
142
+
143
+ mallcop_raise_last_error(m_channel->m_session, rb_eRuntimeError);
144
+ return Qnil;
145
+ }
146
+
147
+ static VALUE channel_close(VALUE self)
148
+ {
149
+ MallCopChannel *m_channel;
150
+ int ret;
151
+
152
+ Data_Get_Struct(self, MallCopChannel, m_channel);
153
+
154
+ ret = libssh2_channel_close(m_channel->libssh2_channel);
155
+
156
+ if ( ret >= 0 || ret == LIBSSH2_ERROR_EAGAIN ) {
157
+ return INT2FIX(ret);
158
+ }
159
+
160
+ mallcop_raise_last_error(m_channel->m_session, rb_eRuntimeError);
161
+ return Qnil;
162
+ }
163
+
164
+ static VALUE eof(VALUE self)
165
+ {
166
+ MallCopChannel *m_channel;
167
+
168
+ Data_Get_Struct(self, MallCopChannel, m_channel);
169
+
170
+ return libssh2_channel_eof(m_channel->libssh2_channel) ? Qtrue : Qfalse;
171
+ }
172
+
173
+ static VALUE send_eof(VALUE self)
174
+ {
175
+ MallCopChannel *m_channel;
176
+ int ret;
177
+
178
+ Data_Get_Struct(self, MallCopChannel, m_channel);
179
+
180
+ ret = libssh2_channel_send_eof(m_channel->libssh2_channel);
181
+
182
+ if ( ret == 0 || ret == LIBSSH2_ERROR_EAGAIN ) {
183
+ return INT2FIX(ret);
184
+ }
185
+
186
+ mallcop_raise_last_error(m_channel->m_session, rb_eRuntimeError);
187
+ return Qnil;
188
+ }
189
+
190
+ static VALUE flush(VALUE self)
191
+ {
192
+ MallCopChannel *m_channel;
193
+ int ret;
194
+
195
+ Data_Get_Struct(self, MallCopChannel, m_channel);
196
+
197
+ ret = libssh2_channel_flush(m_channel->libssh2_channel);
198
+
199
+ if ( ret == 0 || ret == LIBSSH2_ERROR_EAGAIN ) {
200
+ return INT2FIX(ret);
201
+ }
202
+
203
+ mallcop_raise_last_error(m_channel->m_session, rb_eRuntimeError);
204
+ return Qnil;
205
+ }
206
+
207
+ void init_mallcop_channel()
208
+ {
209
+ rb_cMallCopChannel = rb_define_class_under(rb_mMallCop, "Channel", rb_cObject);
210
+ rb_define_alloc_func(rb_cMallCopChannel, allocate);
211
+
212
+ rb_define_private_method(rb_cMallCopChannel, "native_eof?", eof, 0);
213
+ rb_define_private_method(rb_cMallCopChannel, "native_initialize", initialize, 0);
214
+ rb_define_private_method(rb_cMallCopChannel, "native_channel_close", channel_close, 0);
215
+ rb_define_private_method(rb_cMallCopChannel, "native_channel_read", channel_read, 0);
216
+ rb_define_private_method(rb_cMallCopChannel, "native_channel_write", channel_write, 1);
217
+ rb_define_private_method(rb_cMallCopChannel, "native_request_pty", request_pty, 1);
218
+ rb_define_private_method(rb_cMallCopChannel, "native_shell", shell, 0);
219
+ rb_define_private_method(rb_cMallCopChannel, "native_channel_exec", channel_exec, 1);
220
+ rb_define_private_method(rb_cMallCopChannel, "native_send_eof", send_eof, 0);
221
+ rb_define_private_method(rb_cMallCopChannel, "native_flush", flush, 0);
222
+ }
@@ -0,0 +1,16 @@
1
+ require 'mkmf'
2
+
3
+ # :stopdoc:
4
+
5
+ dir_config 'libssh2', '/opt/local/include', '/opt/local/lib'
6
+
7
+ def asplode missing
8
+ abort "#{missing} is missing. Please install libssh2."
9
+ end
10
+
11
+ asplode('libssh2.h') unless find_header 'libssh2.h'
12
+ asplode('libssh2') unless find_library 'ssh2', 'libssh2_session_init_ex'
13
+
14
+ create_makefile 'mallcop/mallcop'
15
+
16
+ # :startdoc:
@@ -0,0 +1,33 @@
1
+ #include <mallcop.h>
2
+
3
+ VALUE rb_mMallCop;
4
+
5
+ void mallcop_raise_last_error(MallCopSession *m_session, VALUE klass)
6
+ {
7
+ char *errmsg;
8
+
9
+ libssh2_session_last_error(m_session->libssh2_session, &errmsg, NULL, 0);
10
+
11
+ rb_raise(klass, errmsg);
12
+ }
13
+
14
+ void Init_mallcop()
15
+ {
16
+ int rc;
17
+
18
+ rb_mMallCop = rb_define_module("MallCop");
19
+
20
+ rb_define_const(rb_mMallCop, "ERROR_ALLOC", INT2FIX(LIBSSH2_ERROR_ALLOC));
21
+ rb_define_const(rb_mMallCop, "ERROR_BANNER_SEND", INT2FIX(LIBSSH2_ERROR_BANNER_SEND));
22
+ rb_define_const(rb_mMallCop, "ERROR_CHANNEL_FAILURE", INT2FIX(LIBSSH2_ERROR_CHANNEL_FAILURE));
23
+ rb_define_const(rb_mMallCop, "ERROR_EAGAIN", INT2FIX(LIBSSH2_ERROR_EAGAIN));
24
+ rb_define_const(rb_mMallCop, "ERROR_KEX_FAILURE", INT2FIX(LIBSSH2_ERROR_KEX_FAILURE));
25
+ rb_define_const(rb_mMallCop, "ERROR_SOCKET_DISCONNECT", INT2FIX(LIBSSH2_ERROR_SOCKET_DISCONNECT));
26
+ rb_define_const(rb_mMallCop, "ERROR_SOCKET_NONE", INT2FIX(LIBSSH2_ERROR_SOCKET_NONE));
27
+ rb_define_const(rb_mMallCop, "ERROR_SOCKET_SEND", INT2FIX(LIBSSH2_ERROR_SOCKET_SEND));
28
+ rb_define_const(rb_mMallCop, "ERROR_PROTO", INT2FIX(LIBSSH2_ERROR_PROTO));
29
+ rb_define_const(rb_mMallCop, "HASH_SHA1", INT2FIX(LIBSSH2_HOSTKEY_HASH_SHA1));
30
+
31
+ init_mallcop_session();
32
+ init_mallcop_channel();
33
+ }
@@ -0,0 +1,31 @@
1
+ #ifndef MALLCOP_H
2
+ #define MALLCOP_H
3
+
4
+ #include <ruby.h>
5
+ #include <libssh2.h>
6
+
7
+ #define BLOCK(stmt) while ((stmt) == LIBSSH2_ERROR_EAGAIN)
8
+
9
+ extern VALUE rb_mMallCop;
10
+ extern VALUE rb_cMallCopSession;
11
+ extern VALUE rb_cMallCopChannel;
12
+
13
+ typedef struct {
14
+ LIBSSH2_SESSION *libssh2_session;
15
+ int count;
16
+ } MallCopSession;
17
+
18
+ typedef struct {
19
+ LIBSSH2_CHANNEL *libssh2_channel;
20
+ MallCopSession *m_session;
21
+ } MallCopChannel;
22
+
23
+ void init_mallcop_channel();
24
+ void init_mallcop_session();
25
+
26
+ void mallcop_session_retain(MallCopSession *session);
27
+ void mallcop_session_release(MallCopSession *session);
28
+
29
+ void mallcop_raise_last_error(MallCopSession *m_session, VALUE klass);
30
+
31
+ #endif
@@ -0,0 +1,228 @@
1
+ #include <mallcop.h>
2
+
3
+ VALUE rb_cMallCopSession;
4
+
5
+ void mallcop_session_retain(MallCopSession *m_session)
6
+ {
7
+ ++m_session->count;
8
+ }
9
+
10
+ void mallcop_session_release(MallCopSession *m_session)
11
+ {
12
+ if ( --m_session->count == 0 ) {
13
+ BLOCK(libssh2_session_free( m_session->libssh2_session ));
14
+ free( m_session );
15
+ }
16
+ }
17
+
18
+ static void dealloc(MallCopSession *m_session)
19
+ {
20
+ mallcop_session_release( m_session );
21
+ }
22
+
23
+ static VALUE allocate(VALUE klass)
24
+ {
25
+ MallCopSession *m_session = calloc( 1, sizeof(MallCopSession) );
26
+ mallcop_session_retain(m_session);
27
+
28
+ return Data_Wrap_Struct(klass, 0, dealloc, m_session);
29
+ }
30
+
31
+ static VALUE initialize(VALUE self)
32
+ {
33
+ MallCopSession *m_session;
34
+
35
+ Data_Get_Struct(self, MallCopSession, m_session);
36
+
37
+ if ( m_session->libssh2_session ) {
38
+ rb_raise(rb_eRuntimeError, "the libssh2 session is already initialized");
39
+ return Qnil;
40
+ }
41
+
42
+ m_session->libssh2_session = libssh2_session_init();
43
+
44
+ if ( !m_session->libssh2_session ) {
45
+ rb_raise(rb_eRuntimeError, "session init failed");
46
+ return Qnil;
47
+ }
48
+
49
+ return Qtrue;
50
+ }
51
+
52
+ static VALUE set_blocking(VALUE self, VALUE blocking)
53
+ {
54
+ MallCopSession *m_session;
55
+
56
+ Data_Get_Struct(self, MallCopSession, m_session);
57
+
58
+ if ( !m_session->libssh2_session ) {
59
+ rb_raise(rb_eRuntimeError, "session not initialized yet");
60
+ return Qnil;
61
+ }
62
+
63
+ libssh2_session_set_blocking( m_session->libssh2_session, NUM2INT(blocking) );
64
+
65
+ return Qnil;
66
+ }
67
+
68
+ static VALUE start(VALUE self, VALUE r_fd)
69
+ {
70
+ MallCopSession *m_session;
71
+ int ret;
72
+ int fd = NUM2INT(r_fd);
73
+
74
+ Data_Get_Struct(self, MallCopSession, m_session);
75
+
76
+ ret = libssh2_session_startup(m_session->libssh2_session, fd);
77
+
78
+ if ( ret == 0 || ret == LIBSSH2_ERROR_EAGAIN ) {
79
+ return INT2FIX(ret);
80
+ }
81
+
82
+ mallcop_raise_last_error(m_session, rb_eRuntimeError);
83
+ return Qnil;
84
+ }
85
+
86
+ static VALUE hostkey_hash(VALUE self, VALUE hashtype)
87
+ {
88
+ MallCopSession *m_session;
89
+ const char *key;
90
+ int keytype = NUM2INT(hashtype);
91
+
92
+ Data_Get_Struct(self, MallCopSession, m_session);
93
+
94
+ key = libssh2_hostkey_hash(m_session->libssh2_session, keytype);
95
+
96
+ if ( !key ) {
97
+ return Qnil;
98
+ }
99
+
100
+ if ( keytype == LIBSSH2_HOSTKEY_HASH_SHA1 ) {
101
+ return rb_str_new(key, 20);
102
+ }
103
+
104
+ return rb_str_new(key, 16);
105
+ }
106
+
107
+ static VALUE userauth_list(VALUE self, VALUE user)
108
+ {
109
+ MallCopSession *m_session;
110
+ char * list;
111
+
112
+ Data_Get_Struct(self, MallCopSession, m_session);
113
+
114
+ do {
115
+ list = libssh2_userauth_list(m_session->libssh2_session,
116
+ StringValuePtr(user), RSTRING_LEN(user));
117
+ } while ( !list && libssh2_session_last_errno(m_session->libssh2_session) == LIBSSH2_ERROR_EAGAIN );
118
+
119
+ return rb_str_new2(list);
120
+ }
121
+
122
+ static VALUE userauth_password(VALUE self, VALUE user, VALUE password)
123
+ {
124
+ MallCopSession *m_session;
125
+ int ret;
126
+
127
+ Data_Get_Struct(self, MallCopSession, m_session);
128
+
129
+ ret = libssh2_userauth_password(m_session->libssh2_session,
130
+ StringValuePtr(user), StringValuePtr(password));
131
+
132
+ if ( ret == 0 || ret == LIBSSH2_ERROR_EAGAIN ) {
133
+ return INT2FIX(ret);
134
+ }
135
+
136
+ ret = libssh2_session_last_errno(m_session->libssh2_session);
137
+
138
+ switch (ret) {
139
+ case LIBSSH2_ERROR_PASSWORD_EXPIRED:
140
+ case LIBSSH2_ERROR_AUTHENTICATION_FAILED:
141
+ return INT2FIX(ret);
142
+ break;
143
+ }
144
+
145
+ mallcop_raise_last_error(m_session, rb_eRuntimeError);
146
+ return Qnil;
147
+ }
148
+
149
+ static VALUE userauth_publickey_fromfile(VALUE self, VALUE user, VALUE public_key, VALUE private_key, VALUE password)
150
+ {
151
+ MallCopSession *m_session;
152
+ int ret;
153
+
154
+ Data_Get_Struct(self, MallCopSession, m_session);
155
+
156
+ BLOCK(ret = libssh2_userauth_publickey_fromfile(m_session->libssh2_session,
157
+ StringValuePtr(user), StringValuePtr(public_key),
158
+ StringValuePtr(private_key), StringValuePtr(password)));
159
+
160
+ if(ret) {
161
+ switch(ret) {
162
+ case LIBSSH2_ERROR_AUTHENTICATION_FAILED:
163
+ case LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED:
164
+ return Qfalse;
165
+ break;
166
+ default:
167
+ rb_raise(rb_eRuntimeError, "authentication failed (%d)", ret);
168
+ break;
169
+ }
170
+ }
171
+
172
+ return Qtrue;
173
+ }
174
+
175
+ static VALUE disconnect(VALUE self, VALUE description)
176
+ {
177
+ MallCopSession *m_session;
178
+ int ret;
179
+
180
+ Data_Get_Struct(self, MallCopSession, m_session);
181
+
182
+ BLOCK(ret = libssh2_session_disconnect(m_session->libssh2_session,
183
+ StringValuePtr(description)));
184
+
185
+ return INT2NUM(ret);
186
+ }
187
+
188
+ static VALUE last_errno(VALUE self)
189
+ {
190
+ MallCopSession *m_session;
191
+ int ret;
192
+
193
+ Data_Get_Struct(self, MallCopSession, m_session);
194
+
195
+ ret = libssh2_session_last_errno(m_session->libssh2_session);
196
+
197
+ return INT2FIX(ret);
198
+ }
199
+
200
+ static VALUE last_errmsg(VALUE self)
201
+ {
202
+ MallCopSession *m_session;
203
+ char *errmsg;
204
+ int errlen;
205
+
206
+ Data_Get_Struct(self, MallCopSession, m_session);
207
+
208
+ libssh2_session_last_error(m_session->libssh2_session, &errmsg, &errlen, 0);
209
+
210
+ return rb_str_new(errmsg, (long) errlen);
211
+ }
212
+
213
+ void init_mallcop_session()
214
+ {
215
+ rb_cMallCopSession = rb_define_class_under(rb_mMallCop, "Session", rb_cObject);
216
+ rb_define_alloc_func(rb_cMallCopSession, allocate);
217
+
218
+ rb_define_private_method(rb_cMallCopSession, "native_initialize", initialize, 0);
219
+ rb_define_private_method(rb_cMallCopSession, "native_start", start, 1);
220
+ rb_define_private_method(rb_cMallCopSession, "native_set_blocking", set_blocking, 1);
221
+ rb_define_private_method(rb_cMallCopSession, "native_userauth_list", userauth_list, 1);
222
+ rb_define_private_method(rb_cMallCopSession, "native_last_errno", last_errno, 0);
223
+ rb_define_private_method(rb_cMallCopSession, "native_last_errmsg", last_errmsg, 0);
224
+ rb_define_private_method(rb_cMallCopSession, "native_userauth_password", userauth_password, 2);
225
+ rb_define_private_method(rb_cMallCopSession, "native_hostkey_hash", hostkey_hash, 1);
226
+ rb_define_private_method(rb_cMallCopSession, "native_userauth_publickey_fromfile", userauth_publickey_fromfile, 4);
227
+ rb_define_private_method(rb_cMallCopSession, "native_disconnect", disconnect, 1);
228
+ }
@@ -0,0 +1,48 @@
1
+ require 'mallcop/mallcop'
2
+ require 'mallcop/channel'
3
+ require 'mallcop/session'
4
+ require 'mallcop/shell'
5
+
6
+ module MallCop
7
+ VERSION = '1.0.0'
8
+
9
+ # Raised when something happens with connecting to the server
10
+ class RuntimeError < ::RuntimeError; end
11
+ class ConnectionError < StandardError ; end
12
+ class SessionError < StandardError ; end
13
+ class ChannelError < StandardError ; end
14
+
15
+ # Establishes a connection to the SSH server.
16
+ #
17
+ # Arguments
18
+ # =====
19
+ # +host+ The host to connect to
20
+ # +username+ The username to connect under.
21
+ #
22
+ # Optional:
23
+ # +options+
24
+ # :port The port to connect on (defaults to 22)
25
+ # :password Use a password based authentication strategy.
26
+ def self.connect(host, username, options = {})
27
+ session = Session.start(host, options[:port] || 22)
28
+
29
+ unless session.authenticate username, options
30
+ raise ChannelError, "Could not authenticate user '#{username}'"
31
+ end
32
+
33
+ if block_given?
34
+ yield session
35
+ session.disconnect("Closing session")
36
+ end
37
+
38
+ session
39
+ end
40
+
41
+ def self.shell(host, username, options = {})
42
+ connect(host, username, options) do |session|
43
+ shell = Shell.new(session)
44
+ yield shell
45
+ shell.cleanup
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,93 @@
1
+ module MallCop
2
+ class Channel
3
+
4
+ def initialize(session)
5
+ @session, @open = session, true
6
+
7
+ block { native_initialize }
8
+ end
9
+
10
+ def read
11
+ retval = ERROR_EAGAIN
12
+
13
+ until retval != ERROR_EAGAIN
14
+ @session.io_select
15
+ retval = read_nonblock
16
+ end
17
+
18
+ retval
19
+ end
20
+
21
+ def read_nonblock
22
+ raise ChannelError, "The channel has been closed" unless @open
23
+ native_channel_read
24
+ end
25
+
26
+ def write(cmd)
27
+ retval = ERROR_EAGAIN
28
+
29
+ until retval != ERROR_EAGAIN
30
+ @session.io_select(true)
31
+ retval = write_nonblock(cmd)
32
+ end
33
+
34
+ retval
35
+ end
36
+
37
+ def write_nonblock(cmd)
38
+ raise ChannelError, "The channel has been closed" unless @open
39
+ native_channel_write(cmd)
40
+ end
41
+
42
+ def eof?
43
+ native_eof?
44
+ end
45
+
46
+ def close
47
+ return true unless @open
48
+ block { native_channel_close }
49
+ true
50
+ ensure
51
+ @open = false
52
+ end
53
+
54
+ def request_pty(term)
55
+ block { native_request_pty(term) }
56
+ true
57
+ end
58
+
59
+ def shell
60
+ block { native_shell }
61
+ true
62
+ end
63
+
64
+ def channel_exec(cmd)
65
+ block { native_channel_exec(cmd) }
66
+ true
67
+ end
68
+
69
+ def send_eof
70
+ block { native_send_eof }
71
+ true
72
+ end
73
+
74
+ def flush
75
+ block { native_flush }
76
+ true
77
+ end
78
+
79
+ private
80
+
81
+ def block
82
+ retval = yield
83
+
84
+ until retval != ERROR_EAGAIN
85
+ @session.io_select
86
+ retval = yield
87
+ end
88
+
89
+ retval
90
+ end
91
+
92
+ end
93
+ end
@@ -0,0 +1,107 @@
1
+ require 'socket'
2
+
3
+ module MallCop
4
+ class Session
5
+
6
+ def self.start(host, port)
7
+ session = new(host, port)
8
+ session.start
9
+ session
10
+ end
11
+
12
+ def initialize(host, port)
13
+ native_initialize
14
+ native_set_blocking(0)
15
+ @host = host
16
+ @port = port
17
+ end
18
+
19
+ def start
20
+ block { native_start(socket.fileno) }
21
+ true
22
+ end
23
+
24
+ def authenticate(username, options)
25
+ allowed = authlist_for username
26
+
27
+ attempted = []
28
+
29
+ auth_methods.each do |method|
30
+ next unless allowed.include?(method)
31
+
32
+ attempted << method
33
+
34
+ if send "authenticate_via_#{method.downcase}", username, options
35
+ return true
36
+ end
37
+ end
38
+
39
+ false
40
+ end
41
+
42
+ def open_channel
43
+ Channel.new(self)
44
+ end
45
+
46
+ def hostkey_hash(hashtype)
47
+ native_hostkey_hash(hashtype)
48
+ end
49
+
50
+ def userauth_publickey_fromfile(user, public_key, private_key, password)
51
+ native_userauth_publickey_fromfile(user, public_key, private_key, password)
52
+ end
53
+
54
+ def disconnect(description)
55
+ native_disconnect(description)
56
+ end
57
+
58
+ def io_select(write = false)
59
+ write ?
60
+ select([], [socket], nil, nil) :
61
+ select([socket], nil, nil, nil)
62
+ end
63
+
64
+ private
65
+
66
+ def socket
67
+ @socket ||= Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0).tap do |s|
68
+ s.connect Socket.sockaddr_in(@port, @host)
69
+ end
70
+ rescue Errno::ECONNREFUSED => e
71
+ raise ConnectionError, "Cannot establish a connection to #{@host} on port #{@port}"
72
+ end
73
+
74
+ def auth_methods
75
+ %w(publickey hostbased password)
76
+ end
77
+
78
+ def authlist_for(username)
79
+ native_userauth_list(username).split ','
80
+ end
81
+
82
+ def authenticate_via_password(username, options)
83
+ return unless options[:password]
84
+ block { native_userauth_password(username, options[:password]) } == 0
85
+ end
86
+
87
+ def authenticate_via_publickey(username, options)
88
+ return unless options[:keys] && options[:keys].any?
89
+ # Not implemented yet
90
+ end
91
+
92
+ def authenticate_via_hostbased(username, options)
93
+ # Not implemented yet
94
+ end
95
+
96
+ def block
97
+ retval = yield
98
+
99
+ until retval != ERROR_EAGAIN
100
+ io_select
101
+ retval = yield
102
+ end
103
+
104
+ retval
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,49 @@
1
+ require 'digest/sha1'
2
+
3
+ module MallCop
4
+ class Shell
5
+ attr_reader :channel
6
+
7
+ def initialize(session)
8
+ @session = session
9
+ @channel = session.open_channel
10
+
11
+ @channel.shell
12
+ end
13
+
14
+ def sh(cmd)
15
+ res, code = exec_with_status(cmd)
16
+ res
17
+ end
18
+
19
+ def cleanup
20
+ @channel.close
21
+ end
22
+
23
+ private
24
+
25
+ def exec_with_status(cmd)
26
+ separator = generate_separator
27
+ # An `echo ''` is appended for now, mostly because I don't know of
28
+ # a better way to do this.
29
+ cmd = cmd.dup.strip
30
+ cmd << ";" if cmd !~ /[;&]$/
31
+ cmd << %[ echo #{separator} $?\n]
32
+
33
+ @channel.write(cmd)
34
+
35
+ res = ""
36
+ until res =~ /#{separator} (\d+)\Z/m
37
+ res << @channel.read
38
+ end
39
+
40
+ return $`.strip, $1.to_i
41
+ end
42
+
43
+ def generate_separator
44
+ s = Digest::SHA1.hexdigest([channel.object_id, object_id, Time.now.to_i, Time.now.usec, rand(0xFFFFFFFF)].join(":"))
45
+ s << Digest::SHA1.hexdigest(s)
46
+ end
47
+
48
+ end
49
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mallcop
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Carl Lerche
14
+ - Aaron Patterson
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2010-08-18 00:00:00 -07:00
20
+ default_executable:
21
+ dependencies: []
22
+
23
+ description: Don't reinvent the wheel.
24
+ email:
25
+ - me@carllerche.com
26
+ executables: []
27
+
28
+ extensions:
29
+ - ext/mallcop/extconf.rb
30
+ extra_rdoc_files: []
31
+
32
+ files:
33
+ - ext/mallcop/extconf.rb
34
+ - ext/mallcop/channel.c
35
+ - ext/mallcop/mallcop.c
36
+ - ext/mallcop/session.c
37
+ - ext/mallcop/mallcop.h
38
+ - lib/mallcop.rb
39
+ - lib/mallcop/channel.rb
40
+ - lib/mallcop/session.rb
41
+ - lib/mallcop/shell.rb
42
+ - README.md
43
+ - LICENSE
44
+ has_rdoc: true
45
+ homepage: http://github.com/carllerche/mallcop
46
+ licenses: []
47
+
48
+ post_install_message:
49
+ rdoc_options: []
50
+
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ hash: 3
59
+ segments:
60
+ - 0
61
+ version: "0"
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ hash: 23
68
+ segments:
69
+ - 1
70
+ - 3
71
+ - 6
72
+ version: 1.3.6
73
+ requirements: []
74
+
75
+ rubyforge_project: mallcop
76
+ rubygems_version: 1.3.7
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: Bindings to libssh2
80
+ test_files: []
81
+