detect-libc.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. 'use strict';
  2. const childProcess = require('child_process');
  3. const { isLinux, getReport } = require('./process');
  4. const command = 'getconf GNU_LIBC_VERSION 2>&1 || true; ldd --version 2>&1 || true';
  5. let commandOut = '';
  6. const safeCommand = () => {
  7. if (!commandOut) {
  8. return new Promise((resolve) => {
  9. childProcess.exec(command, (err, out) => {
  10. commandOut = err ? ' ' : out;
  11. resolve(commandOut);
  12. });
  13. });
  14. }
  15. return commandOut;
  16. };
  17. const safeCommandSync = () => {
  18. if (!commandOut) {
  19. try {
  20. commandOut = childProcess.execSync(command, { encoding: 'utf8' });
  21. } catch (_err) {
  22. commandOut = ' ';
  23. }
  24. }
  25. return commandOut;
  26. };
  27. /**
  28. * A String constant containing the value `glibc`.
  29. * @type {string}
  30. * @public
  31. */
  32. const GLIBC = 'glibc';
  33. /**
  34. * A String constant containing the value `musl`.
  35. * @type {string}
  36. * @public
  37. */
  38. const MUSL = 'musl';
  39. const isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-');
  40. const familyFromReport = () => {
  41. const report = getReport();
  42. if (report.header && report.header.glibcVersionRuntime) {
  43. return GLIBC;
  44. }
  45. if (Array.isArray(report.sharedObjects)) {
  46. if (report.sharedObjects.some(isFileMusl)) {
  47. return MUSL;
  48. }
  49. }
  50. return null;
  51. };
  52. const familyFromCommand = (out) => {
  53. const [getconf, ldd1] = out.split(/[\r\n]+/);
  54. if (getconf && getconf.includes(GLIBC)) {
  55. return GLIBC;
  56. }
  57. if (ldd1 && ldd1.includes(MUSL)) {
  58. return MUSL;
  59. }
  60. return null;
  61. };
  62. /**
  63. * Resolves with the libc family when it can be determined, `null` otherwise.
  64. * @returns {Promise<?string>}
  65. */
  66. const family = async () => {
  67. let family = null;
  68. if (isLinux()) {
  69. family = familyFromReport();
  70. if (!family) {
  71. const out = await safeCommand();
  72. family = familyFromCommand(out);
  73. }
  74. }
  75. return family;
  76. };
  77. /**
  78. * Returns the libc family when it can be determined, `null` otherwise.
  79. * @returns {?string}
  80. */
  81. const familySync = () => {
  82. let family = null;
  83. if (isLinux()) {
  84. family = familyFromReport();
  85. if (!family) {
  86. const out = safeCommandSync();
  87. family = familyFromCommand(out);
  88. }
  89. }
  90. return family;
  91. };
  92. /**
  93. * Resolves `true` only when the platform is Linux and the libc family is not `glibc`.
  94. * @returns {Promise<boolean>}
  95. */
  96. const isNonGlibcLinux = async () => isLinux() && await family() !== GLIBC;
  97. /**
  98. * Returns `true` only when the platform is Linux and the libc family is not `glibc`.
  99. * @returns {boolean}
  100. */
  101. const isNonGlibcLinuxSync = () => isLinux() && familySync() !== GLIBC;
  102. const versionFromReport = () => {
  103. const report = getReport();
  104. if (report.header && report.header.glibcVersionRuntime) {
  105. return report.header.glibcVersionRuntime;
  106. }
  107. return null;
  108. };
  109. const versionSuffix = (s) => s.trim().split(/\s+/)[1];
  110. const versionFromCommand = (out) => {
  111. const [getconf, ldd1, ldd2] = out.split(/[\r\n]+/);
  112. if (getconf && getconf.includes(GLIBC)) {
  113. return versionSuffix(getconf);
  114. }
  115. if (ldd1 && ldd2 && ldd1.includes(MUSL)) {
  116. return versionSuffix(ldd2);
  117. }
  118. return null;
  119. };
  120. /**
  121. * Resolves with the libc version when it can be determined, `null` otherwise.
  122. * @returns {Promise<?string>}
  123. */
  124. const version = async () => {
  125. let version = null;
  126. if (isLinux()) {
  127. version = versionFromReport();
  128. if (!version) {
  129. const out = await safeCommand();
  130. version = versionFromCommand(out);
  131. }
  132. }
  133. return version;
  134. };
  135. /**
  136. * Returns the libc version when it can be determined, `null` otherwise.
  137. * @returns {?string}
  138. */
  139. const versionSync = () => {
  140. let version = null;
  141. if (isLinux()) {
  142. version = versionFromReport();
  143. if (!version) {
  144. const out = safeCommandSync();
  145. version = versionFromCommand(out);
  146. }
  147. }
  148. return version;
  149. };
  150. module.exports = {
  151. GLIBC,
  152. MUSL,
  153. family,
  154. familySync,
  155. isNonGlibcLinux,
  156. isNonGlibcLinuxSync,
  157. version,
  158. versionSync
  159. };