index.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. /**
  2. * Things we will need
  3. */
  4. var async = require('async')
  5. var distros = require('./os.json')
  6. var fs = require('fs')
  7. var os = require('os')
  8. /**
  9. * Begin definition of globals.
  10. */
  11. var cachedDistro = null // Store result of getLinuxDistro() after first call
  12. /**
  13. * Module definition.
  14. */
  15. module.exports = function getOs (cb) {
  16. // Node builtin as first line of defense.
  17. var osName = os.platform()
  18. // Linux is a special case.
  19. if (osName === 'linux') return getLinuxDistro(cb)
  20. // Else, node's builtin is acceptable.
  21. return cb(null, { 'os': osName })
  22. }
  23. /**
  24. * Identify the actual distribution name on a linux box.
  25. */
  26. function getLinuxDistro (cb) {
  27. /**
  28. * First, we check to see if this function has been called before.
  29. * Since an OS doesn't change during runtime, its safe to cache
  30. * the result and return it for future calls.
  31. */
  32. if (cachedDistro) return cb(null, cachedDistro)
  33. /**
  34. * We are going to take our list of release files from os.json and
  35. * check to see which one exists. It is safe to assume that no more
  36. * than 1 file in the list from os.json will exist on a distribution.
  37. */
  38. getReleaseFile(Object.keys(distros), function (e, file) {
  39. if (e) return cb(e)
  40. /**
  41. * Multiple distributions may share the same release file.
  42. * We get our array of candidates and match the format of the release
  43. * files and match them to a potential distribution
  44. */
  45. var candidates = distros[file]
  46. var os = { 'os': 'linux', 'dist': candidates[0] }
  47. fs.readFile(file, 'utf-8', function (e, file) {
  48. if (e) return cb(e)
  49. /**
  50. * If we only know of one distribution that has this file, its
  51. * somewhat safe to assume that it is the distribution we are
  52. * running on.
  53. */
  54. if (candidates.length === 1) {
  55. return customLogic(os, getName(os.dist), file, function (e, os) {
  56. if (e) return cb(e)
  57. cachedDistro = os
  58. return cb(null, os)
  59. })
  60. }
  61. /**
  62. * First, set everything to lower case to keep inconsistent
  63. * specifications from mucking up our logic.
  64. */
  65. file = file.toLowerCase()
  66. /**
  67. * Now we need to check all of our potential candidates one by one.
  68. * If their name is in the release file, it is guarenteed to be the
  69. * distribution we are running on. If distributions share the same
  70. * release file, it is reasonably safe to assume they will have the
  71. * distribution name stored in their release file.
  72. */
  73. async.each(candidates, function (candidate, done) {
  74. var name = getName(candidate)
  75. if (file.indexOf(name) >= 0) {
  76. os.dist = candidate
  77. return customLogic(os, name, file, function (e, augmentedOs) {
  78. if (e) return done(e)
  79. os = augmentedOs
  80. return done()
  81. })
  82. } else {
  83. return done()
  84. }
  85. }, function (e) {
  86. if (e) return cb(e)
  87. cachedDistro = os
  88. return cb(null, os)
  89. })
  90. })
  91. })() // sneaky sneaky.
  92. }
  93. function getName (candidate) {
  94. /**
  95. * We only care about the first word. I.E. for Arch Linux it is safe
  96. * to simply search for "arch". Also note, we force lower case to
  97. * match file.toLowerCase() above.
  98. */
  99. var index = 0
  100. var name = 'linux'
  101. /**
  102. * Don't include 'linux' when searching since it is too aggressive when
  103. * matching (see #54)
  104. */
  105. while (name === 'linux') {
  106. name = candidate.split(' ')[index++].toLowerCase()
  107. }
  108. return name
  109. }
  110. /**
  111. * Loads a custom logic module to populate additional distribution information
  112. */
  113. function customLogic (os, name, file, cb) {
  114. var logic = './logic/' + name + '.js'
  115. try { require(logic)(os, file, cb) } catch (e) { cb(null, os) }
  116. }
  117. /**
  118. * getReleaseFile() checks an array of filenames and returns the first one it
  119. * finds on the filesystem.
  120. */
  121. function getReleaseFile (names, cb) {
  122. var index = 0 // Lets keep track of which file we are on.
  123. /**
  124. * checkExists() is a first class function that we are using for recursion.
  125. */
  126. return function checkExists () {
  127. /**
  128. * Lets get the file metadata off the current file.
  129. */
  130. fs.stat(names[index], function (e, stat) {
  131. /**
  132. * Now we check if either the file didn't exist, or it is something
  133. * other than a file for some very very bizzar reason.
  134. */
  135. if (e || !stat.isFile()) {
  136. index++ // If it is not a file, we will check the next one!
  137. if (names.length <= index) { // Unless we are out of files.
  138. return cb(new Error('No unique release file found!')) // Then error.
  139. }
  140. return checkExists() // Re-call this function to check the next file.
  141. }
  142. cb(null, names[index]) // If we found a file, return it!
  143. })
  144. }
  145. }