All files / universal.klown/gpii/node_modules/singleInstance/src singleInstance.js

97.78% Statements 44/45
96.43% Branches 27/28
100% Functions 4/4
97.78% Lines 44/45

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143                                        1x 1x   1x 1x                             1x 177x 171x     177x 177x     161x     177x 177x 1x   177x     1x                     1x 362x       362x   362x   362x 185x   185x 6x   6x         3x       185x   6x           362x     1x                   1x 177x 171x     177x 177x 177x 159x 159x     177x     1x             1x 342x 342x 342x 342x    
/*
 * 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 pidFile {String} [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 pidFile {String} [Optional] The path of the pid file. Default is "gpii.pid" in the settings directory.
 * @returns {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) {
    Iif (!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 pidFile
 * @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;
};