/** | |
* Things we will need | |
*/ | |
var async = require('async') | |
var distros = require('./os.json') | |
var fs = require('fs') | |
var os = require('os') | |
/** | |
* Begin definition of globals. | |
*/ | |
var cachedDistro = null // Store result of getLinuxDistro() after first call | |
/** | |
* Module definition. | |
*/ | |
module.exports = function getOs (cb) { | |
// Node builtin as first line of defense. | |
var osName = os.platform() | |
// Linux is a special case. | |
if (osName === 'linux') return getLinuxDistro(cb) | |
// Else, node's builtin is acceptable. | |
return cb(null, { os: osName }) | |
} | |
/** | |
* Identify the actual distribution name on a linux box. | |
*/ | |
function getLinuxDistro (cb) { | |
/** | |
* First, we check to see if this function has been called before. | |
* Since an OS doesn't change during runtime, its safe to cache | |
* the result and return it for future calls. | |
*/ | |
if (cachedDistro) return cb(null, cachedDistro) | |
/** | |
* We are going to take our list of release files from os.json and | |
* check to see which one exists. It is safe to assume that no more | |
* than 1 file in the list from os.json will exist on a distribution. | |
*/ | |
getReleaseFile(Object.keys(distros), function (e, file) { | |
if (e) return cb(e) | |
/** | |
* Multiple distributions may share the same release file. | |
* We get our array of candidates and match the format of the release | |
* files and match them to a potential distribution | |
*/ | |
var candidates = distros[file] | |
var os = { os: 'linux', dist: candidates[0] } | |
fs.readFile(file, 'utf-8', function (e, file) { | |
if (e) return cb(e) | |
/** | |
* If we only know of one distribution that has this file, its | |
* somewhat safe to assume that it is the distribution we are | |
* running on. | |
*/ | |
if (candidates.length === 1) { | |
return customLogic(os, getName(os.dist), file, function (e, os) { | |
if (e) return cb(e) | |
cachedDistro = os | |
return cb(null, os) | |
}) | |
} | |
/** | |
* First, set everything to lower case to keep inconsistent | |
* specifications from mucking up our logic. | |
*/ | |
file = file.toLowerCase() | |
/** | |
* Now we need to check all of our potential candidates one by one. | |
* If their name is in the release file, it is guarenteed to be the | |
* distribution we are running on. If distributions share the same | |
* release file, it is reasonably safe to assume they will have the | |
* distribution name stored in their release file. | |
*/ | |
async.each(candidates, function (candidate, done) { | |
var name = getName(candidate) | |
if (file.indexOf(name) >= 0) { | |
os.dist = candidate | |
return customLogic(os, name, file, function (e, augmentedOs) { | |
if (e) return done(e) | |
os = augmentedOs | |
return done() | |
}) | |
} else { | |
return done() | |
} | |
}, function (e) { | |
if (e) return cb(e) | |
cachedDistro = os | |
return cb(null, os) | |
}) | |
}) | |
})() // sneaky sneaky. | |
} | |
function getName (candidate) { | |
/** | |
* We only care about the first word. I.E. for Arch Linux it is safe | |
* to simply search for "arch". Also note, we force lower case to | |
* match file.toLowerCase() above. | |
*/ | |
var index = 0 | |
var name = 'linux' | |
/** | |
* Don't include 'linux' when searching since it is too aggressive when | |
* matching (see #54) | |
*/ | |
while (name === 'linux') { | |
name = candidate.split(' ')[index++].toLowerCase() | |
} | |
return name | |
} | |
/** | |
* Loads a custom logic module to populate additional distribution information | |
*/ | |
function customLogic (os, name, file, cb) { | |
try { require(`./logic/${name}.js`)(os, file, cb) } catch (e) { cb(null, os) } | |
} | |
/** | |
* getReleaseFile() checks an array of filenames and returns the first one it | |
* finds on the filesystem. | |
*/ | |
function getReleaseFile (names, cb) { | |
var index = 0 // Lets keep track of which file we are on. | |
/** | |
* checkExists() is a first class function that we are using for recursion. | |
*/ | |
return function checkExists () { | |
/** | |
* Lets get the file metadata off the current file. | |
*/ | |
fs.stat(names[index], function (e, stat) { | |
/** | |
* Now we check if either the file didn't exist, or it is something | |
* other than a file for some very very bizzar reason. | |
*/ | |
if (e || !stat.isFile()) { | |
index++ // If it is not a file, we will check the next one! | |
if (names.length <= index) { // Unless we are out of files. | |
return cb(new Error('No unique release file found!')) // Then error. | |
} | |
return checkExists() // Re-call this function to check the next file. | |
} | |
cb(null, names[index]) // If we found a file, return it! | |
}) | |
} | |
} | |