sbin/init patch, adding chroot feature

Oliver Fromme olli at haluter.fromme.com
Thu Nov 18 17:03:31 PST 2004


Hi,

The patch below adds a feature to sbin/init that has been
discussed in the users list recently.

It enables init to optionally perform a chroot() operation
very early, so that the rest of the boot process runs from
a subdirectory of the root file system.  This feature
enables you to have several bootable versions of DragonFly
and FreeBSD on the same file system (such as a CD or DVD).
Personally I'm using this to let the DragonFly install
co-exist with the FreeBSD install + fix-it on the same
DVD.  The feature might also be useful for other things,
such as diskless booting.

The chroot feature is controlled by a kernel environment
variable "init_chroot" which can be set in loader.conf.
If it is set to a non-empty value containing the name of
an existing directory, init will chroot() into it.

To test it, I prepared a CD-R like this:

1.  Make a copy of the latest DF stable ISO (20041107).
2.  Replace sbin/init with the patched version.
3.  # mkdir dragonfly
4.  Copy everything except boot into dragonfly.
    Now the root directory of the CD contains only /boot
    and /dragonfly, nothing else.
5.  The installer needs /boot, too, so we need a copy of
    it in the dragonfly subdirectory.  To save space,
    we make hardlinks instead of plain copies:
    # find boot | cpio -dumpl dragonfly
6.  # cd dragonfly; ln -s . dragonfly
7.  Modify /boot/loader.conf so everything is loaded from
    the /dragonfly directory.  Add these lines:
       kernel="/dragonfly/kernel"
       bootfile="/dragonfly/kernel"
       module_path="/;/modules;/dragonfly/modules"
       init_path="/dragonfly/sbin/init:/sbin/init"
       init_chroot="/dragonfly"
8.  Run mkisofs as usual and write to CD-R/RW.

Booting from that CD-R works perfectly.  You don't notice
that the system actually runs from a chroot directory
of the CD.  It is possible to add further directories to
the CD containing different DragonFly BSD versions, or
even FreeBSD.  The loader menu can be modified to allow
booting the various systems by setting the appropriate
variables (see above).

The patch is pretty straight-forward.  Most of the actual
code has been copied from src/usr.bin/kenv/kenv.c with
only minor modifications.

Best regards
   Oliver

--- src/sbin/init.c.orig	Thu Nov 18 18:26:13 2004
+++ src/sbin/init.c	Fri Nov 19 00:43:57 2004
@@ -162,6 +162,7 @@
 void alrm_handler(int);
 void setsecuritylevel(int);
 int getsecuritylevel(void);
+char *get_chroot(void);
 int setupargv(session_t *, struct ttyent *);
 #ifdef LOGIN_CAP
 void setprocresources(const char *);
@@ -182,6 +183,7 @@
 int
 main(int argc, char **argv)
 {
+	char *init_chroot;
 	int c;
 	struct sigaction sa;
 	sigset_t mask;
@@ -236,6 +238,18 @@
 	openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH);
 
 	/*
+	 * If chroot has been requested by the boot loader,
+	 * do it now.  Try to be robust:  If the directory
+	 * doesn't exist, continue anyway.
+	 */
+	init_chroot = get_chroot();
+	if (init_chroot != NULL) {
+		if (chdir(init_chroot) == -1 || chroot(".") == -1)
+			warning("can't chroot to %s: %m", init_chroot);
+		free(init_chroot);
+	}
+
+	/*
 	 * Create an initial session.
 	 */
 	if (setsid() < 0)
@@ -455,6 +469,59 @@
 #else
 	return (-1);
 #endif
+}
+
+/*
+ * Get the value of the "init_chroot" variable from the
+ * kernel environment (or NULL if not set).
+ * (Most of this has been copied from src/usr.bin/kenv/kenv.c.)
+ */
+char *
+get_chroot(void)
+{
+	char sbuf[1024];
+	int name2oid_oid[2];
+	int real_oid[CTL_MAXNAME+4];
+	size_t oidlen;
+	int i, slen;
+	char *eq, *name;
+
+	name2oid_oid[0] = 0;	/* This is magic & undocumented! */
+	name2oid_oid[1] = 3;
+	oidlen = sizeof(real_oid);
+	name = "kern.environment";
+	if (sysctl(name2oid_oid, 2, real_oid, &oidlen, name, strlen(name)) < 0) {
+		warning("cannot find kern.environment base sysctl OID");
+		return NULL;
+	}
+	oidlen /= sizeof(int);
+	if (oidlen >= CTL_MAXNAME) {
+		warning("kern.environment OID is too large!");
+		return NULL;
+	}
+	real_oid[oidlen] = 0;
+	for (i = 0; ; i++) {
+		real_oid[oidlen + 1] = i;
+		slen = sizeof(sbuf) - 1;
+		if (sysctl(real_oid, oidlen + 2, sbuf, &slen, NULL, 0) < 0) {
+			if (errno != ENOENT)
+				warning("sysctl kern.environment.%d: %m", i);
+			break;
+		}
+		sbuf[sizeof(sbuf) - 1] = '\0';
+		eq = strchr(sbuf, '=');
+		if (eq == NULL) {
+			warning("malformed environment string: %s", sbuf);
+			break;
+		}
+		*eq = '\0';
+		if (strcmp(sbuf, "init_chroot") == 0)
+			if (eq[1])
+				return strdup(eq + 1);
+			else
+				break;
+	}
+	return NULL;
 }
 
 /*





More information about the Submit mailing list