/** * 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! }) } }