From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp2 ([2001:41d0:2:4a6f::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms11 with LMTPS id YB5dC/Q5fl86OgAA0tVLHw (envelope-from ) for ; Wed, 07 Oct 2020 21:58:12 +0000 Received: from aspmx1.migadu.com ([2001:41d0:2:4a6f::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp2 with LMTPS id AEQeB/Q5fl9hLQAAB5/wlQ (envelope-from ) for ; Wed, 07 Oct 2020 21:58:12 +0000 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id 0B8E99402A4 for ; Wed, 7 Oct 2020 21:58:11 +0000 (UTC) Received: from localhost ([::1]:50694 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kQHRp-0004RN-Hm for larch@yhetil.org; Wed, 07 Oct 2020 17:58:09 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:58688) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kQHRi-0004R8-Pf for guix-patches@gnu.org; Wed, 07 Oct 2020 17:58:02 -0400 Received: from debbugs.gnu.org ([209.51.188.43]:46734) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1kQHRi-0004J8-G3; Wed, 07 Oct 2020 17:58:02 -0400 Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1kQHRi-0005M9-FT; Wed, 07 Oct 2020 17:58:02 -0400 X-Loop: help-debbugs@gnu.org Subject: [bug#43857] Supporting chroot builds on GNU/Hurd Resent-From: Ludovic =?UTF-8?Q?Court=C3=A8s?= Original-Sender: "Debbugs-submit" Resent-CC: janneke@gnu.org, guix-patches@gnu.org Resent-Date: Wed, 07 Oct 2020 21:58:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: report 43857 X-GNU-PR-Package: guix-patches X-GNU-PR-Keywords: To: 43857@debbugs.gnu.org Cc: Jan Nieuwenhuizen X-Debbugs-Original-To: guix-patches@gnu.org X-Debbugs-Original-Xcc: Jan Nieuwenhuizen Received: via spool by submit@debbugs.gnu.org id=B.160210785720556 (code B ref -1); Wed, 07 Oct 2020 21:58:02 +0000 Received: (at submit) by debbugs.gnu.org; 7 Oct 2020 21:57:37 +0000 Received: from localhost ([127.0.0.1]:58280 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1kQHRE-0005LP-PL for submit@debbugs.gnu.org; Wed, 07 Oct 2020 17:57:37 -0400 Received: from lists.gnu.org ([209.51.188.17]:38176) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1kQHR9-0005LE-UZ for submit@debbugs.gnu.org; Wed, 07 Oct 2020 17:57:31 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:58610) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kQHR9-0003t0-La for guix-patches@gnu.org; Wed, 07 Oct 2020 17:57:27 -0400 Received: from fencepost.gnu.org ([2001:470:142:3::e]:52899) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kQHR9-00048U-CI for guix-patches@gnu.org; Wed, 07 Oct 2020 17:57:27 -0400 Received: from [2a01:e0a:1d:7270:af76:b9b:ca24:c465] (port=49304 helo=ribbon) by fencepost.gnu.org with esmtpsa (TLS1.2:RSA_AES_256_CBC_SHA1:256) (Exim 4.82) (envelope-from ) id 1kQHR7-0003fe-OE for guix-patches@gnu.org; Wed, 07 Oct 2020 17:57:26 -0400 From: Ludovic =?UTF-8?Q?Court=C3=A8s?= X-URL: http://www.fdn.fr/~lcourtes/ X-Revolutionary-Date: 16 =?UTF-8?Q?Vend=C3=A9miaire?= an 229 de la =?UTF-8?Q?R=C3=A9volution?= X-PGP-Key-ID: 0x090B11993D9AEBB5 X-PGP-Key: http://www.fdn.fr/~lcourtes/ludovic.asc X-PGP-Fingerprint: 3CE4 6455 8A84 FDC6 9DB4 0CFB 090B 1199 3D9A EBB5 X-OS: x86_64-pc-linux-gnu Date: Wed, 07 Oct 2020 23:57:24 +0200 Message-ID: <87sgapsnqz.fsf@gnu.org> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Spam-Score: -2.3 (--) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-Spam-Score: -3.3 (---) X-BeenThere: guix-patches@gnu.org List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-patches-bounces+larch=yhetil.org@gnu.org Sender: "Guix-patches" X-Scanner: scn0 Authentication-Results: aspmx1.migadu.com; dkim=none; dmarc=pass (policy=none) header.from=gnu.org; spf=pass (aspmx1.migadu.com: domain of guix-patches-bounces@gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=guix-patches-bounces@gnu.org X-Spam-Score: -1.51 X-TUID: SpwI7bdAMDLV --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Hello! The patch below is an attempt at supporting =E2=80=9Cchroot builds=E2=80=9D= on GNU/Hurd; it=E2=80=99s =E2=80=9Calmost working=E2=80=9D. The main feature we need is= firmlinks (or =E2=80=9Cbind mounts=E2=80=9D) and commenting out Linux-specific things (private bind mou= nts, =E2=80=98pivot_root=E2=80=99, etc.). The patch introduces a =E2=80=98firmlink=E2=80=99 function that creates a b= ind mount on GNU/Linux and otherwise spawns the /hurd/firmlink translator. It also adjusts =E2=80=98deletePath=E2=80=99 to remove any active translators from = files it=E2=80=99s about to delete, the goal being to terminate those firmlink translators. (Apparently that code isn=E2=80=99t reached yet though.) The set of /dev nodes firmlinked into the chroot is reduced compared to GNU/Linux; I added /servers, meaning that /servers is firmlinked as is, which is not ideal (see below). With this patch, I can run =E2=80=9Cguix build hello --check=E2=80=9D in a = chroot=E2=80=A6 but it eventually hangs somewhere in =E2=80=98DerivationGoal::buildDone=E2=80= =99 (I presume) once the build has completed. It leaves behind it all its firmlink processes: --8<---------------cut here---------------start------------->8--- root@childhurd ~# ps --width=3D200 aux|grep firmlink root 16426 120.0 0.0 147M 616K p1 RNm 11:38PM 0:00.00 grep --color= =3Dauto firmlink root 11191 0.0 0.0 147M 888K - S8--- This has yet to be debugged. :-) Apart from that, this raises the question of what to put in the build environment. As written in the blog post about childhurds that should go out tomorrow, on GNU/Linux, we do not include any piece of userland software in the environment. But here, we=E2=80=99d be doing just that: ru= nning Hurd translators that are not specified as derivation inputs. It=E2=80=99s= OK for /dev/null, but maybe questionable for /servers/socket/*. Regarding /servers, we=E2=80=99ll probably want to spawn separate servers (a separate /servers/proc would give up a new =E2=80=9CPID namespace=E2=80=9D.= ) Should that be done magically by the daemon, or should we leave it up to build processes, so that build processes really specify all the user-land software they depend on? We could imagine a new phase in =E2=80=98gnu-build-system=E2=80=99, on GNU/Hurd, that would start by settin= g up a bunch of translators. Food for thought=E2=80=A6 As a first step, we can firmlin= k it from the host, with an eye towards =E2=80=9Cdoing it right=E2=80=9D. I felt a need to hack on this as I was investigating an util-linux test failure in a =E2=80=98--disable-chroot=E2=80=99 setup: the test would find = /proc and would later fail for some reason. Had /proc been missing from the build environment (as is the case with this patch), the test would have been skipped (it explicitly handles that case). I think we=E2=80=99d rather not fiddle too much with test suites until we have defined what=E2=80=99s avail= able in the default build environment. Thoughts? For the record, I=E2=80=99ve been testing this by: 1. Adding =E2=80=98hurd=E2=80=99 as an input to the =E2=80=98guix=E2=80= =99 package. 2. Cross-building the daemon using code from my branch: ~/src/guix/pre-inst-env guix build --with-git-url=3Dguix-daemon=3D$PWD = --with-branch=3Dguix-daemon=3Dwip-hurd-chroot --target=3Di586-pc-gnu -e '(= @ (gnu packages package-management) guix-daemon)' 3. Sending it over to the childhurd: guix copy --to=3Dlocalhost:10022 /gnu/store/=E2=80=A6 4. Running it in the childhurd with: GUIX=3D/run/current-system/profile/bin/guix /gnu/store/=E2=80=A6-guix-= daemon-git.wip-hurd-chroot/bin/guix-daemon --build-users-group=3Dguixbuild = --disable-deduplication I guess I should probably go ahead and do everything natively in the childhurd, but I thought I=E2=80=99d share my weird workflow in case that g= ives me a chance to participate in some weirdness contest. Ludo=E2=80=99. --=-=-= Content-Type: text/x-patch; charset=utf-8 Content-Disposition: inline; filename=0001-DRAFT-daemon-Support-chroot-builds-on-GNU-Hurd.patch Content-Transfer-Encoding: quoted-printable Content-Description: the patch >From 1887d0dee0031df0de117b3a6339495504b4b489 Mon Sep 17 00:00:00 2001 From: =3D?UTF-8?q?Ludovic=3D20Court=3DC3=3DA8s?=3D Date: Tue, 6 Oct 2020 23:53:24 +0200 Subject: [PATCH] DRAFT daemon: Support chroot builds on GNU/Hurd. * nix/libutil/util.cc (firmlink): New functions. (_deletePath) [__GNU__]: Check whether a translator is set on PATH. Call 'fsys_goaway' if this is the case. * nix/libutil/util.hh (firmlink): New declaration. * nix/libstore/build.cc (CHROOT_ENABLED): Define to 1. Error out when both __GNU__ and __linux__ are undefined. (DerivationGoal::runChild): Remove special treatment of /proc. Use 'firmlink' instead of 'mount' with MS_BIND. Wrap /proc, /dev/shm, /dev/pts, and /proc/self handling in #ifdef __linux__. Same for 'pivot_root' call. * config-daemon.ac: Set and substitute 'HURD_LIBS'. * nix/local.mk (guix_daemon_LDADD): Add $(HURD_LIBS). --- config-daemon.ac | 13 ++++ nix/libstore/build.cc | 43 +++++++++---- nix/libutil/util.cc | 139 ++++++++++++++++++++++++++++++++++++++++++ nix/libutil/util.hh | 3 + nix/local.mk | 3 +- 5 files changed, 189 insertions(+), 12 deletions(-) diff --git a/config-daemon.ac b/config-daemon.ac index 50ead355a8..bdaee82fb8 100644 --- a/config-daemon.ac +++ b/config-daemon.ac @@ -38,6 +38,19 @@ if test "x$guix_build_daemon" =3D "xyes"; then AC_DEFINE_UNQUOTED([SYSTEM], ["$guix_system"], [Guix host system type--i.e., platform and OS kernel tuple.]) =20 + dnl On GNU/Hurd guix-daemon depends on libfshelp. + case "$guix_system" in + *-gnu) + AC_CHECK_LIB([fshelp], [fshelp_start_translator]) + if test "x$ac_cv_lib_fshelp_fshelp_start_translator" !=3D "xyes"; th= en + AC_MSG_ERROR([libfshelp (GNU Hurd) could not be found]) + fi + HURD_LIBS=3D"-lfshelp";; + *) + HURD_LIBS=3D"";; + esac + AC_SUBST([HURD_LIBS]) + case "$LIBGCRYPT_PREFIX" in no) LIBGCRYPT_CFLAGS=3D"" diff --git a/nix/libstore/build.cc b/nix/libstore/build.cc index ccec513d8d..7151932403 100644 --- a/nix/libstore/build.cc +++ b/nix/libstore/build.cc @@ -52,7 +52,13 @@ #endif =20 =20 -#define CHROOT_ENABLED HAVE_CHROOT && HAVE_SYS_MOUNT_H && defined(MS_BIND)= && defined(MS_PRIVATE) +/* Chroot builds are supported both on GNU/Linux and on GNU/Hurd. */ +#if defined __linux__ || defined __GNU__ +# define CHROOT_ENABLED 1 +#else +# error unsupported operating system +#endif + #define CLONE_ENABLED defined(CLONE_NEWNS) =20 #if defined(SYS_pivot_root) @@ -1991,6 +1997,7 @@ void DerivationGoal::runChild() if (setdomainname(domainname, sizeof(domainname)) =3D=3D -1) throw SysError("cannot set domain name"); =20 +#ifdef __linux__ /* Make all filesystems private. This is necessary because subtrees may have been mounted as "shared" (MS_SHARED). (Systemd does this, for instance.) Even @@ -2007,27 +2014,30 @@ void DerivationGoal::runChild() different filesystem from /, as needed for pivot_root. */ if (mount(chrootRootDir.c_str(), chrootRootDir.c_str(), 0, MS_= BIND, 0) =3D=3D -1) throw SysError(format("unable to bind mount =E2=80=98%1%= =E2=80=99") % chrootRootDir); +#endif =20 /* Set up a nearly empty /dev, unless the user asked to bind-mount the host /dev. */ Strings ss; if (dirsInChroot.find("/dev") =3D=3D dirsInChroot.end()) { +#ifdef __linux__ createDirs(chrootRootDir + "/dev/shm"); createDirs(chrootRootDir + "/dev/pts"); - ss.push_back("/dev/full"); -#ifdef __linux__ + createSymlink("/proc/self/fd", chrootRootDir + "/dev/fd"); + createSymlink("/proc/self/fd/0", chrootRootDir + "/dev/std= in"); + createSymlink("/proc/self/fd/1", chrootRootDir + "/dev/std= out"); + createSymlink("/proc/self/fd/2", chrootRootDir + "/dev/std= err"); + ss.push_back("/dev/tty"); if (pathExists("/dev/kvm")) ss.push_back("/dev/kvm"); +#elif __GNU__ + ss.push_back("/servers"); #endif + ss.push_back("/dev/full"); ss.push_back("/dev/null"); ss.push_back("/dev/random"); - ss.push_back("/dev/tty"); ss.push_back("/dev/urandom"); ss.push_back("/dev/zero"); - createSymlink("/proc/self/fd", chrootRootDir + "/dev/fd"); - createSymlink("/proc/self/fd/0", chrootRootDir + "/dev/std= in"); - createSymlink("/proc/self/fd/1", chrootRootDir + "/dev/std= out"); - createSymlink("/proc/self/fd/2", chrootRootDir + "/dev/std= err"); } =20 /* Fixed-output derivations typically need to access the @@ -2049,7 +2059,7 @@ void DerivationGoal::runChild() struct stat st; Path source =3D i->second; Path target =3D chrootRootDir + i->first; - if (source =3D=3D "/proc") continue; // backwards compatib= ility + debug(format("bind mounting `%1%' to `%2%'") % source % ta= rget); if (stat(source.c_str(), &st) =3D=3D -1) throw SysError(format("getting attributes of path `%1%= '") % source); @@ -2059,10 +2069,11 @@ void DerivationGoal::runChild() createDirs(dirOf(target)); writeFile(target, ""); } - if (mount(source.c_str(), target.c_str(), "", MS_BIND, 0) = =3D=3D -1) + if (firmlink(source, target) =3D=3D -1) throw SysError(format("bind mount from `%1%' to `%2%' = failed") % source % target); } =20 +#ifdef __linux__ /* Bind a new instance of procfs on /proc to reflect our private PID namespace. */ createDirs(chrootRootDir + "/proc"); @@ -2090,11 +2101,16 @@ void DerivationGoal::runChild() Linux versions, it is created with permissions 0. */ chmod_(chrootRootDir + "/dev/pts/ptmx", 0666); } +#elif __GNU__ + /* Do not mount things that are implemented in user land: /proc, + /dev/shm, /dev/pts, etc. */ +#endif =20 /* Do the chroot(). */ if (chdir(chrootRootDir.c_str()) =3D=3D -1) throw SysError(format("cannot change directory to '%1%'") = % chrootRootDir); =20 +#ifdef __linux__ if (mkdir("real-root", 0) =3D=3D -1) throw SysError("cannot create real-root directory"); =20 @@ -2109,8 +2125,13 @@ void DerivationGoal::runChild() =20 if (rmdir("real-root") =3D=3D -1) throw SysError("cannot remove real-root directory"); - } +#elif __GNU__ + if (chroot(".") =3D=3D -1) + throw SysError(format("cannot change root directory to '%1= %'") % chrootRootDir); + #endif + } +#endif // CHROOT_ENABLED =20 if (chdir(tmpDirInSandbox.c_str()) =3D=3D -1) throw SysError(format("changing into `%1%'") % tmpDir); diff --git a/nix/libutil/util.cc b/nix/libutil/util.cc index 59a2981359..b49a17a6eb 100644 --- a/nix/libutil/util.cc +++ b/nix/libutil/util.cc @@ -23,6 +23,41 @@ #include #endif =20 +#if HAVE_SYS_MOUNT_H +#include +#endif + +#ifdef __GNU__ + +extern "C" { + +/* XXX: uses 'new' as a parameter name. Work around it. */ +# define new new_param + +# include +# include +# include +# include + +# undef new + +/* XXX: is not C++-compatible. Copy these declarations to work + around it. */ + +typedef error_t (*fshelp_open_fn_t) (int flags, + file_t *node, + mach_msg_type_name_t *node_type, + task_t, void *cookie); + +extern error_t +fshelp_start_translator (fshelp_open_fn_t underlying_open_fn, void *cookie, + char *name, char *argz, int argz_len, + int timeout, fsys_t *control); + +} + +# define _HURD_FIRMLINK _HURD "firmlink" +#endif =20 extern char * * environ; =20 @@ -214,6 +249,89 @@ bool isLink(const Path & path) return S_ISLNK(st.st_mode); } =20 +#if __linux__ + +int firmlink(const Path &source, const Path &target) +{ + return mount(source.c_str(), target.c_str(), "", MS_BIND, 0); +} + +#elif __GNU__ + +static error_t return_node (int flags, + mach_port_t *underlying, + mach_msg_type_name_t *underlying_type, + task_t task, void *node) +{ + *underlying =3D * (mach_port_t *) node; + *underlying_type =3D MACH_MSG_TYPE_COPY_SEND; + + return ESUCCESS; +} + +int firmlink(const Path &source, const Path &target) +{ + static char firmlink[] =3D _HURD_FIRMLINK; + char *arg_vec[] =3D { firmlink, (char *) source.c_str (), NULL }; + char *args =3D NULL; + size_t args_len; + + error_t err; + file_t target_file =3D MACH_PORT_NULL; + + printMsg (lvlChatty, format("creating firmlink from '%1%' to '%2%'") + % source % target); + + target_file =3D file_name_lookup (target.c_str (), O_NOTRANS, 0); + if (! MACH_PORT_VALID (target_file)) { + printMsg (lvlChatty, format("firmlink target '%s' unavailable: %s") + % target % strerror (errno)); + goto fail; + } + + err =3D argz_create (arg_vec, &args, &args_len); + if (err !=3D 0) goto fail; + + mach_port_t control; + err =3D fshelp_start_translator (return_node, &target_file, + firmlink, args, args_len, + 3000, &control); + if (err) { + printMsg (lvlChatty, format("failed to start '%s' translator: %s") % + firmlink % strerror(errno)); + goto fail; + } + + free ((void *) args); + args =3D NULL; + + err =3D (error_t) file_set_translator (target_file, 0, FS_TRANS_SET, 0, + NULL, 0, + control, MACH_MSG_TYPE_COPY_SEND); + mach_port_deallocate (mach_task_self (), control); + mach_port_deallocate (mach_task_self (), target_file); + + if (err) { + printMsg (lvlChatty, format("failed to set '%s' translator on node '%s': = %s") % + firmlink % target % strerror(errno)); + goto fail; + } + + return err; + +fail: + int saved_errno =3D errno; + if (MACH_PORT_VALID (target_file)) + mach_port_deallocate (mach_task_self (), target_file); + if (args !=3D NULL) + free ((void *) args); + errno =3D saved_errno; + return -1; +} + +#elif +# error unsupported operating system +#endif =20 DirEntries readDirectory(const Path & path) { @@ -311,6 +429,27 @@ static void _deletePath(const Path & path, unsigned lo= ng long & bytesFreed, size =20 printMsg(lvlVomit, format("%1%") % path); =20 +#ifdef __GNU__ + /* Check whether there's an active translator on PATH--typically + /hurd/firmlink. If there is one, let it go away. */ + { + file_t file =3D file_name_lookup (path.c_str (), O_NOTRANS, 0); + if (MACH_PORT_VALID (file)) { + fsys_t fsys; + int err =3D file_get_translator_cntl (file, &fsys); + mach_port_deallocate (mach_task_self (), file); + if (err =3D=3D 0) { + /* There's a translator, tell it to leave. */ + err =3D fsys_goaway (fsys, FSYS_GOAWAY_FORCE | FSYS_GOAWAY_RECURSE); + mach_port_deallocate (mach_task_self (), fsys); + if (err !=3D 0) { + throw SysError(format("removing translator from '%1%'") % path); + } + } + } + } +#endif + #ifdef HAVE_STATX # define st_mode stx_mode # define st_size stx_size diff --git a/nix/libutil/util.hh b/nix/libutil/util.hh index 13cff44316..353c758895 100644 --- a/nix/libutil/util.hh +++ b/nix/libutil/util.hh @@ -62,6 +62,9 @@ Path readLink(const Path & path); =20 bool isLink(const Path & path); =20 +/* Make TARGET a firmlink (aka. "bind mount") to SOURCE. */ +int firmlink(const Path & source, const Path & target); + /* Read the contents of a directory. The entries `.' and `..' are removed. */ struct DirEntry diff --git a/nix/local.mk b/nix/local.mk index 2bb01041b9..782e2c85cc 100644 --- a/nix/local.mk +++ b/nix/local.mk @@ -124,7 +124,8 @@ guix_daemon_CPPFLAGS =3D \ =20 guix_daemon_LDADD =3D \ libstore.a libutil.a libformat.a -lz \ - $(SQLITE3_LIBS) $(LIBGCRYPT_LIBS) + $(SQLITE3_LIBS) $(LIBGCRYPT_LIBS) \ + $(HURD_LIBS) =20 guix_daemon_headers =3D \ %D%/nix-daemon/shared.hh --=20 2.28.0 --=-=-=--