24 #include <unistd.h> |
24 #include <unistd.h> |
25 |
25 |
26 |
26 |
27 VALUE rbjail_mBSD; |
27 VALUE rbjail_mBSD; |
28 VALUE rbjail_cBSDJail; |
28 VALUE rbjail_cBSDJail; |
|
29 |
|
30 |
|
31 /* |
|
32 * Debug logging function |
|
33 */ |
|
34 void |
|
35 #ifdef HAVE_STDARG_PROTOTYPES |
|
36 rlink_debug(const char *fmt, ...) |
|
37 #else |
|
38 rlink_debug( const char *fmt, va_dcl ) |
|
39 #endif |
|
40 { |
|
41 char buf[BUFSIZ], buf2[BUFSIZ]; |
|
42 va_list args; |
|
43 |
|
44 if ( !RTEST(ruby_debug) ) return; |
|
45 |
|
46 snprintf( buf, BUFSIZ, "Jail Debug>>> %s", fmt ); |
|
47 |
|
48 va_init_list( args, fmt ); |
|
49 vsnprintf( buf2, BUFSIZ, buf, args ); |
|
50 fputs( buf2, stderr ); |
|
51 fputs( "\n", stderr ); |
|
52 fflush( stderr ); |
|
53 va_end( args ); |
|
54 } |
29 |
55 |
30 |
56 |
31 /* |
57 /* |
32 struct jail { |
58 struct jail { |
33 u_int32_t version; |
59 u_int32_t version; |
57 |
82 |
58 /* |
83 /* |
59 * GC Free function |
84 * GC Free function |
60 */ |
85 */ |
61 static void |
86 static void |
62 rbjail_jail_gc_free( ptr ) |
87 rbjail_gc_free( jail *ptr ) { |
63 jail *ptr; |
|
64 { |
|
65 if ( ptr ) { |
88 if ( ptr ) { |
|
89 if ( ptr->path ) xfree( ptr->path ); |
|
90 if ( ptr->hostname ) xfree( ptr->hostname ); |
|
91 |
66 ptr->path = NULL; |
92 ptr->path = NULL; |
67 ptr->hostname = NULL; |
93 ptr->hostname = NULL; |
|
94 |
68 xfree( ptr ); |
95 xfree( ptr ); |
69 } |
96 } |
70 |
97 |
71 else { |
98 else { |
72 debugMsg(( "Not freeing an uninitialized rlink_SENTENCE" )); |
99 debugMsg(( "Not freeing an uninitialized jail" )); |
73 } |
100 } |
74 } |
101 } |
75 |
102 |
76 |
103 |
77 /* |
104 /* |
78 * Object validity checker. Returns the data pointer. |
105 * Object validity checker. Returns the data pointer. |
79 */ |
106 */ |
80 static rlink_SENTENCE * |
107 static jail * |
81 check_sentence( self ) |
108 rbjail_check_jail( VALUE self ) { |
82 VALUE self; |
109 debugMsg(( "Checking a BSD::Jail object (%d).", self )); |
83 { |
|
84 debugMsg(( "Checking a LinkParser::Sentence object (%d).", self )); |
|
85 Check_Type( self, T_DATA ); |
110 Check_Type( self, T_DATA ); |
86 |
111 |
87 if ( !IsSentence(self) ) { |
112 if ( !rb_obj_is_kind_of(self, rbjail_cBSDJail) ) { |
88 rb_raise( rb_eTypeError, "wrong argument type %s (expected LinkParser::Sentence)", |
113 rb_raise( rb_eTypeError, "wrong argument type %s (expected BSD::Jail)", |
89 rb_class2name(CLASS_OF( self )) ); |
114 rb_class2name(CLASS_OF( self )) ); |
90 } |
115 } |
91 |
116 |
92 return DATA_PTR( self ); |
117 return DATA_PTR( self ); |
93 } |
118 } |
94 |
119 |
95 |
120 |
96 /* |
121 /* |
97 * Fetch the data pointer and check it for sanity. |
122 * Fetch the data pointer and check it for sanity. |
98 */ |
123 */ |
99 static rlink_SENTENCE * |
124 static jail * |
100 get_sentence( self ) |
125 rbjail_get_jail( VALUE self ) { |
101 VALUE self; |
126 jail *ptr = check_sentence( self ); |
102 { |
127 |
103 rlink_SENTENCE *ptr = check_sentence( self ); |
128 debugMsg(( "Fetching a Jail (%p).", ptr )); |
104 |
|
105 debugMsg(( "Fetching a Sentence (%p).", ptr )); |
|
106 if ( !ptr ) |
129 if ( !ptr ) |
107 rb_raise( rb_eRuntimeError, "uninitialized Sentence" ); |
130 rb_raise( rb_eRuntimeError, "uninitialized Sentence" ); |
108 |
131 |
109 return ptr; |
132 return ptr; |
110 } |
133 } |
111 |
134 |
112 |
135 |
113 /* |
136 /* -------------------------------------------------------------- |
114 * Publicly-usable sentence-fetcher |
137 * Jail utility functions |
115 */ |
138 * -------------------------------------------------------------- */ |
116 rlink_SENTENCE * |
139 |
117 rlink_get_sentence( self ) |
140 /* |
118 VALUE self; |
141 * Try to jail_attach() to the specified +jid+, raising an exception if it fails. |
119 { |
142 */ |
120 return get_sentence( self ); |
|
121 } |
|
122 |
|
123 |
|
124 |
|
125 |
|
126 |
|
127 |
|
128 static void |
143 static void |
129 rbjail_do_jail_attach( int jid ) |
144 rbjail_do_jail_attach( int jid ) |
130 { |
145 { |
131 if ( jail_attach(jid) == -1 ) |
146 if ( jail_attach(jid) == -1 ) |
132 rb_sys_fail( "jail_attach" ); |
147 rb_sys_fail( "jail_attach" ); |
133 } |
148 } |
134 |
149 |
135 /* Mostly ripped off from Ruby's process.c */ |
150 |
|
151 /* |
|
152 * Fork + Block function for rbjail_attach(). Mostly stolen from Ruby's process.c. |
|
153 */ |
136 static VALUE |
154 static VALUE |
137 rbjail_attach_block( int jid ) |
155 rbjail_attach_block( int jid ) |
138 { |
156 { |
139 int pid; |
157 int pid; |
140 |
158 |
141 rb_secure(2); |
159 rb_secure(2); |
142 |
160 |
143 fflush(stdout); |
161 fflush( stdout ); |
144 fflush(stderr); |
162 fflush( stderr ); |
145 |
163 |
146 switch ( pid = fork() ) { |
164 switch ( pid = fork() ) { |
147 case 0: |
165 case 0: |
148 rb_thread_atfork(); |
166 rb_thread_atfork(); |
149 if ( rb_block_given_p() ) { |
167 if ( rb_block_given_p() ) { |
162 default: |
180 default: |
163 return INT2FIX( pid ); |
181 return INT2FIX( pid ); |
164 } |
182 } |
165 } |
183 } |
166 |
184 |
167 static VALUE |
185 |
168 rbjail_attach( int argc, VALUE *argv, VALUE self ) |
186 /* -------------------------------------------------------------- |
169 { |
187 * Class methods |
|
188 * -------------------------------------------------------------- */ |
|
189 |
|
190 /* |
|
191 * call-seq: |
|
192 * BSD::Jail.allocate -> bsdjail |
|
193 * |
|
194 * Allocate a new BSD::Jail object. |
|
195 * |
|
196 */ |
|
197 static VALUE |
|
198 rbjail_jail_s_allocate( VALUE klass ) { |
|
199 return Data_Wrap_Struct( klass, 0, rbjail_gc_free, 0 ); |
|
200 } |
|
201 |
|
202 |
|
203 /* |
|
204 * call-seq: |
|
205 * BSD::Jail.list -> array |
|
206 * |
|
207 * Return an Array of all the running jails on the host. |
|
208 * |
|
209 */ |
|
210 static VALUE |
|
211 rbjail_jail_s_list( VALUE klass ) { |
|
212 VALUE rval = rb_ary_new(); |
|
213 struct xprison *xp; |
|
214 struct in_addr in; |
|
215 size_t i, len; |
|
216 |
|
217 if ( sysctlbyname("jail.list", NULL, &len, NULL, 0) == -1 ) |
|
218 rb_sys_fail( "sysctlbyname(): jail.list" ); |
|
219 |
|
220 xp = ALLOCA_N( xprison, 1 ); |
|
221 |
|
222 if ( sysctlbyname("jail.list", xp, &len, NULL, 0) == -1 ) { |
|
223 rb_sys_fail( "sysctlbyname(): jail.list" ); |
|
224 } |
|
225 |
|
226 if ( len < sizeof(*xp) || len % sizeof(*xp) || |
|
227 xp->pr_version != KINFO_PRISON_VERSION ) |
|
228 rb_fatal( "Kernel and userland out of sync" ); |
|
229 |
|
230 len /= sizeof( *xp ); |
|
231 for ( i = 0; i < len; i++ ) { |
|
232 VALUE jail, args[3]; |
|
233 |
|
234 /* Hostname */ |
|
235 args[0] = rb_str_new2( xp->pr_host ); |
|
236 |
|
237 /* IP */ |
|
238 in.s_addr = ntohl( xp->pr_ip ); |
|
239 args[1] = rb_str_new2( inet_ntoa(in) ); |
|
240 |
|
241 /* Path */ |
|
242 args[2] = rb_str_new2( xp->pr_path ); |
|
243 |
|
244 jail = rb_class_new_instance( 3, args, klass ); |
|
245 rb_ary_push( rval, jail ); |
|
246 |
|
247 xp++; |
|
248 } |
|
249 |
|
250 return rval; |
|
251 } |
|
252 |
|
253 |
|
254 |
|
255 |
|
256 /* -------------------------------------------------------------- |
|
257 * Instance methods |
|
258 * -------------------------------------------------------------- */ |
|
259 |
|
260 /* |
|
261 * call-seq: |
|
262 * BSD::Jail.new( hostname, ip, path ) -> new_jail |
|
263 * |
|
264 * Create a new jail for the given +hostname+, +ip+, and +path+. |
|
265 */ |
|
266 static VALUE |
|
267 rbjail_jail_initialize( VALUE self, VALUE hostname, VALUE ip, VALUE path ) { |
|
268 if ( !rbjail_check_jail(self) ) { |
|
269 struct jail *ptr = rbjail_jail_alloc(); |
|
270 char *pathstr = NULL, *hostnamestr = NULL; |
|
271 |
|
272 Check_Type( hostname, T_STRING ); |
|
273 Check_Type( ip, T_STRING ); |
|
274 |
|
275 pathstr = ALLOC_N( char, RSTRING(path)->len ); |
|
276 strncpy( pathstr, RSTRING(path)->ptr, RSTRING(path)->len ); |
|
277 |
|
278 hostnamestr = ALLOC_N( char, RSTRING(hostname)->len ); |
|
279 strncpy( hostnamestr, RSTRING(hostname)->ptr, RSTRING(hostname)->len ); |
|
280 |
|
281 /* |
|
282 struct jail { |
|
283 u_int32_t version; |
|
284 char *path; |
|
285 char *hostname; |
|
286 u_int32_t ip_number; |
|
287 }; |
|
288 */ |
|
289 ptr->version = 0; /* Per the manpage's recommendation */ |
|
290 ptr->ip_number = inet_addr( StringValuePtr(ip) ); |
|
291 ptr->path = path; |
|
292 ptr->hostname = hostname; |
|
293 } |
|
294 } |
|
295 |
|
296 |
|
297 /* |
|
298 * call-seq: |
|
299 * jail.attach -> true or false |
|
300 * jail.attach { block } -> pid |
|
301 * |
|
302 * Attach to the given jail. In the non-block form, attach the current process to the |
|
303 * jail and return +true+ if it succeeds. This is a one-way operation, and requires root |
|
304 * privileges. |
|
305 * |
|
306 * In the block form, the process will be forked, and the block will be attached to the jail and |
|
307 * run by the child. The parent process will receive the process ID of the child. |
|
308 */ |
|
309 static VALUE |
|
310 rbjail_attach( int argc, VALUE *argv, VALUE self ) { |
170 VALUE jidnum, rval; |
311 VALUE jidnum, rval; |
171 int jid; |
312 int jid; |
172 |
313 |
173 rb_scan_args( argc, argv, "1", &jidnum ); |
314 rb_scan_args( argc, argv, "1", &jidnum ); |
174 jid = NUM2INT( jidnum ); |
315 jid = NUM2INT( jidnum ); |
183 } |
324 } |
184 |
325 |
185 return rval; |
326 return rval; |
186 } |
327 } |
187 |
328 |
188 static VALUE |
329 |
189 rbjail_list( VALUE self ) |
330 /* |
190 { |
331 * I can't remember how the hell you document a class, but that will go here. |
191 struct kinfo_prison *sxp, *xp; |
332 */ |
192 struct in_addr in; |
|
193 size_t i, len; |
|
194 |
|
195 if (sysctlbyname("jail.list", NULL, &len, NULL, 0) == -1) |
|
196 rb_sys_fail("sysctlbyname(): jail.list"); |
|
197 |
|
198 xp = ALLOCA_N( kinfo_prison, 1 ); |
|
199 |
|
200 if (sysctlbyname("jail.list", xp, &len, NULL, 0) == -1) { |
|
201 rb_sys_fail("sysctlbyname(): jail.list"); |
|
202 } |
|
203 |
|
204 if (len < sizeof(*xp) || len % sizeof(*xp) || |
|
205 xp->pr_version != KINFO_PRISON_VERSION) |
|
206 rb_fatal("Kernel and userland out of sync"); |
|
207 |
|
208 len /= sizeof(*xp); |
|
209 printf(" JID IP Address Hostname Path\n"); |
|
210 for (i = 0; i < len; i++) { |
|
211 in.s_addr = ntohl(xp->pr_ip); |
|
212 printf("%6d %-15.15s %-29.29s %.74s\n", |
|
213 xp->pr_id, inet_ntoa(in), xp->pr_host, xp->pr_path); |
|
214 xp++; |
|
215 } |
|
216 free(sxp); |
|
217 exit(0); |
|
218 |
|
219 } |
|
220 |
|
221 void |
333 void |
222 Init_bsdjail( void ) |
334 Init_bsdjail( void ) { |
223 { |
|
224 rbjail_mBSD = rb_define_module( "BSD" ); |
335 rbjail_mBSD = rb_define_module( "BSD" ); |
225 rbjail_cBSDJail = rb_define_class_under( rbjail_mBSD, "Jail" ); |
336 rbjail_cBSDJail = rb_define_class_under( rbjail_mBSD, "Jail" ); |
226 |
337 |
227 rb_define_singleton_method( rbjail_cBSDJail, "list", rbjail_list, 0 ); |
338 /* Class methods */ |
228 rb_define_alloc_function( rbjail_cBSDJail, ) |
339 rb_define_alloc_function( rbjail_cBSDJail, rbjail_jail_s_allocate ); |
229 |
340 rb_define_singleton_method( rbjail_cBSDJail, "jail", rbjail_jail_s_jail, 0 ); |
230 rb_define_method( rbjail_cBSDJail, "attach", rbjail_attach, -1 ); |
341 rb_define_singleton_method( rbjail_cBSDJail, "list", rbjail_jail_s_list, 0 ); |
231 |
342 |
232 } |
343 /* Instance methods */ |
233 |
344 rb_define_method( rbjail_cBSDJail, "initialize", rbjail_jail_initialize, 3 ); |
|
345 rb_define_method( rbjail_cBSDJail, "attach", rbjail_jail_attach, -1 ); |
|
346 rb_define_method( rbjail_cBSDJail, "hostname", rbjail_jail_hostname, 0 ); |
|
347 rb_define_method( rbjail_cBSDJail, "path", rbjail_jail_path, 0 ); |
|
348 rb_define_method( rbjail_cBSDJail, "ip", rbjail_jail_ip, 0 ); |
|
349 rb_define_method( rbjail_cBSDJail, "jid", rbjail_jail_jid, 0 ); |
|
350 |
|
351 } |
|
352 |