let arguments = JSON.parse(process.argv[2]); const chalk = require(__dirname + '/../../MistyCore/node_modules/chalk'); const YAML = require(__dirname + '/../../MistyCore/node_modules/yaml'); const si = require(__dirname + '/../../MistyCore/node_modules/systeminformation'); const fs = require('fs'); const net = require("net"); let systemRoot = "/System/Library"; function size(bytes) { if (bytes > 1024) { if (bytes > 1024**2) { if (bytes > 1024**3) { if (bytes > 1024**4) { return (bytes / 1024**4).toFixed(2) + "T"; } else { return (bytes / 1024**3).toFixed(2) + "G"; } } else { return (bytes / 1024**2).toFixed(2) + "M"; } } else { return (bytes / 1024).toFixed(2) + "K"; } } else { return bytes + "B"; } } function waitForState(daemon, status) { return new Promise((resolve) => { let i = 0; let me = setInterval(() => { if (status === "running") { if (fs.readFileSync(systemRoot + "/../Volumes/VM/LaunchDaemons/" + daemon).toString().trim() !== "-1" && fs.readFileSync(systemRoot + "/../Volumes/VM/LaunchDaemons/" + daemon).toString().trim() !== "-3" && fs.readFileSync(systemRoot + "/../Volumes/VM/LaunchDaemons/" + daemon).toString().trim() !== "-2") { clearInterval(me); if (fs.readFileSync(systemRoot + "/../Volumes/VM/LaunchDaemons/" + daemon).toString().trim() !== "0") { console.log(daemon + " is now running with PID " + fs.readFileSync(systemRoot + "/../Volumes/VM/LaunchDaemons/" + daemon).toString().trim()); } else if (fs.readFileSync(systemRoot + "/../Volumes/VM/LaunchDaemons/" + daemon).toString().trim() !== "-2") { console.log(daemon + " has failed\nRun 'CoreDaemon -Status " + daemon + "' for details; 'CoreDaemon -ForceStop " + daemon + "' to force stop."); process.exit(2); } else { console.log(daemon + " has now started"); } resolve(); } } else if (status === "stopped") { if (fs.readFileSync(systemRoot + "/../Volumes/VM/LaunchDaemons/" + daemon).toString().trim() === "-1") { clearInterval(me); console.log(daemon + " is now stopped"); resolve(); } if (fs.readFileSync(systemRoot + "/../Volumes/VM/LaunchDaemons/" + daemon).toString().trim() === "-2") { clearInterval(me); console.log(daemon + " has failed\nRun 'CoreDaemon -Status " + daemon + "' for details; 'CoreDaemon -ForceStop " + daemon + "' to force stop."); process.exit(2); resolve(); } } else { clearInterval(me); resolve(); } if (i >= 100) { console.log("Timed out waiting for " + daemon + " after 10 seconds. Has the launch daemon failed?\nRun 'CoreDaemon -Status " + daemon + "' for details; 'CoreDaemon -ForceStop " + daemon + "' to force stop."); process.exit(2); } i++; }, 100); }); } (async () => { let processes = (await si.processes()).list; if (arguments['start'] && !arguments['stop'] && !arguments['restart'] && !arguments['status'] && !arguments['forcestop']) { if (arguments['_finals'].length > 0) { if (!fs.existsSync(systemRoot + "/../Volumes/VM/LaunchDaemons/" + arguments['_finals'][0])) { console.log(chalk.bgYellow.white("") + " " + chalk.yellow("No such launch daemon: ") + arguments['_finals'][0]); return; } let state; switch (fs.readFileSync(systemRoot + "/../Volumes/VM/LaunchDaemons/" + arguments['_finals'][0]).toString()) { case "0": state = "running"; break; case "-1": state = "stopped"; break; case "-2": state = "failed"; break; default: state = "running"; break } if (state === "running") { console.log(chalk.bgYellow.white("") + " " + chalk.yellow("The launch daemon is not stopped or failed")); return; } const socket = net.createConnection(systemRoot + "/../Volumes/VM/MistyCore-Socket", () => { console.log("Waiting for " + arguments['_finals'][0] + " to have started..."); socket.write(JSON.stringify({ action: "SERVICE", payload: { option: "start", service: arguments['_finals'][0] + ".yml" } })); waitForState(arguments['_finals'][0], "running"); }); } else { console.log(chalk.bgYellow.white("") + " " + chalk.yellow("Missing operand")); } } else if (arguments['stop'] && !arguments['start'] && !arguments['restart'] && !arguments['status'] && !arguments['forcestop']) { if (arguments['_finals'].length > 0) { if (!fs.existsSync(systemRoot + "/../Volumes/VM/LaunchDaemons/" + arguments['_finals'][0])) { console.log(chalk.bgYellow.white("") + " " + chalk.yellow("No such launch daemon: ") + arguments['_finals'][0]); return; } let state; switch (fs.readFileSync(systemRoot + "/../Volumes/VM/LaunchDaemons/" + arguments['_finals'][0]).toString()) { case "0": state = "running"; break; case "-1": state = "stopped"; break; case "-2": state = "failed"; break; default: state = "running"; break } if (state !== "running") { console.log(chalk.bgYellow.white("") + " " + chalk.yellow("The launch daemon is not running")); return; } const socket = net.createConnection(systemRoot + "/../Volumes/VM/MistyCore-Socket", async () => { console.log("Waiting for " + arguments['_finals'][0] + " to have stopped..."); socket.write(JSON.stringify({ action: "SERVICE", payload: { option: "stop", service: arguments['_finals'][0] + ".yml" } })); await waitForState(arguments['_finals'][0], "stopped"); }); } else { console.log(chalk.bgYellow.white("") + " " + chalk.yellow("Missing operand")); } } else if (arguments['forcestop'] && !arguments['start'] && !arguments['restart'] && !arguments['status'] && !arguments['stop']) { if (arguments['_finals'].length > 0) { if (!fs.existsSync(systemRoot + "/../Volumes/VM/LaunchDaemons/" + arguments['_finals'][0])) { console.log(chalk.bgYellow.white("") + " " + chalk.yellow("No such launch daemon: ") + arguments['_finals'][0]); return; } let state; switch (fs.readFileSync(systemRoot + "/../Volumes/VM/LaunchDaemons/" + arguments['_finals'][0]).toString()) { case "0": state = "running"; break; case "-1": state = "stopped"; break; case "-2": state = "failed"; break; default: state = "running"; break } if (state !== "running") { console.log(chalk.bgYellow.white("") + " " + chalk.yellow("The launch daemon is not running")); return; } const socket = net.createConnection(systemRoot + "/../Volumes/VM/MistyCore-Socket", async () => { console.log("Waiting for " + arguments['_finals'][0] + " to have force-stopped..."); socket.write(JSON.stringify({ action: "SERVICE", payload: { option: "forcestop", service: arguments['_finals'][0] + ".yml" } })); await waitForState(arguments['_finals'][0], "stopped"); }); } else { console.log(chalk.bgYellow.white("") + " " + chalk.yellow("Missing operand")); } } else if (arguments['restart'] && !arguments['stop'] && !arguments['start'] && !arguments['status'] && !arguments['forcestop']) { if (arguments['_finals'].length > 0) { if (!fs.existsSync(systemRoot + "/../Volumes/VM/LaunchDaemons/" + arguments['_finals'][0])) { console.log(chalk.bgYellow.white("") + " " + chalk.yellow("No such launch daemon: ") + arguments['_finals'][0]); return; } let state; switch (fs.readFileSync(systemRoot + "/../Volumes/VM/LaunchDaemons/" + arguments['_finals'][0]).toString()) { case "0": state = "running"; break; case "-1": state = "stopped"; break; case "-2": state = "failed"; break; default: state = "running"; break } if (state !== "running") { console.log(chalk.bgYellow.white("") + " " + chalk.yellow("The launch daemon is not running")); return; } const socket = net.createConnection(systemRoot + "/../Volumes/VM/MistyCore-Socket", async () => { console.log("Waiting for " + arguments['_finals'][0] + " to have restarted..."); socket.write(JSON.stringify({ action: "SERVICE", payload: { option: "restart", service: arguments['_finals'][0] + ".yml" } })); await waitForState(arguments['_finals'][0], "running"); }); } else { console.log(chalk.bgYellow.white("") + " " + chalk.yellow("Missing operand")); } } else if (arguments['status'] && !arguments['stop'] && !arguments['restart'] && !arguments['start'] && !arguments['forcestop']) { if (arguments['_finals'].length > 0) { if (!fs.existsSync(systemRoot + "/../Volumes/VM/LaunchDaemons/" + arguments['_finals'][0])) { console.log(chalk.bgYellow.white("") + " " + chalk.yellow("No such launch daemon: ") + arguments['_finals'][0]); return; } let state; switch (fs.readFileSync(systemRoot + "/../Volumes/VM/LaunchDaemons/" + arguments['_finals'][0]).toString()) { case "0": state = "running"; break; case "-1": state = "stopped"; break; case "-3": state = "starting"; break; case "-2": state = "failed"; break; default: state = "running"; break } let name = arguments['_finals'][0], displayName = arguments['_finals'][0]; let service = YAML.parse(fs.readFileSync(systemRoot + "/LaunchDaemons/" + name + ".yml").toString()); let memoryUsage; let pid; let parent = "-"; switch (fs.readFileSync(systemRoot + "/../Volumes/VM/LaunchDaemons/" + arguments['_finals'][0]).toString()) { case "0": case "-1": case "-2": case "-3": memoryUsage = "-"; pid = "-"; break; default: let _processInfo = processes.filter(i => i.pid === parseInt(fs.readFileSync(systemRoot + "/../Volumes/VM/LaunchDaemons/" + arguments['_finals'][0]).toString())); if (_processInfo.length > 0) { pid = fs.readFileSync(systemRoot + "/../Volumes/VM/LaunchDaemons/" + arguments['_finals'][0]).toString() + " (" + _processInfo[0].name + ")"; let _parentInfo = processes.filter(i => i.pid === _processInfo[0].parentPid); if (_parentInfo.length > 0) { parent = _processInfo[0].parentPid + " (" + _parentInfo[0].name + ")"; } else { parent = _processInfo[0].parentPid; } memoryUsage = size(_processInfo[0].memVsz); } else { pid = fs.readFileSync(systemRoot + "/../Volumes/VM/LaunchDaemons/" + arguments['_finals'][0]).toString(); memoryUsage = "-"; } break; } let statusColor; switch (state) { case "running": statusColor = chalk.green("running"); break; case "starting": statusColor = chalk.yellow("starting"); break; case "failed": statusColor = chalk.cyan("failed"); break; case "stopped": statusColor = chalk.red("stopped"); break; default: statusColor = chalk.gray("unknown"); break; } let reachDate = "-"; if (fs.existsSync(systemRoot + "/../Volumes/VM/LaunchDaemonsTimes/" + arguments['_finals'][0])) { reachDate = new Date(parseInt(fs.readFileSync(systemRoot + "/../Volumes/VM/LaunchDaemonsTimes/" + arguments['_finals'][0]).toString())).toString(); } let autoStart; if (service.metadata.target === false) { autoStart = chalk.gray("manual"); } else { if (!isNaN(parseInt(service.metadata.target)) && parseInt(service.metadata.target) > -1 && parseInt(service.metadata.target) < 10) { autoStart = "on target " + parseInt(service.metadata.target); } else { autoStart = chalk.red("invalid"); } } console.log(name + " - " + (service.metadata.description ?? "")); console.log(chalk.cyan(" Loaded from:") + " " + systemRoot + "/LaunchDaemons/" + name + ".yml"); console.log(chalk.cyan(" Auto-start:") + " " + autoStart); console.log(chalk.cyan(" Status:") + " " + statusColor); console.log(chalk.cyan(" Reached:") + " " + reachDate); console.log(chalk.cyan(" Main PID:") + " " + pid); console.log(chalk.cyan(" Initiator:") + " " + parent); console.log(chalk.cyan(" Memory:") + " " + memoryUsage); if (service.commands.stop || service.commands.restart) { console.log(chalk.cyan(" Type:") + " MistyCore:RunOnce"); } else { console.log(chalk.cyan(" Type:") + " MistyCore:Daemon"); } console.log(""); switch (state) { case "running": console.log("Run 'Logger -Unit " + name + "' to view logs; 'CoreDaemon -Stop " + name + "' to stop."); break; case "starting": console.log("Run 'Logger -Unit " + name + "' to view logs; 'CoreDaemon -ForceStop " + name + "' to abort."); break; case "failed": console.log("Run 'Logger -Unit " + name + "' to view logs; 'CoreDaemon -Start " + name + "' to start."); break; case "stopped": console.log("Run 'Logger -Unit " + name + "' to view logs; 'CoreDaemon -Start " + name + "' to start."); break; default: console.log("Run 'Logger -Unit " + name + "' to view logs"); break; } } else { for (let name of fs.readdirSync(systemRoot + "/LaunchDaemons")) { let daemon = name.substring(0, name.length - 4); if (fs.existsSync(systemRoot + "/../Volumes/VM/LaunchDaemons/" + daemon)) { let state; switch (fs.readFileSync(systemRoot + "/../Volumes/VM/LaunchDaemons/" + daemon).toString()) { case "0": state = "running"; break; case "-1": state = "stopped"; break; case "-3": state = "starting"; break; case "-2": state = "failed"; break; default: state = "running"; break } switch (state) { case "running": console.log(" [ " + chalk.green("+") + " ] " + daemon); break; case "starting": console.log(" [ " + chalk.yellow("W") + " ] " + daemon); break; case "failed": console.log(" [ " + chalk.cyan("x") + " ] " + daemon); break; case "stopped": console.log(" [ " + chalk.red("-") + " ] " + daemon); break; default: console.log(" [ " + chalk.gray("?") + " ] " + daemon); break; } } else { console.log(" [ " + chalk.gray("?") + " ] " + daemon); } } } } else { console.log(chalk.bgYellow.white("") + " " + chalk.yellow("Only exactly one operation can be done at a time")); } })();