@php-wasm/node 1.1.2 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/asyncify/7_2_34/php_7_2.wasm +0 -0
- package/asyncify/7_3_33/php_7_3.wasm +0 -0
- package/asyncify/7_4_33/php_7_4.wasm +0 -0
- package/asyncify/8_0_30/php_8_0.wasm +0 -0
- package/asyncify/8_1_23/php_8_1.wasm +0 -0
- package/asyncify/8_2_10/php_8_2.wasm +0 -0
- package/asyncify/8_3_0/php_8_3.wasm +0 -0
- package/asyncify/8_4_0/php_8_4.wasm +0 -0
- package/asyncify/php_7_2.js +351 -157
- package/asyncify/php_7_3.js +350 -156
- package/asyncify/php_7_4.js +349 -155
- package/asyncify/php_8_0.js +353 -159
- package/asyncify/php_8_1.js +352 -158
- package/asyncify/php_8_2.js +353 -159
- package/asyncify/php_8_3.js +353 -159
- package/asyncify/php_8_4.js +352 -158
- package/fs_ext.node +0 -0
- package/index.cjs +9896 -2782
- package/index.js +9780 -2782
- package/jspi/7_2_34/php_7_2.wasm +0 -0
- package/jspi/7_3_33/php_7_3.wasm +0 -0
- package/jspi/7_4_33/php_7_4.wasm +0 -0
- package/jspi/8_0_30/php_8_0.wasm +0 -0
- package/jspi/8_1_23/php_8_1.wasm +0 -0
- package/jspi/8_2_10/php_8_2.wasm +0 -0
- package/jspi/8_3_0/php_8_3.wasm +0 -0
- package/jspi/8_4_0/php_8_4.wasm +0 -0
- package/jspi/php_7_2.js +1130 -340
- package/jspi/php_7_3.js +1130 -340
- package/jspi/php_7_4.js +1130 -340
- package/jspi/php_8_0.js +1130 -340
- package/jspi/php_8_1.js +1129 -339
- package/jspi/php_8_2.js +1129 -339
- package/jspi/php_8_3.js +1129 -339
- package/jspi/php_8_4.js +1129 -339
- package/lib/file-lock-manager-for-node.d.ts +149 -0
- package/lib/file-lock-manager.d.ts +96 -0
- package/lib/index.d.ts +2 -0
- package/lib/load-runtime.d.ts +31 -2
- package/package.json +10 -9
package/jspi/php_7_4.js
CHANGED
|
@@ -8,7 +8,7 @@ import path from 'path';
|
|
|
8
8
|
|
|
9
9
|
const dependencyFilename = path.join(__dirname, '7_4_33', 'php_7_4.wasm');
|
|
10
10
|
export { dependencyFilename };
|
|
11
|
-
export const dependenciesTotalSize =
|
|
11
|
+
export const dependenciesTotalSize = 18397523;
|
|
12
12
|
export function init(RuntimeName, PHPLoader) {
|
|
13
13
|
// The rest of the code comes from the built php.js file and esm-suffix.js
|
|
14
14
|
// include: shell.js
|
|
@@ -5209,7 +5209,360 @@ export function init(RuntimeName, PHPLoader) {
|
|
|
5209
5209
|
|
|
5210
5210
|
var syscallGetVarargP = syscallGetVarargI;
|
|
5211
5211
|
|
|
5212
|
-
|
|
5212
|
+
var stringToUTF8 = (str, outPtr, maxBytesToWrite) =>
|
|
5213
|
+
stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite);
|
|
5214
|
+
|
|
5215
|
+
Module['stringToUTF8'] = stringToUTF8;
|
|
5216
|
+
|
|
5217
|
+
var stackAlloc = (sz) => __emscripten_stack_alloc(sz);
|
|
5218
|
+
|
|
5219
|
+
/** @suppress {duplicate } */ var stringToUTF8OnStack = (str) => {
|
|
5220
|
+
var size = lengthBytesUTF8(str) + 1;
|
|
5221
|
+
var ret = stackAlloc(size);
|
|
5222
|
+
stringToUTF8(str, ret, size);
|
|
5223
|
+
return ret;
|
|
5224
|
+
};
|
|
5225
|
+
|
|
5226
|
+
var allocateUTF8OnStack = stringToUTF8OnStack;
|
|
5227
|
+
|
|
5228
|
+
var PHPWASM = {
|
|
5229
|
+
init: function () {
|
|
5230
|
+
// The /internal directory is required by the C module. It's where the
|
|
5231
|
+
// stdout, stderr, and headers information are written for the JavaScript
|
|
5232
|
+
// code to read later on.
|
|
5233
|
+
FS.mkdir('/internal');
|
|
5234
|
+
// The files from the shared directory are shared between all the
|
|
5235
|
+
// PHP processes managed by PHPProcessManager.
|
|
5236
|
+
FS.mkdir('/internal/shared');
|
|
5237
|
+
// The files from the preload directory are preloaded using the
|
|
5238
|
+
// auto_prepend_file php.ini directive.
|
|
5239
|
+
FS.mkdir('/internal/shared/preload');
|
|
5240
|
+
// Create stdout and stderr devices. We can't just use Emscripten's
|
|
5241
|
+
// default stdout and stderr devices because they stop processing data
|
|
5242
|
+
// on the first null byte. However, when dealing with binary data,
|
|
5243
|
+
// null bytes are valid and common.
|
|
5244
|
+
FS.registerDevice(FS.makedev(64, 0), {
|
|
5245
|
+
open: () => {},
|
|
5246
|
+
close: () => {},
|
|
5247
|
+
read: () => 0,
|
|
5248
|
+
write: (stream, buffer, offset, length, pos) => {
|
|
5249
|
+
const chunk = buffer.subarray(offset, offset + length);
|
|
5250
|
+
PHPWASM.onStdout(chunk);
|
|
5251
|
+
return length;
|
|
5252
|
+
},
|
|
5253
|
+
});
|
|
5254
|
+
FS.mkdev('/internal/stdout', FS.makedev(64, 0));
|
|
5255
|
+
FS.registerDevice(FS.makedev(63, 0), {
|
|
5256
|
+
open: () => {},
|
|
5257
|
+
close: () => {},
|
|
5258
|
+
read: () => 0,
|
|
5259
|
+
write: (stream, buffer, offset, length, pos) => {
|
|
5260
|
+
const chunk = buffer.subarray(offset, offset + length);
|
|
5261
|
+
PHPWASM.onStderr(chunk);
|
|
5262
|
+
return length;
|
|
5263
|
+
},
|
|
5264
|
+
});
|
|
5265
|
+
FS.mkdev('/internal/stderr', FS.makedev(63, 0));
|
|
5266
|
+
FS.registerDevice(FS.makedev(62, 0), {
|
|
5267
|
+
open: () => {},
|
|
5268
|
+
close: () => {},
|
|
5269
|
+
read: () => 0,
|
|
5270
|
+
write: (stream, buffer, offset, length, pos) => {
|
|
5271
|
+
const chunk = buffer.subarray(offset, offset + length);
|
|
5272
|
+
PHPWASM.onHeaders(chunk);
|
|
5273
|
+
return length;
|
|
5274
|
+
},
|
|
5275
|
+
});
|
|
5276
|
+
FS.mkdev('/internal/headers', FS.makedev(62, 0));
|
|
5277
|
+
// Handle events.
|
|
5278
|
+
PHPWASM.EventEmitter = ENVIRONMENT_IS_NODE
|
|
5279
|
+
? require('events').EventEmitter
|
|
5280
|
+
: class EventEmitter {
|
|
5281
|
+
constructor() {
|
|
5282
|
+
this.listeners = {};
|
|
5283
|
+
}
|
|
5284
|
+
emit(eventName, data) {
|
|
5285
|
+
if (this.listeners[eventName]) {
|
|
5286
|
+
this.listeners[eventName].forEach(
|
|
5287
|
+
(callback) => {
|
|
5288
|
+
callback(data);
|
|
5289
|
+
}
|
|
5290
|
+
);
|
|
5291
|
+
}
|
|
5292
|
+
}
|
|
5293
|
+
once(eventName, callback) {
|
|
5294
|
+
const self = this;
|
|
5295
|
+
function removedCallback() {
|
|
5296
|
+
callback(...arguments);
|
|
5297
|
+
self.removeListener(eventName, removedCallback);
|
|
5298
|
+
}
|
|
5299
|
+
this.on(eventName, removedCallback);
|
|
5300
|
+
}
|
|
5301
|
+
removeAllListeners(eventName) {
|
|
5302
|
+
if (eventName) {
|
|
5303
|
+
delete this.listeners[eventName];
|
|
5304
|
+
} else {
|
|
5305
|
+
this.listeners = {};
|
|
5306
|
+
}
|
|
5307
|
+
}
|
|
5308
|
+
removeListener(eventName, callback) {
|
|
5309
|
+
if (this.listeners[eventName]) {
|
|
5310
|
+
const idx =
|
|
5311
|
+
this.listeners[eventName].indexOf(callback);
|
|
5312
|
+
if (idx !== -1) {
|
|
5313
|
+
this.listeners[eventName].splice(idx, 1);
|
|
5314
|
+
}
|
|
5315
|
+
}
|
|
5316
|
+
}
|
|
5317
|
+
};
|
|
5318
|
+
// Clean up the fd -> childProcess mapping when the fd is closed:
|
|
5319
|
+
const originalClose = FS.close;
|
|
5320
|
+
FS.close = function (stream) {
|
|
5321
|
+
originalClose(stream);
|
|
5322
|
+
delete PHPWASM.child_proc_by_fd[stream.fd];
|
|
5323
|
+
};
|
|
5324
|
+
PHPWASM.child_proc_by_fd = {};
|
|
5325
|
+
PHPWASM.child_proc_by_pid = {};
|
|
5326
|
+
PHPWASM.input_devices = {};
|
|
5327
|
+
const originalWrite = TTY.stream_ops.write;
|
|
5328
|
+
TTY.stream_ops.write = function (stream, ...rest) {
|
|
5329
|
+
const retval = originalWrite(stream, ...rest);
|
|
5330
|
+
// Implicit flush since PHP's fflush() doesn't seem to trigger the fsync event
|
|
5331
|
+
// @TODO: Fix this at the wasm level
|
|
5332
|
+
stream.tty.ops.fsync(stream.tty);
|
|
5333
|
+
return retval;
|
|
5334
|
+
};
|
|
5335
|
+
const originalPutChar = TTY.stream_ops.put_char;
|
|
5336
|
+
TTY.stream_ops.put_char = function (tty, val) {
|
|
5337
|
+
/**
|
|
5338
|
+
* Buffer newlines that Emscripten normally ignores.
|
|
5339
|
+
*
|
|
5340
|
+
* Emscripten doesn't do it by default because its default
|
|
5341
|
+
* print function is console.log that implicitly adds a newline. We are overwriting
|
|
5342
|
+
* it with an environment-specific function that outputs exaclty what it was given,
|
|
5343
|
+
* e.g. in Node.js it's process.stdout.write(). Therefore, we need to mak sure
|
|
5344
|
+
* all the newlines make it to the output buffer.
|
|
5345
|
+
*/ if (val === 10) tty.output.push(val);
|
|
5346
|
+
return originalPutChar(tty, val);
|
|
5347
|
+
};
|
|
5348
|
+
},
|
|
5349
|
+
onHeaders: function (chunk) {
|
|
5350
|
+
if (Module['onHeaders']) {
|
|
5351
|
+
Module['onHeaders'](chunk);
|
|
5352
|
+
return;
|
|
5353
|
+
}
|
|
5354
|
+
console.log('headers', {
|
|
5355
|
+
chunk,
|
|
5356
|
+
});
|
|
5357
|
+
},
|
|
5358
|
+
onStdout: function (chunk) {
|
|
5359
|
+
if (Module['onStdout']) {
|
|
5360
|
+
Module['onStdout'](chunk);
|
|
5361
|
+
return;
|
|
5362
|
+
}
|
|
5363
|
+
if (ENVIRONMENT_IS_NODE) {
|
|
5364
|
+
process.stdout.write(chunk);
|
|
5365
|
+
} else {
|
|
5366
|
+
console.log('stdout', {
|
|
5367
|
+
chunk,
|
|
5368
|
+
});
|
|
5369
|
+
}
|
|
5370
|
+
},
|
|
5371
|
+
onStderr: function (chunk) {
|
|
5372
|
+
if (Module['onStderr']) {
|
|
5373
|
+
Module['onStderr'](chunk);
|
|
5374
|
+
return;
|
|
5375
|
+
}
|
|
5376
|
+
if (ENVIRONMENT_IS_NODE) {
|
|
5377
|
+
process.stderr.write(chunk);
|
|
5378
|
+
} else {
|
|
5379
|
+
console.warn('stderr', {
|
|
5380
|
+
chunk,
|
|
5381
|
+
});
|
|
5382
|
+
}
|
|
5383
|
+
},
|
|
5384
|
+
getAllWebSockets: function (sock) {
|
|
5385
|
+
const webSockets = new Set();
|
|
5386
|
+
if (sock.server) {
|
|
5387
|
+
sock.server.clients.forEach((ws) => {
|
|
5388
|
+
webSockets.add(ws);
|
|
5389
|
+
});
|
|
5390
|
+
}
|
|
5391
|
+
for (const peer of PHPWASM.getAllPeers(sock)) {
|
|
5392
|
+
webSockets.add(peer.socket);
|
|
5393
|
+
}
|
|
5394
|
+
return Array.from(webSockets);
|
|
5395
|
+
},
|
|
5396
|
+
getAllPeers: function (sock) {
|
|
5397
|
+
const peers = new Set();
|
|
5398
|
+
if (sock.server) {
|
|
5399
|
+
sock.pending
|
|
5400
|
+
.filter((pending) => pending.peers)
|
|
5401
|
+
.forEach((pending) => {
|
|
5402
|
+
for (const peer of Object.values(pending.peers)) {
|
|
5403
|
+
peers.add(peer);
|
|
5404
|
+
}
|
|
5405
|
+
});
|
|
5406
|
+
}
|
|
5407
|
+
if (sock.peers) {
|
|
5408
|
+
for (const peer of Object.values(sock.peers)) {
|
|
5409
|
+
peers.add(peer);
|
|
5410
|
+
}
|
|
5411
|
+
}
|
|
5412
|
+
return Array.from(peers);
|
|
5413
|
+
},
|
|
5414
|
+
awaitData: function (ws) {
|
|
5415
|
+
return PHPWASM.awaitEvent(ws, 'message');
|
|
5416
|
+
},
|
|
5417
|
+
awaitConnection: function (ws) {
|
|
5418
|
+
if (ws.OPEN === ws.readyState) {
|
|
5419
|
+
return [Promise.resolve(), PHPWASM.noop];
|
|
5420
|
+
}
|
|
5421
|
+
return PHPWASM.awaitEvent(ws, 'open');
|
|
5422
|
+
},
|
|
5423
|
+
awaitClose: function (ws) {
|
|
5424
|
+
if ([ws.CLOSING, ws.CLOSED].includes(ws.readyState)) {
|
|
5425
|
+
return [Promise.resolve(), PHPWASM.noop];
|
|
5426
|
+
}
|
|
5427
|
+
return PHPWASM.awaitEvent(ws, 'close');
|
|
5428
|
+
},
|
|
5429
|
+
awaitError: function (ws) {
|
|
5430
|
+
if ([ws.CLOSING, ws.CLOSED].includes(ws.readyState)) {
|
|
5431
|
+
return [Promise.resolve(), PHPWASM.noop];
|
|
5432
|
+
}
|
|
5433
|
+
return PHPWASM.awaitEvent(ws, 'error');
|
|
5434
|
+
},
|
|
5435
|
+
awaitEvent: function (ws, event) {
|
|
5436
|
+
let resolve;
|
|
5437
|
+
const listener = () => {
|
|
5438
|
+
resolve();
|
|
5439
|
+
};
|
|
5440
|
+
const promise = new Promise(function (_resolve) {
|
|
5441
|
+
resolve = _resolve;
|
|
5442
|
+
ws.once(event, listener);
|
|
5443
|
+
});
|
|
5444
|
+
const cancel = () => {
|
|
5445
|
+
ws.removeListener(event, listener);
|
|
5446
|
+
// Rejecting the promises bubbles up and kills the entire
|
|
5447
|
+
// node process. Let's resolve them on the next tick instead
|
|
5448
|
+
// to give the caller some space to unbind any handlers.
|
|
5449
|
+
setTimeout(resolve);
|
|
5450
|
+
};
|
|
5451
|
+
return [promise, cancel];
|
|
5452
|
+
},
|
|
5453
|
+
noop: function () {},
|
|
5454
|
+
spawnProcess: function (command, args, options) {
|
|
5455
|
+
if (Module['spawnProcess']) {
|
|
5456
|
+
const spawnedPromise = Module['spawnProcess'](
|
|
5457
|
+
command,
|
|
5458
|
+
args,
|
|
5459
|
+
options
|
|
5460
|
+
);
|
|
5461
|
+
return Promise.resolve(spawnedPromise).then(function (spawned) {
|
|
5462
|
+
if (!spawned || !spawned.on) {
|
|
5463
|
+
throw new Error(
|
|
5464
|
+
'spawnProcess() must return an EventEmitter but returned a different type.'
|
|
5465
|
+
);
|
|
5466
|
+
}
|
|
5467
|
+
return spawned;
|
|
5468
|
+
});
|
|
5469
|
+
}
|
|
5470
|
+
if (ENVIRONMENT_IS_NODE) {
|
|
5471
|
+
return require('child_process').spawn(command, args, {
|
|
5472
|
+
...options,
|
|
5473
|
+
shell: true,
|
|
5474
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
5475
|
+
timeout: 100,
|
|
5476
|
+
});
|
|
5477
|
+
}
|
|
5478
|
+
const e = new Error(
|
|
5479
|
+
'popen(), proc_open() etc. are unsupported in the browser. Call php.setSpawnHandler() ' +
|
|
5480
|
+
'and provide a callback to handle spawning processes, or disable a popen(), proc_open() ' +
|
|
5481
|
+
'and similar functions via php.ini.'
|
|
5482
|
+
);
|
|
5483
|
+
e.code = 'SPAWN_UNSUPPORTED';
|
|
5484
|
+
throw e;
|
|
5485
|
+
},
|
|
5486
|
+
shutdownSocket: function (socketd, how) {
|
|
5487
|
+
// This implementation only supports websockets at the moment
|
|
5488
|
+
const sock = getSocketFromFD(socketd);
|
|
5489
|
+
const peer = Object.values(sock.peers)[0];
|
|
5490
|
+
if (!peer) {
|
|
5491
|
+
return -1;
|
|
5492
|
+
}
|
|
5493
|
+
try {
|
|
5494
|
+
peer.socket.close();
|
|
5495
|
+
SOCKFS.websocket_sock_ops.removePeer(sock, peer);
|
|
5496
|
+
return 0;
|
|
5497
|
+
} catch (e) {
|
|
5498
|
+
console.log('Socket shutdown error', e);
|
|
5499
|
+
return -1;
|
|
5500
|
+
}
|
|
5501
|
+
},
|
|
5502
|
+
};
|
|
5503
|
+
|
|
5504
|
+
function _js_getpid() {
|
|
5505
|
+
return PHPLoader.processId ?? 42;
|
|
5506
|
+
}
|
|
5507
|
+
|
|
5508
|
+
function _js_wasm_trace(format, ...args) {
|
|
5509
|
+
if (PHPLoader.trace instanceof Function) {
|
|
5510
|
+
PHPLoader.trace(_js_getpid(), format, ...args);
|
|
5511
|
+
}
|
|
5512
|
+
}
|
|
5513
|
+
|
|
5514
|
+
function _fd_close(fd) {
|
|
5515
|
+
_js_wasm_trace('fd_close(%d)', fd);
|
|
5516
|
+
const [vfsPath, pathResolutionErrno] = locking.get_vfs_path_from_fd(fd);
|
|
5517
|
+
if (pathResolutionErrno !== 0) {
|
|
5518
|
+
_js_wasm_trace(
|
|
5519
|
+
'fd_close(%d) get_vfs_path_from_fd error %d',
|
|
5520
|
+
fd,
|
|
5521
|
+
pathResolutionErrno
|
|
5522
|
+
);
|
|
5523
|
+
return -ERRNO_CODES.EBADF;
|
|
5524
|
+
}
|
|
5525
|
+
const result = _builtin_fd_close(fd);
|
|
5526
|
+
if (result === 0 && locking.maybeLockedFds.has(fd)) {
|
|
5527
|
+
const nativeFilePath =
|
|
5528
|
+
locking.get_native_path_from_vfs_path(vfsPath);
|
|
5529
|
+
return PHPLoader.fileLockManager
|
|
5530
|
+
.releaseLocksForProcessFd(
|
|
5531
|
+
PHPLoader.processId,
|
|
5532
|
+
fd,
|
|
5533
|
+
nativeFilePath
|
|
5534
|
+
)
|
|
5535
|
+
.then(() => {
|
|
5536
|
+
_js_wasm_trace('fd_close(%d) release locks success', fd);
|
|
5537
|
+
})
|
|
5538
|
+
.catch((e) => {
|
|
5539
|
+
_js_wasm_trace("fd_close(%d) error '%s'", fd, e);
|
|
5540
|
+
})
|
|
5541
|
+
.then(() => {
|
|
5542
|
+
_js_wasm_trace('fd_close(%d) result %d', fd, result);
|
|
5543
|
+
return result;
|
|
5544
|
+
})
|
|
5545
|
+
.finally(() => {
|
|
5546
|
+
locking.maybeLockedFds.delete(fd);
|
|
5547
|
+
});
|
|
5548
|
+
} else {
|
|
5549
|
+
_js_wasm_trace('fd_close(%d) result %d', fd, result);
|
|
5550
|
+
return result;
|
|
5551
|
+
}
|
|
5552
|
+
}
|
|
5553
|
+
|
|
5554
|
+
function _builtin_fd_close(fd) {
|
|
5555
|
+
try {
|
|
5556
|
+
var stream = SYSCALLS.getStreamFromFD(fd);
|
|
5557
|
+
FS.close(stream);
|
|
5558
|
+
return 0;
|
|
5559
|
+
} catch (e) {
|
|
5560
|
+
if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e;
|
|
5561
|
+
return e.errno;
|
|
5562
|
+
}
|
|
5563
|
+
}
|
|
5564
|
+
|
|
5565
|
+
function _builtin_fcntl64(fd, cmd, varargs) {
|
|
5213
5566
|
SYSCALLS.varargs = varargs;
|
|
5214
5567
|
try {
|
|
5215
5568
|
var stream = SYSCALLS.getStreamFromFD(fd);
|
|
@@ -5264,6 +5617,439 @@ export function init(RuntimeName, PHPLoader) {
|
|
|
5264
5617
|
}
|
|
5265
5618
|
}
|
|
5266
5619
|
|
|
5620
|
+
var locking = {
|
|
5621
|
+
maybeLockedFds: new Set(),
|
|
5622
|
+
F_RDLCK: 0,
|
|
5623
|
+
F_WRLCK: 1,
|
|
5624
|
+
F_UNLCK: 2,
|
|
5625
|
+
lockStateToFcntl: {
|
|
5626
|
+
shared: 0,
|
|
5627
|
+
exclusive: 1,
|
|
5628
|
+
unlocked: 2,
|
|
5629
|
+
},
|
|
5630
|
+
fcntlToLockState: {
|
|
5631
|
+
0: 'shared',
|
|
5632
|
+
1: 'exclusive',
|
|
5633
|
+
2: 'unlocked',
|
|
5634
|
+
},
|
|
5635
|
+
is_shared_fs_node(node) {
|
|
5636
|
+
if (node?.isSharedFS) {
|
|
5637
|
+
return true;
|
|
5638
|
+
}
|
|
5639
|
+
// Handle PROXYFS nodes which wrap other nodes.
|
|
5640
|
+
if (!node?.mount?.opts?.fs?.lookupPath) {
|
|
5641
|
+
return false;
|
|
5642
|
+
}
|
|
5643
|
+
const vfsPath = NODEFS.realPath(node);
|
|
5644
|
+
const underlyingNode = node.mount.opts.fs.lookupPath(vfsPath)?.node;
|
|
5645
|
+
return !!underlyingNode?.isSharedFS;
|
|
5646
|
+
},
|
|
5647
|
+
is_path_to_shared_fs(path) {
|
|
5648
|
+
const { node } = FS.lookupPath(path);
|
|
5649
|
+
return locking.is_shared_fs_node(node);
|
|
5650
|
+
},
|
|
5651
|
+
get_fd_access_mode(fd) {
|
|
5652
|
+
const emscripten_F_GETFL = Number('3');
|
|
5653
|
+
const emscripten_O_ACCMODE = Number('2097155');
|
|
5654
|
+
return (
|
|
5655
|
+
_builtin_fcntl64(fd, emscripten_F_GETFL) & emscripten_O_ACCMODE
|
|
5656
|
+
);
|
|
5657
|
+
},
|
|
5658
|
+
get_vfs_path_from_fd(fd) {
|
|
5659
|
+
try {
|
|
5660
|
+
return [FS.readlink(`/proc/self/fd/${fd}`), 0];
|
|
5661
|
+
} catch (error) {
|
|
5662
|
+
return [null, ERRNO_CODES.EBADF];
|
|
5663
|
+
}
|
|
5664
|
+
},
|
|
5665
|
+
get_native_path_from_vfs_path(vfsPath) {
|
|
5666
|
+
const { node } = FS.lookupPath(vfsPath);
|
|
5667
|
+
return NODEFS.realPath(node);
|
|
5668
|
+
},
|
|
5669
|
+
check_lock_params(fd, l_type) {
|
|
5670
|
+
const emscripten_O_RDONLY = Number('0');
|
|
5671
|
+
const emscripten_O_WRONLY = Number('1');
|
|
5672
|
+
const accessMode = locking.get_fd_access_mode(fd);
|
|
5673
|
+
if (
|
|
5674
|
+
(l_type === locking.F_WRLCK &&
|
|
5675
|
+
accessMode === emscripten_O_RDONLY) ||
|
|
5676
|
+
(l_type === locking.F_RDLCK &&
|
|
5677
|
+
accessMode === emscripten_O_WRONLY)
|
|
5678
|
+
) {
|
|
5679
|
+
return ERRNO_CODES.EBADF;
|
|
5680
|
+
}
|
|
5681
|
+
return 0;
|
|
5682
|
+
},
|
|
5683
|
+
};
|
|
5684
|
+
|
|
5685
|
+
async function ___syscall_fcntl64(fd, cmd, varargs) {
|
|
5686
|
+
// Necessary to use varargs accessor
|
|
5687
|
+
SYSCALLS.varargs = varargs;
|
|
5688
|
+
// These constants are replaced by Emscripten during the build process
|
|
5689
|
+
const emscripten_F_GETLK = Number('12');
|
|
5690
|
+
const emscripten_F_SETLK = Number('13');
|
|
5691
|
+
const emscripten_F_SETLKW = Number('14');
|
|
5692
|
+
const emscripten_SEEK_SET = Number('0');
|
|
5693
|
+
// NOTE: With the exception of l_type, these offsets are not exposed to
|
|
5694
|
+
// JS by Emscripten, so we hardcode them here.
|
|
5695
|
+
const emscripten_flock_l_type_offset = 0;
|
|
5696
|
+
const emscripten_flock_l_whence_offset = 2;
|
|
5697
|
+
const emscripten_flock_l_start_offset = 8;
|
|
5698
|
+
const emscripten_flock_l_len_offset = 16;
|
|
5699
|
+
const emscripten_flock_l_pid_offset = 24;
|
|
5700
|
+
/**
|
|
5701
|
+
* Read the flock struct at the given address.
|
|
5702
|
+
*
|
|
5703
|
+
* @param {bigint} flockStructAddress - the address of the flock struct
|
|
5704
|
+
* @returns the flock struct
|
|
5705
|
+
*/ function read_flock_struct(flockStructAddress) {
|
|
5706
|
+
/*
|
|
5707
|
+
* NOTE: Since we are using HEAP<WORD_SIZE> vars like HEAP16 and HEAP64,
|
|
5708
|
+
* we need to adjust offsets to address the word size of each HEAP.
|
|
5709
|
+
*
|
|
5710
|
+
* For example, an offset of 64 bytes is the following for each HEAP:
|
|
5711
|
+
* - HEAP8: 64 (the 64th byte)
|
|
5712
|
+
* - HEAP16: 32 (the 32nd 16-bit word)
|
|
5713
|
+
* - HEAP32: 16 (the 16th 32-bit word)
|
|
5714
|
+
* - HEAP64: 8 (the 8th 64-bit word)
|
|
5715
|
+
*
|
|
5716
|
+
* We get a word offset by dividing the byte offset by the word size.
|
|
5717
|
+
*/ return {
|
|
5718
|
+
l_type: HEAP16[ // Shift right by 1 to divide by 2^1.
|
|
5719
|
+
(flockStructAddress + emscripten_flock_l_type_offset) >> 1
|
|
5720
|
+
],
|
|
5721
|
+
l_whence:
|
|
5722
|
+
HEAP16[ // Shift right by 1 to divide by 2^1.
|
|
5723
|
+
(flockStructAddress +
|
|
5724
|
+
emscripten_flock_l_whence_offset) >>
|
|
5725
|
+
1
|
|
5726
|
+
],
|
|
5727
|
+
l_start:
|
|
5728
|
+
HEAP64[ // Shift right by 3 to divide by 2^3.
|
|
5729
|
+
(flockStructAddress +
|
|
5730
|
+
emscripten_flock_l_start_offset) >>
|
|
5731
|
+
3
|
|
5732
|
+
],
|
|
5733
|
+
l_len: HEAP64[ // Shift right by 3 to divide by 2^3.
|
|
5734
|
+
(flockStructAddress + emscripten_flock_l_len_offset) >> 3
|
|
5735
|
+
],
|
|
5736
|
+
l_pid: HEAP32[ // Shift right by 2 to divide by 2^2.
|
|
5737
|
+
(flockStructAddress + emscripten_flock_l_pid_offset) >> 2
|
|
5738
|
+
],
|
|
5739
|
+
};
|
|
5740
|
+
}
|
|
5741
|
+
/**
|
|
5742
|
+
* Update the flock struct at the given address with the given fields.
|
|
5743
|
+
*
|
|
5744
|
+
* @param {bigint} flockStructAddress - the address of the flock struct
|
|
5745
|
+
* @param {object} fields - the fields to update
|
|
5746
|
+
*/ function update_flock_struct(flockStructAddress, fields) {
|
|
5747
|
+
/*
|
|
5748
|
+
* NOTE: Since we are using HEAP<WORD_SIZE> vars like HEAP16 and HEAP64,
|
|
5749
|
+
* we need to adjust offsets to address the word size of each HEAP.
|
|
5750
|
+
*
|
|
5751
|
+
* For example, an offset of 64 bytes is the following for each HEAP:
|
|
5752
|
+
* - HEAP8: 64 (the 64th byte)
|
|
5753
|
+
* - HEAP16: 32 (the 32nd 16-bit word)
|
|
5754
|
+
* - HEAP32: 16 (the 16th 32-bit word)
|
|
5755
|
+
* - HEAP64: 8 (the 8th 64-bit word)
|
|
5756
|
+
*
|
|
5757
|
+
* We get a word offset by dividing the byte offset by the word size.
|
|
5758
|
+
*/ if (fields.l_type !== undefined) {
|
|
5759
|
+
HEAP16[ // Shift right by 1 to divide by 2^1.
|
|
5760
|
+
(flockStructAddress + emscripten_flock_l_type_offset) >> 1
|
|
5761
|
+
] = fields.l_type;
|
|
5762
|
+
}
|
|
5763
|
+
if (fields.l_whence !== undefined) {
|
|
5764
|
+
HEAP16[ // Shift right by 1 to divide by 2^1.
|
|
5765
|
+
(flockStructAddress + emscripten_flock_l_whence_offset) >> 1
|
|
5766
|
+
] = fields.l_whence;
|
|
5767
|
+
}
|
|
5768
|
+
if (fields.l_start !== undefined) {
|
|
5769
|
+
HEAP64[ // Shift right by 3 to divide by 2^3.
|
|
5770
|
+
(flockStructAddress + emscripten_flock_l_start_offset) >> 3
|
|
5771
|
+
] = fields.l_start;
|
|
5772
|
+
}
|
|
5773
|
+
if (fields.l_len !== undefined) {
|
|
5774
|
+
HEAP64[ // Shift right by 3 to divide by 2^3.
|
|
5775
|
+
(flockStructAddress + emscripten_flock_l_len_offset) >> 3
|
|
5776
|
+
] = fields.l_len;
|
|
5777
|
+
}
|
|
5778
|
+
if (fields.l_pid !== undefined) {
|
|
5779
|
+
HEAP32[ // Shift right by 2 to divide by 2^2.
|
|
5780
|
+
(flockStructAddress + emscripten_flock_l_pid_offset) >> 2
|
|
5781
|
+
] = fields.l_pid;
|
|
5782
|
+
}
|
|
5783
|
+
}
|
|
5784
|
+
/**
|
|
5785
|
+
* Resolve the base address of the range depending on the whence and start offset.
|
|
5786
|
+
*
|
|
5787
|
+
* @param {number} fd - the file descriptor
|
|
5788
|
+
* @param {number} whence - what the start offset is relative to
|
|
5789
|
+
* @param {bigint} startOffset - the offset from the whence
|
|
5790
|
+
* @returns The resolved offset and the errno. If there is an error,
|
|
5791
|
+
* the resolved offset is null, and the errno is non-zero.
|
|
5792
|
+
*/ function get_base_address(fd, whence, startOffset) {
|
|
5793
|
+
let baseAddress;
|
|
5794
|
+
switch (whence) {
|
|
5795
|
+
case emscripten_SEEK_SET:
|
|
5796
|
+
baseAddress = 0n;
|
|
5797
|
+
break;
|
|
5798
|
+
|
|
5799
|
+
case emscripten_SEEK_CUR:
|
|
5800
|
+
baseAddress = FS.lseek(fd, 0, whence);
|
|
5801
|
+
break;
|
|
5802
|
+
|
|
5803
|
+
case emscripten_SEEK_END:
|
|
5804
|
+
baseAddress = _wasm_get_end_offset(fd);
|
|
5805
|
+
break;
|
|
5806
|
+
|
|
5807
|
+
default:
|
|
5808
|
+
return [null, ERRNO_CODES.EINVAL];
|
|
5809
|
+
}
|
|
5810
|
+
if (baseAddress == -1) {
|
|
5811
|
+
// We cannot resolve the offset within the file.
|
|
5812
|
+
// Let's treat this as a problem with the file descriptor.
|
|
5813
|
+
return [null, ERRNO_CODES.EBADF];
|
|
5814
|
+
}
|
|
5815
|
+
const resolvedOffset = baseAddress + startOffset;
|
|
5816
|
+
if (resolvedOffset < 0) {
|
|
5817
|
+
// This is not a valid offset. Report args as invalid.
|
|
5818
|
+
return [null, ERRNO_CODES.EINVAL];
|
|
5819
|
+
}
|
|
5820
|
+
return [resolvedOffset, 0];
|
|
5821
|
+
}
|
|
5822
|
+
const pid = PHPLoader.processId;
|
|
5823
|
+
switch (cmd) {
|
|
5824
|
+
case emscripten_F_GETLK: {
|
|
5825
|
+
_js_wasm_trace('fcntl(%d, F_GETLK)', fd);
|
|
5826
|
+
let vfsPath;
|
|
5827
|
+
let errno;
|
|
5828
|
+
[vfsPath, errno] = locking.get_vfs_path_from_fd(fd);
|
|
5829
|
+
if (errno !== 0) {
|
|
5830
|
+
_js_wasm_trace(
|
|
5831
|
+
'fcntl(%d, F_GETLK) %s get_vfs_path_from_fd errno %d',
|
|
5832
|
+
fd,
|
|
5833
|
+
vfsPath,
|
|
5834
|
+
errno
|
|
5835
|
+
);
|
|
5836
|
+
return -ERRNO_CODES.EBADF;
|
|
5837
|
+
}
|
|
5838
|
+
if (!locking.is_path_to_shared_fs(vfsPath)) {
|
|
5839
|
+
_js_wasm_trace(
|
|
5840
|
+
"fcntl(%d, F_GETLK) locking is not implemented for non-NodeFS path '%s'",
|
|
5841
|
+
fd,
|
|
5842
|
+
vfsPath
|
|
5843
|
+
);
|
|
5844
|
+
// If not a NodeFS path, we can't lock it.
|
|
5845
|
+
// Default to succeeding as Emscripten does.
|
|
5846
|
+
update_flock_struct(flockStructAddr, {
|
|
5847
|
+
l_type: F_UNLCK,
|
|
5848
|
+
});
|
|
5849
|
+
return 0;
|
|
5850
|
+
}
|
|
5851
|
+
const flockStructAddr = syscallGetVarargP();
|
|
5852
|
+
const flockStruct = read_flock_struct(flockStructAddr);
|
|
5853
|
+
if (!(flockStruct.l_type in locking.fcntlToLockState)) {
|
|
5854
|
+
return -ERRNO_CODES.EINVAL;
|
|
5855
|
+
}
|
|
5856
|
+
errno = locking.check_lock_params(fd, flockStruct.l_type);
|
|
5857
|
+
if (errno !== 0) {
|
|
5858
|
+
_js_wasm_trace(
|
|
5859
|
+
'fcntl(%d, F_GETLK) %s check_lock_params errno %d',
|
|
5860
|
+
fd,
|
|
5861
|
+
vfsPath,
|
|
5862
|
+
errno
|
|
5863
|
+
);
|
|
5864
|
+
return -ERRNO_CODES.EINVAL;
|
|
5865
|
+
}
|
|
5866
|
+
const requestedLockType =
|
|
5867
|
+
locking.fcntlToLockState[flockStruct.l_type];
|
|
5868
|
+
let absoluteStartOffset;
|
|
5869
|
+
[absoluteStartOffset, errno] = get_base_address(
|
|
5870
|
+
fd,
|
|
5871
|
+
flockStruct.l_whence,
|
|
5872
|
+
flockStruct.l_start
|
|
5873
|
+
);
|
|
5874
|
+
if (errno !== 0) {
|
|
5875
|
+
_js_wasm_trace(
|
|
5876
|
+
'fcntl(%d, F_GETLK) %s get_base_address errno %d',
|
|
5877
|
+
fd,
|
|
5878
|
+
vfsPath,
|
|
5879
|
+
errno
|
|
5880
|
+
);
|
|
5881
|
+
return -ERRNO_CODES.EINVAL;
|
|
5882
|
+
}
|
|
5883
|
+
const nativeFilePath =
|
|
5884
|
+
locking.get_native_path_from_vfs_path(vfsPath);
|
|
5885
|
+
return PHPLoader.fileLockManager
|
|
5886
|
+
.findFirstConflictingByteRangeLock(nativeFilePath, {
|
|
5887
|
+
type: requestedLockType,
|
|
5888
|
+
start: absoluteStartOffset,
|
|
5889
|
+
end: absoluteStartOffset + flockStruct.l_len,
|
|
5890
|
+
pid,
|
|
5891
|
+
})
|
|
5892
|
+
.then((conflictingLock) => {
|
|
5893
|
+
if (conflictingLock === undefined) {
|
|
5894
|
+
_js_wasm_trace(
|
|
5895
|
+
'fcntl(%d, F_GETLK) %s findFirstConflictingByteRangeLock type=unlocked start=0x%x end=0x%x',
|
|
5896
|
+
fd,
|
|
5897
|
+
vfsPath,
|
|
5898
|
+
absoluteStartOffset,
|
|
5899
|
+
absoluteStartOffset + flockStruct.l_len
|
|
5900
|
+
);
|
|
5901
|
+
update_flock_struct(flockStructAddr, {
|
|
5902
|
+
l_type: F_UNLCK,
|
|
5903
|
+
});
|
|
5904
|
+
return 0;
|
|
5905
|
+
}
|
|
5906
|
+
_js_wasm_trace(
|
|
5907
|
+
'fcntl(%d, F_GETLK) %s findFirstConflictingByteRangeLock type=%s start=0x%x end=0x%x conflictingLock %d',
|
|
5908
|
+
fd,
|
|
5909
|
+
vfsPath,
|
|
5910
|
+
conflictingLock.type,
|
|
5911
|
+
conflictingLock.start,
|
|
5912
|
+
conflictingLock.end,
|
|
5913
|
+
conflictingLock.pid
|
|
5914
|
+
);
|
|
5915
|
+
const fcntlLockState =
|
|
5916
|
+
locking.lockStateToFcntl[conflictingLock.type];
|
|
5917
|
+
update_flock_struct(flockStructAddr, {
|
|
5918
|
+
l_type: fcntlLockState,
|
|
5919
|
+
l_whence: emscripten_SEEK_SET,
|
|
5920
|
+
l_start: conflictingLock.start,
|
|
5921
|
+
l_len: conflictingLock.end - conflictingLock.start,
|
|
5922
|
+
l_pid: conflictingLock.pid,
|
|
5923
|
+
});
|
|
5924
|
+
return 0;
|
|
5925
|
+
})
|
|
5926
|
+
.catch((e) => {
|
|
5927
|
+
_js_wasm_trace(
|
|
5928
|
+
'fcntl(%d, F_GETLK) %s findFirstConflictingByteRangeLock error %s',
|
|
5929
|
+
fd,
|
|
5930
|
+
vfsPath,
|
|
5931
|
+
e
|
|
5932
|
+
);
|
|
5933
|
+
return -ERRNO_CODES.EINVAL;
|
|
5934
|
+
});
|
|
5935
|
+
}
|
|
5936
|
+
|
|
5937
|
+
case emscripten_F_SETLK: {
|
|
5938
|
+
_js_wasm_trace('fcntl(%d, F_SETLK)', fd);
|
|
5939
|
+
let vfsPath;
|
|
5940
|
+
let errno;
|
|
5941
|
+
[vfsPath, errno] = locking.get_vfs_path_from_fd(fd);
|
|
5942
|
+
if (errno !== 0) {
|
|
5943
|
+
_js_wasm_trace(
|
|
5944
|
+
'fcntl(%d, F_SETLK) %s get_vfs_path_from_fd errno %d',
|
|
5945
|
+
fd,
|
|
5946
|
+
vfsPath,
|
|
5947
|
+
errno
|
|
5948
|
+
);
|
|
5949
|
+
return -errno;
|
|
5950
|
+
}
|
|
5951
|
+
if (!locking.is_path_to_shared_fs(vfsPath)) {
|
|
5952
|
+
_js_wasm_trace(
|
|
5953
|
+
'fcntl(%d, F_SETLK) locking is not implemented for non-NodeFS path %s',
|
|
5954
|
+
fd,
|
|
5955
|
+
vfsPath
|
|
5956
|
+
);
|
|
5957
|
+
// If not a NodeFS path, we can't lock it.
|
|
5958
|
+
// Default to succeeding as Emscripten does.
|
|
5959
|
+
return 0;
|
|
5960
|
+
}
|
|
5961
|
+
var flockStructAddr = syscallGetVarargP();
|
|
5962
|
+
const flockStruct = read_flock_struct(flockStructAddr);
|
|
5963
|
+
let absoluteStartOffset;
|
|
5964
|
+
[absoluteStartOffset, errno] = get_base_address(
|
|
5965
|
+
fd,
|
|
5966
|
+
flockStruct.l_whence,
|
|
5967
|
+
flockStruct.l_start
|
|
5968
|
+
);
|
|
5969
|
+
if (errno !== 0) {
|
|
5970
|
+
_js_wasm_trace(
|
|
5971
|
+
'fcntl(%d, F_SETLK) %s get_base_address errno %d',
|
|
5972
|
+
fd,
|
|
5973
|
+
vfsPath,
|
|
5974
|
+
errno
|
|
5975
|
+
);
|
|
5976
|
+
return -errno;
|
|
5977
|
+
}
|
|
5978
|
+
if (!(flockStruct.l_type in locking.fcntlToLockState)) {
|
|
5979
|
+
_js_wasm_trace(
|
|
5980
|
+
'fcntl(%d, F_SETLK) %s invalid lock type %d',
|
|
5981
|
+
fd,
|
|
5982
|
+
vfsPath,
|
|
5983
|
+
flockStruct.l_type
|
|
5984
|
+
);
|
|
5985
|
+
return -ERRNO_CODES.EINVAL;
|
|
5986
|
+
}
|
|
5987
|
+
errno = locking.check_lock_params(fd, flockStruct.l_type);
|
|
5988
|
+
if (errno !== 0) {
|
|
5989
|
+
_js_wasm_trace(
|
|
5990
|
+
'fcntl(%d, F_SETLK) %s check_lock_params errno %d',
|
|
5991
|
+
fd,
|
|
5992
|
+
vfsPath,
|
|
5993
|
+
errno
|
|
5994
|
+
);
|
|
5995
|
+
return -errno;
|
|
5996
|
+
}
|
|
5997
|
+
locking.maybeLockedFds.add(fd);
|
|
5998
|
+
const requestedLockType =
|
|
5999
|
+
locking.fcntlToLockState[flockStruct.l_type];
|
|
6000
|
+
const rangeLock = {
|
|
6001
|
+
type: requestedLockType,
|
|
6002
|
+
start: absoluteStartOffset,
|
|
6003
|
+
end: absoluteStartOffset + flockStruct.l_len,
|
|
6004
|
+
pid,
|
|
6005
|
+
};
|
|
6006
|
+
const nativeFilePath =
|
|
6007
|
+
locking.get_native_path_from_vfs_path(vfsPath);
|
|
6008
|
+
_js_wasm_trace(
|
|
6009
|
+
'fcntl(%d, F_SETLK) %s calling lockFileByteRange for range lock %s',
|
|
6010
|
+
fd,
|
|
6011
|
+
vfsPath,
|
|
6012
|
+
rangeLock
|
|
6013
|
+
);
|
|
6014
|
+
return PHPLoader.fileLockManager
|
|
6015
|
+
.lockFileByteRange(nativeFilePath, rangeLock)
|
|
6016
|
+
.then((succeeded) => {
|
|
6017
|
+
_js_wasm_trace(
|
|
6018
|
+
'fcntl(%d, F_SETLK) %s lockFileByteRange returned %d for range lock %s',
|
|
6019
|
+
fd,
|
|
6020
|
+
vfsPath,
|
|
6021
|
+
succeeded,
|
|
6022
|
+
rangeLock
|
|
6023
|
+
);
|
|
6024
|
+
return succeeded ? 0 : -ERRNO_CODES.EAGAIN;
|
|
6025
|
+
})
|
|
6026
|
+
.catch((e) => {
|
|
6027
|
+
_js_wasm_trace(
|
|
6028
|
+
'fcntl(%d, F_SETLK) %s lockFileByteRange error %s for range lock %s',
|
|
6029
|
+
fd,
|
|
6030
|
+
vfsPath,
|
|
6031
|
+
e,
|
|
6032
|
+
rangeLock
|
|
6033
|
+
);
|
|
6034
|
+
return -ERRNO_CODES.EINVAL;
|
|
6035
|
+
});
|
|
6036
|
+
}
|
|
6037
|
+
|
|
6038
|
+
// @TODO: Implement waiting for lock
|
|
6039
|
+
case emscripten_F_SETLKW: {
|
|
6040
|
+
// We do not yet support the blocking form of flock().
|
|
6041
|
+
// We respond with EDEADLK to indicate failure
|
|
6042
|
+
// because it is a known errno for a failed F_SETLKW command.
|
|
6043
|
+
return -ERRNO_CODES.EDEADLK;
|
|
6044
|
+
}
|
|
6045
|
+
|
|
6046
|
+
default:
|
|
6047
|
+
return _builtin_fcntl64(fd, cmd, varargs);
|
|
6048
|
+
}
|
|
6049
|
+
}
|
|
6050
|
+
|
|
6051
|
+
___syscall_fcntl64.isAsync = true;
|
|
6052
|
+
|
|
5267
6053
|
function ___syscall_fdatasync(fd) {
|
|
5268
6054
|
try {
|
|
5269
6055
|
var stream = SYSCALLS.getStreamFromFD(fd);
|
|
@@ -5295,11 +6081,6 @@ export function init(RuntimeName, PHPLoader) {
|
|
|
5295
6081
|
}
|
|
5296
6082
|
}
|
|
5297
6083
|
|
|
5298
|
-
var stringToUTF8 = (str, outPtr, maxBytesToWrite) =>
|
|
5299
|
-
stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite);
|
|
5300
|
-
|
|
5301
|
-
Module['stringToUTF8'] = stringToUTF8;
|
|
5302
|
-
|
|
5303
6084
|
function ___syscall_getcwd(buf, size) {
|
|
5304
6085
|
try {
|
|
5305
6086
|
if (size === 0) return -28;
|
|
@@ -6559,17 +7340,6 @@ export function init(RuntimeName, PHPLoader) {
|
|
|
6559
7340
|
return 0;
|
|
6560
7341
|
};
|
|
6561
7342
|
|
|
6562
|
-
function _fd_close(fd) {
|
|
6563
|
-
try {
|
|
6564
|
-
var stream = SYSCALLS.getStreamFromFD(fd);
|
|
6565
|
-
FS.close(stream);
|
|
6566
|
-
return 0;
|
|
6567
|
-
} catch (e) {
|
|
6568
|
-
if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e;
|
|
6569
|
-
return e.errno;
|
|
6570
|
-
}
|
|
6571
|
-
}
|
|
6572
|
-
|
|
6573
7343
|
function _fd_fdstat_get(fd, pbuf) {
|
|
6574
7344
|
try {
|
|
6575
7345
|
var rightsBase = 0;
|
|
@@ -6806,312 +7576,125 @@ export function init(RuntimeName, PHPLoader) {
|
|
|
6806
7576
|
return -2;
|
|
6807
7577
|
}
|
|
6808
7578
|
}
|
|
6809
|
-
}
|
|
6810
|
-
if (addr != null) {
|
|
6811
|
-
ai = allocaddrinfo(family, type, proto, node, addr, port);
|
|
6812
|
-
HEAPU32[out >> 2] = ai;
|
|
6813
|
-
return 0;
|
|
6814
|
-
}
|
|
6815
|
-
if (flags & 4) {
|
|
6816
|
-
return -2;
|
|
6817
|
-
}
|
|
6818
|
-
// try as a hostname
|
|
6819
|
-
// resolve the hostname to a temporary fake address
|
|
6820
|
-
node = DNS.lookup_name(node);
|
|
6821
|
-
addr = inetPton4(node);
|
|
6822
|
-
if (family === 0) {
|
|
6823
|
-
family = 2;
|
|
6824
|
-
} else if (family === 10) {
|
|
6825
|
-
addr = [0, 0, _htonl(65535), addr];
|
|
6826
|
-
}
|
|
6827
|
-
ai = allocaddrinfo(family, type, proto, null, addr, port);
|
|
6828
|
-
HEAPU32[out >> 2] = ai;
|
|
6829
|
-
return 0;
|
|
6830
|
-
};
|
|
6831
|
-
|
|
6832
|
-
var _getnameinfo = (sa, salen, node, nodelen, serv, servlen, flags) => {
|
|
6833
|
-
var info = readSockaddr(sa, salen);
|
|
6834
|
-
if (info.errno) {
|
|
6835
|
-
return -6;
|
|
6836
|
-
}
|
|
6837
|
-
var port = info.port;
|
|
6838
|
-
var addr = info.addr;
|
|
6839
|
-
var overflowed = false;
|
|
6840
|
-
if (node && nodelen) {
|
|
6841
|
-
var lookup;
|
|
6842
|
-
if (flags & 1 || !(lookup = DNS.lookup_addr(addr))) {
|
|
6843
|
-
if (flags & 8) {
|
|
6844
|
-
return -2;
|
|
6845
|
-
}
|
|
6846
|
-
} else {
|
|
6847
|
-
addr = lookup;
|
|
6848
|
-
}
|
|
6849
|
-
var numBytesWrittenExclNull = stringToUTF8(addr, node, nodelen);
|
|
6850
|
-
if (numBytesWrittenExclNull + 1 >= nodelen) {
|
|
6851
|
-
overflowed = true;
|
|
6852
|
-
}
|
|
6853
|
-
}
|
|
6854
|
-
if (serv && servlen) {
|
|
6855
|
-
port = '' + port;
|
|
6856
|
-
var numBytesWrittenExclNull = stringToUTF8(port, serv, servlen);
|
|
6857
|
-
if (numBytesWrittenExclNull + 1 >= servlen) {
|
|
6858
|
-
overflowed = true;
|
|
6859
|
-
}
|
|
6860
|
-
}
|
|
6861
|
-
if (overflowed) {
|
|
6862
|
-
// Note: even when we overflow, getnameinfo() is specced to write out the truncated results.
|
|
6863
|
-
return -12;
|
|
6864
|
-
}
|
|
6865
|
-
return 0;
|
|
6866
|
-
};
|
|
6867
|
-
|
|
6868
|
-
var Protocols = {
|
|
6869
|
-
list: [],
|
|
6870
|
-
map: {},
|
|
6871
|
-
};
|
|
6872
|
-
|
|
6873
|
-
var _setprotoent = (stayopen) => {
|
|
6874
|
-
// void setprotoent(int stayopen);
|
|
6875
|
-
// Allocate and populate a protoent structure given a name, protocol number and array of aliases
|
|
6876
|
-
function allocprotoent(name, proto, aliases) {
|
|
6877
|
-
// write name into buffer
|
|
6878
|
-
var nameBuf = _malloc(name.length + 1);
|
|
6879
|
-
stringToAscii(name, nameBuf);
|
|
6880
|
-
// write aliases into buffer
|
|
6881
|
-
var j = 0;
|
|
6882
|
-
var length = aliases.length;
|
|
6883
|
-
var aliasListBuf = _malloc((length + 1) * 4);
|
|
6884
|
-
// Use length + 1 so we have space for the terminating NULL ptr.
|
|
6885
|
-
for (var i = 0; i < length; i++, j += 4) {
|
|
6886
|
-
var alias = aliases[i];
|
|
6887
|
-
var aliasBuf = _malloc(alias.length + 1);
|
|
6888
|
-
stringToAscii(alias, aliasBuf);
|
|
6889
|
-
HEAPU32[(aliasListBuf + j) >> 2] = aliasBuf;
|
|
6890
|
-
}
|
|
6891
|
-
HEAPU32[(aliasListBuf + j) >> 2] = 0;
|
|
6892
|
-
// Terminating NULL pointer.
|
|
6893
|
-
// generate protoent
|
|
6894
|
-
var pe = _malloc(12);
|
|
6895
|
-
HEAPU32[pe >> 2] = nameBuf;
|
|
6896
|
-
HEAPU32[(pe + 4) >> 2] = aliasListBuf;
|
|
6897
|
-
HEAP32[(pe + 8) >> 2] = proto;
|
|
6898
|
-
return pe;
|
|
6899
|
-
}
|
|
6900
|
-
// Populate the protocol 'database'. The entries are limited to tcp and udp, though it is fairly trivial
|
|
6901
|
-
// to add extra entries from /etc/protocols if desired - though not sure if that'd actually be useful.
|
|
6902
|
-
var list = Protocols.list;
|
|
6903
|
-
var map = Protocols.map;
|
|
6904
|
-
if (list.length === 0) {
|
|
6905
|
-
var entry = allocprotoent('tcp', 6, ['TCP']);
|
|
6906
|
-
list.push(entry);
|
|
6907
|
-
map['tcp'] = map['6'] = entry;
|
|
6908
|
-
entry = allocprotoent('udp', 17, ['UDP']);
|
|
6909
|
-
list.push(entry);
|
|
6910
|
-
map['udp'] = map['17'] = entry;
|
|
6911
|
-
}
|
|
6912
|
-
_setprotoent.index = 0;
|
|
6913
|
-
};
|
|
6914
|
-
|
|
6915
|
-
var _getprotobyname = (name) => {
|
|
6916
|
-
// struct protoent *getprotobyname(const char *);
|
|
6917
|
-
name = UTF8ToString(name);
|
|
6918
|
-
_setprotoent(true);
|
|
6919
|
-
var result = Protocols.map[name];
|
|
6920
|
-
return result;
|
|
6921
|
-
};
|
|
6922
|
-
|
|
6923
|
-
var _getprotobynumber = (number) => {
|
|
6924
|
-
// struct protoent *getprotobynumber(int proto);
|
|
6925
|
-
_setprotoent(true);
|
|
6926
|
-
var result = Protocols.map[number];
|
|
6927
|
-
return result;
|
|
6928
|
-
};
|
|
6929
|
-
|
|
6930
|
-
var stackAlloc = (sz) => __emscripten_stack_alloc(sz);
|
|
6931
|
-
|
|
6932
|
-
/** @suppress {duplicate } */ var stringToUTF8OnStack = (str) => {
|
|
6933
|
-
var size = lengthBytesUTF8(str) + 1;
|
|
6934
|
-
var ret = stackAlloc(size);
|
|
6935
|
-
stringToUTF8(str, ret, size);
|
|
6936
|
-
return ret;
|
|
6937
|
-
};
|
|
6938
|
-
|
|
6939
|
-
var allocateUTF8OnStack = stringToUTF8OnStack;
|
|
6940
|
-
|
|
6941
|
-
var PHPWASM = {
|
|
6942
|
-
init: function () {
|
|
6943
|
-
// The /internal directory is required by the C module. It's where the
|
|
6944
|
-
// stdout, stderr, and headers information are written for the JavaScript
|
|
6945
|
-
// code to read later on.
|
|
6946
|
-
FS.mkdir('/internal');
|
|
6947
|
-
// The files from the shared directory are shared between all the
|
|
6948
|
-
// PHP processes managed by PHPProcessManager.
|
|
6949
|
-
FS.mkdir('/internal/shared');
|
|
6950
|
-
// The files from the preload directory are preloaded using the
|
|
6951
|
-
// auto_prepend_file php.ini directive.
|
|
6952
|
-
FS.mkdir('/internal/shared/preload');
|
|
6953
|
-
PHPWASM.EventEmitter = ENVIRONMENT_IS_NODE
|
|
6954
|
-
? require('events').EventEmitter
|
|
6955
|
-
: class EventEmitter {
|
|
6956
|
-
constructor() {
|
|
6957
|
-
this.listeners = {};
|
|
6958
|
-
}
|
|
6959
|
-
emit(eventName, data) {
|
|
6960
|
-
if (this.listeners[eventName]) {
|
|
6961
|
-
this.listeners[eventName].forEach(
|
|
6962
|
-
(callback) => {
|
|
6963
|
-
callback(data);
|
|
6964
|
-
}
|
|
6965
|
-
);
|
|
6966
|
-
}
|
|
6967
|
-
}
|
|
6968
|
-
once(eventName, callback) {
|
|
6969
|
-
const self = this;
|
|
6970
|
-
function removedCallback() {
|
|
6971
|
-
callback(...arguments);
|
|
6972
|
-
self.removeListener(eventName, removedCallback);
|
|
6973
|
-
}
|
|
6974
|
-
this.on(eventName, removedCallback);
|
|
6975
|
-
}
|
|
6976
|
-
removeAllListeners(eventName) {
|
|
6977
|
-
if (eventName) {
|
|
6978
|
-
delete this.listeners[eventName];
|
|
6979
|
-
} else {
|
|
6980
|
-
this.listeners = {};
|
|
6981
|
-
}
|
|
6982
|
-
}
|
|
6983
|
-
removeListener(eventName, callback) {
|
|
6984
|
-
if (this.listeners[eventName]) {
|
|
6985
|
-
const idx =
|
|
6986
|
-
this.listeners[eventName].indexOf(callback);
|
|
6987
|
-
if (idx !== -1) {
|
|
6988
|
-
this.listeners[eventName].splice(idx, 1);
|
|
6989
|
-
}
|
|
6990
|
-
}
|
|
6991
|
-
}
|
|
6992
|
-
};
|
|
6993
|
-
PHPWASM.child_proc_by_fd = {};
|
|
6994
|
-
PHPWASM.child_proc_by_pid = {};
|
|
6995
|
-
PHPWASM.input_devices = {};
|
|
6996
|
-
},
|
|
6997
|
-
getAllWebSockets: function (sock) {
|
|
6998
|
-
const webSockets = new Set();
|
|
6999
|
-
if (sock.server) {
|
|
7000
|
-
sock.server.clients.forEach((ws) => {
|
|
7001
|
-
webSockets.add(ws);
|
|
7002
|
-
});
|
|
7003
|
-
}
|
|
7004
|
-
for (const peer of PHPWASM.getAllPeers(sock)) {
|
|
7005
|
-
webSockets.add(peer.socket);
|
|
7006
|
-
}
|
|
7007
|
-
return Array.from(webSockets);
|
|
7008
|
-
},
|
|
7009
|
-
getAllPeers: function (sock) {
|
|
7010
|
-
const peers = new Set();
|
|
7011
|
-
if (sock.server) {
|
|
7012
|
-
sock.pending
|
|
7013
|
-
.filter((pending) => pending.peers)
|
|
7014
|
-
.forEach((pending) => {
|
|
7015
|
-
for (const peer of Object.values(pending.peers)) {
|
|
7016
|
-
peers.add(peer);
|
|
7017
|
-
}
|
|
7018
|
-
});
|
|
7019
|
-
}
|
|
7020
|
-
if (sock.peers) {
|
|
7021
|
-
for (const peer of Object.values(sock.peers)) {
|
|
7022
|
-
peers.add(peer);
|
|
7023
|
-
}
|
|
7024
|
-
}
|
|
7025
|
-
return Array.from(peers);
|
|
7026
|
-
},
|
|
7027
|
-
awaitData: function (ws) {
|
|
7028
|
-
return PHPWASM.awaitEvent(ws, 'message');
|
|
7029
|
-
},
|
|
7030
|
-
awaitConnection: function (ws) {
|
|
7031
|
-
if (ws.OPEN === ws.readyState) {
|
|
7032
|
-
return [Promise.resolve(), PHPWASM.noop];
|
|
7033
|
-
}
|
|
7034
|
-
return PHPWASM.awaitEvent(ws, 'open');
|
|
7035
|
-
},
|
|
7036
|
-
awaitClose: function (ws) {
|
|
7037
|
-
if ([ws.CLOSING, ws.CLOSED].includes(ws.readyState)) {
|
|
7038
|
-
return [Promise.resolve(), PHPWASM.noop];
|
|
7039
|
-
}
|
|
7040
|
-
return PHPWASM.awaitEvent(ws, 'close');
|
|
7041
|
-
},
|
|
7042
|
-
awaitError: function (ws) {
|
|
7043
|
-
if ([ws.CLOSING, ws.CLOSED].includes(ws.readyState)) {
|
|
7044
|
-
return [Promise.resolve(), PHPWASM.noop];
|
|
7045
|
-
}
|
|
7046
|
-
return PHPWASM.awaitEvent(ws, 'error');
|
|
7047
|
-
},
|
|
7048
|
-
awaitEvent: function (ws, event) {
|
|
7049
|
-
let resolve;
|
|
7050
|
-
const listener = () => {
|
|
7051
|
-
resolve();
|
|
7052
|
-
};
|
|
7053
|
-
const promise = new Promise(function (_resolve) {
|
|
7054
|
-
resolve = _resolve;
|
|
7055
|
-
ws.once(event, listener);
|
|
7056
|
-
});
|
|
7057
|
-
const cancel = () => {
|
|
7058
|
-
ws.removeListener(event, listener);
|
|
7059
|
-
// Rejecting the promises bubbles up and kills the entire
|
|
7060
|
-
// node process. Let's resolve them on the next tick instead
|
|
7061
|
-
// to give the caller some space to unbind any handlers.
|
|
7062
|
-
setTimeout(resolve);
|
|
7063
|
-
};
|
|
7064
|
-
return [promise, cancel];
|
|
7065
|
-
},
|
|
7066
|
-
noop: function () {},
|
|
7067
|
-
spawnProcess: function (command, args, options) {
|
|
7068
|
-
if (Module['spawnProcess']) {
|
|
7069
|
-
const spawnedPromise = Module['spawnProcess'](
|
|
7070
|
-
command,
|
|
7071
|
-
args,
|
|
7072
|
-
options
|
|
7073
|
-
);
|
|
7074
|
-
return Promise.resolve(spawnedPromise).then(function (spawned) {
|
|
7075
|
-
if (!spawned || !spawned.on) {
|
|
7076
|
-
throw new Error(
|
|
7077
|
-
'spawnProcess() must return an EventEmitter but returned a different type.'
|
|
7078
|
-
);
|
|
7079
|
-
}
|
|
7080
|
-
return spawned;
|
|
7081
|
-
});
|
|
7579
|
+
}
|
|
7580
|
+
if (addr != null) {
|
|
7581
|
+
ai = allocaddrinfo(family, type, proto, node, addr, port);
|
|
7582
|
+
HEAPU32[out >> 2] = ai;
|
|
7583
|
+
return 0;
|
|
7584
|
+
}
|
|
7585
|
+
if (flags & 4) {
|
|
7586
|
+
return -2;
|
|
7587
|
+
}
|
|
7588
|
+
// try as a hostname
|
|
7589
|
+
// resolve the hostname to a temporary fake address
|
|
7590
|
+
node = DNS.lookup_name(node);
|
|
7591
|
+
addr = inetPton4(node);
|
|
7592
|
+
if (family === 0) {
|
|
7593
|
+
family = 2;
|
|
7594
|
+
} else if (family === 10) {
|
|
7595
|
+
addr = [0, 0, _htonl(65535), addr];
|
|
7596
|
+
}
|
|
7597
|
+
ai = allocaddrinfo(family, type, proto, null, addr, port);
|
|
7598
|
+
HEAPU32[out >> 2] = ai;
|
|
7599
|
+
return 0;
|
|
7600
|
+
};
|
|
7601
|
+
|
|
7602
|
+
var _getnameinfo = (sa, salen, node, nodelen, serv, servlen, flags) => {
|
|
7603
|
+
var info = readSockaddr(sa, salen);
|
|
7604
|
+
if (info.errno) {
|
|
7605
|
+
return -6;
|
|
7606
|
+
}
|
|
7607
|
+
var port = info.port;
|
|
7608
|
+
var addr = info.addr;
|
|
7609
|
+
var overflowed = false;
|
|
7610
|
+
if (node && nodelen) {
|
|
7611
|
+
var lookup;
|
|
7612
|
+
if (flags & 1 || !(lookup = DNS.lookup_addr(addr))) {
|
|
7613
|
+
if (flags & 8) {
|
|
7614
|
+
return -2;
|
|
7615
|
+
}
|
|
7616
|
+
} else {
|
|
7617
|
+
addr = lookup;
|
|
7082
7618
|
}
|
|
7083
|
-
|
|
7084
|
-
|
|
7085
|
-
|
|
7086
|
-
shell: true,
|
|
7087
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
7088
|
-
timeout: 100,
|
|
7089
|
-
});
|
|
7619
|
+
var numBytesWrittenExclNull = stringToUTF8(addr, node, nodelen);
|
|
7620
|
+
if (numBytesWrittenExclNull + 1 >= nodelen) {
|
|
7621
|
+
overflowed = true;
|
|
7090
7622
|
}
|
|
7091
|
-
|
|
7092
|
-
|
|
7093
|
-
|
|
7094
|
-
|
|
7095
|
-
)
|
|
7096
|
-
|
|
7097
|
-
throw e;
|
|
7098
|
-
},
|
|
7099
|
-
shutdownSocket: function (socketd, how) {
|
|
7100
|
-
// This implementation only supports websockets at the moment
|
|
7101
|
-
const sock = getSocketFromFD(socketd);
|
|
7102
|
-
const peer = Object.values(sock.peers)[0];
|
|
7103
|
-
if (!peer) {
|
|
7104
|
-
return -1;
|
|
7623
|
+
}
|
|
7624
|
+
if (serv && servlen) {
|
|
7625
|
+
port = '' + port;
|
|
7626
|
+
var numBytesWrittenExclNull = stringToUTF8(port, serv, servlen);
|
|
7627
|
+
if (numBytesWrittenExclNull + 1 >= servlen) {
|
|
7628
|
+
overflowed = true;
|
|
7105
7629
|
}
|
|
7106
|
-
|
|
7107
|
-
|
|
7108
|
-
|
|
7109
|
-
|
|
7110
|
-
|
|
7111
|
-
|
|
7112
|
-
|
|
7630
|
+
}
|
|
7631
|
+
if (overflowed) {
|
|
7632
|
+
// Note: even when we overflow, getnameinfo() is specced to write out the truncated results.
|
|
7633
|
+
return -12;
|
|
7634
|
+
}
|
|
7635
|
+
return 0;
|
|
7636
|
+
};
|
|
7637
|
+
|
|
7638
|
+
var Protocols = {
|
|
7639
|
+
list: [],
|
|
7640
|
+
map: {},
|
|
7641
|
+
};
|
|
7642
|
+
|
|
7643
|
+
var _setprotoent = (stayopen) => {
|
|
7644
|
+
// void setprotoent(int stayopen);
|
|
7645
|
+
// Allocate and populate a protoent structure given a name, protocol number and array of aliases
|
|
7646
|
+
function allocprotoent(name, proto, aliases) {
|
|
7647
|
+
// write name into buffer
|
|
7648
|
+
var nameBuf = _malloc(name.length + 1);
|
|
7649
|
+
stringToAscii(name, nameBuf);
|
|
7650
|
+
// write aliases into buffer
|
|
7651
|
+
var j = 0;
|
|
7652
|
+
var length = aliases.length;
|
|
7653
|
+
var aliasListBuf = _malloc((length + 1) * 4);
|
|
7654
|
+
// Use length + 1 so we have space for the terminating NULL ptr.
|
|
7655
|
+
for (var i = 0; i < length; i++, j += 4) {
|
|
7656
|
+
var alias = aliases[i];
|
|
7657
|
+
var aliasBuf = _malloc(alias.length + 1);
|
|
7658
|
+
stringToAscii(alias, aliasBuf);
|
|
7659
|
+
HEAPU32[(aliasListBuf + j) >> 2] = aliasBuf;
|
|
7113
7660
|
}
|
|
7114
|
-
|
|
7661
|
+
HEAPU32[(aliasListBuf + j) >> 2] = 0;
|
|
7662
|
+
// Terminating NULL pointer.
|
|
7663
|
+
// generate protoent
|
|
7664
|
+
var pe = _malloc(12);
|
|
7665
|
+
HEAPU32[pe >> 2] = nameBuf;
|
|
7666
|
+
HEAPU32[(pe + 4) >> 2] = aliasListBuf;
|
|
7667
|
+
HEAP32[(pe + 8) >> 2] = proto;
|
|
7668
|
+
return pe;
|
|
7669
|
+
}
|
|
7670
|
+
// Populate the protocol 'database'. The entries are limited to tcp and udp, though it is fairly trivial
|
|
7671
|
+
// to add extra entries from /etc/protocols if desired - though not sure if that'd actually be useful.
|
|
7672
|
+
var list = Protocols.list;
|
|
7673
|
+
var map = Protocols.map;
|
|
7674
|
+
if (list.length === 0) {
|
|
7675
|
+
var entry = allocprotoent('tcp', 6, ['TCP']);
|
|
7676
|
+
list.push(entry);
|
|
7677
|
+
map['tcp'] = map['6'] = entry;
|
|
7678
|
+
entry = allocprotoent('udp', 17, ['UDP']);
|
|
7679
|
+
list.push(entry);
|
|
7680
|
+
map['udp'] = map['17'] = entry;
|
|
7681
|
+
}
|
|
7682
|
+
_setprotoent.index = 0;
|
|
7683
|
+
};
|
|
7684
|
+
|
|
7685
|
+
var _getprotobyname = (name) => {
|
|
7686
|
+
// struct protoent *getprotobyname(const char *);
|
|
7687
|
+
name = UTF8ToString(name);
|
|
7688
|
+
_setprotoent(true);
|
|
7689
|
+
var result = Protocols.map[name];
|
|
7690
|
+
return result;
|
|
7691
|
+
};
|
|
7692
|
+
|
|
7693
|
+
var _getprotobynumber = (number) => {
|
|
7694
|
+
// struct protoent *getprotobynumber(int proto);
|
|
7695
|
+
_setprotoent(true);
|
|
7696
|
+
var result = Protocols.map[number];
|
|
7697
|
+
return result;
|
|
7115
7698
|
};
|
|
7116
7699
|
|
|
7117
7700
|
function _js_create_input_device(deviceId) {
|
|
@@ -7149,6 +7732,98 @@ export function init(RuntimeName, PHPLoader) {
|
|
|
7149
7732
|
return allocateUTF8OnStack(devicePath);
|
|
7150
7733
|
}
|
|
7151
7734
|
|
|
7735
|
+
async function _js_flock(fd, op) {
|
|
7736
|
+
_js_wasm_trace('js_flock(%d, %d)', fd, op);
|
|
7737
|
+
// Emscripten does not expose these constants to JS, so we hardcode them here.
|
|
7738
|
+
// Based on
|
|
7739
|
+
// https://github.com/emscripten-core/emscripten/blob/76860cc47cef67f5712a7a03a247bc1baabf7ba4/system/lib/libc/musl/include/sys/file.h#L7-L10
|
|
7740
|
+
const emscripten_LOCK_SH = 1;
|
|
7741
|
+
const emscripten_LOCK_EX = 2;
|
|
7742
|
+
const emscripten_LOCK_NB = 4;
|
|
7743
|
+
const emscripten_LOCK_UN = 8;
|
|
7744
|
+
const flockToLockOpType = {
|
|
7745
|
+
[emscripten_LOCK_SH]: 'shared',
|
|
7746
|
+
[emscripten_LOCK_EX]: 'exclusive',
|
|
7747
|
+
[emscripten_LOCK_UN]: 'unlocked',
|
|
7748
|
+
};
|
|
7749
|
+
let vfsPath;
|
|
7750
|
+
let errno;
|
|
7751
|
+
[vfsPath, errno] = locking.get_vfs_path_from_fd(fd);
|
|
7752
|
+
if (errno !== 0) {
|
|
7753
|
+
_js_wasm_trace(
|
|
7754
|
+
'js_flock(%d, %d) get_vfs_path_from_fd errno %d',
|
|
7755
|
+
fd,
|
|
7756
|
+
op,
|
|
7757
|
+
vfsPath,
|
|
7758
|
+
errno
|
|
7759
|
+
);
|
|
7760
|
+
return -errno;
|
|
7761
|
+
}
|
|
7762
|
+
if (!locking.is_path_to_shared_fs(vfsPath)) {
|
|
7763
|
+
_js_wasm_trace(
|
|
7764
|
+
'flock(%d, %d) locking is not implemented for non-NodeFS path %s',
|
|
7765
|
+
fd,
|
|
7766
|
+
op,
|
|
7767
|
+
vfsPath
|
|
7768
|
+
);
|
|
7769
|
+
// If not a NodeFS path, we can't lock it.
|
|
7770
|
+
// Default to succeeding as Emscripten does.
|
|
7771
|
+
return 0;
|
|
7772
|
+
}
|
|
7773
|
+
errno = locking.check_lock_params(fd, op);
|
|
7774
|
+
if (errno !== 0) {
|
|
7775
|
+
_js_wasm_trace(
|
|
7776
|
+
'js_flock(%d, %d) check_lock_params errno %d',
|
|
7777
|
+
fd,
|
|
7778
|
+
op,
|
|
7779
|
+
errno
|
|
7780
|
+
);
|
|
7781
|
+
return -errno;
|
|
7782
|
+
}
|
|
7783
|
+
// @TODO: Consider supporting blocking mode of flock()
|
|
7784
|
+
if (op & (emscripten_LOCK_NB === 0)) {
|
|
7785
|
+
_js_wasm_trace(
|
|
7786
|
+
'js_flock(%d, %d) blocking mode of flock() is not implemented',
|
|
7787
|
+
fd,
|
|
7788
|
+
op
|
|
7789
|
+
);
|
|
7790
|
+
// We do not yet support the blocking form of flock().
|
|
7791
|
+
// We respond with EINVAL to indicate failure
|
|
7792
|
+
// because it is a known errno for a failed blocking flock().
|
|
7793
|
+
return -ERRNO_CODES.EINVAL;
|
|
7794
|
+
}
|
|
7795
|
+
const maskedOp =
|
|
7796
|
+
op & (emscripten_LOCK_SH | emscripten_LOCK_EX | emscripten_LOCK_UN);
|
|
7797
|
+
const lockOpType = flockToLockOpType[maskedOp];
|
|
7798
|
+
if (lockOpType === undefined) {
|
|
7799
|
+
_js_wasm_trace(
|
|
7800
|
+
'js_flock(%d, %d) invalid flock() operation',
|
|
7801
|
+
fd,
|
|
7802
|
+
op
|
|
7803
|
+
);
|
|
7804
|
+
return -ERRNO_CODES.EINVAL;
|
|
7805
|
+
}
|
|
7806
|
+
const nativeFilePath = locking.get_native_path_from_vfs_path(vfsPath);
|
|
7807
|
+
const obtainedLock = await PHPLoader.fileLockManager.lockWholeFile(
|
|
7808
|
+
nativeFilePath,
|
|
7809
|
+
{
|
|
7810
|
+
type: lockOpType,
|
|
7811
|
+
pid: PHPLoader.processId,
|
|
7812
|
+
fd,
|
|
7813
|
+
}
|
|
7814
|
+
);
|
|
7815
|
+
_js_wasm_trace(
|
|
7816
|
+
'js_flock(%d, %d) lockWholeFile %s returned %d',
|
|
7817
|
+
fd,
|
|
7818
|
+
op,
|
|
7819
|
+
vfsPath,
|
|
7820
|
+
obtainedLock
|
|
7821
|
+
);
|
|
7822
|
+
return obtainedLock ? 0 : -ERRNO_CODES.EWOULDBLOCK;
|
|
7823
|
+
}
|
|
7824
|
+
|
|
7825
|
+
_js_flock.isAsync = true;
|
|
7826
|
+
|
|
7152
7827
|
function _js_open_process(
|
|
7153
7828
|
command,
|
|
7154
7829
|
argsPtr,
|
|
@@ -7174,7 +7849,7 @@ export function init(RuntimeName, PHPLoader) {
|
|
|
7174
7849
|
argsArray.push(UTF8ToString(HEAPU32[charPointer >> 2]));
|
|
7175
7850
|
}
|
|
7176
7851
|
}
|
|
7177
|
-
const cwdstr = cwdPtr ? UTF8ToString(cwdPtr) :
|
|
7852
|
+
const cwdstr = cwdPtr ? UTF8ToString(cwdPtr) : FS.cwd();
|
|
7178
7853
|
let envObject = null;
|
|
7179
7854
|
if (envLength) {
|
|
7180
7855
|
envObject = {};
|
|
@@ -7247,6 +7922,15 @@ export function init(RuntimeName, PHPLoader) {
|
|
|
7247
7922
|
PHPWASM.child_proc_by_fd[ProcInfo.stderrParentFd] = ProcInfo;
|
|
7248
7923
|
PHPWASM.child_proc_by_pid[ProcInfo.pid] = ProcInfo;
|
|
7249
7924
|
cp.on('exit', function (code) {
|
|
7925
|
+
for (const fd of [
|
|
7926
|
+
// The child process exited. Let's clean up its output streams:
|
|
7927
|
+
ProcInfo.stdoutChildFd,
|
|
7928
|
+
ProcInfo.stderrChildFd,
|
|
7929
|
+
]) {
|
|
7930
|
+
if (FS.streams[fd] && !FS.isClosed(FS.streams[fd])) {
|
|
7931
|
+
FS.close(FS.streams[fd]);
|
|
7932
|
+
}
|
|
7933
|
+
}
|
|
7250
7934
|
ProcInfo.exitCode = code;
|
|
7251
7935
|
ProcInfo.exited = true;
|
|
7252
7936
|
// Emit events for the wasm_poll_socket function.
|
|
@@ -7297,12 +7981,52 @@ export function init(RuntimeName, PHPLoader) {
|
|
|
7297
7981
|
* listen to the 'exit' event.
|
|
7298
7982
|
*/ try {
|
|
7299
7983
|
await new Promise((resolve, reject) => {
|
|
7300
|
-
|
|
7301
|
-
|
|
7984
|
+
/**
|
|
7985
|
+
* There was no `await` between the `spawnProcess` call
|
|
7986
|
+
* and the `await` below so the process haven't had a chance
|
|
7987
|
+
* to run any of the exit-related callbacks yet.
|
|
7988
|
+
*
|
|
7989
|
+
* Good.
|
|
7990
|
+
*
|
|
7991
|
+
* Let's listen to all the lifecycle events and resolve
|
|
7992
|
+
* the promise when the process starts or immediately crashes.
|
|
7993
|
+
*/ let resolved = false;
|
|
7994
|
+
cp.on('spawn', () => {
|
|
7995
|
+
if (resolved) return;
|
|
7996
|
+
resolved = true;
|
|
7997
|
+
resolve();
|
|
7998
|
+
});
|
|
7999
|
+
cp.on('error', (e) => {
|
|
8000
|
+
if (resolved) return;
|
|
8001
|
+
resolved = true;
|
|
8002
|
+
reject(e);
|
|
8003
|
+
});
|
|
8004
|
+
cp.on('exit', function (code) {
|
|
8005
|
+
if (resolved) return;
|
|
8006
|
+
resolved = true;
|
|
8007
|
+
if (code === 0) {
|
|
8008
|
+
resolve();
|
|
8009
|
+
} else {
|
|
8010
|
+
reject(
|
|
8011
|
+
new Error(`Process exited with code ${code}`)
|
|
8012
|
+
);
|
|
8013
|
+
}
|
|
8014
|
+
});
|
|
8015
|
+
/**
|
|
8016
|
+
* If the process haven't even started after 5 seconds, something
|
|
8017
|
+
* is wrong. Perhaps we're missing an event listener, or perhaps
|
|
8018
|
+
* the `spawnProcess` implementation failed to dispatch the relevant
|
|
8019
|
+
* event. Either way, let's crash to avoid blocking the proc_open()
|
|
8020
|
+
* call indefinitely.
|
|
8021
|
+
*/ setTimeout(() => {
|
|
8022
|
+
if (resolved) return;
|
|
8023
|
+
resolved = true;
|
|
8024
|
+
reject(new Error('Process timed out'));
|
|
8025
|
+
}, 5e3);
|
|
7302
8026
|
});
|
|
7303
8027
|
} catch (e) {
|
|
7304
8028
|
console.error(e);
|
|
7305
|
-
wakeUp(
|
|
8029
|
+
wakeUp(ProcInfo.pid);
|
|
7306
8030
|
return;
|
|
7307
8031
|
}
|
|
7308
8032
|
// Now we want to pass data from the STDIN source supplied by PHP
|
|
@@ -7372,6 +8096,21 @@ export function init(RuntimeName, PHPLoader) {
|
|
|
7372
8096
|
return 0;
|
|
7373
8097
|
}
|
|
7374
8098
|
|
|
8099
|
+
var _js_release_file_locks = async function js_release_file_locks() {
|
|
8100
|
+
_js_wasm_trace('js_release_file_locks()');
|
|
8101
|
+
const pid = PHPLoader.processId;
|
|
8102
|
+
return await PHPLoader.fileLockManager
|
|
8103
|
+
.releaseLocksForProcess(pid)
|
|
8104
|
+
.then(() => {
|
|
8105
|
+
_js_wasm_trace('js_release_file_locks succeeded');
|
|
8106
|
+
})
|
|
8107
|
+
.catch((e) => {
|
|
8108
|
+
_js_wasm_trace('js_release_file_locks error %s', e);
|
|
8109
|
+
});
|
|
8110
|
+
};
|
|
8111
|
+
|
|
8112
|
+
_js_release_file_locks.isAsync = true;
|
|
8113
|
+
|
|
7375
8114
|
function _js_waitpid(pid, exitCodePtr) {
|
|
7376
8115
|
if (!PHPWASM.child_proc_by_pid[pid]) {
|
|
7377
8116
|
return -1;
|
|
@@ -8012,14 +8751,7 @@ export function init(RuntimeName, PHPLoader) {
|
|
|
8012
8751
|
const POLLNVAL = 32;
|
|
8013
8752
|
return returnCallback((wakeUp) => {
|
|
8014
8753
|
const polls = [];
|
|
8015
|
-
if (socketd
|
|
8016
|
-
const procInfo = PHPWASM.child_proc_by_fd[socketd];
|
|
8017
|
-
if (procInfo.exited) {
|
|
8018
|
-
wakeUp(0);
|
|
8019
|
-
return;
|
|
8020
|
-
}
|
|
8021
|
-
polls.push(PHPWASM.awaitEvent(procInfo.stdout, 'data'));
|
|
8022
|
-
} else if (FS.isSocket(FS.getStream(socketd)?.node.mode)) {
|
|
8754
|
+
if (FS.isSocket(FS.getStream(socketd)?.node.mode)) {
|
|
8023
8755
|
const sock = getSocketFromFD(socketd);
|
|
8024
8756
|
if (!sock) {
|
|
8025
8757
|
wakeUp(0);
|
|
@@ -8053,7 +8785,12 @@ export function init(RuntimeName, PHPLoader) {
|
|
|
8053
8785
|
polls.push(PHPWASM.awaitConnection(ws));
|
|
8054
8786
|
lookingFor.add('POLLOUT');
|
|
8055
8787
|
}
|
|
8056
|
-
if (
|
|
8788
|
+
if (
|
|
8789
|
+
events & POLLHUP ||
|
|
8790
|
+
events & POLLIN ||
|
|
8791
|
+
events & POLLOUT ||
|
|
8792
|
+
events & POLLERR
|
|
8793
|
+
) {
|
|
8057
8794
|
polls.push(PHPWASM.awaitClose(ws));
|
|
8058
8795
|
lookingFor.add('POLLHUP');
|
|
8059
8796
|
}
|
|
@@ -8062,6 +8799,13 @@ export function init(RuntimeName, PHPLoader) {
|
|
|
8062
8799
|
lookingFor.add('POLLERR');
|
|
8063
8800
|
}
|
|
8064
8801
|
}
|
|
8802
|
+
} else if (socketd in PHPWASM.child_proc_by_fd) {
|
|
8803
|
+
const procInfo = PHPWASM.child_proc_by_fd[socketd];
|
|
8804
|
+
if (procInfo.exited) {
|
|
8805
|
+
wakeUp(0);
|
|
8806
|
+
return;
|
|
8807
|
+
}
|
|
8808
|
+
polls.push(PHPWASM.awaitEvent(procInfo.stdout, 'data'));
|
|
8065
8809
|
} else {
|
|
8066
8810
|
setTimeout(function () {
|
|
8067
8811
|
wakeUp(1);
|
|
@@ -8293,9 +9037,13 @@ export function init(RuntimeName, PHPLoader) {
|
|
|
8293
9037
|
/** @export */ getprotobyname: _getprotobyname,
|
|
8294
9038
|
/** @export */ getprotobynumber: _getprotobynumber,
|
|
8295
9039
|
/** @export */ js_create_input_device: _js_create_input_device,
|
|
9040
|
+
/** @export */ js_flock: _js_flock,
|
|
9041
|
+
/** @export */ js_getpid: _js_getpid,
|
|
8296
9042
|
/** @export */ js_open_process: _js_open_process,
|
|
8297
9043
|
/** @export */ js_process_status: _js_process_status,
|
|
9044
|
+
/** @export */ js_release_file_locks: _js_release_file_locks,
|
|
8298
9045
|
/** @export */ js_waitpid: _js_waitpid,
|
|
9046
|
+
/** @export */ js_wasm_trace: _js_wasm_trace,
|
|
8299
9047
|
/** @export */ proc_exit: _proc_exit,
|
|
8300
9048
|
/** @export */ strptime: _strptime,
|
|
8301
9049
|
/** @export */ wasm_close: _wasm_close,
|
|
@@ -8312,6 +9060,9 @@ export function init(RuntimeName, PHPLoader) {
|
|
|
8312
9060
|
|
|
8313
9061
|
var _malloc = (a0) => (_malloc = wasmExports['malloc'])(a0);
|
|
8314
9062
|
|
|
9063
|
+
var _getpid = (Module['_getpid'] = () =>
|
|
9064
|
+
(_getpid = Module['_getpid'] = wasmExports['getpid'])());
|
|
9065
|
+
|
|
8315
9066
|
var _wasm_read = (Module['_wasm_read'] = (a0, a1, a2) =>
|
|
8316
9067
|
(_wasm_read = Module['_wasm_read'] = wasmExports['wasm_read'])(
|
|
8317
9068
|
a0,
|
|
@@ -8325,6 +9076,9 @@ export function init(RuntimeName, PHPLoader) {
|
|
|
8325
9076
|
|
|
8326
9077
|
var _fflush = (a0) => (_fflush = wasmExports['fflush'])(a0);
|
|
8327
9078
|
|
|
9079
|
+
var _flock = (Module['_flock'] = (a0, a1) =>
|
|
9080
|
+
(_flock = Module['_flock'] = wasmExports['flock'])(a0, a1));
|
|
9081
|
+
|
|
8328
9082
|
var _wasm_popen = (Module['_wasm_popen'] = (a0, a1) =>
|
|
8329
9083
|
(_wasm_popen = Module['_wasm_popen'] = wasmExports['wasm_popen'])(
|
|
8330
9084
|
a0,
|
|
@@ -8348,13 +9102,6 @@ export function init(RuntimeName, PHPLoader) {
|
|
|
8348
9102
|
(___wrap_select = Module['___wrap_select'] =
|
|
8349
9103
|
wasmExports['__wrap_select'])(a0, a1, a2, a3, a4));
|
|
8350
9104
|
|
|
8351
|
-
var _wasm_add_cli_arg = (Module['_wasm_add_cli_arg'] = (a0) =>
|
|
8352
|
-
(_wasm_add_cli_arg = Module['_wasm_add_cli_arg'] =
|
|
8353
|
-
wasmExports['wasm_add_cli_arg'])(a0));
|
|
8354
|
-
|
|
8355
|
-
var _run_cli = (Module['_run_cli'] = () =>
|
|
8356
|
-
(_run_cli = Module['_run_cli'] = wasmExports['run_cli'])());
|
|
8357
|
-
|
|
8358
9105
|
var _wasm_set_sapi_name = (Module['_wasm_set_sapi_name'] = (a0) =>
|
|
8359
9106
|
(_wasm_set_sapi_name = Module['_wasm_set_sapi_name'] =
|
|
8360
9107
|
wasmExports['wasm_set_sapi_name'])(a0));
|
|
@@ -8363,6 +9110,13 @@ export function init(RuntimeName, PHPLoader) {
|
|
|
8363
9110
|
(_wasm_set_phpini_path = Module['_wasm_set_phpini_path'] =
|
|
8364
9111
|
wasmExports['wasm_set_phpini_path'])(a0));
|
|
8365
9112
|
|
|
9113
|
+
var _wasm_add_cli_arg = (Module['_wasm_add_cli_arg'] = (a0) =>
|
|
9114
|
+
(_wasm_add_cli_arg = Module['_wasm_add_cli_arg'] =
|
|
9115
|
+
wasmExports['wasm_add_cli_arg'])(a0));
|
|
9116
|
+
|
|
9117
|
+
var _run_cli = (Module['_run_cli'] = () =>
|
|
9118
|
+
(_run_cli = Module['_run_cli'] = wasmExports['run_cli'])());
|
|
9119
|
+
|
|
8366
9120
|
var _wasm_add_SERVER_entry = (Module['_wasm_add_SERVER_entry'] = (a0, a1) =>
|
|
8367
9121
|
(_wasm_add_SERVER_entry = Module['_wasm_add_SERVER_entry'] =
|
|
8368
9122
|
wasmExports['wasm_add_SERVER_entry'])(a0, a1));
|
|
@@ -8435,6 +9189,16 @@ export function init(RuntimeName, PHPLoader) {
|
|
|
8435
9189
|
var _wasm_free = (Module['_wasm_free'] = (a0) =>
|
|
8436
9190
|
(_wasm_free = Module['_wasm_free'] = wasmExports['wasm_free'])(a0));
|
|
8437
9191
|
|
|
9192
|
+
var _wasm_get_end_offset = (Module['_wasm_get_end_offset'] = (a0) =>
|
|
9193
|
+
(_wasm_get_end_offset = Module['_wasm_get_end_offset'] =
|
|
9194
|
+
wasmExports['wasm_get_end_offset'])(a0));
|
|
9195
|
+
|
|
9196
|
+
var _wasm_trace = (Module['_wasm_trace'] = (a0, a1) =>
|
|
9197
|
+
(_wasm_trace = Module['_wasm_trace'] = wasmExports['wasm_trace'])(
|
|
9198
|
+
a0,
|
|
9199
|
+
a1
|
|
9200
|
+
));
|
|
9201
|
+
|
|
8438
9202
|
var ___funcs_on_exit = () =>
|
|
8439
9203
|
(___funcs_on_exit = wasmExports['__funcs_on_exit'])();
|
|
8440
9204
|
|
|
@@ -8588,6 +9352,32 @@ export function init(RuntimeName, PHPLoader) {
|
|
|
8588
9352
|
PHPLoader['free'] =
|
|
8589
9353
|
typeof _free === 'function' ? _free : PHPLoader['_wasm_free'];
|
|
8590
9354
|
|
|
9355
|
+
if (typeof NODEFS === 'object') {
|
|
9356
|
+
// We override NODEFS.createNode() to add an `isSharedFS` flag to all NODEFS
|
|
9357
|
+
// nodes. This way we can tell whether file-locking is needed and possible
|
|
9358
|
+
// for an FS node, even if wrapped with PROXYFS.
|
|
9359
|
+
const originalCreateNode = NODEFS.createNode;
|
|
9360
|
+
NODEFS.createNode = function createNodeWithSharedFlag() {
|
|
9361
|
+
const node = originalCreateNode.apply(NODEFS, arguments);
|
|
9362
|
+
node.isSharedFS = true;
|
|
9363
|
+
return node;
|
|
9364
|
+
};
|
|
9365
|
+
|
|
9366
|
+
var originalHashAddNode = FS.hashAddNode;
|
|
9367
|
+
FS.hashAddNode = function hashAddNodeIfNotSharedFS(node) {
|
|
9368
|
+
if (
|
|
9369
|
+
typeof locking === 'object' &&
|
|
9370
|
+
locking?.is_shared_fs_node(node)
|
|
9371
|
+
) {
|
|
9372
|
+
// Avoid caching shared VFS nodes so multiple instances
|
|
9373
|
+
// can access the same underlying filesystem without
|
|
9374
|
+
// conflicting caches.
|
|
9375
|
+
return;
|
|
9376
|
+
}
|
|
9377
|
+
return originalHashAddNode.apply(FS, arguments);
|
|
9378
|
+
};
|
|
9379
|
+
}
|
|
9380
|
+
|
|
8591
9381
|
return PHPLoader;
|
|
8592
9382
|
|
|
8593
9383
|
// Close the opening bracket from esm-prefix.js:
|