/
/
usr
/
local
/
lsws
/
fcgi-bin
EDITOR
/usr/local/lsws/fcgi-bin/lsnode.js
SAVE
CLOSE
/* * Copyright 2002-2018 Lite Speed Technologies Inc, All Rights Reserved. * LITE SPEED PROPRIETARY/CONFIDENTIAL. */ var EventEmitter = require('events').EventEmitter; var os = require('os'); var fs = require('fs'); var http = require('http'); var util = require('util'); var net = require('net'); var socketObject = process.env.LSNODE_BIND_SOCKET ? { path: process.env.LSNODE_SOCKET } : { fd: 0 }; /* * Child process supervision. * * An application may create its own child processes. When this process goes * away those children have to go away as well, otherwise they are re-parented * to init and stay around forever, holding memory, sockets and, on a restart, * possibly the listening socket of the application. * * - every child created through child_process is tracked and signaled when * this process terminates in a way it can observe: normal exit, exit() * call, uncaught exception, SIGTERM/SIGINT/SIGHUP/SIGQUIT. * - children created with fork() additionally load this file through * --require. That copy only installs a watcher which terminates the child * as soon as its parent is gone. It is the only thing that covers a * SIGKILL of the parent, where no handler of ours can run, and it applies * to grandchildren as well because the child tracks its own children too. * * Children started with spawn()/exec() cannot be given that watcher - they are * not necessarily node processes - so for them only the observable paths above * are handled. * * Set LSNODE_KEEP_CHILDREN=1 to disable all of this. */ var PPID_ENV = 'LSNODE_GUARD_PPID'; var PARENT_CHECK_INTERVAL = 1000; var LISTEN_SOCKET_CHECK_INTERVAL = 1000; var FORCE_KILL_DELAY = 3000; var children = []; var terminating = false; if (require.main !== module) { // Loaded with --require into a child process, act as the watchdog only. // Read the parent pid first, supervising resets PPID_ENV to our own pid. var guardPpid = parseInt(process.env[PPID_ENV], 10); superviseChildProcesses(); watchParent(guardPpid); } else { module.isApplicationLoader = true; global.LsNode = new EventEmitter(); superviseChildProcesses(); startApplication(); } function isKeepChildren() { var keep = process.env.LSNODE_KEEP_CHILDREN; return keep !== undefined && keep !== '' && keep !== '0'; } function trackChild(child, detached) { if (!child || typeof child.on !== 'function') { return child; } for (var i = 0; i < children.length; i++) { if (children[i].child === child) { return child; } } var entry = { child: child, detached: detached === true }; children.push(entry); child.on('exit', function() { var idx = children.indexOf(entry); if (idx >= 0) { children.splice(idx, 1); } }); return child; } function readProcessTree() { // { ppid: [ pid, ... ] } of every process on the box, null when /proc is // not available. Used to reach grandchildren, exec() for instance runs // the command under a shell and only that shell is a child of ours. var tree; try { var entries = fs.readdirSync('/proc'); tree = {}; for (var i = 0; i < entries.length; i++) { var name = entries[i]; if (!/^[0-9]+$/.test(name)) { continue; } var ppid; try { var stat = fs.readFileSync('/proc/' + name + '/stat', 'ascii'); // The command name is in parentheses and may itself contain // spaces and parentheses, ppid is the 2nd field after it. ppid = stat.substring(stat.lastIndexOf(')') + 2).split(' ')[1]; } catch (err) { continue; // Process went away while we were looking. } if (!ppid) { continue; } if (tree[ppid]) { tree[ppid].push(parseInt(name, 10)); } else { tree[ppid] = [parseInt(name, 10)]; } } } catch (err) { tree = null; } return tree; } function collectDescendants(pid, tree, found) { var kids = tree[String(pid)]; for (var i = 0; kids && i < kids.length; i++) { if (found.indexOf(kids[i]) < 0) { found.push(kids[i]); collectDescendants(kids[i], tree, found); } } return found; } function getParentPid() { if (typeof process.ppid === 'number') { return process.ppid; } try { var stat = fs.readFileSync('/proc/self/stat', 'ascii'); return parseInt(stat.substring(stat.lastIndexOf(')') + 2) .split(' ')[1], 10); } catch (err) { return 0; } } function killChildren(signal) { // Called from a process 'exit' handler as well, must stay synchronous. var live = []; for (var i = 0; i < children.length; i++) { var child = children[i].child; if (child.pid && child.exitCode === null && child.signalCode === null) { live.push(children[i]); } } if (!live.length) { return; } var tree = readProcessTree(); for (i = 0; i < live.length; i++) { var entry = live[i]; var pids = tree ? collectDescendants(entry.child.pid, tree, []) : []; try { if (entry.detached) { // Own process group leader, take the whole group with it. process.kill(-entry.child.pid, signal); } else { entry.child.kill(signal); } } catch (err) { // Already gone or not ours anymore, nothing to do. } for (var j = 0; j < pids.length; j++) { try { process.kill(pids[j], signal); } catch (err) { } } } } function findOptions(args, from) { for (var i = from; i < args.length; i++) { if (args[i] && typeof args[i] === 'object' && !Array.isArray(args[i])) { return i; } } return -1; } function isDetached(args, from) { var idx = findOptions(args, from); return idx >= 0 && args[idx].detached === true; } function canPreload(execPath) { // The watchdog is preloaded with --require, which node understands and bun // takes as an alias of its own --preload. A child that runs the binary we // are running always gets it, no guessing involved. fork() may name some // other runtime through execPath; that one is judged by name, after // resolving symlinks, so a versioned install reached through something // like /opt/runtime/current is still recognized. Anything left is started // untouched: losing the watchdog only costs a child its parent-death // check, while a flag an unknown runtime rejects costs it startup. if (!execPath || execPath === process.execPath) { return true; } var resolved = execPath; try { resolved = fs.realpathSync(execPath); } catch (err) { } return /^(node|bun)/.test(resolved.substring(resolved.lastIndexOf('/') + 1)); } function superviseChildProcesses() { if (isKeepChildren()) { return; } var cp = require('child_process'); var origSpawn = cp.spawn; var origFork = cp.fork; var origExec = cp.exec; var origExecFile = cp.execFile; // Inherited by every child unless it is given an environment of its own. process.env[PPID_ENV] = String(process.pid); cp.spawn = function(command) { return trackChild(origSpawn.apply(this, arguments), isDetached(arguments, 1)); }; cp.execFile = function(file) { return trackChild(origExecFile.apply(this, arguments), isDetached(arguments, 1)); }; // Some Node versions implement exec() through the exported execFile(), // while others call an internal function. trackChild() de-duplicates the // former case. cp.exec = function(command) { return trackChild(origExec.apply(this, arguments), isDetached(arguments, 1)); }; // execSync()/spawnSync() have been reaped by the time they return, so // fork() is the last one left. Normalize its two valid signatures before // adding the preloader: fork(module[, options]) and // fork(module[, args][, options]). cp.fork = function(modulePath, args, options) { if (args === undefined || args === null) { args = []; } else if (typeof args === 'object' && !Array.isArray(args)) { options = args; args = []; } if (options === undefined || options === null) { options = {}; } if (typeof options !== 'object' || Array.isArray(options)) { // Preserve Node's normal argument validation and error. return origFork.call(this, modulePath, args, options); } var opts = Object.assign({}, options); var execArgv = opts.execArgv || process.execArgv || []; if (canPreload(opts.execPath) && execArgv.indexOf(__filename) < 0) { opts.execArgv = execArgv.concat(['--require', __filename]); } if (opts.env) { // A private environment was given, our own copy of PPID_ENV would // not be inherited, put it in explicitly. opts.env = Object.assign({}, opts.env); opts.env[PPID_ENV] = String(process.pid); } return trackChild(origFork.call(this, modulePath, args, opts), opts.detached === true); }; process.on('exit', function() { killChildren('SIGTERM'); }); var signals = { SIGHUP: 1, SIGINT: 2, SIGQUIT: 3, SIGTERM: 15 }; Object.keys(signals).forEach(function(name) { process.on(name, function() { if (process.listenerCount(name) > 1) { // The application installed its own handler, leave the // shutdown to it, the 'exit' handler cleans up afterwards. return; } // Restore the default behavior of dying on the signal, the 'exit' // handler takes the children along. process.exit(128 + signals[name]); }); }); } function watchParent(ppid) { if (isKeepChildren() || !ppid) { return; } var timer = setInterval(function() { var currentPpid = getParentPid(); if (currentPpid && currentPpid !== ppid) { terminateSelf(); } }, PARENT_CHECK_INTERVAL); if (typeof timer.unref === 'function') { timer.unref(); } // A dead parent closes the fork() IPC channel. Applications may also call // disconnect() deliberately, so only terminate if the OS parent changed. // If re-parenting has not completed yet, the interval catches it shortly. process.on('disconnect', function() { var currentPpid = getParentPid(); if (currentPpid && currentPpid !== ppid) { terminateSelf(); } }); } function terminateSelf() { if (terminating) { return; } terminating = true; killChildren('SIGTERM'); try { // Gives the application a chance to shut itself down cleanly, when it // has no handler of its own this exits immediately. process.kill(process.pid, 'SIGTERM'); } catch (err) { } setTimeout(function() { killChildren('SIGKILL'); process.exit(0); }, FORCE_KILL_DELAY); } function watchListeningSocket(path) { // A Unix-domain listener remains open after its directory entry is // unlinked. A replacement listener also gets a different inode. In both // cases this process is no longer the one LiteSpeed reaches by path, so do // not leave it serving inherited connections or consuming resources. var expected; try { expected = fs.lstatSync(path); } catch (err) { terminateSelf(); return; } if (!expected.isSocket()) { terminateSelf(); return; } var timer = setInterval(function() { var current; try { current = fs.lstatSync(path); } catch (err) { clearInterval(timer); terminateSelf(); return; } if (!current.isSocket() || current.dev !== expected.dev || current.ino !== expected.ino) { clearInterval(timer); terminateSelf(); } }, LISTEN_SOCKET_CHECK_INTERVAL); if (typeof timer.unref === 'function') { timer.unref(); } } function startApplication() { var appRoot = process.env.LSNODE_ROOT || process.cwd(); var startupFile = process.env.LSNODE_STARTUP_FILE || 'app.js'; LsNode.listenDone = false; if (process.env.LSNODE_ROOT != undefined) { try { process.chdir(process.env.LSNODE_ROOT); } catch (err) { console.error("Error setting directory to: " + process.env.LSNODE_ROOT + ": " + err); } } if (!startupFile.startsWith('/')) { if (!appRoot.endsWith('/')) { appRoot = appRoot + '/'; } startupFile = appRoot + startupFile; } process.title = 'lsnode:' + appRoot; var consoleLog = process.env.LSNODE_CONSOLE_LOG || '/dev/null'; fs.closeSync(1); try { fs.openSync(consoleLog, "a"); } catch(e) { fs.openSync('/dev/null', "a"); } http.Server.prototype.realListen = http.Server.prototype.listen; http.Server.prototype.listen = customListen; http.Server.prototype.address = lsnode_address; var app = require(startupFile); if (!LsNode.listenDone) { if (typeof app.listen === "function") app.listen(3000); } } function lsnode_address() { return process.env.LSNODE_SOCKET; } function customListen(port) { function onListenError(error) { restoreBindMask(); server.emit('error', error); } function restoreBindMask() { // Idempotent, listen() may fail synchronously, asynchronously, or not // at all; the mask must go back exactly once in every case, an // application that handles the error keeps running with it otherwise. if (lsBindMask !== undefined) { var mask = lsBindMask; lsBindMask = undefined; process.umask(mask); } } // The replacement for the listen call! var server = this; if (LsNode.listenDone) { console.error("http.Server.listen() was called more than once, ignore."); return server; } LsNode.listenDone = true; var listeners = server.listeners('request'); var i; server.removeAllListeners('request'); server.on('request', function(req) { req.connection.__defineGetter__('remoteAddress', function() { return '127.0.0.1'; }); req.connection.__defineGetter__('remotePort', function() { return port; }); req.connection.__defineGetter__('addressType', function() { return 4; }); }); for (i = 0; i < listeners.length; i++) { server.on('request', listeners[i]); } var callback; if (arguments.length > 1 && typeof(arguments[arguments.length - 1]) == 'function') { callback = arguments[arguments.length - 1]; } server.once('error', onListenError); var lsBindMask; var lsBindPath = process.env.LSNODE_BIND_SOCKET ? process.env.LSNODE_SOCKET : null; if (lsBindPath) { // The application owns and binds the unix socket; LiteSpeed connects to // it by path. Remove any stale socket from a previous run, and bind // under a umask that makes it group-writable (0660) so the web server // worker -- which shares the socket's group via the set-gid directory -- // can connect. try { fs.unlinkSync(lsBindPath); } catch (e) {} lsBindMask = process.umask(0o117); } try { server.realListen(socketObject, function() { if (lsBindPath) { // bind() applied the mask above, this covers the socket being // created after it was restored, and is a no-op otherwise. try { fs.chmodSync(lsBindPath, 0o660); } catch (e) {} restoreBindMask(); } // Inherited-fd and app-bind listeners are both reached through // LSNODE_SOCKET. Stop if its pathname no longer refers to this // listener, regardless of how the listener was obtained. if (process.env.LSNODE_SOCKET) { watchListeningSocket(process.env.LSNODE_SOCKET); } server.removeListener('error', onListenError); if (callback) { server.once('listening', callback); } server.emit('listening'); }); } catch (err) { restoreBindMask(); throw err; } // Binding a unix socket finishes inside listen(), so the mask has done its // job by the time it returns. Restore it here rather than from the // callback above: node emits 'listening' to the application's own handlers // first, and those must not run under our mask. restoreBindMask(); return server; }
Ln 1, Col 1
FORMAT
WRAP
SAVE FILE
ONLINE
fcgi-bin
10 items
04:22:55
TERMINAL FM
×
NAVIGATION
Root
Parent Dir
Refresh
ACTIONS
New Folder
New File
Upload Files
New Symlink
SELECTION
Select All
Deselect
Delete Selected
NEW FOLDER
FOLDER NAME
NEW FILE
FILE NAME
UPLOAD FILES
Click to browse or drag & drop files
No files selected
RENAME
CURRENT NAME
NEW NAME
COPY FILE
SOURCE
DESTINATION NAME
CHMOD
FILE / FOLDER
CURRENT PERMISSIONS
NEW MODE (octal)
755 (rwxr-xr-x)
644 (rw-r--r--)
777 (rwxrwxrwx)
600 (rw-------)
444 (r--r--r--)
CONFIRM DELETE
You are about to delete:
This action is irreversible. All contents will be permanently removed.
NEW SYMLINK
LINK TARGET (path)
LINK NAME
Edit
Preview
Download
Rename
Copy
Chmod
Delete