profoundjs
Version:
Profound.js Framework and Server
728 lines (654 loc) • 26.2 kB
JavaScript
/*
This is NPM script: npm run setup
* Performs additional installation.
* Kicked off from 'complete_install.js' which is manually run by user.
* Can also be run directly for advanced/automated installation.
*/
// Platform-specific dependency versions.
const idb_connector_version = "1.2.16";
const node_pty_version = "^2";
const child_process = require("child_process");
const compareVersions = require("compare-versions");
const crypto = require("crypto");
const fs = require("fs-extra");
const iutils = require("./install_utils.js");
const os = require("os");
const minimist = require("minimist");
const path = require("path");
const util = require("util");
const uuidv4 = require("uuid").v4;
const IBMi = iutils.isIBMi();
let RED = "\x1b[31m";
let CYAN = "\x1b[36m";
let RESET = "\x1b[0m";
const logDir = path.join(os.tmpdir(), "profoundjs-" + uuidv4());
const logPath = path.join(logDir, "setup.log");
let logFile;
let SILENT_MODE = false;
(async() => {
try {
// Parse arguments.
const args = minimist(
process.argv.slice(2),
{
alias: {
"c": "config-file",
"h": "help",
"s": "silent"
},
boolean: [
"h",
"ibmi-connector-library",
"ibmi-instance-autostart",
"nodegit",
"silent",
"installSamples"
],
string: [
"c",
"ibmi-instance",
"ibmi-instance-ccsid",
"ibmi-instance-node-path",
"nodegit-version"
],
unknown: function(arg) {
console.error("Unknown argument:", arg);
process.exit(1);
}
}
);
// Show help and quit, if requested.
if (args["help"]) {
const HELP = `Usage: npm run setup -- [OPTION]
-c, --config-file=<file> Alternate config file.
-h, --help Print this help and exit.
--nodegit Install nodegit.
--nodegit-version=version Override nodegit version.
--ibmi-connector-library Install IBM i Connector library.
--ibmi-instance=<name> Create/update STRTCPSVR instance config.
--ibmi-instance-autostart Autostart flag for STRTCPSVR instance.
--ibmi-instance-ccsid=<ccsid> CCSID for STRTCPSVR instance.
--ibmi-instance-node-path=<path> Node.js binary path for STRTCPSVR instance.
--samples Install sample code.
Performs additional installation using config values and CLI options.
Default config file is <INSTALL_DIRECTORY>/config.js.
--config-file can be an absolute path or path relative to working directory.
--nodegit-version is ignored if --nodegit is not used.
--nodegit-version can accept an NPM package version, tag, or version range.
IBM i-related values are invalid when not installing on IBM i.
IBM i instance-related values are ignored unless --ibmi-instance is specified.
--ibmi-connector-library installs to library specified in config file.
--ibmi-instance creates or replaces STRTCPSVR config.
--ibmi-instance-autostart sets "autostart=1" in STRTCPSVR config.
--ibmi-instance-ccsid sets "ccsid" in STRTCPSVR config.
--ibmi-instance-node-path sets "nodePath" in STRTCPSVR config.
`;
console.log(HELP);
process.exit(0);
}
if (args.silent === true) {
SILENT_MODE = true;
RED = CYAN = RESET = "";
}
if (!SILENT_MODE) {
// Initialize log file.
fs.mkdirSync(logDir);
logFile = fs.openSync(logPath, "w");
}
// Validate arguments.
if (args._.length > 0) {
logError("Unknown argument:", args._[0]);
die();
}
const deployDir = iutils.getDeployDir();
if (!deployDir) {
logError("Can't find deployment directory.");
die();
}
const configPath = args["config-file"] !== undefined ? path.resolve(args["config-file"]) : iutils.getConfigPath();
if (!fileExists(configPath)) {
logError(`Configuration file ${configPath} not found.`);
die();
}
const config = require(configPath);
if (args["ibmi-connector-library"]) {
if (!IBMi) {
logError(`--ibmi-connector-library is not valid on ${os.platform()}`);
die();
}
if (typeof config.connectorLibrary !== "string") {
logError("connectorLibrary is missing from config or is invalid.");
die();
}
let error = iutils.validateIBMiName(config.connectorLibrary);
if (error) {
logError("connectorLibrary is invalid: " + error);
die();
}
if (config.connectorIASP !== undefined) {
error = iutils.validateIBMiIASP(config.connectorIASP);
if (error) {
logError("connectorIASP is invalid: " + error);
die();
}
}
}
if (args["ibmi-instance"] !== undefined) {
if (!IBMi) {
logError(`--ibmi-instance is not valid on ${os.platform()}`);
die();
}
let error = iutils.validateIBMiName(args["ibmi-instance"]);
if (error) {
logError("--ibmi-instance is invalid: " + error);
die();
}
if (args["ibmi-instance-ccsid"] !== undefined) {
error = iutils.validateIBMiCCSID(parseInt(args["ibmi-instance-ccsid"], 10));
if (error) {
logError("--ibmi-instance-ccsid is invalid: " + error);
die();
}
}
if (args["ibmi-instance-node-path"] !== undefined) {
const nodePath = args["ibmi-instance-node-path"].trim();
if (!fileExists(nodePath)) {
logError("--ibmi-instance-node-path is invalid: " + nodePath + " does not exist.");
die();
}
}
}
// Install platform-specific dependencies.
if (IBMi) {
log(`Installing idb-connector...`);
child_process.execSync(
`npm install idb-connector@"${idb_connector_version}"`,
{
cwd: iutils.getDeployDir(),
stdio: [
"ignore",
SILENT_MODE ? process.stdout : logFile,
SILENT_MODE ? process.stderr : logFile
]
}
);
}
else {
log(`Installing profoundjs-node-pty...`);
child_process.execSync(
`npm install --save-optional profoundjs-node-pty@"${node_pty_version}"`,
{
cwd: iutils.getDeployDir(),
stdio: [
"ignore",
SILENT_MODE ? process.stdout : logFile,
SILENT_MODE ? process.stderr : logFile
]
}
);
}
// Install nodegit, if necessary.
if (args["nodegit"]) {
let version;
if (args["nodegit-version"]) {
version = args["nodegit-version"];
}
else {
// Current stable version of nodegit doesn't have pre-built binaries for Node > 14.
// If installing on Node > 14, use alpha version with pre-built binaries.
version = "0.27.0"; // Current stable version.
if (compareVersions("14.*.*", process.versions.node) === -1) {
version = "0.28.0-alpha.18";
}
}
log(`Installing nodegit...`);
try {
child_process.execSync(
`npm install nodegit@"${version}"`,
{
cwd: iutils.getDeployDir(),
stdio: [
"ignore",
SILENT_MODE ? process.stdout : logFile,
SILENT_MODE ? process.stderr : logFile
]
}
);
}
catch (error) {
logError("nodegit installation failed.");
die();
}
}
// Create modules directory.
if (directoryExists(path.join(deployDir, "modules"))) {
log("modules directory exists.");
}
else {
log("Creating modules directory.");
fs.mkdirSync(path.join(deployDir, "modules"));
}
// Create plugins directory.
if (directoryExists(path.join(deployDir, "plugins"))) {
log("plugins directory exists.");
}
else {
log("Creating plugins directory.");
fs.mkdirSync(path.join(deployDir, "plugins"));
}
// Grant PROFOUNDJS user all permissions to modules directory tree.
if (IBMi) {
runCommand("CHGAUT OBJ('" + deployDir + "') USER(PROFOUNDJS) DTAAUT(*RWX) OBJAUT(*ALL)");
runCommand("CHGAUT OBJ('" + path.join(deployDir, "modules") + "') USER(PROFOUNDJS) DTAAUT(*RWX) OBJAUT(*ALL) SUBTREE(*ALL)");
}
// Create puiscreens.json.
createPuiscreens(deployDir);
// Create puiuplexit.js.
if (fileExists(path.join(deployDir, "modules", "puiuplexit.js"))) {
log("puiuplexit.js file exists.");
}
else {
log("Creating puiuplexit.js.");
copyFile(path.join(__dirname, "modules", "puiuplexit.js"), path.join(deployDir, "modules"), "utf8");
}
// Create puidnlexit.js.
if (fileExists(path.join(deployDir, "modules", "puidnlexit.js"))) {
log("puidnlexit.js file exists.");
}
else {
log("Creating puidnlexit.js.");
copyFile(path.join(__dirname, "modules", "puidnlexit.js"), path.join(deployDir, "modules"), "utf8");
}
if (args["installSamples"]) {
// Create samples directory.
if (directoryExists(path.join(deployDir, "modules", "pjssamples"))) {
fs.removeSync(path.join(deployDir, "modules", "pjssamples"));
}
log("Copying pjssamples.");
copyDir(path.join(__dirname, "modules", "pjssamples"), path.join(deployDir, "modules"));
const copyWSMsg = "Copying sample workspace into ";
// Create PAPI samples directory
let wsPath = path.join(deployDir, "modules", "papisamples");
if (directoryExists(wsPath)) {
log(`Skipping sample workspace, ${wsPath} -- directory exists.`);
}
else {
log(copyWSMsg + wsPath);
copyDir(path.join(__dirname, "modules", "papisamples"), path.join(deployDir, "modules"));
}
// Create oauth2sample directory.
wsPath = path.join(deployDir, "modules", "oauth2sample");
if (directoryExists(wsPath)) {
log(`Skipping sample workspace, ${wsPath} -- directory exists.`);
}
else {
log(copyWSMsg + wsPath);
copyDir(path.join(__dirname, "modules", "oauth2sample"), path.join(deployDir, "modules"));
}
// Create mathoperation.js.
const fPath = path.join(deployDir, "plugins", "mathoperation.js");
if (fileExists(fPath)) {
log(`Skipping sample Low-code plugin: ${fPath} -- file exists.`);
}
else {
log(`Creating sample Low-code plugin: ${fPath}`);
copyFile(path.join(__dirname, "plugins", "mathoperation.js"), path.join(deployDir, "plugins"), "utf8");
}
}
// Create store_credentials.js.
if (fileExists(path.join(deployDir, "store_credentials.js"))) {
log("store_credentials.js file exists.");
}
else {
copyFile(path.join(__dirname, "store_credentials.js"), deployDir, "utf8");
log("store_credentials.js created.");
}
// Create store_options.js.
if (fileExists(path.join(deployDir, "store_options.js"))) {
log("store_options.js file exists.");
}
else {
copyFile(path.join(__dirname, "store_options.js"), deployDir, "utf8");
log("store_options.js created.");
}
// Create call.js.
if (fileExists(path.join(deployDir, "call.js"))) {
log("call.js file exists.");
}
else {
copyFile(path.join(__dirname, "call.js"), deployDir, "utf8");
log("call.js created.");
}
// Create lic_client.js
copyFile(path.join(__dirname, "lic_client.js"), deployDir, "utf8");
log("lic_client.js created.");
// Create gen_key.js.
if (fileExists(path.join(deployDir, "gen_key.js"))) {
log("gen_key.js file exists.");
}
else {
copyFile(path.join(__dirname, "gen_key.js"), deployDir, "utf8");
log("gen_key.js created.");
}
// Create get_pjscall_key.js.
if (fileExists(path.join(deployDir, "get_pjscall_key.js"))) {
log("get_pjscall_key.js file exists.");
}
else {
copyFile(path.join(__dirname, "get_pjscall_key.js"), deployDir, "utf8");
log("get_pjscall_key.js created.");
}
// Convert start.js to Promises, if necessary.
const convertStartJS = require("./convertStartJS.js");
if (convertStartJS(path.join(deployDir, "start.js"))) {
log("start.js file converted to Promises.");
}
// Install native IBM i components, if necessary.
if (IBMi) {
log("");
if (args["ibmi-connector-library"] || args["ibmi-instance"] !== undefined) {
const portNumber = config.port || 8081;
let connectorLibrary = "*NONE";
let connectorIASP;
if (args["ibmi-connector-library"]) {
connectorLibrary = config.connectorLibrary;
connectorIASP = config.connectorIASP || "*SYSBAS";
}
let svrname = "*NONE";
let autostart = true;
let ccsid = 37;
let nodePath = process.argv[0];
if (args["ibmi-instance"] !== undefined) {
svrname = args["ibmi-instance"].toUpperCase();
if (process.argv.find(arg => arg.includes("ibmi-instance-autostart"))) { // minimist sets booleans to false if not passed.
autostart = args["ibmi-instance-autostart"];
}
if (args["ibmi-instance-ccsid"] !== undefined) {
ccsid = args["ibmi-instance-ccsid"];
}
if (args["ibmi-instance-node-path"] !== undefined) {
nodePath = args["ibmi-instance-node-path"];
}
}
// First, try copying the save file
let success = runCommand("CPYFRMSTMF FROMSTMF('" + __dirname + "/pjsdist.savf') TOMBR('/QSYS.LIB/QGPL.LIB/PJSDIST.FILE') MBROPT(*REPLACE)");
if (!success) {
logError("Unable to create installer save file.");
die();
}
// Now, try restoring the PJSINSTALL program
success = runCommand("RSTOBJ OBJ(PJSINSTALL) SAVLIB(QTEMP) DEV(*SAVF) OBJTYPE(*ALL) SAVF(QGPL/PJSDIST) RSTLIB(QGPL)");
if (!success) {
// Clean up save file
log("");
log("Cleaning up...");
runCommand("DLTF FILE(QGPL/PJSDIST)");
logError("Unable to restore installer save file.");
die();
}
// Now, try running PJSINSTALL
let command = "QGPL/PJSINSTALL CONNLIB(" + connectorLibrary + ")";
if (connectorLibrary != "*NONE") {
command += " CONNIASP(" + connectorIASP + ") CONNHOST('localhost') CONNPORT(" + portNumber + ")";
}
command += " SVRNAME(" + svrname + ")";
if (svrname != "*NONE") {
command += " SVRDIR('" + deployDir + "') " +
"SVRAUTO(" + ((autostart) ? "*YES" : "*NO") + ") CCSID(" + ccsid + ") NODEPATH('" + nodePath + "')";
}
success = runCommand(command, "-Ke");
// Clean up program and save file
log("");
log("Cleaning up...");
runCommand("DLTPGM PGM(QGPL/PJSINSTALL)");
runCommand("DLTCMD CMD(QGPL/PJSINSTALL)");
runCommand("DLTPNLGRP PNLGRP(QGPL/PJSINSTALL)");
runCommand("DLTMSGF MSGF(QGPL/PJSINSTALL)");
runCommand("DLTF FILE(QGPL/PJSDIST)");
if (!success) {
logError("PJSINSTALL command failed.");
die();
}
}
if (args["ibmi-instance"] === undefined) {
// Instance is restarted by PJSINSTALL command only when config is created/replaced.
const instances = iutils.getIBMiInstances(deployDir);
if (instances.length > 0) {
const instance = instances[0];
log("");
runCommand(`ENDTCPSVR SERVER(*PJS) INSTANCE(${instance.name})`);
log("Waiting for instance to end...");
// This should generally work as shutdown is relatively fast.
await new Promise(resolve => setTimeout(resolve, 10000));
runCommand(`STRTCPSVR SERVER(*PJS) INSTANCE(${instance.name})`);
}
}
}
log(`\n${CYAN}Profound.js installation complete.${RESET}\n`);
}
catch (error) {
logError(error);
die();
}
})();
function log(...args) {
console.log(...args);
logToFile(...args);
}
function logError(...args) {
console.error(...args);
logToFile(...args);
}
function logToFile(...args) {
if (SILENT_MODE) {
return;
}
let output = `${util.format(...args)}\n`; // Mimic console.log().
output = output.replace(/\x1b\[[0-9]+m/g, ""); // Strip terminal escape codes for colors.
fs.writeFileSync(logFile, output, { flag: "a" });
}
function die() {
logError(`\n${RED}Profound.js installation failed.${RESET}`);
if (!SILENT_MODE) {
console.error(`${RED}See installation log:${RESET} ${logPath}\n`);
}
process.exit(1);
}
function copyDir(dir, destinationDir) {
const dirName = path.basename(dir);
fs.mkdirSync(path.join(destinationDir, dirName));
const files = fs.readdirSync(dir);
files.forEach(function(file, index) {
if (fs.lstatSync(path.join(dir, file)).isDirectory()) { // recurse
copyDir(path.join(dir, file), path.join(destinationDir, dirName));
}
else {
copyFile(path.join(dir, file), path.join(destinationDir, dirName), null, null);
}
});
}
function copyFile(file, destination, type) {
if (type == null) type = "binary";
let toFile;
if (directoryExists(destination)) {
toFile = path.join(destination, path.basename(file));
}
else {
toFile = destination;
}
const content = fs.readFileSync(file, type);
fs.writeFileSync(toFile, content, type);
}
function createPuiscreens(deployDir) {
const targetPuiScreensFile = path.join(deployDir, "modules", "puiscreens.json");
function copyFileOldVersionIs(version, compareStr) {
compareStr = compareStr || "=";
log("Found Standard puiscreens.json file version " + compareStr + " " + version + ", replacing with current version...");
copyFile(path.join(__dirname, "modules", "puiscreens.json"), targetPuiScreensFile, "utf8");
log("puiscreens.json file was replaced.");
}
// Check which version of puiscreens.json is installed.
// Version 4.7.0 ships with a new version of puiscreens where the bound fields (SSUSER, SSPASSWORD, SSSUBMIT, SSERROR, etc) are now lower case,
// so this would be a breaking change. So our install logic is now as follows: -
// 1. Check the SHA key of existing puiscreens.json.
// 2. If it's an official PJS screen, no customizations have been made, so it's safe to replace with this version.
// 3. If it's not an official version, check if we have lower case User & Password. If not, then backup existing version to puiscreens_bak.json and install this official version.
// 4. Otherwise it's not an official version but should work OK. So leave existing puiscreens.json, and check if we already have a puiscreens_orig.json, and
// create it if not.
if (fileExists(targetPuiScreensFile)) {
const data = fs.readFileSync(targetPuiScreensFile);
const checksum = crypto.createHash("sha512").update(data).digest("hex"); // sha512 best for 64-bit
let format = null;
let puiScreens;
let goodUser = false;
let goodPassword = false;
let goodSubmit = false;
let goodError = false;
switch (checksum) {
case "4fb38daa851aacd21cf3aeaa92304df4e0353cae2b6dab3a5fd53c0cf4d42875b5efadbb4bb29642cbff07bfdd8e1c7a9c107f582905f7fc798c3841e888ee61":
copyFileOldVersionIs("4.6.1", "<=");
break;
case "2e63c0e9816f52b515c45479ada102af806bc73b79d14109567500c529bfaa166fb981ddb6062a1ba0a7f8e36a11d674a955d2f003806f21165cb26ad0fbdf33":
copyFileOldVersionIs("4.7.0");
break;
case "b5eadc2df96e97de184cc331f133933c5f2945ab6c84997cfb30f2e6b478dfa6c8753bdb328b8257382f16a6d8e85b469af09f6d0eb872642300225590031c83":
copyFileOldVersionIs("4.8.0");
break;
case "05e7b8a2a69dd12f5dafecdd032be8e42bfe07d02dc0bce2c90d8586135130dcfa134e8851dd1df00fe52021ef4d81dab09be419711837b4a4cddf536019ccfe":
case "b5e4d420d49d48c1dd08d0f50b88018fb64ee77248edcd9a656aee9f8ba723d0a91b18751f34cfc2efcd905f71834d5c2ac5cbff552fd85d164b710c3c2e079d":
copyFileOldVersionIs("4.9.1");
break;
case "1d469f6d2e4549dc259155a4b00d8f278ea89dc3566e18860ed9f6ccd406db8a47b0b3cbc359b397c59feac6faeb6a7fdc6ce2edbd3f120e295464dd41dd28f8":
copyFileOldVersionIs("4.11.1");
break;
case "313d7e363fc9f2df66f2803116487bc75d14e647be6da33fb7be8ac886a96a58c970eecc5b147fd1a07af3c559a0de61a32893875ec0be26e6840e1945a441c3":
copyFileOldVersionIs("4.12.0");
break;
case "ea10b5e751b736f4164d23e45399e6364dd95e074b36c4c996a5824fc5ba1a0436be6811c0f421510eecc57b6125e388150e5f0664dbaea681cd42e73855eb34":
copyFileOldVersionIs("4.13.0");
break;
case "9768e7f3df273d809845b93390a832ed6e5b77ccbd1b51bc7ad681c8261dba0b75bf9c3f802e9784ce01a1812e45bbbf95dc8a99bd6d3b3ea43f6e5a4c1a878e":
case "4353f5ae5067b91821825ec20190e0adf96bc87493141fba3cf1da7cea8863af2e4358cd24019223a2704eee4170c75cb4037843ca7c1f289416555ee146a851":
copyFileOldVersionIs("5.0.0");
break;
case "2becc968e9db1acc1ffd6e3885d95c153d678468e69a4fdef0df725c06897b7f6ddcff82d703cb76724774729b18b56307445e728521311dbf256b97e17538d8":
copyFileOldVersionIs("5.2.0");
break;
case "af04803ac52709fd25f6fc87ff3ea309ad03ee6a9631f32511a5aa32c5ae50f34fd0e38e0d47dea223ac0c5dfb595199c623a83772af830edd21378055403e5c":
copyFileOldVersionIs("6.0.0-beta.0");
break;
case "cd3e32138fbf1dac7755c233e1b5fc0d14f800694184c92989ddc5e62bacf86306013bcf423bf837183c979f61d6b323e0581363297ca6cf7f1b15435e0fbff5":
copyFileOldVersionIs("6.0.0-beta.5");
break;
case "3aaab88d4cf922f01a09bab3424ecf81460c7679a6d51ecb0f187fdce97e627499128e9b8c46d83075098e7e8f7150b73936056b1accedff55a79c69d65cb20b":
// Note: Betas were considered pre-release versions. 6.0.0 is the release version; so 6.0.0 is newer than 6.0.0-beta.X
copyFileOldVersionIs("6.0.0");
break;
case "41a350ae45c7616fe1824fb236b48ab64ca6d0de1023946e16ef9de4eea804649ff13123fa4ce630bb93308c8312f75e12448bbe58e91802dc8b12cc27bfd051":
log("Found Standard puiscreens.json file version > 6.0.0, no need for update.");
break;
default:
puiScreens = JSON.parse(data);
log("Found Customized puiscreens.json file, analyzing contents...");
// We need to check the signon screen and make sure we have lower case bound fields
if (Array.isArray(puiScreens.formats)) {
format = puiScreens.formats.find(format => typeof format.screen["record format name"] === "string" && format.screen["record format name"].toLowerCase() === "signonscrn");
}
if (format) {
for (const j in format.items) {
const itemValue = format.items[j].value;
if (itemValue && typeof itemValue === "object") {
if (itemValue.fieldName === "ssuser") goodUser = true;
else if (itemValue.fieldName === "sspassword") goodPassword = true;
}
const itemResponse = format.items[j].response;
if (itemResponse && typeof itemResponse === "object") {
if (itemResponse.fieldName === "sssubmit") goodSubmit = true;
}
const itemHtml = format.items[j].html;
if (itemHtml && typeof itemHtml === "object") {
if (itemHtml.fieldName === "sserror") goodError = true;
}
if (goodUser && goodPassword && goodSubmit && goodError) break;
}
}
if (goodUser && goodPassword && goodSubmit && goodError) {
log("The Customized puiscreens.json file is compatible, so leaving it in place. Creating puiscreens_orig.json");
copyFile(path.join(__dirname, "modules", "puiscreens.json"), path.join(deployDir, "modules", "puiscreens_orig.json"), "utf8");
log("puiscreens_orig.json created. For latest functionality, please migrate standard fields into your customized version.");
}
else {
log("The Customized puiscreens.json file is NOT compatible, backing it up to puiscreens_bak.json...");
copyFile(path.join(deployDir, "modules", "puiscreens.json"), path.join(deployDir, "modules", "puiscreens_bak.json"), "utf8");
log("puiscreens_bak.json created. Creating puiscreens.json ...");
copyFile(path.join(__dirname, "modules", "puiscreens.json"), targetPuiScreensFile, "utf8");
log("puiscreens.json created. Please migrate standard fields into your customized version.");
}
}
}
else {
log("Creating puiscreens.json.");
copyFile(path.join(__dirname, "modules", "puiscreens.json"), targetPuiScreensFile, "utf8");
}
}
function directoryExists(dir) {
let exists = false;
try {
const stat = fs.statSync(dir);
if (stat && stat.isDirectory()) exists = true;
}
catch (err) {
exists = false;
}
return exists;
}
function fileExists(file) {
let exists = false;
try {
const stat = fs.statSync(file);
if (stat && stat.isFile()) exists = true;
}
catch (err) {
exists = false;
}
return exists;
}
function runCommand(command, switches) {
log("Executing IBM i command: " + command);
const args = [command];
if (typeof switches == "string") {
args.unshift(switches);
}
else {
args.unshift("-e");
}
const options = {
stdio: [
"ignore",
SILENT_MODE ? process.stdout : logFile,
SILENT_MODE ? process.stderr : logFile
],
cwd: __dirname,
env: {
QIBM_USE_DESCRIPTOR_STDIO: "Y",
QIBM_MULTI_THREADED: "N" // Many IBM commands cannot run in multi-threaded mode...
}
};
const results = child_process.spawnSync("system", args, options);
const code = results.status;
const signal = results.signal;
if (code != null) {
if (code === 0) {
return true;
}
else {
logError("Process exited, code %d", code);
}
}
else {
logError("Process ended due to signal %s", signal);
logError(results); // show full results including detailed error
}
return false;
}
;