|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
|
4 */ |
|
5 |
|
6 /* |
|
7 * ChromeWorker Object subprocess.jsm on Unix-like systems (Linux, Mac OS X, ...) |
|
8 * to process stdin/stdout/stderr on separate threads. |
|
9 * |
|
10 */ |
|
11 |
|
12 // Being a ChromeWorker object, implicitly uses the following: |
|
13 // Components.utils.import("resource://gre/modules/ctypes.jsm"); |
|
14 |
|
15 'use strict'; |
|
16 |
|
17 const BufferSize = 1024; |
|
18 |
|
19 var libc = null; |
|
20 var libcFunc = {}; |
|
21 |
|
22 |
|
23 /* |
|
24 struct pollfd { |
|
25 int fd; // file descriptor |
|
26 short events; // events to look for |
|
27 short revents; // events returned |
|
28 }; |
|
29 */ |
|
30 |
|
31 var pollfd = new ctypes.StructType("pollfd", |
|
32 [ {'fd': ctypes.int}, |
|
33 {'events': ctypes.short}, |
|
34 {'revents': ctypes.short} |
|
35 ]); |
|
36 |
|
37 var WriteBuffer = ctypes.uint8_t.array(BufferSize); |
|
38 var ReadBuffer = ctypes.char.array(BufferSize); |
|
39 |
|
40 |
|
41 const POLLIN = 0x0001; |
|
42 const POLLOUT = 0x0004; |
|
43 |
|
44 const POLLERR = 0x0008; // some poll error occurred |
|
45 const POLLHUP = 0x0010; // file descriptor was "hung up" |
|
46 const POLLNVAL = 0x0020; // requested events "invalid" |
|
47 |
|
48 const WNOHANG = 0x01; |
|
49 |
|
50 const pid_t = ctypes.int32_t; |
|
51 |
|
52 const INDEFINITE = -1; |
|
53 const NOWAIT = 0; |
|
54 const WAITTIME = 200 // wait time for poll() in ms |
|
55 |
|
56 function initLibc(libName) { |
|
57 postMessage({msg: "debug", data: "initialising library with "+ libName}); |
|
58 |
|
59 libc = ctypes.open(libName); |
|
60 |
|
61 libcFunc.pollFds = pollfd.array(1); |
|
62 |
|
63 // int poll(struct pollfd fds[], nfds_t nfds, int timeout); |
|
64 libcFunc.poll = libc.declare("poll", |
|
65 ctypes.default_abi, |
|
66 ctypes.int, |
|
67 libcFunc.pollFds, |
|
68 ctypes.unsigned_int, |
|
69 ctypes.int); |
|
70 |
|
71 //ssize_t write(int fd, const void *buf, size_t count); |
|
72 // NOTE: buf is declared as array of unsigned int8 instead of char to avoid |
|
73 // implicit charset conversion |
|
74 libcFunc.write = libc.declare("write", |
|
75 ctypes.default_abi, |
|
76 ctypes.int, |
|
77 ctypes.int, |
|
78 WriteBuffer, |
|
79 ctypes.int); |
|
80 |
|
81 //int read(int fd, void *buf, size_t count); |
|
82 libcFunc.read = libc.declare("read", |
|
83 ctypes.default_abi, |
|
84 ctypes.int, |
|
85 ctypes.int, |
|
86 ReadBuffer, |
|
87 ctypes.int); |
|
88 |
|
89 //int pipe(int pipefd[2]); |
|
90 libcFunc.pipefd = ctypes.int.array(2); |
|
91 |
|
92 //int close(int fd); |
|
93 libcFunc.close = libc.declare("close", |
|
94 ctypes.default_abi, |
|
95 ctypes.int, |
|
96 ctypes.int); |
|
97 |
|
98 //pid_t waitpid(pid_t pid, int *status, int options); |
|
99 libcFunc.waitpid = libc.declare("waitpid", |
|
100 ctypes.default_abi, |
|
101 pid_t, |
|
102 pid_t, |
|
103 ctypes.int.ptr, |
|
104 ctypes.int); |
|
105 } |
|
106 |
|
107 function closePipe(pipe) { |
|
108 libcFunc.close(pipe); |
|
109 } |
|
110 |
|
111 function writePipe(pipe, data) { |
|
112 |
|
113 postMessage({msg: "debug", data: "trying to write to "+pipe}); |
|
114 |
|
115 let numChunks = Math.floor(data.length / BufferSize); |
|
116 let pData = new WriteBuffer(); |
|
117 |
|
118 for (var chunk = 0; chunk <= numChunks; chunk ++) { |
|
119 let numBytes = chunk < numChunks ? BufferSize : data.length - chunk * BufferSize; |
|
120 for (var i=0; i < numBytes; i++) { |
|
121 pData[i] = data.charCodeAt(chunk * BufferSize + i) % 256; |
|
122 } |
|
123 |
|
124 let bytesWritten = libcFunc.write(pipe, pData, numBytes); |
|
125 if (bytesWritten != numBytes) { |
|
126 closePipe(); |
|
127 libc.close(); |
|
128 postMessage({ msg: "error", data: "error: wrote "+bytesWritten+" instead of "+numBytes+" bytes"}); |
|
129 close(); |
|
130 } |
|
131 } |
|
132 postMessage({msg: "info", data: "wrote "+data.length+" bytes of data"}); |
|
133 } |
|
134 |
|
135 |
|
136 function readString(data, length, charset) { |
|
137 var string = '', bytes = []; |
|
138 for(var i = 0;i < length; i++) { |
|
139 if(data[i] == 0 && charset != "null") // stop on NULL character for non-binary data |
|
140 break; |
|
141 |
|
142 bytes.push(data[i]); |
|
143 } |
|
144 |
|
145 return bytes; |
|
146 } |
|
147 |
|
148 function readPipe(pipe, charset, pid) { |
|
149 var p = new libcFunc.pollFds; |
|
150 p[0].fd = pipe; |
|
151 p[0].events = POLLIN | POLLERR | POLLHUP; |
|
152 p[0].revents = 0; |
|
153 var pollTimeout = WAITTIME; |
|
154 var exitCode = -1; |
|
155 var readCount = 0; |
|
156 var result, status = ctypes.int(); |
|
157 result = 0; |
|
158 |
|
159 |
|
160 const i=0; |
|
161 while (true) { |
|
162 if (result == 0) { |
|
163 result = libcFunc.waitpid(pid, status.address(), WNOHANG); |
|
164 if (result > 0) { |
|
165 pollTimeout = NOWAIT; |
|
166 exitCode = parseInt(status.value); |
|
167 postMessage({msg: "debug", data: "waitpid signaled subprocess stop, exitcode="+status.value }); |
|
168 } |
|
169 } |
|
170 var r = libcFunc.poll(p, 1, pollTimeout); |
|
171 if (r > 0) { |
|
172 if (p[i].revents & POLLIN) { |
|
173 postMessage({msg: "debug", data: "reading next chunk"}); |
|
174 readCount = readPolledFd(p[i].fd, charset); |
|
175 if (readCount == 0) break; |
|
176 } |
|
177 |
|
178 if (p[i].revents & POLLHUP) { |
|
179 postMessage({msg: "debug", data: "poll returned HUP"}); |
|
180 break; |
|
181 } |
|
182 else if (p[i].revents & POLLERR) { |
|
183 postMessage({msg: "error", data: "poll returned error"}); |
|
184 break; |
|
185 } |
|
186 else if (p[i].revents != POLLIN) { |
|
187 postMessage({msg: "error", data: "poll returned "+p[i]}); |
|
188 break; |
|
189 } |
|
190 } |
|
191 else |
|
192 if (pollTimeout == 0 || r < 0) break; |
|
193 } |
|
194 |
|
195 // continue reading until the buffer is empty |
|
196 while (readCount > 0) { |
|
197 readCount = readPolledFd(pipe, charset); |
|
198 } |
|
199 |
|
200 libcFunc.close(pipe); |
|
201 postMessage({msg: "done", data: exitCode }); |
|
202 libc.close(); |
|
203 close(); |
|
204 } |
|
205 |
|
206 function readPolledFd(pipe, charset) { |
|
207 var line = new ReadBuffer(); |
|
208 var r = libcFunc.read(pipe, line, BufferSize); |
|
209 |
|
210 if (r > 0) { |
|
211 var c = readString(line, r, charset); |
|
212 postMessage({msg: "data", data: c, count: c.length}); |
|
213 } |
|
214 return r; |
|
215 } |
|
216 |
|
217 onmessage = function (event) { |
|
218 switch (event.data.msg) { |
|
219 case "init": |
|
220 initLibc(event.data.libc); |
|
221 break; |
|
222 case "read": |
|
223 initLibc(event.data.libc); |
|
224 readPipe(event.data.pipe, event.data.charset, event.data.pid); |
|
225 break; |
|
226 case "write": |
|
227 // data contents: |
|
228 // msg: 'write' |
|
229 // data: the data (string) to write |
|
230 // pipe: ptr to pipe |
|
231 writePipe(event.data.pipe, event.data.data); |
|
232 postMessage({msg: "info", data: "WriteOK"}); |
|
233 break; |
|
234 case "close": |
|
235 postMessage({msg: "debug", data: "closing stdin\n"}); |
|
236 |
|
237 closePipe(event.data.pipe); |
|
238 postMessage({msg: "info", data: "ClosedOK"}); |
|
239 break; |
|
240 case "stop": |
|
241 libc.close(); // do not use libc after this point |
|
242 close(); |
|
243 break; |
|
244 default: |
|
245 throw("error: Unknown command"+event.data.msg+"\n"); |
|
246 } |
|
247 return; |
|
248 }; |