diff -r 09d0d209d06d -r 0c24586f579a ext/bsdjail.c --- 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 ); }