dev.fron.io rc / 1627cb2
users::bao: init pkgs.guix-ns and use for rootless guix Tony Olagbaiye 1 year, 14 days ago
7 changed file(s) with 255 addition(s) and 5 deletion(s). Raw diff Collapse all Expand all
55 ; (locate-dominating-file buffer-file-name ".dir-locals.el")))
66 (compile-command . "systemd-run --user --scope nix run . -- -Tbasv")
77 (eval . (setq projectile-project-compilation-cmd
8 "systemd-run --user -u nixos --scope if nix build --log-format bar-with-logs '' if ./result '' git push"))
8 "systemd-run --user -u nixos --scope if nix build '' if ./result '' git push"))
99 )))
5252 headPat = "''\\n";
5353 tailPat = "^\\s*''";
5454 };
55 nix-c = basicPolymode {
56 host = "nix";
57 inner = "c";
58 discrim = "dsquoted";
59 headPat = "''\\n";
60 tailPat = "^\\s*''";
61 };
5562 };
5663 config = (lib.concatStrings (builtins.attrValues config.polymodes)) + ''
5764
749749 echo Deploying ${toplevel}
750750 exec doas ${toplevel}/bin/switch-to-configuration test $@
751751 '').outPath;
752 bao = rec {
753 type = "app";
754 inherit (inputs.self.homeConfigurations.delta.bao.home) activationPackage;
755 program = (pkgs.writeShellScript "test-delta-bao" ''
756 echo Deploying ${activationPackage}
757 exec ${activationPackage}/activate $@
758 '').outPath;
759 };
752760 };
753761 nixos = {
754762 type = "app";
800808 ${lib.optionalString (builtins.pathExists /etc/nix/nix.conf)
801809 (builtins.readFile /etc/nix/nix.conf)}
802810 experimental-features = nix-command flakes ca-references
803 log-format = bar-with-logs
811 print-build-logs = true
804812 '';
805813 in linkFarm "nix-conf-dir" ( [
806814 { name = "nix.conf"; path = writeText "flakes-nix.conf" nixConf; }
7474 "/home" = hdd // { options = [ "subvol=home" ]; };
7575 "/srv" = hdd // { options = [ "subvol=srv" ]; };
7676 "/nix" = ssd // { options = [ "subvol=nix" "noatime" "nodiratime" "discard=async" ]; };
77 "/gnu" = ssd // { options = [ "subvol=gnu" "noatime" "nodiratime" "discard=async" ]; };
77 #"/gnu" = ssd // { options = [ "subvol=gnu" "noatime" "nodiratime" "discard=async" ]; };
7878 "/games" = hdd // { options = [ "subvol=games" ]; };
7979 "/run/hdd" = hdd // { options = [ "subvolid=0" ]; };
8080 "/run/ssd" = ssd // { options = [ "subvolid=0" "noatime" "nodiratime" "discard=async" ]; };
3636
3737 greetd = prev.callPackage ./applications/display-managers/greetd { };
3838
39 guix-ns = prev.callPackage ./tools/misc/guix-ns { };
40
3941 haskellPackages = recurseIntoAttrs (prev.haskellPackages.override { overrides = haskellOverride; });
4042
4143 ipfscat = prev.callPackage ./applications/misc/ipfscat { };
0 { pkgs ? import <nixpkgs> {}, ... }:
1
2 let
3 src = pkgs.writeText "main.c" ''
4 #define _GNU_SOURCE
5 #include <sched.h>
6 #include <unistd.h>
7 #include <stdlib.h>
8 #include <sys/wait.h>
9 #include <sys/syscall.h>
10 #include <signal.h>
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <limits.h>
15 #include <errno.h>
16 #include <sys/mount.h>
17 #include <sys/types.h>
18 #include <dirent.h>
19 #include <sys/stat.h>
20
21 #define err_exit(format, ...) { fprintf(stderr, format ": %s\n", ##__VA_ARGS__, strerror(errno)); exit(EXIT_FAILURE); }
22
23 static int pivot_root(const char *new_root, const char *put_old) {
24 return syscall(SYS_pivot_root,new_root,put_old);
25 }
26
27 static void usage(char *pname) {
28 fprintf(stderr, "Usage: %s <gnupath> <command>\n", pname);
29
30 exit(EXIT_FAILURE);
31 }
32
33 static void update_map(char *mapping, char *map_file) {
34 int fd;
35
36 fd = open(map_file, O_WRONLY);
37 if (fd < 0) {
38 err_exit("map open");
39 }
40
41 int map_len = strlen(mapping);
42 if (write(fd, mapping, map_len) != map_len) {
43 err_exit("map write");
44 }
45
46 close(fd);
47 }
48
49 int main(int argc, char *argv[]) {
50 char map_buf[1024];
51 char path_buf[PATH_MAX];
52 char path_buf2[PATH_MAX];
53 char cwd[PATH_MAX];
54
55 if (argc < 3) {
56 usage(argv[0]);
57 }
58
59 char *tmpdir = getenv("TMPDIR");
60 if (!tmpdir) {
61 tmpdir = "/tmp";
62 }
63
64 char template[PATH_MAX];
65 int needed = snprintf(template, PATH_MAX, "%s/.guix.XXXXXX", tmpdir);
66 if (needed < 0) {
67 err_exit("TMPDIR too long: '%s'", tmpdir);
68 }
69
70 char *rootdir = mkdtemp(template);
71 if (!rootdir) {
72 err_exit("mkdtemp(%s)", template);
73 }
74
75 char *gnudir = realpath(argv[1], NULL);
76 if (!gnudir) {
77 err_exit("realpath(%s)", argv[1]);
78 }
79
80 // save cwd to restore it later
81 if (!getcwd(cwd, PATH_MAX)) {
82 err_exit("getcwd()");
83 }
84
85 uid_t uid = getuid();
86 gid_t gid = getgid();
87
88 if (unshare(CLONE_NEWNS | CLONE_NEWUSER) < 0) {
89 err_exit("unshare()");
90 }
91
92 // prepare pivot_root call:
93 // rootdir must be a mount point
94 if (mount(rootdir, rootdir, "none", MS_BIND | MS_REC, NULL) < 0) {
95 err_exit("mount --bind %s %s", rootdir, rootdir);
96 }
97 if (mount(rootdir, rootdir, "none", MS_PRIVATE | MS_REC, NULL) < 0) {
98 err_exit("mount --make-rprivate %s", rootdir);
99 }
100
101 // create the mount point for the old root
102 // The old root cannot be unmounted/removed after pivot_root, the only way to
103 // keep / clean is to hide the directory with another mountpoint. Therefore
104 // we pivot the old root to /gnu. This is somewhat confusing, though.
105 snprintf(path_buf, sizeof(path_buf), "%s/gnu", rootdir);
106 if (mkdir(path_buf, 0) < 0) { // the mode is irrelevant
107 err_exit("mkdir(%s, 0)", path_buf);
108 }
109
110 // pivot_root
111 if (pivot_root(rootdir, path_buf) < 0) {
112 err_exit("pivot_root(%s, %s)", rootdir, path_buf);
113 }
114 chdir("/");
115
116 // bind mount all / stuff into rootdir
117 // the orginal content of / now available under /gnu
118 DIR* d = opendir("/gnu");
119 if (!d) {
120 err_exit("open /gnu");
121 }
122
123 struct dirent *ent;
124 while ((ent = readdir(d))) {
125 // do not bind mount an existing guix installation
126 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..") || !strcmp(ent->d_name, "gnu")) {
127 continue;
128 }
129
130 snprintf(path_buf, sizeof(path_buf), "/gnu/%s", ent->d_name);
131
132 struct stat statbuf;
133 if (stat(path_buf, &statbuf) < 0) {
134 fprintf(stderr, "Cannot stat %s: %s\n", path_buf, strerror(errno));
135 continue;
136 }
137
138 snprintf(path_buf2, sizeof(path_buf2), "/%s", ent->d_name);
139
140 if (S_ISDIR(statbuf.st_mode)) {
141 mkdir(path_buf2, statbuf.st_mode & ~S_IFMT);
142 if (mount(path_buf, path_buf2, "none", MS_BIND | MS_REC, NULL) < 0) {
143 fprintf(stderr, "Cannot bind mount %s to %s: %s\n", path_buf, path_buf2, strerror(errno));
144 }
145 }
146 }
147
148 // mount the store and hide the old root
149 // we fetch gnudir under the old root
150 snprintf(path_buf, sizeof(path_buf), "/gnu/%s", gnudir);
151 if (mount(path_buf, "/gnu", "none", MS_BIND | MS_REC, NULL) < 0) {
152 err_exit("mount --bind %s /gnu", path_buf);
153 }
154
155 // fixes issue #1 where writing to /proc/self/gid_map fails
156 // see user_namespaces(7) for more documentation
157 int fd_setgroups = open("/proc/self/setgroups", O_WRONLY);
158 if (fd_setgroups > 0) {
159 write(fd_setgroups, "deny", 4);
160 }
161
162 // map the original uid/gid in the new ns
163 #ifdef FAKEROOT
164 snprintf(map_buf, sizeof(map_buf), "%d %d 1", 0, uid);
165 #else
166 snprintf(map_buf, sizeof(map_buf), "%d %d 1", uid, uid);
167 #endif
168 update_map(map_buf, "/proc/self/uid_map");
169
170 #ifdef FAKEROOT
171 snprintf(map_buf, sizeof(map_buf), "%d %d 1", 0, gid);
172 #else
173 snprintf(map_buf, sizeof(map_buf), "%d %d 1", gid, gid);
174 #endif
175 update_map(map_buf, "/proc/self/gid_map");
176
177 // restore cwd
178 chdir(cwd);
179
180 // execute the command
181 setenv("GUIX_CONFIGURATION_DIRECTORY", "/gnu/etc/guix", 1);
182 setenv("GUIX_STATE_DIRECTORY", "/gnu/var", 1);
183 setenv("GUIX_LOG_DIRECTORY", "/gnu/var/log", 1);
184 setenv("GUIX_DATABASE_DIRECTORY", "/gnu/var/db", 1);
185 execvp(argv[2], argv+2);
186 err_exit("execvp(%s)", argv[2]);
187 }
188 '';
189 in pkgs.stdenv.mkDerivation rec {
190 name = "guix-ns";
191 inherit src;
192 dontUnpack = true;
193 buildPhase = "$CC $src -o user; $CC -DFAKEROOT $src -o root";
194 installPhase = ''mkdir -p $out/bin;
195 mv user $out/bin/${name}-user
196 mv root $out/bin/${name}-root
197 cd $out/bin && ln -s ${name}-root ${name}
198 '';
199 }
200
201 ## Local Variables: ***
202 ## mode: nix-dsquoted-c ***
203 ## End: ***
133133 services.ckb.enable = !config.headless;
134134
135135 #systemd.user.startServices = true; # broken by the [nix-env -> nix profile] move
136 systemd.user.services.guix-daemon = with home-config.lib.guix; {
137 Service = {
138 ExecStart = "${pkgs.guix-ns}/bin/guix-ns-root ${homeDirectory}/gnu ${guix}/bin/guix-daemon --max-jobs=4 --debug";
139 Environment = [ "GUIX_LOCPATH='${profile}/lib/locale'" ];
140 };
141 Install = {
142 WantedBy = [ "default.target" ];
143 };
144 };
145
146 lib.guix = rec {
147 inherit (home-config.home) homeDirectory username;
148 guix = "/gnu/var/profiles/per-user/${username}/current-guix";
149 profile = "/gnu/var/profiles/per-user/${username}/guix-profile";
150 ns-env = pkgs.writeShellScriptBin "guix-ns-env" ''
151 export GUIX_PROFILE=$(readlink -f ${homeDirectory}/${profile})
152 export GUIX_LOCPATH=$GUIX_PROFILE/lib/locale
153 source ${homeDirectory}/$GUIX_PROFILE/etc/profile
154 exec ${pkgs.guix-ns}/bin/guix-ns ${homeDirectory}/gnu $@
155 '';
156 packages = {
157 inherit ns-env;
158 jami = pkgs.writeScriptBin "jami" ''
159 #!${pkgs.execline}/bin/execlineb -S0
160 ${ns-env}/bin/guix-ns-env ${pkgs.dbus.lib}/bin/dbus-launch env CLUTTER_BACKEND=x11 jami $@
161 '';
162 };
163 };
136164
137165 home.packages = with pkgs; let
138166 emms-play-file = pkgs.writeScriptBin "emms-play-file" ''
139 !#${pkgs.execline}/bin/execlineb -W
167 #!${pkgs.execline}/bin/execlineb -W
140168 ${home-config.programs.emacs.package}/bin/emacsclient --eval "(emms-play-file \"$@\")"
141169 '';
142170 in [
153181 audacity # Audio Utilities
154182 xpra xsel xclip scrot # X11 Utilities
155183 gdb lldb radare2 radare2-cutter jadx stress # Debug Utilities
156 ] ++ lib.optional home-config.programs.emacs.enable emms-play-file;
184 ] ++ lib.optional home-config.programs.emacs.enable emms-play-file
185 ++ (builtins.attrValues home-config.lib.guix.packages);
157186
158187 home.activation.preloadNixSearch = home-config.lib.dag.entryAnywhere ''
159188 function preloadNixSearch() {