Skip to content

Commit c9da451

Browse files
committed
Detect libc from ldd file and cache result of libc
1 parent 4751cb4 commit c9da451

File tree

2 files changed

+84
-38
lines changed

2 files changed

+84
-38
lines changed

lib/current-env.js

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const process = require('node:process')
22
const nodeOs = require('node:os')
3+
const fs = require('node:fs')
34

45
function isMusl (file) {
56
return file.includes('libc.musl-') || file.includes('ld-musl-')
@@ -13,20 +14,42 @@ function cpu () {
1314
return process.arch
1415
}
1516

17+
const LDD_PATH = '/usr/bin/ldd'
18+
function getFamilyFromFilesystem () {
19+
try {
20+
const content = fs.readFileSync(LDD_PATH, 'utf-8')
21+
if (content.includes('musl')) {
22+
return 'musl'
23+
}
24+
if (content.includes('GNU C Library')) {
25+
return 'glibc'
26+
}
27+
return null
28+
} catch {
29+
return undefined
30+
}
31+
}
32+
33+
let family
1634
function libc (osName) {
17-
// this is to make it faster on non linux machines
1835
if (osName !== 'linux') {
1936
return undefined
2037
}
21-
let family
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'
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+
}
52+
}
3053
}
3154
return family
3255
}

test/check-platform.js

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

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

115127
t.test('fails when not in linux', (t) => {
116128
PLATFORM = 'darwin'
117129

118-
t.throws(() => checkPlatform({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
130+
t.throws(() => noCacheChckPtfm({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
119131
'fails for glibc when not in linux')
120-
t.throws(() => checkPlatform({ libc: 'musl' }), { code: 'EBADPLATFORM' },
132+
t.throws(() => noCacheChckPtfm({ libc: 'musl' }), { code: 'EBADPLATFORM' },
121133
'fails for musl when not in linux')
122134
t.end()
123135
})
124136

125137
t.test('glibc', (t) => {
126138
PLATFORM = 'linux'
127139

140+
readFileSync = () => 'this ldd file contains GNU C Library'
141+
t.doesNotThrow(() => noCacheChckPtfm({ libc: 'glibc' }), 'allows glibc on glibc from ldd file')
142+
128143
REPORT = {}
129-
t.throws(() => checkPlatform({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
144+
t.throws(() => noCacheChckPtfm({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
130145
'fails when report is missing header property')
131146

132147
REPORT = { header: {} }
133-
t.throws(() => checkPlatform({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
148+
t.throws(() => noCacheChckPtfm({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
134149
'fails when header is missing glibcVersionRuntime property')
135150

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

141161
t.end()
@@ -144,25 +164,28 @@ t.test('libc', (t) => {
144164
t.test('musl', (t) => {
145165
PLATFORM = 'linux'
146166

167+
readFileSync = () => 'this ldd file contains musl'
168+
t.doesNotThrow(() => noCacheChckPtfm({ libc: 'musl' }), 'allows musl on musl from ldd file')
169+
147170
REPORT = {}
148-
t.throws(() => checkPlatform({ libc: 'musl' }), { code: 'EBADPLATFORM' },
171+
t.throws(() => noCacheChckPtfm({ libc: 'musl' }), { code: 'EBADPLATFORM' },
149172
'fails when report is missing sharedObjects property')
150173

151174
REPORT = { sharedObjects: {} }
152-
t.throws(() => checkPlatform({ libc: 'musl' }), { code: 'EBADPLATFORM' },
175+
t.throws(() => noCacheChckPtfm({ libc: 'musl' }), { code: 'EBADPLATFORM' },
153176
'fails when sharedObjects property is not an array')
154177

155178
REPORT = { sharedObjects: [] }
156-
t.throws(() => checkPlatform({ libc: 'musl' }), { code: 'EBADPLATFORM' },
179+
t.throws(() => noCacheChckPtfm({ libc: 'musl' }), { code: 'EBADPLATFORM' },
157180
'fails when sharedObjects does not contain musl')
158181

159182
REPORT = { sharedObjects: ['ld-musl-foo'] }
160-
t.doesNotThrow(() => checkPlatform({ libc: 'musl' }), 'allows musl on musl as ld-musl-')
183+
t.doesNotThrow(() => noCacheChckPtfm({ libc: 'musl' }), 'allows musl on musl as ld-musl-')
161184

162185
REPORT = { sharedObjects: ['libc.musl-'] }
163-
t.doesNotThrow(() => checkPlatform({ libc: 'musl' }), 'allows musl on musl as libc.musl-')
186+
t.doesNotThrow(() => noCacheChckPtfm({ libc: 'musl' }), 'allows musl on musl as libc.musl-')
164187

165-
t.throws(() => checkPlatform({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
188+
t.throws(() => noCacheChckPtfm({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
166189
'does not allow glibc on musl')
167190

168191
t.end()

0 commit comments

Comments
 (0)