Skip to content

Commit 3ffab75

Browse files
committed
Detect libc from ldd file
1 parent 20d4da9 commit 3ffab75

File tree

2 files changed

+86
-46
lines changed

2 files changed

+86
-46
lines changed

lib/current-env.js

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
const process = require('node:process')
22
const nodeOs = require('node:os')
3-
4-
function isMusl (file) {
5-
return file.includes('libc.musl-') || file.includes('ld-musl-')
6-
}
3+
const fs = require('node:fs')
74

85
function os () {
96
return process.platform
@@ -13,28 +10,49 @@ function cpu () {
1310
return process.arch
1411
}
1512

13+
const LDD_PATH = '/usr/bin/ldd'
14+
function getFamilyFromFilesystem () {
15+
try {
16+
const content = fs.readFileSync(LDD_PATH, 'utf-8')
17+
if (content.includes('musl')) {
18+
return 'musl'
19+
}
20+
if (content.includes('GNU C Library')) {
21+
return 'glibc'
22+
}
23+
return null
24+
} catch {
25+
return undefined
26+
}
27+
}
28+
29+
function isMusl (file) {
30+
return file.includes('libc.musl-') || file.includes('ld-musl-')
31+
}
32+
1633
let family
1734
function libc (osName) {
1835
if (osName !== 'linux') {
1936
return undefined
2037
}
21-
if (family === undefined || process.report.forceCheck) {
22-
const originalExclude = process.report.excludeNetwork
23-
process.report.excludeNetwork = true
24-
const report = process.report.getReport()
25-
process.report.excludeNetwork = originalExclude
26-
if (report.header?.glibcVersionRuntime) {
27-
family = 'glibc'
28-
} else if (Array.isArray(report.sharedObjects) && report.sharedObjects.some(isMusl)) {
29-
family = 'musl'
30-
} else {
31-
family = null
38+
if (family === undefined) {
39+
family = getFamilyFromFilesystem()
40+
if (family === undefined) {
41+
const originalExclude = process.report.excludeNetwork
42+
process.report.excludeNetwork = true
43+
const report = process.report.getReport()
44+
process.report.excludeNetwork = originalExclude
45+
if (report.header?.glibcVersionRuntime) {
46+
family = 'glibc'
47+
} else if (Array.isArray(report.sharedObjects) && report.sharedObjects.some(isMusl)) {
48+
family = 'musl'
49+
} else {
50+
family = null
51+
}
3252
}
3353
}
3454
return family
3555
}
36-
// Cache getReport straight away because of node bug https://github.com/nodejs/node/issues/55576
37-
libc(os())
3856

3957
function devEngines (env = {}) {
4058
const osName = env.os || os()

test/check-platform.js

Lines changed: 51 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -95,49 +95,68 @@ t.test('wrong libc with overridden libc', async t =>
9595
}), { code: 'EBADPLATFORM' }))
9696

9797
t.test('libc', (t) => {
98-
let PLATFORM = ''
99-
100-
const _processPlatform = Object.getOwnPropertyDescriptor(process, 'platform')
101-
Object.defineProperty(process, 'platform', {
102-
enumerable: true,
103-
configurable: true,
104-
get: () => PLATFORM,
105-
})
106-
98+
let noCacheChckPtfm
99+
let PLATFORM = 'linux'
107100
let REPORT = {}
108-
const _processReport = process.report.getReport
109-
process.report.getReport = () => REPORT
110-
process.report.forceCheck = true
111-
112-
t.teardown(() => {
113-
Object.defineProperty(process, 'platform', _processPlatform)
114-
process.report.getReport = _processReport
115-
})
101+
let readFileSync
102+
103+
function withoutLibcCache () {
104+
readFileSync = () => {
105+
throw new Error('File not found')
106+
}
107+
noCacheChckPtfm = (...args) => {
108+
const original = t.mock('..', {
109+
'../lib/current-env': t.mock('../lib/current-env', {
110+
'node:fs': {
111+
readFileSync,
112+
},
113+
'node:process': {
114+
platform: PLATFORM,
115+
report: {
116+
getReport: () => REPORT,
117+
},
118+
},
119+
}),
120+
}).checkPlatform
121+
withoutLibcCache()
122+
return original(...args)
123+
}
124+
}
125+
126+
withoutLibcCache()
116127

117128
t.test('fails when not in linux', (t) => {
118129
PLATFORM = 'darwin'
119130

120-
t.throws(() => checkPlatform({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
131+
t.throws(() => noCacheChckPtfm({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
121132
'fails for glibc when not in linux')
122-
t.throws(() => checkPlatform({ libc: 'musl' }), { code: 'EBADPLATFORM' },
133+
t.throws(() => noCacheChckPtfm({ libc: 'musl' }), { code: 'EBADPLATFORM' },
123134
'fails for musl when not in linux')
124135
t.end()
125136
})
126137

127138
t.test('glibc', (t) => {
128139
PLATFORM = 'linux'
129140

141+
readFileSync = () => 'this ldd file contains GNU C Library'
142+
t.doesNotThrow(() => noCacheChckPtfm({ libc: 'glibc' }), 'allows glibc on glibc from ldd file')
143+
130144
REPORT = {}
131-
t.throws(() => checkPlatform({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
145+
t.throws(() => noCacheChckPtfm({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
132146
'fails when report is missing header property')
133147

134148
REPORT = { header: {} }
135-
t.throws(() => checkPlatform({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
149+
t.throws(() => noCacheChckPtfm({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
136150
'fails when header is missing glibcVersionRuntime property')
137151

138152
REPORT = { header: { glibcVersionRuntime: '1' } }
139-
t.doesNotThrow(() => checkPlatform({ libc: 'glibc' }), 'allows glibc on glibc')
140-
t.throws(() => checkPlatform({ libc: 'musl' }), { code: 'EBADPLATFORM' },
153+
t.doesNotThrow(() => noCacheChckPtfm({ libc: 'glibc' }), 'allows glibc on glibc')
154+
155+
readFileSync = () => 'this ldd file is unsupported'
156+
t.throws(() => noCacheChckPtfm({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
157+
'fails when ldd file exists but is not something known')
158+
159+
t.throws(() => noCacheChckPtfm({ libc: 'musl' }), { code: 'EBADPLATFORM' },
141160
'does not allow musl on glibc')
142161

143162
t.end()
@@ -146,25 +165,28 @@ t.test('libc', (t) => {
146165
t.test('musl', (t) => {
147166
PLATFORM = 'linux'
148167

168+
readFileSync = () => 'this ldd file contains musl'
169+
t.doesNotThrow(() => noCacheChckPtfm({ libc: 'musl' }), 'allows musl on musl from ldd file')
170+
149171
REPORT = {}
150-
t.throws(() => checkPlatform({ libc: 'musl' }), { code: 'EBADPLATFORM' },
172+
t.throws(() => noCacheChckPtfm({ libc: 'musl' }), { code: 'EBADPLATFORM' },
151173
'fails when report is missing sharedObjects property')
152174

153175
REPORT = { sharedObjects: {} }
154-
t.throws(() => checkPlatform({ libc: 'musl' }), { code: 'EBADPLATFORM' },
176+
t.throws(() => noCacheChckPtfm({ libc: 'musl' }), { code: 'EBADPLATFORM' },
155177
'fails when sharedObjects property is not an array')
156178

157179
REPORT = { sharedObjects: [] }
158-
t.throws(() => checkPlatform({ libc: 'musl' }), { code: 'EBADPLATFORM' },
180+
t.throws(() => noCacheChckPtfm({ libc: 'musl' }), { code: 'EBADPLATFORM' },
159181
'fails when sharedObjects does not contain musl')
160182

161183
REPORT = { sharedObjects: ['ld-musl-foo'] }
162-
t.doesNotThrow(() => checkPlatform({ libc: 'musl' }), 'allows musl on musl as ld-musl-')
184+
t.doesNotThrow(() => noCacheChckPtfm({ libc: 'musl' }), 'allows musl on musl as ld-musl-')
163185

164186
REPORT = { sharedObjects: ['libc.musl-'] }
165-
t.doesNotThrow(() => checkPlatform({ libc: 'musl' }), 'allows musl on musl as libc.musl-')
187+
t.doesNotThrow(() => noCacheChckPtfm({ libc: 'musl' }), 'allows musl on musl as libc.musl-')
166188

167-
t.throws(() => checkPlatform({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
189+
t.throws(() => noCacheChckPtfm({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
168190
'does not allow glibc on musl')
169191

170192
t.end()

0 commit comments

Comments
 (0)