/
ipc.d
168 lines (121 loc) · 4.68 KB
/
ipc.d
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
module user.ipc;
import user.environment;
// place to store values that must be communicated to the child process from the parent
struct MessageInAbottle {
ubyte[] stdin;
ubyte[] stdout;
bool stdinIsTTY, stdoutIsTTY;
char[][] argv;
int exitCode;
// assumes alloc on write beyond end of exe
void setArgv(char[][] parentArgv, ubyte[] to = (cast(ubyte*)oneGB)[0..oneGB]){
// assumes allocation on write region exists immediately following bottle
// allocate argv's array reference array first, since we know how long it is
argv = (cast(char[]*)this + MessageInAbottle.sizeof)[0..parentArgv.length];
// this will be a sliding window for the strngs themselves, allocated after the argv array reference array
char[] storage = (cast(char*)argv[length..length].ptr)[0..0];
foreach(i, str; parentArgv){
storage = storage[length..length].ptr[0..(str.length+1)]; // allocate an extra space for null terminator
storage[0..(str.length)] = str[];
storage[(str.length)] = '\0'; // stick on null terminator
argv[i] = storage[0..(str.length)];
}
// adjust pointers
adjustArgvPointers(to);
}
void setArgv(char[] parentArgv, ubyte[] to = (cast(ubyte*)oneGB)[0..oneGB]){
// allocate strings first, since we know how long they are
char[] storage = (cast(char*)this + MessageInAbottle.sizeof)[0..(parentArgv.length +1)];
storage[0..($-1)] = parentArgv[];
// determine length of array reference array
int substrings = 1;
foreach(ch; storage){
if(ch == ' '){
substrings++;
}
}
storage[($-1)] = '\0';
// allocate array reference array
argv = (cast(char[]*)storage[length..length].ptr)[0..substrings];
char* arg = storage.ptr;
int len, i;
foreach(ref ch; storage){
if(ch == ' '){
ch = '\0';
argv[i] = arg[0..len];
len++;
len++;
arg = arg[len..len].ptr;
len = 0;
i++;
}else{
len++;
}
}//end foreach
// final sub array isn't (hopefully) followed by a space, so it
// will bot get assigned in loop, and we must do it here instead
argv[i] = (arg)[0..len];
adjustArgvPointers(to);
}
private:
void adjustArgvPointers(ubyte[] to){
// exploits fact that all argv pointers are intra-segment, so it
// is enought to mod (mask) by the segment size and then add the
// new segment base address
foreach(ref str; argv){
str = (cast(char*)(to.ptr + (cast(ulong)str.ptr & (to.length -1) )))[0..str.length];
}
argv = (cast(char[]*)(to.ptr + (cast(ulong)argv.ptr & (to.length -1) )))[0..argv.length];
}
public static:
MessageInAbottle* getBottleForSegment(ubyte* seg){
return cast(MessageInAbottle*)(seg + (oneGB - 4096));
}
MessageInAbottle* getMyBottle(){
return getBottleForSegment(cast(ubyte*) oneGB);
}
}
template populateChild(T){
void populateChild(T argv, AddressSpace child, ubyte[] f, ubyte[] stdin = null, ubyte[] stdout = null){
// XXX: restrict T to char[] and char[][]
// map executable to default (kernel hardcoded) location in the child address space
ubyte* dest = cast(ubyte*)oneGB;
assert(child !is null && f !is null && dest !is null, "NULLS!!!!!\n");
version(KERNEL){
// kernel only executes init once, so its OK not to copy
}else{
ubyte* g = findFreeSegment(false).ptr;
Syscall.create(g, oneGB, AccessMode.Writable|AccessMode.User|AccessMode.Executable|AccessMode.AllocOnAccess);
// XXX: instead of copying the whole thing we should only be duping the r/w data section
uint len = *(cast(ulong*)f.ptr) + ulong.sizeof;
g[0..len] = f.ptr[0..len];
f = g[0..f.length];
}
Syscall.map(child, f, dest, AccessMode.Writable|AccessMode.User|AccessMode.Executable|AccessMode.AllocOnAccess);
// bottle to bottle transfer of stdin/out isthe default case
MessageInAbottle* bottle = MessageInAbottle.getMyBottle();
MessageInAbottle* childBottle = MessageInAbottle.getBottleForSegment(f.ptr);
childBottle.stdoutIsTTY = false;
childBottle.stdinIsTTY = false;
childBottle.setArgv(argv);
AccessMode stdoutMode = AccessMode.Writable|AccessMode.User;
// if no stdin/out is specified, us the same buffer as parent
if(stdout is null){
stdout = bottle.stdout;
childBottle.stdoutIsTTY = bottle.stdoutIsTTY;
}
if(!childBottle.stdoutIsTTY){
stdoutMode |= AccessMode.AllocOnAccess;
}
if(stdin is null){
stdin = bottle.stdin;
childBottle.stdinIsTTY = bottle.stdinIsTTY;
}
// XXX: use findFreeSemgent to pick gib locations in child
childBottle.stdout = (cast(ubyte*)(2*oneGB))[0..stdout.length];
childBottle.stdin = (cast(ubyte*)(3*oneGB))[0..stdin.length];
// map stdin/out into child process
Syscall.map(child, stdout, childBottle.stdout.ptr, stdoutMode);
Syscall.map(child, stdin, childBottle.stdin.ptr, AccessMode.Writable|AccessMode.User);
}
}