ext/bsdjail.c
changeset 2 0c24586f579a
parent 0 92d00ff32c56
child 5 41a1542b3b10
equal deleted inserted replaced
1:09d0d209d06d 2:0c24586f579a
    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;
    39 
    65 
    40 /*
    66 /*
    41  * Allocation function
    67  * Allocation function
    42  */
    68  */
    43 static jail *
    69 static jail *
    44 rbjail_jail_alloc()
    70 rbjail_jail_alloc() {
    45 {
       
    46 	jail *ptr = ALLOC( jail );
    71 	jail *ptr = ALLOC( jail );
    47 	
    72 	
    48 	ptr->version	= 0;
    73 	ptr->version	= 0;
    49 	ptr->path		= NULL;
    74 	ptr->path		= NULL;
    50 	ptr->hostname	= NULL;
    75 	ptr->hostname	= NULL;
    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