ext/bsdjail.c
changeset 2 0c24586f579a
parent 0 92d00ff32c56
child 5 41a1542b3b10
--- a/ext/bsdjail.c	Fri Aug 15 16:23:03 2008 +0000
+++ b/ext/bsdjail.c	Thu Oct 16 02:43:08 2008 +0000
@@ -29,6 +29,32 @@
 
 
 /*
+ *  Debug logging function
+ */
+void
+#ifdef HAVE_STDARG_PROTOTYPES
+rlink_debug(const char *fmt, ...)
+#else
+rlink_debug( const char *fmt, va_dcl )
+#endif
+{
+	char buf[BUFSIZ], buf2[BUFSIZ];
+	va_list	args;
+
+	if ( !RTEST(ruby_debug) ) return;
+
+	snprintf( buf, BUFSIZ, "Jail Debug>>> %s", fmt );
+
+	va_init_list( args, fmt );
+	vsnprintf( buf2, BUFSIZ, buf, args );
+	fputs( buf2, stderr );
+	fputs( "\n", stderr );
+	fflush( stderr );
+	va_end( args );
+}
+
+
+/*
 struct jail {
 	u_int32_t       version;
 	char            *path;
@@ -41,8 +67,7 @@
  * Allocation function
  */
 static jail *
-rbjail_jail_alloc()
-{
+rbjail_jail_alloc() {
 	jail *ptr = ALLOC( jail );
 	
 	ptr->version	= 0;
@@ -59,17 +84,19 @@
  * GC Free function
  */
 static void
-rbjail_jail_gc_free( ptr )
-	jail *ptr;
-{
+rbjail_gc_free( jail *ptr ) {
 	if ( ptr ) {
+		if ( ptr->path ) xfree( ptr->path );
+		if ( ptr->hostname ) xfree( ptr->hostname );
+		
 		ptr->path		= NULL;
 		ptr->hostname	= NULL;
+
 		xfree( ptr );
 	}
 	
 	else {
-		debugMsg(( "Not freeing an uninitialized rlink_SENTENCE" ));
+		debugMsg(( "Not freeing an uninitialized jail" ));
 	}
 }
 
@@ -77,15 +104,13 @@
 /*
  * Object validity checker. Returns the data pointer.
  */
-static rlink_SENTENCE *
-check_sentence( self )
-	 VALUE	self;
-{
-	debugMsg(( "Checking a LinkParser::Sentence object (%d).", self ));
+static jail *
+rbjail_check_jail( VALUE self ) {
+	debugMsg(( "Checking a BSD::Jail object (%d).", self ));
 	Check_Type( self, T_DATA );
 
-    if ( !IsSentence(self) ) {
-		rb_raise( rb_eTypeError, "wrong argument type %s (expected LinkParser::Sentence)",
+    if ( !rb_obj_is_kind_of(self, rbjail_cBSDJail) ) {
+		rb_raise( rb_eTypeError, "wrong argument type %s (expected BSD::Jail)",
 				  rb_class2name(CLASS_OF( self )) );
     }
 	
@@ -96,13 +121,11 @@
 /*
  * Fetch the data pointer and check it for sanity.
  */
-static rlink_SENTENCE *
-get_sentence( self )
-	 VALUE self;
-{
-	rlink_SENTENCE *ptr = check_sentence( self );
+static jail *
+rbjail_get_jail( VALUE self ) {
+	jail *ptr = check_sentence( self );
 
-	debugMsg(( "Fetching a Sentence (%p).", ptr ));
+	debugMsg(( "Fetching a Jail (%p).", ptr ));
 	if ( !ptr )
 		rb_raise( rb_eRuntimeError, "uninitialized Sentence" );
 
@@ -110,21 +133,13 @@
 }
 
 
+/* --------------------------------------------------------------
+ * Jail utility functions
+ * -------------------------------------------------------------- */
+
 /*
- * Publicly-usable sentence-fetcher
+ * Try to jail_attach() to the specified +jid+, raising an exception if it fails.
  */
-rlink_SENTENCE *
-rlink_get_sentence( self )
-	VALUE self;
-{
-	return get_sentence( self );
-}
-
-
-
-
-
-
 static void
 rbjail_do_jail_attach( int jid )
 {
@@ -132,7 +147,10 @@
 		rb_sys_fail( "jail_attach" );
 }
 
-/* Mostly ripped off from Ruby's process.c */
+
+/* 
+ * Fork + Block function for rbjail_attach(). Mostly stolen from Ruby's process.c.
+ */
 static VALUE
 rbjail_attach_block( int jid )
 {
@@ -140,8 +158,8 @@
 
     rb_secure(2);
 
-    fflush(stdout);
-    fflush(stderr);
+    fflush( stdout );
+    fflush( stderr );
 
 	switch ( pid = fork() ) {
 		case 0:
@@ -164,9 +182,132 @@
 	}
 }
 
+
+/* --------------------------------------------------------------
+ * Class methods
+ * -------------------------------------------------------------- */
+
+/*
+ *  call-seq:
+ *     BSD::Jail.allocate   -> bsdjail
+ *
+ *  Allocate a new BSD::Jail object.
+ *
+ */
 static VALUE
-rbjail_attach( int argc, VALUE *argv, VALUE self )
-{
+rbjail_jail_s_allocate( VALUE klass ) {
+	return Data_Wrap_Struct( klass, 0, rbjail_gc_free, 0 );
+}
+
+
+/*
+ *  call-seq:
+ *     BSD::Jail.list   -> array
+ *
+ *  Return an Array of all the running jails on the host.
+ *
+ */
+static VALUE
+rbjail_jail_s_list( VALUE klass ) {
+	VALUE rval = rb_ary_new();
+	struct xprison *xp;
+	struct in_addr in;
+	size_t i, len;
+
+	if ( sysctlbyname("jail.list", NULL, &len, NULL, 0) == -1 )
+		rb_sys_fail( "sysctlbyname(): jail.list" );
+
+	xp = ALLOCA_N( xprison, 1 );
+
+	if ( sysctlbyname("jail.list", xp, &len, NULL, 0) == -1 ) {
+		rb_sys_fail( "sysctlbyname(): jail.list" );
+	}
+
+	if ( len < sizeof(*xp) || len % sizeof(*xp) ||
+		xp->pr_version != KINFO_PRISON_VERSION )
+		rb_fatal( "Kernel and userland out of sync" );
+
+	len /= sizeof( *xp );
+	for ( i = 0; i < len; i++ ) {
+		VALUE jail, args[3];
+		
+		/* Hostname */
+		args[0] = rb_str_new2( xp->pr_host );
+
+		/* IP */
+		in.s_addr = ntohl( xp->pr_ip );
+		args[1] = rb_str_new2( inet_ntoa(in) );
+		
+		/* Path */
+		args[2] = rb_str_new2( xp->pr_path );
+		
+		jail = rb_class_new_instance( 3, args, klass );
+		rb_ary_push( rval, jail );
+
+		xp++;
+	}
+
+	return rval;
+}
+
+
+
+
+/* --------------------------------------------------------------
+ * Instance methods
+ * -------------------------------------------------------------- */
+
+/*
+ *  call-seq:
+ *     BSD::Jail.new( hostname, ip, path )           -> new_jail
+ *
+ *  Create a new jail for the given +hostname+, +ip+, and +path+.
+ */
+static VALUE
+rbjail_jail_initialize( VALUE self, VALUE hostname, VALUE ip, VALUE path ) {
+	if ( !rbjail_check_jail(self) ) {
+		struct jail *ptr = rbjail_jail_alloc();
+		char *pathstr = NULL, *hostnamestr = NULL;
+
+		Check_Type( hostname, T_STRING );
+		Check_Type( ip, T_STRING );
+
+		pathstr = ALLOC_N( char, RSTRING(path)->len );
+		strncpy( pathstr, RSTRING(path)->ptr, RSTRING(path)->len );
+
+		hostnamestr = ALLOC_N( char, RSTRING(hostname)->len );
+		strncpy( hostnamestr, RSTRING(hostname)->ptr, RSTRING(hostname)->len );
+		
+		/*
+		struct jail {
+			u_int32_t       version;
+			char            *path;
+			char            *hostname;
+			u_int32_t       ip_number;
+		};
+		*/
+		ptr->version = 0;   /* Per the manpage's recommendation */
+		ptr->ip_number = inet_addr( StringValuePtr(ip) );
+		ptr->path = path;
+		ptr->hostname = hostname;
+	}
+}
+
+
+/*
+ *  call-seq:
+ *     jail.attach             -> true or false
+ *     jail.attach { block }   -> pid
+ *
+ *  Attach to the given jail. In the non-block form, attach the current process to the
+ *  jail and return +true+ if it succeeds. This is a one-way operation, and requires root
+ *  privileges.
+ *  
+ *  In the block form, the process will be forked, and the block will be attached to the jail and 
+ *  run by the child. The parent process will receive the process ID of the child.
+ */
+static VALUE
+rbjail_attach( int argc, VALUE *argv, VALUE self ) {
 	VALUE jidnum, rval;
 	int jid;
 	
@@ -185,49 +326,27 @@
 	return rval;
 }
 
-static VALUE
-	rbjail_list( VALUE self )
-{
-	struct kinfo_prison *sxp, *xp;
-	struct in_addr in;
-	size_t i, len;
 
-	if (sysctlbyname("jail.list", NULL, &len, NULL, 0) == -1)
-		rb_sys_fail("sysctlbyname(): jail.list");
-
-	xp = ALLOCA_N( kinfo_prison, 1 );
-
-	if (sysctlbyname("jail.list", xp, &len, NULL, 0) == -1) {
-		rb_sys_fail("sysctlbyname(): jail.list");
-	}
-
-	if (len < sizeof(*xp) || len % sizeof(*xp) ||
-		xp->pr_version != KINFO_PRISON_VERSION)
-		rb_fatal("Kernel and userland out of sync");
-
-	len /= sizeof(*xp);
-	printf("   JID  IP Address      Hostname                      Path\n");
-	for (i = 0; i < len; i++) {
-		in.s_addr = ntohl(xp->pr_ip);
-		printf("%6d  %-15.15s %-29.29s %.74s\n",
-			xp->pr_id, inet_ntoa(in), xp->pr_host, xp->pr_path);
-		xp++;
-	}
-	free(sxp);
-	exit(0);
-
-}
-
+/*
+ * I can't remember how the hell you document a class, but that will go here.
+ */
 void
-Init_bsdjail( void )
-{
+Init_bsdjail( void ) {
 	rbjail_mBSD = rb_define_module( "BSD" );
 	rbjail_cBSDJail = rb_define_class_under( rbjail_mBSD, "Jail" );
 
-	rb_define_singleton_method( rbjail_cBSDJail, "list", rbjail_list, 0 );
-	rb_define_alloc_function( rbjail_cBSDJail, )
+	/* Class methods */
+	rb_define_alloc_function( rbjail_cBSDJail, rbjail_jail_s_allocate );
+	rb_define_singleton_method( rbjail_cBSDJail, "jail", rbjail_jail_s_jail, 0 );
+	rb_define_singleton_method( rbjail_cBSDJail, "list", rbjail_jail_s_list, 0 );
 	
-	rb_define_method( rbjail_cBSDJail, "attach", rbjail_attach, -1 );
+	/* Instance methods */
+	rb_define_method( rbjail_cBSDJail, "initialize", rbjail_jail_initialize, 3 );
+	rb_define_method( rbjail_cBSDJail, "attach", rbjail_jail_attach, -1 );
+	rb_define_method( rbjail_cBSDJail, "hostname", rbjail_jail_hostname, 0 );
+	rb_define_method( rbjail_cBSDJail, "path", rbjail_jail_path, 0 );
+	rb_define_method( rbjail_cBSDJail, "ip", rbjail_jail_ip, 0 );
+	rb_define_method( rbjail_cBSDJail, "jid", rbjail_jail_jid, 0 );
 	
 }