Skip to content

Commit eb20e66

Browse files
authored
Merge pull request #32 from r-burns/opengl-driver-link
2 parents fe3da3a + f7ca790 commit eb20e66

File tree

3 files changed

+64
-17
lines changed

3 files changed

+64
-17
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,20 @@ The nix config is not in `/etc/nix` but in `/nix/etc/nix`, so that you can
103103
modify it. This is done with the `NIX_CONF_DIR`, which you can override at any
104104
time.
105105

106+
Libraries and applications from Nixpkgs with OpenGL or CUDA support need to
107+
load libraries from /run/opengl-driver/lib. For convenience, nix-user-chroot
108+
will bind mount /nix/var/nix/opengl-driver/lib (if it exists) to this location.
109+
You will still need to link the system libraries here, as their original
110+
locations are distro-dependent. For example, for CUDA support on Ubuntu 20.04:
111+
112+
```console
113+
$ mkdir -p /nix/var/nix/opengl-driver/lib
114+
$ ln -s /usr/lib/x86_64-linux-gnu/libcuda.so.1 /nix/var/nix/opengl-driver/lib
115+
```
116+
117+
If this directory didn't exist when you first entered the nix user chroot, you
118+
will need to reenter for /run/opengl-driver/lib to be mounted.
119+
106120
## Whishlist
107121

108122
These are features the author would like to see, let me know, if you want to work

src/main.rs

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use nix;
21
use nix::mount::{mount, MsFlags};
32
use nix::sched::{unshare, CloneFlags};
43
use nix::sys::signal::{kill, Signal};
@@ -46,28 +45,52 @@ impl<'a> RunChroot<'a> {
4645

4746
fn bind_mount_directory(&self, entry: &fs::DirEntry) {
4847
let mountpoint = self.rootdir.join(entry.file_name());
49-
if let Err(e) = fs::create_dir(&mountpoint) {
50-
if e.kind() != io::ErrorKind::AlreadyExists {
51-
panic!("failed to create {}: {}", &mountpoint.display(), e);
48+
49+
// if the destination doesn't exist we can proceed as normal
50+
if !mountpoint.exists() {
51+
if let Err(e) = fs::create_dir(&mountpoint) {
52+
if e.kind() != io::ErrorKind::AlreadyExists {
53+
panic!("failed to create {}: {}", &mountpoint.display(), e);
54+
}
5255
}
53-
}
5456

55-
bind_mount(&entry.path(), &mountpoint)
57+
bind_mount(&entry.path(), &mountpoint)
58+
} else {
59+
// otherwise, if the dest is also a dir, we can recurse into it
60+
// and mount subdirectory siblings of existing paths
61+
if mountpoint.is_dir() {
62+
let dir = fs::read_dir(entry.path()).unwrap_or_else(|err| {
63+
panic!("failed to list dir {}: {}", entry.path().display(), err)
64+
});
65+
66+
let child = RunChroot::new(&mountpoint);
67+
for entry in dir {
68+
let entry = entry.expect("error while listing subdir");
69+
child.bind_mount_direntry(&entry);
70+
}
71+
}
72+
}
5673
}
5774

5875
fn bind_mount_file(&self, entry: &fs::DirEntry) {
5976
let mountpoint = self.rootdir.join(entry.file_name());
77+
if mountpoint.exists() {
78+
return;
79+
}
6080
fs::File::create(&mountpoint)
6181
.unwrap_or_else(|err| panic!("failed to create {}: {}", &mountpoint.display(), err));
6282

6383
bind_mount(&entry.path(), &mountpoint)
6484
}
6585

6686
fn mirror_symlink(&self, entry: &fs::DirEntry) {
87+
let link_path = self.rootdir.join(entry.file_name());
88+
if link_path.exists() {
89+
return;
90+
}
6791
let path = entry.path();
6892
let target = fs::read_link(&path)
6993
.unwrap_or_else(|err| panic!("failed to resolve symlink {}: {}", &path.display(), err));
70-
let link_path = self.rootdir.join(entry.file_name());
7194
symlink(&target, &link_path).unwrap_or_else(|_| {
7295
panic!(
7396
"failed to create symlink {} -> {}",
@@ -77,16 +100,12 @@ impl<'a> RunChroot<'a> {
77100
});
78101
}
79102

80-
fn bind_mount_direntry(&self, entry: io::Result<fs::DirEntry>) {
81-
let entry = entry.expect("error while listing from /nix directory");
82-
// do not bind mount an existing nix installation
83-
if entry.file_name() == PathBuf::from("nix") {
84-
return;
85-
}
103+
fn bind_mount_direntry(&self, entry: &fs::DirEntry) {
86104
let path = entry.path();
87105
let stat = entry
88106
.metadata()
89107
.unwrap_or_else(|err| panic!("cannot get stat of {}: {}", path.display(), err));
108+
90109
if stat.is_dir() {
91110
self.bind_mount_directory(&entry);
92111
} else if stat.is_file() {
@@ -104,11 +123,26 @@ impl<'a> RunChroot<'a> {
104123

105124
unshare(CloneFlags::CLONE_NEWNS | CloneFlags::CLONE_NEWUSER).expect("unshare failed");
106125

107-
// bind mount all / stuff into rootdir
126+
// create /run/opengl-driver/lib in chroot, to behave like NixOS
127+
// (needed for nix pkgs with OpenGL or CUDA support to work)
128+
let ogldir = nixdir.join("var/nix/opengl-driver/lib");
129+
if ogldir.is_dir() {
130+
let ogl_mount = self.rootdir.join("run/opengl-driver/lib");
131+
fs::create_dir_all(&ogl_mount)
132+
.unwrap_or_else(|err| panic!("failed to create {}: {}", &ogl_mount.display(), err));
133+
bind_mount(&ogldir, &ogl_mount);
134+
}
135+
136+
// bind the rest of / stuff into rootdir
108137
let nix_root = PathBuf::from("/");
109138
let dir = fs::read_dir(&nix_root).expect("failed to list /nix directory");
110139
for entry in dir {
111-
self.bind_mount_direntry(entry);
140+
let entry = entry.expect("error while listing from /nix directory");
141+
// do not bind mount an existing nix installation
142+
if entry.file_name() == PathBuf::from("nix") {
143+
continue;
144+
}
145+
self.bind_mount_direntry(&entry);
112146
}
113147

114148
// mount the store

src/mkdtemp.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use std::env;
33
use std::ffi::OsString;
44
use std::os::unix::ffi::OsStringExt;
55
use std::path::PathBuf;
6-
use std::ptr;
76

87
mod ffi {
98
extern "C" {
@@ -18,7 +17,7 @@ pub fn mkdtemp(template: &str) -> nix::Result<PathBuf> {
1817
buf.push(b'\0'); // make a c string
1918

2019
let res = unsafe { ffi::mkdtemp(buf.as_mut_ptr() as *mut libc::c_char) };
21-
if res == ptr::null_mut() {
20+
if res.is_null() {
2221
Err(nix::Error::Sys(Errno::last()))
2322
} else {
2423
buf.pop(); // strip null byte

0 commit comments

Comments
 (0)