From owner-freebsd-hackers@FreeBSD.ORG Thu Jan 4 17:51:17 2007 Return-Path: X-Original-To: freebsd-hackers@freebsd.org Delivered-To: freebsd-hackers@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id ADAB216A412 for ; Thu, 4 Jan 2007 17:51:17 +0000 (UTC) (envelope-from erik.udo@gmail.com) Received: from satakieli.dnainternet.net (satakieli.dnainternet.net [212.149.75.40]) by mx1.freebsd.org (Postfix) with ESMTP id 5401D13C468 for ; Thu, 4 Jan 2007 17:51:17 +0000 (UTC) (envelope-from erik.udo@gmail.com) Received: from [192.168.1.11] (host-212-149-186-30.kpylaajakaista.net [212.149.186.30]) by satakieli.dnainternet.net (Postfix) with ESMTP id 4DB55CA4B; Thu, 4 Jan 2007 19:51:15 +0200 (EET) Message-ID: <459D3E8E.10903@gmail.com> Date: Thu, 04 Jan 2007 19:51:10 +0200 From: Erik Udo User-Agent: Thunderbird 1.5.0.9 (X11/20061226) MIME-Version: 1.0 To: Oliver Fromme References: <200701041432.l04EWgXA057039@lurza.secnetix.de> In-Reply-To: <200701041432.l04EWgXA057039@lurza.secnetix.de> Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Cc: freebsd-hackers@freebsd.org Subject: Re: Init.c, making it chroot X-BeenThere: freebsd-hackers@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Technical Discussions relating to FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 04 Jan 2007 17:51:17 -0000 Oh and i tested it on 6.1-RELEASE on my own livecd. It works! Oliver Fromme wrote: > M. Warner Losh wrote: > > In message: <45975B7B.7030002@FreeBSD.org> > > Doug Barton writes: > > : Erik Udo wrote: > > : > That's nice. But NetBSDs init.c executes /etc/rc before calling > > : > chroot(), and that's what i'm looking for > > : > > : Sorry if I missed your rationale earlier, but could you perhaps > > : explain a bit more about why you want to do this? I ask because I'm > > : generally interested in boot-time issues, and this sounds like an > > : interesting problem. > > > > This allows one to have a 'simple' /etc/rc that arranges things so > > that a new '/' is ready to 'boot'. > > Sorry, I missed that part of the thread because I wasn't > on Cc and didn't look at the list for a while. > > I've created (and tested!) a new patch. I've tested on > RELENG_6, but I think init(8) isn't very different on > HEAD, so it should work there, too. > > The patched init does the following: > > - If the kenv variable "init_script" is set, it is > expected to be the name of a shell script that is > executed before init(8) enters its usual state > machine, and before chrooting (if requested, see > below). If the script terminates with an exit code > other than 0, single user mode is enforced. > > - If the kenv variable "init_chroot" is set, init(8) > performs a chroot(2) operation into that directory. > That happens after executing the init_script, if > any, but before entering the usual state machine, > i.e. before going into single or multi user mode. > > - A check is performed whether /dev is mounted (inside > the chroot, if any). If not, it is mounted. > > - Afterwards, init(8) proceeds normally. > > It should be noted that the init_script can create or > modify the "init_chroot" variable using the kenv(1) > tool. So the chroot directory can be specified > dynamically by the init_script; it does not have to > be hardcoded in /boot/loader.conf. > > It should also be noted that the init_script requires > a few files and directories to be present _outside_ of > any chroot: > > /bin/sh > /dev > /lib/libc.so.6 > /lib/libedit.so.5 > /lib/libncurses.so.6 > /libexec/ld-elf.so.1 > > Alternatively you can compile a static shell binary, > then you only need /bin/sh and dev (I haven't tested > this, though). Note that the /dev directory must > exist (outside the chroot), because running the > init_script requires certain devices (/dev/console). > It is mounted by the kernel before starting init(8). > > As usual, have prepared an ISO image which tests and > demonstrates the patch. It's 17.5 MB compressed: > > http://www.secnetix.de/tmp/init_chroot/ > > The /boot/loader.conf file looks like this: > > kernel_options="-C" > init_path="/ochroot/sbin/init" > init_script="/ochroot/etc/rc.init" > init_chroot="/ochroot" > > The /ochroot/etc/rc.init just prints a few messages, > so you can see that it actually does something. It > does not have to be inside the chroot (I put it there > because of convenience only). > > Any comments are welcome. I particularly appreciate > if others test this stuff. > > Best regards > Oliver > > PS: Here's the patch, relatve to rev. 1.60.2.2. > > I had to move some parts of the original code around, > so the setup of the signal handlers happen before any > script is run, and the mount of /dev happens afterwards. > That's why the diff grew somewhat, even though my actual > code changes aren't that many. > > For executing the init_script, I re-used the runcom() > function which required a few minor modifications to it > so it was a bit more flexible. In particular I had to > move the _PATH_RUNCOM constant into a variable. > > --- src/sbin/init/init.c.orig Mon Aug 7 15:10:25 2006 > +++ src/sbin/init/init.c Thu Jan 4 11:53:00 2007 > @@ -55,6 +55,7 @@ > #include > #include > #include > +#include > #include > #include > #include > @@ -121,6 +122,7 @@ > state_func_t catatonia(void); > state_func_t death(void); > > +const char *runcom_script = _PATH_RUNCOM; > enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT; > #define FALSE 0 > #define TRUE 1 > @@ -187,6 +189,9 @@ > int > main(int argc, char *argv[]) > { > + char kenv_value[PATH_MAX]; > + char ichroot_name[] = "init_chroot"; > + char iscript_name[] = "init_script"; > int c; > struct sigaction sa; > sigset_t mask; > @@ -275,6 +280,66 @@ > if (optind != argc) > warning("ignoring excess arguments"); > > + /* > + * We catch or block signals rather than ignore them, > + * so that they get reset on exec. > + */ > + handle(badsys, SIGSYS, 0); > + handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, > + SIGBUS, SIGXCPU, SIGXFSZ, 0); > + handle(transition_handler, SIGHUP, SIGINT, SIGTERM, SIGTSTP, > + SIGUSR1, SIGUSR2, 0); > + handle(alrm_handler, SIGALRM, 0); > + sigfillset(&mask); > + delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, > + SIGXCPU, SIGXFSZ, SIGHUP, SIGINT, SIGTERM, SIGTSTP, SIGALRM, > + SIGUSR1, SIGUSR2, 0); > + sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); > + sigemptyset(&sa.sa_mask); > + sa.sa_flags = 0; > + sa.sa_handler = SIG_IGN; > + (void) sigaction(SIGTTIN, &sa, (struct sigaction *)0); > + (void) sigaction(SIGTTOU, &sa, (struct sigaction *)0); > + > + /* > + * Paranoia. > + */ > + close(0); > + close(1); > + close(2); > + > + if (kenv(KENV_GET, iscript_name, kenv_value, sizeof(kenv_value)) > 0) { > + runcom_script = kenv_value; > + runcom(); > + runcom_script = _PATH_RUNCOM; > + if (requested_transition == 0) > + requested_transition = runcom; > + } > + > + if (kenv(KENV_GET, ichroot_name, kenv_value, sizeof(kenv_value)) > 0) { > + if (chdir(kenv_value) != 0 || chroot(".") != 0) > + warning("Can't chroot to %s: %m", kenv_value); > + } > + > + /* > + * Additional check if devfs needs to be mounted: > + * If "/" and "/dev" have the same device number, > + * then it hasn't been mounted yet. > + */ > + if (!devfs) { > + struct stat stst; > + dev_t root_devno; > + > + stat("/", &stst); > + root_devno = stst.st_dev; > + if (stat("/dev", &stst) != 0) > + warning("Can't stat /dev: %m"); > + else { > + if (stst.st_dev == root_devno) > + devfs++; > + } > + } > + > if (devfs) { > struct iovec iov[4]; > char *s; > @@ -312,34 +377,6 @@ > } > > /* > - * We catch or block signals rather than ignore them, > - * so that they get reset on exec. > - */ > - handle(badsys, SIGSYS, 0); > - handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, > - SIGBUS, SIGXCPU, SIGXFSZ, 0); > - handle(transition_handler, SIGHUP, SIGINT, SIGTERM, SIGTSTP, > - SIGUSR1, SIGUSR2, 0); > - handle(alrm_handler, SIGALRM, 0); > - sigfillset(&mask); > - delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, > - SIGXCPU, SIGXFSZ, SIGHUP, SIGINT, SIGTERM, SIGTSTP, SIGALRM, > - SIGUSR1, SIGUSR2, 0); > - sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); > - sigemptyset(&sa.sa_mask); > - sa.sa_flags = 0; > - sa.sa_handler = SIG_IGN; > - (void) sigaction(SIGTTIN, &sa, (struct sigaction *)0); > - (void) sigaction(SIGTTOU, &sa, (struct sigaction *)0); > - > - /* > - * Paranoia. > - */ > - close(0); > - close(1); > - close(2); > - > - /* > * Start the state machine. > */ > transition(requested_transition); > @@ -733,11 +770,10 @@ > setctty(_PATH_CONSOLE); > > char _sh[] = "sh"; > - char _path_runcom[] = _PATH_RUNCOM; > char _autoboot[] = "autoboot"; > > argv[0] = _sh; > - argv[1] = _path_runcom; > + argv[1] = __DECONST(char *, runcom_script); > argv[2] = runcom_mode == AUTOBOOT ? _autoboot : 0; > argv[3] = 0; > > @@ -747,13 +783,13 @@ > setprocresources(RESOURCE_RC); > #endif > execv(_PATH_BSHELL, argv); > - stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM); > + stall("can't exec %s for %s: %m", _PATH_BSHELL, runcom_script); > _exit(1); /* force single user mode */ > } > > if (pid == -1) { > emergency("can't fork for %s on %s: %m", > - _PATH_BSHELL, _PATH_RUNCOM); > + _PATH_BSHELL, runcom_script); > while (waitpid(-1, (int *) 0, WNOHANG) > 0) > continue; > sleep(STALL_TIMEOUT); > @@ -773,12 +809,12 @@ > if (errno == EINTR) > continue; > warning("wait for %s on %s failed: %m; going to single user mode", > - _PATH_BSHELL, _PATH_RUNCOM); > + _PATH_BSHELL, runcom_script); > return (state_func_t) single_user; > } > if (wpid == pid && WIFSTOPPED(status)) { > warning("init: %s on %s stopped, restarting\n", > - _PATH_BSHELL, _PATH_RUNCOM); > + _PATH_BSHELL, runcom_script); > kill(pid, SIGCONT); > wpid = -1; > } > @@ -796,7 +832,7 @@ > > if (!WIFEXITED(status)) { > warning("%s on %s terminated abnormally, going to single user mode", > - _PATH_BSHELL, _PATH_RUNCOM); > + _PATH_BSHELL, runcom_script); > return (state_func_t) single_user; > } > > > >