1/**************************************************************************
3Copyright (c) 2004-16 Simon Peter
4Portions Copyright (c) 2010 RazZziel
8Permission is hereby granted, free of charge, to any person obtaining a copy
9of this software and associated documentation files (the "Software"), to deal
10in the Software without restriction, including without limitation the rights
11to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12copies of the Software, and to permit persons to whom the Software is
13furnished to do so, subject to the following conditions:
15The above copyright notice and this permission notice shall be included in
16all copies or substantial portions of the Software.
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26**************************************************************************/
43 fprintf(stderr, "Error: " __VA_ARGS__); \
51#define err_exit(format, ...) { fprintf(stderr, format ": %s\n", ##__VA_ARGS__, strerror(errno)); exit(EXIT_FAILURE); }
53int filter (const struct dirent *dir) {
54 char *p = (char*) &dir->d_name;
56 return p && !strcmp(p, ".desktop");
59static void update_map(char *mapping, char *map_file) {
62 fd = open(map_file, O_WRONLY);
67 int map_len = strlen(mapping);
68 if (write(fd, mapping, map_len) != map_len) {
69 err_exit("map write");
75static void add_path(const char* name, const char* rootdir) {
76 char path_buf[PATH_MAX];
77 snprintf(path_buf, sizeof(path_buf), "/%s", name);
80 if (stat(path_buf, &statbuf) < 0) {
81 fprintf(stderr, "Cannot stat %s: %s\n", path_buf, strerror(errno));
85 char path_buf2[PATH_MAX];
86 snprintf(path_buf2, sizeof(path_buf2), "%s/%s", rootdir, name);
88 mkdir(path_buf2, statbuf.st_mode & ~S_IFMT);
89 if (mount(path_buf, path_buf2, "none", MS_BIND | MS_REC, NULL) < 0) {
90 fprintf(stderr, "Cannot bind mount %s to %s: %s\n", path_buf, path_buf2, strerror(errno));
94#define SAVE_ENV_VAR(x) char *x = getenv(#x)
95#define LOAD_ENV_VAR(x) do { if (x != NULL) setenv(#x, x, 1); } while(0)
97int main(int argc, char *argv[]) {
98 char *appdir = dirname(realpath("/proc/self/exe", NULL));
100 die("Could not access /proc/self/exe\n");
102 char *tmpdir = getenv("TMPDIR");
107 char template[PATH_MAX];
108 int needed = snprintf(template, PATH_MAX, "%s/nixXXXXXX", tmpdir);
110 err_exit("TMPDIR too long: '%s'", tmpdir);
113 char *rootdir = mkdtemp(template);
115 err_exit("mkdtemp(%s)", template);
120 struct dirent **namelist;
122 ret = scandir(appdir, &namelist, filter, NULL);
125 die("No .desktop files found\n");
126 } else if(ret == -1) {
127 die("Could not scan directory %s\n", appdir);
130 /* Extract executable from .desktop file */
133 char *desktop_file = malloc(LINE_SIZE);
134 snprintf(desktop_file, LINE_SIZE-1, "%s/%s", appdir, namelist[0]->d_name);
135 f = fopen(desktop_file, "r");
137 char *line = malloc(LINE_SIZE);
138 size_t n = LINE_SIZE;
141 while (getline(&line, &n, f) != -1)
143 if (!strncmp(line,"Exec=",5))
146 while (*++p && *p != ' ' && *p != '%' && *p != '\n');
156 die("Executable not found, make sure there is a line starting with 'Exec='\n");
159 char *executable = basename(line+5);
161 char full_exec[PATH_MAX];
162 snprintf(full_exec, sizeof(full_exec), "/usr/bin/%s", executable);
164 // get uid, gid before going to new namespace
165 uid_t uid = getuid();
166 gid_t gid = getgid();
168 // "unshare" into new namespace
169 if (unshare(CLONE_NEWNS | CLONE_NEWUSER) < 0) {
170 err_exit("unshare()");
173 // add necessary system stuff to rootdir namespace
174 add_path("dev", rootdir);
175 add_path("proc", rootdir);
176 add_path("sys", rootdir);
177 add_path("run", rootdir);
178 add_path("etc", rootdir);
179 add_path("home", rootdir);
182 char path_buf[PATH_MAX];
183 snprintf(path_buf, sizeof(path_buf), "%s/tmp", rootdir);
185 snprintf(path_buf, sizeof(path_buf), "%s/var", rootdir);
188 // make sure nixdir exists
189 struct stat statbuf2;
190 if (stat(appdir, &statbuf2) < 0) {
191 err_exit("stat(%s)", appdir);
194 char nixdir[PATH_MAX];
195 snprintf(nixdir, sizeof(nixdir), "%s/nix", appdir);
196 snprintf(path_buf, sizeof(path_buf), "%s/nix", rootdir);
197 mkdir(path_buf, statbuf2.st_mode & ~S_IFMT);
198 if (mount(nixdir, path_buf, "none", MS_BIND | MS_REC, NULL) < 0) {
199 err_exit("mount(%s, %s)", nixdir, path_buf);
202 char usrdir[PATH_MAX];
203 snprintf(usrdir, sizeof(usrdir), "%s/usr", appdir);
204 snprintf(path_buf, sizeof(path_buf), "%s/usr", rootdir);
205 mkdir(path_buf, statbuf2.st_mode & ~S_IFMT);
206 if (mount(usrdir, path_buf, "none", MS_BIND | MS_REC, NULL) < 0) {
207 err_exit("mount(%s, %s)", usrdir, path_buf);
210 snprintf(path_buf, sizeof(path_buf), "%s/bin", rootdir);
211 if (symlink("/usr/bin", path_buf) < 0) {
212 err_exit("symlink(/usr/bin, %s)", path_buf);
215 // fixes issue #1 where writing to /proc/self/gid_map fails
216 // see user_namespaces(7) for more documentation
217 int fd_setgroups = open("/proc/self/setgroups", O_WRONLY);
218 if (fd_setgroups > 0) {
219 write(fd_setgroups, "deny", 4);
222 // map the original uid/gid in the new ns
224 snprintf(map_buf, sizeof(map_buf), "%d %d 1", uid, uid);
225 update_map(map_buf, "/proc/self/uid_map");
226 snprintf(map_buf, sizeof(map_buf), "%d %d 1", gid, gid);
227 update_map(map_buf, "/proc/self/gid_map");
230 if (chroot(rootdir) < 0) {
231 err_exit("chroot(%s)", rootdir);
234 char *pwddir = getenv("PWD");
238 SAVE_ENV_VAR(DBUS_SESSION_BUS_ADDRESS);
240 SAVE_ENV_VAR(HOSTNAME);
242 SAVE_ENV_VAR(LC_ALL);
244 SAVE_ENV_VAR(DISPLAY);
245 SAVE_ENV_VAR(XDG_RUNTIME_DIR);
246 SAVE_ENV_VAR(XAUTHORITY);
247 SAVE_ENV_VAR(XDG_SESSION_ID);
248 SAVE_ENV_VAR(XDG_SEAT);
254 LOAD_ENV_VAR(DBUS_SESSION_BUS_ADDRESS);
256 LOAD_ENV_VAR(HOSTNAME);
258 LOAD_ENV_VAR(LC_ALL);
260 LOAD_ENV_VAR(DISPLAY);
261 LOAD_ENV_VAR(XDG_RUNTIME_DIR);
262 LOAD_ENV_VAR(XAUTHORITY);
263 LOAD_ENV_VAR(XDG_SESSION_ID);
264 LOAD_ENV_VAR(XDG_SEAT);
267 setenv("PATH", ENV_PATH, 1);
268 setenv("TMPDIR", "/tmp", 1);
271 // FIXME: What about arguments in the Exec= line of the desktop file?
272 ret = execvp(full_exec, argv);
275 die("Error executing '%s'; return code: %d\n", full_exec, ret);
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list