-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjontainer.java
More file actions
122 lines (96 loc) · 3.24 KB
/
jontainer.java
File metadata and controls
122 lines (96 loc) · 3.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import com.sun.jna.Library;
import com.sun.jna.Native;
import java.util.Arrays;
import java.util.function.Supplier;
import java.util.stream.Stream;
public class jontainer {
public static void main(String[] args) {
System.out.println("args: " + Arrays.toString(args));
if (args.length == 0) {
System.err.println("No command provided");
System.exit(1);
}
var cmd = Arrays.copyOfRange(args, 1, args.length);
switch (args[0]) {
case "run" -> {
Parent.run(cmd);
}
case "child" -> {
Child.run(cmd);
}
default -> {
System.out.println("Unknown command: " + args[0]);
System.exit(1);
}
}
}
}
class Parent {
public static void run(String[] cmd) {
System.out.println(
"Running parent " + Arrays.toString(cmd) + " as " + ProcessHandle.current().pid());
var javaExe = ProcessHandle.current().info().command().orElse("java");
var classPath = System.getProperty("java.class.path");
var mainClass = System.getProperty("sun.java.command").split(" ")[1];
var cmdWithChild =
Stream.concat(Stream.of(javaExe, "-cp", classPath, mainClass, "child"), Arrays.stream(cmd));
// Prepend unshare with namespace args for the child process
var cmdWithUnshare =
Stream.concat(
Stream.of("unshare", "--uts", "--pid", "--mount", "--fork", "--mount-proc"),
cmdWithChild)
.toList();
Util.must(() -> new ProcessBuilder(cmdWithUnshare).inheritIO().start().waitFor());
}
}
class Child {
public static void run(String[] cmd) {
System.out.println(
"Running child " + Arrays.toString(cmd) + " as " + ProcessHandle.current().pid());
// set the hostname of current child container
Util.must(() -> new ProcessBuilder("hostname", "container").inheritIO().start().waitFor());
// change to new debian fs
Util.cmust(() -> c.INSTANCE.chroot("/my-fs/debian-fs"));
Util.cmust(() -> c.INSTANCE.chdir("/"));
// Mount proc filesystem
Util.cmust(() -> c.INSTANCE.mount("proc", "proc", "proc", 0, ""));
// Processbuilder won't work here - cause it forks the process from the original jvm machine
// -- i'm guessing here
Util.cmust(() -> c.INSTANCE.system(String.join(" ", cmd)));
// unmount proc when finishing
Util.cmust(() -> c.INSTANCE.umount("/proc", 0));
}
}
interface c extends Library {
c INSTANCE = Native.load("c", c.class);
int MS_BIND = 4096;
// for testing jna writing to sysout
int write(int fd, String buf, int count);
int chroot(String path);
int chdir(String path);
int system(String command);
int mount(String source, String target, String filesystemtype, int mountflags, String data);
int umount(String target, int flags);
}
class Util {
static void must(ThrowableRunnable x) {
try {
x.run();
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
@FunctionalInterface
interface ThrowableRunnable {
void run() throws Exception;
}
static void cmust(Supplier<Integer> x) {
var res = x.get();
if (res < 0) {
int errno = Native.getLastError();
System.err.println("c call failed with return code: " + res + ", errno: " + errno);
System.exit(1);
}
}
}