ext/jail.c
changeset 7 4460fc10c6a3
parent 6 2d52adc4adcc
equal deleted inserted replaced
6:2d52adc4adcc 7:4460fc10c6a3
     1 /*
     1 /*
     2  *  jail.c - Ruby jparallel
     2  *  jail.c - Ruby jParallel
     3  *
     3  *
     4  *  vim: set nosta noet ts=4 sw=4:
     4  *  vim: set nosta noet ts=4 sw=4:
     5  *
     5  *
     6  *  $Id$
     6  *  $Id$
     7  *  
     7  *  
    37  *  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    37  *  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    38  *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    38  *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    39  *  
    39  *  
    40  */
    40  */
    41 
    41 
    42 
       
    43 #include "jail.h"
    42 #include "jail.h"
    44 
    43 
    45 VALUE rbjail_mBSD;
    44 
    46 VALUE rbjail_cBSDJail;
    45 /* --------------------------------------------------------------
    47 VALUE rbjail_cIPAddr;
    46  * Utility functions
    48 
    47  * -------------------------------------------------------------- */
    49 
    48 
    50 /*
    49 /*
    51  *  Debug logging function
    50  *  Debug logging function
    52  */
    51  */
    53 void
    52 void
    81 	debugMsg(( "Checking a BSD::Jail object (%d).", self ));
    80 	debugMsg(( "Checking a BSD::Jail object (%d).", self ));
    82 	Check_Type( self, T_DATA );
    81 	Check_Type( self, T_DATA );
    83 
    82 
    84 	if ( !rb_obj_is_kind_of(self, rbjail_cBSDJail) ) {
    83 	if ( !rb_obj_is_kind_of(self, rbjail_cBSDJail) ) {
    85 		rb_raise( rb_eTypeError, "wrong argument type %s (expected BSD::Jail)",
    84 		rb_raise( rb_eTypeError, "wrong argument type %s (expected BSD::Jail)",
    86 			rb_class2name(CLASS_OF( self )) );
    85 			rb_obj_classname( self ));
    87 	}
    86 	}
    88 
    87 
    89 	return DATA_PTR( self );
    88 	return DATA_PTR( self );
    90 }
    89 }
    91 
    90 
   104 	return ptr;
   103 	return ptr;
   105 }
   104 }
   106 
   105 
   107 
   106 
   108 /*
   107 /*
       
   108  * Attach to a running jail and chdir to the root.
       
   109  */
       
   110 static int
       
   111 rbjail_do_attach( int jid )
       
   112 {
       
   113 	int attach_status = jail_attach(jid);
       
   114 
       
   115 	if ( attach_status == -1 )
       
   116 		rb_sys_fail( "jail_attach" );
       
   117 	if ( chdir("/") == -1 ) rb_sys_fail( "chdir" );
       
   118 
       
   119 	return attach_status;
       
   120 }
       
   121 
       
   122 
       
   123 /*
       
   124  * Attach to a running jail from within a block, forking first
       
   125  * and returning the child pid.
       
   126  */
       
   127 static VALUE
       
   128 rbjail_attach_block( int jid )
       
   129 {
       
   130 	int pid;
       
   131 	int status;
       
   132 
       
   133 	if ( ! rb_block_given_p() ) return Qnil;
       
   134 
       
   135 	rb_secure(2);
       
   136 
       
   137 	fflush(stdout);
       
   138 	fflush(stderr);
       
   139 
       
   140 	switch ( pid = fork() ) {
       
   141 		case 0:
       
   142 			rb_thread_atfork();
       
   143 			rbjail_do_attach( jid );
       
   144 			rb_protect( rb_yield, Qundef, &status );
       
   145 			rb_exit( status );
       
   146 			return Qnil;
       
   147 
       
   148 		case -1:
       
   149 			rb_sys_fail( "fork(2)" );
       
   150 			return Qnil;
       
   151 
       
   152 		default:
       
   153 			return INT2FIX( pid );
       
   154 	}
       
   155 }
       
   156 
       
   157 
       
   158 
       
   159 /* --------------------------------------------------------------
       
   160  * Memory-management functions
       
   161  * -------------------------------------------------------------- */
       
   162 
       
   163 /*
   109  * Copy memory from the given 'xp' to a ruby managed object.
   164  * Copy memory from the given 'xp' to a ruby managed object.
   110  */
   165  */
   111 static VALUE
   166 static VALUE
   112 rbjail_alloc( VALUE class, struct xprison *xp )
   167 rbjail_alloc( VALUE class, struct xprison *xp )
   113 {
   168 {
   118 	//
   173 	//
   119 	memcpy( rbjail_xp, xp, sizeof( struct xprison ) );
   174 	memcpy( rbjail_xp, xp, sizeof( struct xprison ) );
   120 	DATA_PTR( rbjail ) = rbjail_xp;
   175 	DATA_PTR( rbjail ) = rbjail_xp;
   121 
   176 
   122 	return rbjail;
   177 	return rbjail;
   123 }
       
   124 
       
   125 
       
   126 /*
       
   127  *
       
   128  */
       
   129 static VALUE
       
   130 rbjail_get_ip( VALUE self )
       
   131 {
       
   132 	struct xprison *xp = rbjail_get_jailptr( self );
       
   133 	struct in_addr in;
       
   134 	char *ip;
       
   135 
       
   136 	in.s_addr = ntohl( xp->pr_ip );
       
   137 	ip = inet_ntoa(in);
       
   138 
       
   139 	return rb_funcall( rbjail_cIPAddr, rb_intern("new"), 1, rb_str_new2(ip) );
       
   140 }
       
   141 
       
   142 
       
   143 /*
       
   144  *
       
   145  */
       
   146 static VALUE
       
   147 rbjail_get_jid( VALUE self )
       
   148 {
       
   149 	struct xprison *xp = rbjail_get_jailptr( self );
       
   150 	return INT2FIX( xp->pr_id );
       
   151 }
       
   152 
       
   153 
       
   154 /*
       
   155  *
       
   156  */
       
   157 static VALUE
       
   158 rbjail_get_host( VALUE self )
       
   159 {
       
   160 	struct xprison *xp = rbjail_get_jailptr( self );
       
   161 	return rb_str_new2( xp->pr_host );
       
   162 }
       
   163 
       
   164 
       
   165 /*
       
   166  *
       
   167  */
       
   168 static VALUE
       
   169 rbjail_get_path( VALUE self )
       
   170 {
       
   171 	struct xprison *xp = rbjail_get_jailptr( self );
       
   172 	return rb_str_new2( xp->pr_path );
       
   173 }
   178 }
   174 
   179 
   175 
   180 
   176 /*
   181 /*
   177  * GC Free function
   182  * GC Free function
   196 {
   201 {
   197 	return Data_Wrap_Struct( class, NULL, rbjail_gc_free, NULL );
   202 	return Data_Wrap_Struct( class, NULL, rbjail_gc_free, NULL );
   198 }
   203 }
   199 
   204 
   200 
   205 
   201 /*
   206 /* --------------------------------------------------------------
   202  * Create a new jail object.
   207  * Class methods
   203  * Returns the +id+ of the newly created jail.
   208  * -------------------------------------------------------------- */
   204  */
   209 
   205 static VALUE
   210 /*
   206 rbjail_jail( int argc, VALUE *argv, VALUE self ) {
   211  *  call-seq:
       
   212  *     BSD::Jail.new( ip, path, host )   => jail_id
       
   213  *
       
   214  *  Create a new BSD::Jail object from required +ip+, +path+, and +host+
       
   215  *  arguments.  You can optionally pass a +securelevel+ fourth argument.
       
   216  *
       
   217  *  Returns the +id+ of the newly created jail.
       
   218  */
       
   219 static VALUE
       
   220 rbjail_jail( int argc, VALUE *argv, VALUE self )
       
   221 {
   207 	struct jail j;
   222 	struct jail j;
   208 	struct in_addr in;
   223 	struct in_addr in;
   209 	VALUE ip, path, host, securelevel;
   224 	VALUE ip, path, host, sec_level;
   210 	int id;
   225 	int id;
   211 
   226 	int securelevel = -1;
   212 	rb_scan_args( argc, argv, "31", &ip, &path, &host, &securelevel );
   227 
       
   228 	rb_scan_args( argc, argv, "31", &ip, &path, &host, &sec_level );
       
   229 	if ( sec_level != Qnil ) securelevel = FIX2INT( sec_level );
   213 
   230 
   214 	if ( inet_aton( RSTRING_PTR( rb_obj_as_string(ip) ), &in ) == 0 )
   231 	if ( inet_aton( RSTRING_PTR( rb_obj_as_string(ip) ), &in ) == 0 )
   215 		rb_raise( rb_eArgError, "Could not make sense of ip number: %s", ip );
   232 		rb_raise( rb_eArgError, "Could not make sense of ip number: %s", ip );
   216 
   233 
   217 	SafeStringValue(path);
   234 	SafeStringValue(path);
   224 	id = jail(&j);
   241 	id = jail(&j);
   225 
   242 
   226 	if ( id == -1 ) rb_sys_fail( "jail" );
   243 	if ( id == -1 ) rb_sys_fail( "jail" );
   227 	if ( chdir("/") == -1 ) rb_sys_fail( "chdir" );
   244 	if ( chdir("/") == -1 ) rb_sys_fail( "chdir" );
   228 
   245 
       
   246 	if ( securelevel > -1 && securelevel < 4 ) {
       
   247 		debugMsg(( "Setting securelevel to: %d", securelevel ));
       
   248 		if ( sysctlbyname("kern.securelevel", NULL, 0, &securelevel, sizeof(securelevel)) )
       
   249 			rb_sys_fail( "securelevel" );
       
   250 	}
       
   251 
   229 	debugMsg(( "New jail created with id: %d\n", id ));
   252 	debugMsg(( "New jail created with id: %d\n", id ));
   230 	return INT2FIX( id );
   253 	return INT2FIX( id );
   231 }
   254 }
   232 
   255 
   233 
   256 
   234 /*
   257 /*
   235  * Iterate over the currently instantiated jails, returning a jail
   258  *  call-seq:
   236  * object that matches the given string/ip/JID -- or nil if none do. 
   259  *     BSD::Jail.find_by_jid( jid ) => BSD::Jail
   237  * Without an argument, return an array of all JIDs. 
   260  *
   238  */ 
   261  *  Iterate over the currently instantiated jails, returning a jail
       
   262  *  object that matches the given +jid+, or nil if no jail is found.
       
   263  *
       
   264  */
       
   265 static VALUE
       
   266 rbjail_find_by_jid( VALUE self, VALUE jid )
       
   267 {
       
   268 	VALUE args[0];
       
   269 
       
   270 	if ( TYPE(jid) != T_FIXNUM )
       
   271 		rb_raise( rb_eTypeError, "invalid argument to find_by_jid(): %s",
       
   272 			RSTRING_PTR( rb_inspect(jid)) );
       
   273 
       
   274 	args[0] = jid;
       
   275 	return rbjail_find( 1, args, self );
       
   276 }
       
   277 
       
   278 
       
   279 /*
       
   280  *  call-seq:
       
   281  *     BSD::Jail.search( id )                      => BSD::Jail
       
   282  *     BSD::Jail.search( hostname )                => [ BSD::Jail, ... ]
       
   283  *     BSD::Jail.search( IPAddr.new('127.0.0.1') ) => [ BSD::Jail, ... ]
       
   284  *     BSD::Jail.search( Pathname.new('/tmp') )    => [ BSD::Jail, ... ]
       
   285  *     BSD::Jail.search                            => [ BSD::Jail, BSD::Jail, ... ]
       
   286  *     BSD::Jail.search { |obj| block }            => obj
       
   287  *
       
   288  *  Iterate over the currently instantiated jails, returning a jail
       
   289  *  object that matches the given argument. 
       
   290  *
       
   291  *  If the argument is an integer, it is assumed to be a JID.
       
   292  *  Otherwise, it is converted to a string, and compared against
       
   293  *  IP addresses, hostnames, and paths.  JIDs are unique, so there is
       
   294  *  only one object that can match.  Only the matching object (or nil)
       
   295  *  is returned in that event.  IPs, hostnames, and paths can be
       
   296  *  shared between jails, so searching on those return an array populated
       
   297  *  with matched jail objects -- or if there are no valid matches, an empty array.
       
   298  *
       
   299  *  Without an argument, return an array of all jails as objects.
       
   300  *
       
   301  */
   239 static VALUE
   302 static VALUE
   240 rbjail_find( int argc, VALUE *argv, VALUE self )
   303 rbjail_find( int argc, VALUE *argv, VALUE self )
   241 { 
   304 { 
   242 	struct xprison *sxp, *xp;
   305 	struct xprison *sxp, *xp;
   243 	struct in_addr in;
   306 	struct in_addr in;
   260 			//
   323 			//
   261 			case T_FIXNUM:
   324 			case T_FIXNUM:
   262 				jid = FIX2INT( arg );
   325 				jid = FIX2INT( arg );
   263 				break;
   326 				break;
   264 
   327 
   265 				// find by IP/hostname
   328 			// find by IP/hostname/path
   266 				//
   329 			//
   267 			case T_OBJECT:
   330 			case T_OBJECT:
   268 			case T_DATA:
   331 			case T_DATA:
   269 			case T_STRING:
   332 			case T_STRING:
   270 				str = RSTRING_PTR( rb_obj_as_string(arg) );
   333 				str = RSTRING_PTR( rb_obj_as_string(arg) );
   271 				compare = 1;
   334 				compare = 1;
   272 				break;
   335 				break;
   273 
   336 
   274 			default:
   337 			default:
   275 				rb_raise( rb_eTypeError, "invalid argument to find(): %s",
   338 				rb_raise( rb_eTypeError, "invalid argument: %s",
   276 						RSTRING_PTR(rb_inspect(arg)) );
   339 						RSTRING_PTR( rb_inspect(arg)) );
   277 		}
   340 		}
   278 	}
   341 	}
   279 
   342 
   280 	// Get the size of the xprison and allocate memory to it.
   343 	// Get the size of the xprison and allocate memory to it.
   281 	//
   344 	//
   300 		return Qnil;
   363 		return Qnil;
   301 	}
   364 	}
   302 	if ( len < sizeof(*xp) || len % sizeof(*xp) || xp->pr_version != XPRISON_VERSION )
   365 	if ( len < sizeof(*xp) || len % sizeof(*xp) || xp->pr_version != XPRISON_VERSION )
   303 		rb_fatal("Kernel and userland out of sync");
   366 		rb_fatal("Kernel and userland out of sync");
   304 
   367 
   305 	// No arguments to find() -- return an array of all JIDs
   368 	// No arguments to find() -- yield each successive jail,
       
   369 	// and return an array of all jail objects.
   306 	//
   370 	//
   307 	if ( argc == 0 ) {
   371 	if ( argc == 0 ) {
   308 		for ( i = 0; i < len / sizeof(*xp); i++ ) {
   372 		for ( i = 0; i < len / sizeof(*xp); i++ ) {
       
   373 			rbjail = rbjail_alloc( self, xp );
       
   374 			if ( rb_block_given_p() ) rb_yield( rbjail );
   309 			rb_ary_push( jails, rbjail_alloc( self, xp ) );
   375 			rb_ary_push( jails, rbjail_alloc( self, xp ) );
   310 			xp++;
   376 			xp++;
   311 		}
   377 		}
   312 
   378 
   313 		free( sxp );
   379 		free( sxp );
   314 		return jails;
   380 		return jails;
   315 	}
   381 	}
   316 
   382 
   317 	// Argument passed to find(): walk the jail list, comparing the arg
   383 	// Argument passed to find(): walk the jail list, comparing the arg
   318 	// with each current jail.  Return first match.
   384 	// with each current jail.  Return all matches.
   319 	//
   385 	//
   320 	for ( i = 0; i < len / sizeof(*xp); i++ ) {
   386 	for ( i = 0; i < len / sizeof(*xp); i++ ) {
   321 		in.s_addr = ntohl(xp->pr_ip);
   387 		in.s_addr = ntohl(xp->pr_ip);
   322 		if (( compare == 0 && xp->pr_id == jid ) ||
   388 		if (( compare == 0 && xp->pr_id == jid ) ||
   323 				( compare == 1 &&
   389 				( compare == 1 &&
   324 				  (( strcmp( str, inet_ntoa(in) ) == 0 ) ||
   390 				  (( strcmp( str, inet_ntoa(in) ) == 0 ) ||
   325 				   (  strcmp( str, xp->pr_host )  == 0 ))
   391 				   ( strcmp( str, xp->pr_host )   == 0 ) ||
       
   392 				   ( strcmp( str, xp->pr_path )   == 0 ))
   326 				)) {
   393 				)) {
   327 
   394 
   328 			debugMsg(( "Located jail: %d", xp->pr_id ));
   395 			debugMsg(( "Located jail: %d", xp->pr_id ));
   329 			rbjail = rbjail_alloc( self, xp );
   396 
   330 
   397 			// If the user already knows the JID, there can only be
   331 			free( sxp );
   398 			// one match.  Return it immediately.
   332 			return rbjail;
   399 			//
       
   400 			if ( compare == 0 ) {
       
   401 				free( sxp );
       
   402 				return rbjail_alloc( self, xp );
       
   403 			}
       
   404 
       
   405 			// If searching for anything other than JID, the argument
       
   406 			// isn't unique.  Put matching jails into an array.
       
   407 			//
       
   408 			rb_ary_push( jails, rbjail_alloc( self, xp ) );
       
   409 			xp++;
   333 		}
   410 		}
   334 		else {
   411 		else {
   335 
   412 
   336 			xp++;
   413 			xp++;
   337 		}
   414 		}
   338 	}
   415 	}
   339 
   416 
   340 	free( sxp );
   417 	free( sxp );
   341 	return Qnil;
   418 
   342 }
   419 	if ( compare == 0 && rb_ary_shift( jails ) == Qnil )
   343 
   420 		return Qnil;
   344 
   421 
   345 static void
   422 	return jails;
   346 rbjail_do_jail_attach( int jid )
   423 }
   347 {
   424 
   348 	if ( jail_attach(jid) == -1 )
   425 
   349 		rb_sys_fail( "jail_attach" );
   426 /*
   350 }
   427  *  call-seq:
   351 
   428  *     BSD::Jail.attach( id )       => true
   352 
   429  *     BSD::Jail.attach( hostname ) => true
   353 /* Mostly ripped off from Ruby's process.c */
   430  *
   354 static VALUE
   431  *  Attach to a currently running jail instance directly.
   355 rbjail_attach_block( int jid )
   432  *  Operates under the same rules as BSD::Jail#search -- you may
   356 {
   433  *  specify a jail by IP Address, hostname, or jid.
   357 	int pid;
   434  *
   358 
   435  *  Please note that attaching your process into a jail is a one way
   359 	rb_secure(2);
   436  *  operation that requires root privileges.  You must fork() if
   360 
   437  *  your process needs to continue in the host environment.
   361 	fflush(stdout);
   438  *
   362 	fflush(stderr);
   439  */
   363 
   440 static VALUE
   364 	switch ( pid = fork() ) {
   441 rbjail_class_attach( VALUE self, VALUE arg )
   365 		case 0:
   442 {
   366 			rb_thread_atfork();
   443 	int jid = 0;
   367 			if ( rb_block_given_p() ) {
   444 	VALUE jails;
   368 				int status;
   445 	VALUE find_args [0];
   369 
   446 
   370 				rbjail_do_jail_attach( jid );
   447 	switch ( TYPE(arg) ) {
   371 				rb_protect( rb_yield, Qundef, &status );
   448 
   372 				ruby_stop( status );
   449 		// The user knows the JID already, attach directly.
   373 			}
   450 		//
   374 			return Qnil;
   451 		case T_FIXNUM:
   375 
   452 
   376 		case -1:
   453 			jid = FIX2INT( arg );
   377 			rb_sys_fail( "fork(2)" );
   454 			rbjail_do_attach( jid );
   378 			return Qnil;
   455 			break;
       
   456 
       
   457 		// Find the JID to attach to.  First match wins.
       
   458 		//
       
   459 		case T_OBJECT:
       
   460 		case T_DATA:
       
   461 		case T_STRING:
       
   462 
       
   463 			find_args[0] = arg;
       
   464 			jails = rbjail_find( 1, find_args, self );
       
   465 			jid = FIX2INT( rbjail_get_jid( rb_ary_shift(jails) ));
       
   466 			rbjail_do_attach( jid );
       
   467 			break;
   379 
   468 
   380 		default:
   469 		default:
   381 			return INT2FIX( pid );
   470 			rb_raise( rb_eTypeError, "invalid argument to attach(): %s",
   382 	}
   471 						RSTRING_PTR( rb_inspect(arg)) );
   383 }
   472 	}
   384 
   473 
   385 static VALUE
   474 	return Qtrue;
   386 rbjail_attach( int argc, VALUE *argv, VALUE self )
   475 }
   387 {
   476 
   388 	VALUE jidnum, rval;
   477 
   389 	int jid;
   478 /* --------------------------------------------------------------
   390 
   479  * Instance methods
   391 	rb_scan_args( argc, argv, "1", &jidnum );
   480  * -------------------------------------------------------------- */
   392 	jid = NUM2INT( jidnum );
   481 
       
   482 /*
       
   483  *  call-seq:
       
   484  *     BSD::Jail <=> BSD::Jail
       
   485  *
       
   486  *  Interface for Comparable.
       
   487  *
       
   488  */
       
   489 static VALUE
       
   490 rbjail_compare( VALUE self, VALUE other )
       
   491 {
       
   492 	debugMsg(("self id: %s, other id: %s",
       
   493 				RSTRING_PTR(rb_inspect( self )),
       
   494 				RSTRING_PTR(rb_inspect( other ))));
       
   495 
       
   496 	return rb_funcall( 
       
   497 		rbjail_get_jid( self ),
       
   498 		rb_intern("<=>"),
       
   499 		1,
       
   500 		rbjail_get_jid( other )
       
   501 	);
       
   502 }
       
   503 
       
   504 
       
   505 /*
       
   506  * Fetch the configured IP address for the jail.
       
   507  * Returns an IPAddr object.
       
   508  *
       
   509  */
       
   510 static VALUE
       
   511 rbjail_get_ip( VALUE self )
       
   512 {
       
   513 	struct xprison *xp = rbjail_get_jailptr( self );
       
   514 	struct in_addr in;
       
   515 	VALUE args [0];
       
   516 
       
   517 	in.s_addr = ntohl( xp->pr_ip );
       
   518 	args[0] = rb_str_new2( inet_ntoa(in) );
       
   519 
       
   520 	return rb_class_new_instance( 1, args, rbjail_cIPAddr );
       
   521 }
       
   522 
       
   523 
       
   524 /*
       
   525  * Fetch the assigned JID for the jail.
       
   526  *
       
   527  */
       
   528 static VALUE
       
   529 rbjail_get_jid( VALUE self )
       
   530 {
       
   531 	struct xprison *xp = rbjail_get_jailptr( self );
       
   532 	return INT2FIX( xp->pr_id );
       
   533 }
       
   534 
       
   535 
       
   536 /*
       
   537  * Fetch the configured hostname for the jail as a string.
       
   538  *
       
   539  */
       
   540 static VALUE
       
   541 rbjail_get_host( VALUE self )
       
   542 {
       
   543 	struct xprison *xp = rbjail_get_jailptr( self );
       
   544 	return rb_str_new2( xp->pr_host );
       
   545 }
       
   546 
       
   547 
       
   548 /*
       
   549  * Fetch the configured path for the jail.
       
   550  * Returns a Pathname object.
       
   551  *
       
   552  */
       
   553 static VALUE
       
   554 rbjail_get_path( VALUE self )
       
   555 {
       
   556 	struct xprison *xp = rbjail_get_jailptr( self );
       
   557 	VALUE args[0];
       
   558 
       
   559 	args[0] = rb_str_new2( xp->pr_path );
       
   560 
       
   561 	return rb_class_new_instance( 1, args, rbjail_cPathname );
       
   562 }
       
   563 
       
   564 
       
   565 /*
       
   566  * Return a human readable version of the object.
       
   567  */
       
   568 static VALUE
       
   569 rbjail_inspect( VALUE self )
       
   570 {
       
   571 	char inspect_str[BUFSIZ];
       
   572 
       
   573 	sprintf( inspect_str, "#<%s:0x%07x jid:%d (%s)>",
       
   574 		rb_obj_classname( self ),
       
   575 		NUM2UINT(rb_obj_id( self )) * 2,
       
   576 		FIX2INT(rbjail_get_jid( self )),
       
   577 		RSTRING_PTR(rbjail_get_host( self ))
       
   578 	);
       
   579 
       
   580 	return rb_str_new2( inspect_str );
       
   581 }
       
   582 
       
   583 
       
   584 /*
       
   585  *  call-seq:
       
   586  *     jail.attach
       
   587  *     jail.attach do ... end  => child pid
       
   588  *
       
   589  *  Attach to the jail.
       
   590  *
       
   591  *  Please note that attaching your process into a jail is a one way
       
   592  *  operation that requires root privileges.  You must fork() if
       
   593  *  your process needs to continue in the host environment.
       
   594  *
       
   595  * 	In the block form, a fork() is performed automatically.
       
   596  *
       
   597  */
       
   598 static VALUE
       
   599 rbjail_instance_attach( VALUE self )
       
   600 {
       
   601 	int jid = FIX2INT( rbjail_get_jid( self ) );
   393 
   602 
   394 	if ( rb_block_given_p() ) {
   603 	if ( rb_block_given_p() ) {
   395 		rval = rbjail_attach_block( jid );
   604 		return rbjail_attach_block( jid );
   396 	}
   605 	}
   397 
   606 
   398 	else {
   607 	else {
   399 		rbjail_do_jail_attach( jid );
   608 		rbjail_do_attach( jid );
   400 		rval = Qtrue;
   609 	}
   401 	}
   610 
   402 
   611 	return Qtrue;
   403 	return rval;
   612 }
   404 }
   613 
   405 
   614 
   406 
   615 /* --------------------------------------------------------------
   407 /*
   616  * Initalizer
   408  * Ruby initializer.
   617  * -------------------------------------------------------------- */
       
   618 
       
   619 /*
       
   620  *  BSD::Jail
       
   621  *
       
   622  *  Ruby bindings for the FreeBSD jail(2) subsystem.
       
   623  *
       
   624  *  Example usage:
       
   625  *
       
   626  *  	require 'bsd/jail'
       
   627  *
       
   628  *      # create a new jail
       
   629  *      jid = BSD::Jail.create( '127.0.0.1', '/tmp', 'testjail' )
       
   630  *
       
   631  *      # find existing jail(s)
       
   632  *      jail  = BSD::Jail.find_by_jid( jid )
       
   633  *      jails = BSD::Jail.search( hostname )
       
   634  *      jails = BSD::Jail.search( IPAddr.new('127.0.0.1') )
       
   635  *      jails = BSD::Jail.search( Pathname.new('/tmp') )
       
   636  *
       
   637  *      # attach to jails
       
   638  *      BSD::Jail.attach( jid )
       
   639  *      jail.attach do
       
   640  *     	    ... do something fancy!
       
   641  *      end
       
   642  *
       
   643  *  BSD::Jail includes behaviors from Enumerable and Comparable,
       
   644  *  so you can do things such as the following:
       
   645  *
       
   646  *      # Are these two instantiated jails the same jid?
       
   647  *      BSD::Jail.search( '/tmp' ) == BSD::Jail.search( 'testjail' )
       
   648  *      
       
   649  *      # Alternative interface for finding a jail by jid
       
   650  *      BSD::Jail.find { |j| j.jid == 3 }
       
   651  *      
       
   652  *      # Return all jails as an array
       
   653  *      jails = BSD::Jail.find_all
       
   654  *      
       
   655  *      # What jails have IPs within the 192.168.16.0/24 class C netblock?
       
   656  *      nb = IPAddr.new( '192.168.16.0/24' );
       
   657  *      jails = BSD::Jail.find? { |j| nb.include?(j.ip) }
       
   658  *
   409  */
   659  */
   410 void
   660 void
   411 Init_jail( void )
   661 Init_jail( void )
   412 {
   662 {
       
   663 	// namespacing
       
   664 	//
   413 	rbjail_mBSD	= rb_define_module( "BSD" );
   665 	rbjail_mBSD	= rb_define_module( "BSD" );
   414 	rbjail_cBSDJail = rb_define_class_under( rbjail_mBSD, "Jail", rb_cObject );
   666 	rbjail_cBSDJail = rb_define_class_under( rbjail_mBSD, "Jail", rb_cObject );
   415 
   667 
   416 	rb_require("ipaddr");
   668 	// struct wrapping function
   417 	rbjail_cIPAddr = rb_const_get( rb_cObject, rb_intern("IPAddr") );
       
   418 
       
   419 	rb_define_alloc_func( rbjail_cBSDJail, rbjail_s_alloc );
   669 	rb_define_alloc_func( rbjail_cBSDJail, rbjail_s_alloc );
   420 
   670 
   421 	// Make the 'new' method private.
   671 	// Make the 'new' method private.
   422 	rb_funcall( rbjail_cBSDJail, rb_intern("private_class_method"), 1, ID2SYM(rb_intern("new")) );
   672 	rb_funcall( rbjail_cBSDJail, rb_intern("private_class_method"), 1, ID2SYM(rb_intern("new")) );
   423 
   673 
   424 	// main utility functions
   674 	// class methods
   425 	//
   675 	//
   426 	rb_define_singleton_method( rbjail_cBSDJail, "jail", rbjail_jail, -1 );
   676 	rb_define_singleton_method( rbjail_cBSDJail, "attach", rbjail_class_attach, 1 );
   427 	rb_define_singleton_method( rbjail_cBSDJail, "find", rbjail_find, -1 );
   677 	rb_define_singleton_method( rbjail_cBSDJail, "create", rbjail_jail, -1 );
   428 	rb_define_method( rbjail_cBSDJail, "attach", rbjail_attach, -1 );
   678 	rb_define_singleton_method( rbjail_cBSDJail, "search", rbjail_find, -1 );
   429 	//	rb_define_alias( rbjail_cBSDJail, "list", "find" );
   679 	rb_define_singleton_method( rbjail_cBSDJail, "find_by_jid", rbjail_find_by_jid, 1 );
   430 	
   680 
   431 	// accessor functions
   681 	// instance methods
   432 	//
   682 	//
       
   683 	rb_define_method( rbjail_cBSDJail, "attach", rbjail_instance_attach, 0 );
       
   684 	rb_define_method( rbjail_cBSDJail, "host", rbjail_get_host, 0 );
       
   685 	rb_define_alias(  rbjail_cBSDJail, "hostname", "host" );
       
   686 	rb_define_method( rbjail_cBSDJail, "inspect", rbjail_inspect, 0 );
       
   687 	rb_define_method( rbjail_cBSDJail, "ip",   rbjail_get_ip,   0 );
   433 	rb_define_method( rbjail_cBSDJail, "jid",  rbjail_get_jid,  0 );
   688 	rb_define_method( rbjail_cBSDJail, "jid",  rbjail_get_jid,  0 );
   434 	rb_define_method( rbjail_cBSDJail, "ip",   rbjail_get_ip,   0 );
       
   435 	rb_define_method( rbjail_cBSDJail, "host", rbjail_get_host, 0 );
       
   436 	rb_define_method( rbjail_cBSDJail, "path", rbjail_get_path, 0 );
   689 	rb_define_method( rbjail_cBSDJail, "path", rbjail_get_path, 0 );
   437 }
   690 
   438 
   691 	// Additional (base) modules for accessor wrapping
   439 
   692 	//
       
   693 	rb_require( "ipaddr" );
       
   694 	rb_require( "pathname" );
       
   695 	rbjail_cIPAddr   = rb_const_get( rb_cObject, rb_intern("IPAddr") );
       
   696 	rbjail_cPathname = rb_const_get( rb_cObject, rb_intern("Pathname") );
       
   697 
       
   698 	// Enumerable!
       
   699 	//
       
   700 	rb_define_singleton_method( rbjail_cBSDJail, "each", rbjail_find, -1 );
       
   701 	rb_extend_object( rbjail_cBSDJail, rb_const_get( rb_cObject, rb_intern("Enumerable") ));
       
   702 
       
   703 	// Comparable!
       
   704 	//
       
   705 	rb_define_method( rbjail_cBSDJail, "<=>", rbjail_compare, 1 );
       
   706 	rb_include_module( rbjail_cBSDJail, rb_const_get( rb_cObject, rb_intern("Comparable") ));
       
   707 }
       
   708 
       
   709