UNPKG

gpii-universal

Version:

Cross platform, core components of the GPII personalization infrastructure.

143 lines (123 loc) 4.91 kB
/* * Ensures there's only a single instance of GPII. * * Copyright 2017 Raising the Floor - International * * Licensed under the New BSD license. You may not use this file except in * compliance with this License. * * The R&D leading to these results received funding from the * Department of Education - Grant H421A150005 (GPII-APCP). However, * these results do not necessarily represent the policy of the * Department of Education, and you should not assume endorsement by the * Federal Government. * * You may obtain a copy of the License at * https://github.com/GPII/universal/blob/master/LICENSE.txt */ "use strict"; var fluid = require("infusion"); var fs = require("fs"); var gpii = fluid.registerNamespace("gpii"); fluid.registerNamespace("gpii.singleInstance"); /** * Declares this instance of GPII to be the only instance, unless there's already one running. * * The "pid/lock file" method is used, where a file containing the pid of the current process is created. If it * already exists, and the pid points to a running GPII instance, then the lock will fail. * * File IO is performed synchronously, ensuring the checks are made before GPII does anything else (eg, bind to a port). * * @todo More checks need to be made to ensure the pid is GPII, and not just another process. * * @param {String} [pidFile] - [optional] The path of the pid file. Default is "gpii.pid" in the settings directory. * @return {Boolean} True if this instance is the only GPII instance. */ gpii.singleInstance.registerInstance = function (pidFile) { if (!pidFile || typeof(pidFile) !== "string") { pidFile = gpii.singleInstance.getDefaultPidFile(); } var gpiiPid = gpii.singleInstance.checkInstance(pidFile); if (gpiiPid === null) { // Write the pid, but fail if the file exists. The stale one was deleted, but there might be another // GPII instance just starting. fs.writeFileSync(pidFile, process.pid, { flag: "wx" }); } var success = !gpiiPid || gpiiPid === process.pid; if (!success) { fluid.log(fluid.logLevel.WARN, "There's already an instance of GPII detected."); } return success; }; fluid.defaults("gpii.singleInstance.registerInstance", { gradeNames: "fluid.function" }); /** * Checks for another GPII instance. * * @param {String} [pidFile] - [optional] The path of the pid file. Default is "gpii.pid" in the settings directory. * @return {Number} Non-zero PID of the other GPII process (including this process), or null if this is the only one. */ gpii.singleInstance.checkInstance = function (pidFile) { if (!pidFile || typeof(pidFile) !== "string") { pidFile = gpii.singleInstance.getDefaultPidFile(); } var pid = null; try { // Get the old pid from the lock file var content = fs.readFileSync(pidFile, {encoding: "utf8"}); pid = parseInt(content); // A "0" PID will never be GPII (and process.kill will succeed on Linux). if (pid && pid !== process.pid) { try { // Check if it's still running. process.kill(pid, 0); // No error means the process is running. // TODO: Ensure the process really is GPII, and not just a re-use of the pid. } catch (e) { // Process isn't running (or permission denied, which suggests it's not GPII). pid = null; } } if (!pid) { // Remove the pid file, as it's stale/invalid. fs.unlinkSync(pidFile); } } catch (e) { // The file doesn't exist. } return pid || null; }; fluid.defaults("gpii.singleInstance.checkInstance", { gradeNames: "fluid.function" }); /** * Prevents the current GPII instance from being the only one, called when the GPII process is being shutdown. * * @param {String} [pidFile] - [optional] The path of the pid file. Default is "gpii.pid" in the settings directory. * @return {Boolean} True if this instance was de-registered, false if it wasn't registered. */ gpii.singleInstance.deregisterInstance = function (pidFile) { if (!pidFile || typeof(pidFile) !== "string") { pidFile = gpii.singleInstance.getDefaultPidFile(); } var success = false; var gpiiPid = gpii.singleInstance.checkInstance(pidFile); if (gpiiPid === process.pid) { success = true; fs.unlinkSync(pidFile); } return success; }; fluid.defaults("gpii.singleInstance.deregisterInstance", { gradeNames: "fluid.function" }); /* * Get the default pid file path, "<settings dir>/gpii.pid". */ gpii.singleInstance.getDefaultPidFile = function () { var settingsDirComponent = gpii.settingsDir(); var pidFile = settingsDirComponent.getGpiiSettingsDir() + "/gpii.pid"; settingsDirComponent.destroy(); return pidFile; };