├── .github └── workflows │ ├── integration.yml │ └── unit.yml ├── .gitignore ├── LICENSE ├── README.md ├── benchmark ├── call-familySync.js ├── call-isNonGlibcLinuxSync.js ├── call-versionSync.js └── detect-libc.js ├── index.d.ts ├── lib ├── detect-libc.js ├── filesystem.js └── process.js ├── package.json └── test ├── integration.js ├── test-fixture.txt └── unit.js /.github/workflows/integration.yml: -------------------------------------------------------------------------------- 1 | name: Integration tests 2 | on: 3 | - push 4 | - pull_request 5 | jobs: 6 | integration: 7 | name: ${{ matrix.name }} 8 | container: ${{ matrix.container }} 9 | runs-on: ubuntu-latest 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | include: 14 | - name: Alpine 3.11 / Node.js 8 15 | container: node:8-alpine3.11 16 | node-version: 8 17 | expected-family: musl 18 | expected-version: 1.1.24 19 | - name: Alpine 3.11 / Node.js 16 20 | container: node:16-alpine3.11 21 | node-version: 16 22 | expected-family: musl 23 | expected-version: 1.1.24 24 | - name: Alpine 3.12 / Node.js 12 25 | container: node:12-alpine3.12 26 | node-version: 12 27 | expected-family: musl 28 | expected-version: 1.1.24 29 | - name: Alpine 3.15 / Node.js 16 30 | container: node:16-alpine3.15 31 | node-version: 16 32 | expected-family: musl 33 | expected-version: 1.2.2 34 | - name: Alpine 3.18 / Node.js 20 35 | container: node:20-alpine3.18 36 | node-version: 20 37 | expected-family: musl 38 | expected-version: 1.2.4 39 | - name: Amazon 2 / Node.js 16 40 | container: amazonlinux:2 41 | node-version: 16 42 | expected-family: glibc 43 | expected-version: 2.26 44 | - name: Amazon 2023 / Node.js 20 45 | container: amazonlinux:2023 46 | node-version: 20 47 | expected-family: glibc 48 | expected-version: 2.34 49 | - name: Debian 9 / Node.js 8 50 | container: node:8-stretch-slim 51 | node-version: 8 52 | expected-family: glibc 53 | expected-version: 2.24 54 | - name: Debian 9 / Node.js 16 55 | container: node:16-stretch-slim 56 | node-version: 16 57 | expected-family: glibc 58 | expected-version: 2.24 59 | - name: Debian 10 / Node.js 8 60 | container: node:8-buster-slim 61 | node-version: 8 62 | expected-family: glibc 63 | expected-version: 2.28 64 | - name: Debian 11 / Node.js 16 65 | container: node:16-bullseye-slim 66 | node-version: 16 67 | expected-family: glibc 68 | expected-version: 2.31 69 | - name: Debian 12 / Node.js 20 70 | container: node:20-bookworm-slim 71 | node-version: 20 72 | expected-family: glibc 73 | expected-version: 2.36 74 | - name: Fedora 33 / Node.js 10 75 | container: fedora:33 76 | node-version: 10 77 | expected-family: glibc 78 | expected-version: 2.32 79 | - name: Fedora 33 / Node.js 16 80 | container: fedora:33 81 | node-version: 16 82 | expected-family: glibc 83 | expected-version: 2.32 84 | - name: Fedora 35 / Node.js 12 85 | container: fedora:35 86 | node-version: 12 87 | expected-family: glibc 88 | expected-version: 2.34 89 | - name: Fedora 35 / Node.js 16 90 | container: fedora:35 91 | node-version: 16 92 | expected-family: glibc 93 | expected-version: 2.34 94 | - name: Fedora 39 / Node.js 18 95 | container: fedora:39 96 | node-version: 18 97 | expected-family: glibc 98 | expected-version: 2.38 99 | - name: Fedora 41 / Node.js 22 100 | container: fedora:41 101 | node-version: 22 102 | expected-family: glibc 103 | expected-version: "2.40" 104 | - name: OpenSUSE 15.1 / Node.js 10 105 | container: opensuse/leap:15.1 106 | node-version: 10 107 | expected-family: glibc 108 | expected-version: 2.26 109 | - name: OpenSUSE 15.3 / Node.js 14 110 | container: opensuse/leap:15.3 111 | node-version: 14 112 | expected-family: glibc 113 | expected-version: 2.31 114 | - name: Ubuntu 14.04 / Node.js 8 115 | container: ubuntu:14.04 116 | node-version: 8 117 | expected-family: glibc 118 | expected-version: 2.19 119 | - name: Ubuntu 18.04 / Node.js 8 120 | container: ubuntu:18.04 121 | node-version: 8 122 | expected-family: glibc 123 | expected-version: 2.27 124 | - name: Ubuntu 22.04 / Node.js 18 125 | container: ubuntu:22.04 126 | node-version: 18 127 | expected-family: glibc 128 | expected-version: 2.35 129 | - name: Ubuntu 24.04 / Node.js 22 130 | container: ubuntu:24.04 131 | node-version: 22 132 | expected-family: glibc 133 | expected-version: 2.39 134 | - name: Void glibc / Node.js 20 135 | container: ghcr.io/void-linux/void-linux:latest-thin-x86_64 136 | node-version: 20 137 | expected-family: glibc 138 | expected-version: 2.41 139 | - name: Void musl / Node.js 20 140 | container: ghcr.io/void-linux/void-linux:latest-thin-x86_64-musl 141 | node-version: 20 142 | expected-family: musl 143 | expected-version: 1.1.24 144 | steps: 145 | - name: Install Node.js (RHEL) 146 | if: contains(matrix.container, 'amazonlinux') || contains(matrix.container, 'fedora:33') || contains(matrix.container, 'fedora:35') 147 | run: | 148 | yum module disable -y nodejs || true 149 | curl -sL https://rpm.nodesource.com/setup_${{ matrix.node-version }}.x | bash - 150 | yum install -y git nodejs 151 | - name: Install Node.js (Fedora 39) 152 | if: contains(matrix.container, 'fedora:39') || contains(matrix.container, 'fedora:41') 153 | run: dnf install -y git nodejs 154 | - name: Install Node.js (OpenSUSE) 155 | if: contains(matrix.container, 'opensuse') 156 | run: | 157 | zypper refresh 158 | zypper --non-interactive install git nodejs${{ matrix.node-version }} 159 | - name: Install Node.js (Ubuntu) 160 | if: contains(matrix.container, 'ubuntu') 161 | run: | 162 | apt-get update 163 | apt-get install -y curl git gnupg 164 | curl -sL https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - 165 | curl -sL https://deb.nodesource.com/setup_${{ matrix.node-version }}.x | bash - 166 | apt-get install -y nodejs 167 | - name: Install Node.js (Void) 168 | if: contains(matrix.container, 'void') 169 | run: | 170 | xbps-install -Suy xbps 171 | xbps-install -Sy git nodejs 172 | - name: Checkout 173 | uses: actions/checkout@v1 174 | - name: Verify expectations 175 | run: | 176 | export EXPECTED="${{ matrix.expected-family }} ${{ matrix.expected-version }}" 177 | export ACTUAL="$(node ./test/integration.js)" 178 | echo "Expected: $EXPECTED" 179 | echo "Actual: $ACTUAL" 180 | test "$EXPECTED" = "$ACTUAL" 181 | -------------------------------------------------------------------------------- /.github/workflows/unit.yml: -------------------------------------------------------------------------------- 1 | name: Unit tests 2 | on: 3 | - push 4 | - pull_request 5 | jobs: 6 | unit: 7 | name: Node.js ${{ matrix.node-version }} 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | node-version: 12 | - 10 13 | - 12 14 | - 14 15 | - 16 16 | - 18 17 | - 20 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v4 21 | - name: Install Node.js 22 | uses: actions/setup-node@v4 23 | with: 24 | node-version: ${{ matrix.node-version }} 25 | - name: Install dependencies 26 | run: npm install 27 | - name: Run unit tests 28 | run: npm test 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .nyc_output/ 2 | coverage/ 3 | node_modules/ 4 | package-lock.json 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # detect-libc 2 | 3 | Node.js module to detect details of the C standard library (libc) 4 | implementation provided by a given Linux system. 5 | 6 | Currently supports detection of GNU glibc and MUSL libc. 7 | 8 | Provides asychronous and synchronous functions for the 9 | family (e.g. `glibc`, `musl`) and version (e.g. `1.23`, `1.2.3`). 10 | 11 | The version numbers of libc implementations 12 | are not guaranteed to be semver-compliant. 13 | 14 | For previous v1.x releases, please see the 15 | [v1](https://github.com/lovell/detect-libc/tree/v1) branch. 16 | 17 | ## Install 18 | 19 | ```sh 20 | npm install detect-libc 21 | ``` 22 | 23 | ## API 24 | 25 | ### GLIBC 26 | 27 | ```ts 28 | const GLIBC: string = 'glibc'; 29 | ``` 30 | 31 | A String constant containing the value `glibc`. 32 | 33 | ### MUSL 34 | 35 | ```ts 36 | const MUSL: string = 'musl'; 37 | ``` 38 | 39 | A String constant containing the value `musl`. 40 | 41 | ### family 42 | 43 | ```ts 44 | function family(): Promise; 45 | ``` 46 | 47 | Resolves asychronously with: 48 | 49 | * `glibc` or `musl` when the libc family can be determined 50 | * `null` when the libc family cannot be determined 51 | * `null` when run on a non-Linux platform 52 | 53 | ```js 54 | const { family, GLIBC, MUSL } = require('detect-libc'); 55 | 56 | switch (await family()) { 57 | case GLIBC: ... 58 | case MUSL: ... 59 | case null: ... 60 | } 61 | ``` 62 | 63 | ### familySync 64 | 65 | ```ts 66 | function familySync(): string | null; 67 | ``` 68 | 69 | Synchronous version of `family()`. 70 | 71 | ```js 72 | const { familySync, GLIBC, MUSL } = require('detect-libc'); 73 | 74 | switch (familySync()) { 75 | case GLIBC: ... 76 | case MUSL: ... 77 | case null: ... 78 | } 79 | ``` 80 | 81 | ### version 82 | 83 | ```ts 84 | function version(): Promise; 85 | ``` 86 | 87 | Resolves asychronously with: 88 | 89 | * The version when it can be determined 90 | * `null` when the libc family cannot be determined 91 | * `null` when run on a non-Linux platform 92 | 93 | ```js 94 | const { version } = require('detect-libc'); 95 | 96 | const v = await version(); 97 | if (v) { 98 | const [major, minor, patch] = v.split('.'); 99 | } 100 | ``` 101 | 102 | ### versionSync 103 | 104 | ```ts 105 | function versionSync(): string | null; 106 | ``` 107 | 108 | Synchronous version of `version()`. 109 | 110 | ```js 111 | const { versionSync } = require('detect-libc'); 112 | 113 | const v = versionSync(); 114 | if (v) { 115 | const [major, minor, patch] = v.split('.'); 116 | } 117 | ``` 118 | 119 | ### isNonGlibcLinux 120 | 121 | ```ts 122 | function isNonGlibcLinux(): Promise; 123 | ``` 124 | 125 | Resolves asychronously with: 126 | 127 | * `false` when the libc family is `glibc` 128 | * `true` when the libc family is not `glibc` 129 | * `false` when run on a non-Linux platform 130 | 131 | ```js 132 | const { isNonGlibcLinux } = require('detect-libc'); 133 | 134 | if (await isNonGlibcLinux()) { ... } 135 | ``` 136 | 137 | ### isNonGlibcLinuxSync 138 | 139 | ```ts 140 | function isNonGlibcLinuxSync(): boolean; 141 | ``` 142 | 143 | Synchronous version of `isNonGlibcLinux()`. 144 | 145 | ```js 146 | const { isNonGlibcLinuxSync } = require('detect-libc'); 147 | 148 | if (isNonGlibcLinuxSync()) { ... } 149 | ``` 150 | 151 | ## Licensing 152 | 153 | Copyright 2017 Lovell Fuller and others. 154 | 155 | Licensed under the Apache License, Version 2.0 (the "License"); 156 | you may not use this file except in compliance with the License. 157 | You may obtain a copy of the License at [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0.html) 158 | 159 | Unless required by applicable law or agreed to in writing, software 160 | distributed under the License is distributed on an "AS IS" BASIS, 161 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 162 | See the License for the specific language governing permissions and 163 | limitations under the License. 164 | -------------------------------------------------------------------------------- /benchmark/call-familySync.js: -------------------------------------------------------------------------------- 1 | const performance = require('perf_hooks').performance; 2 | const libc = require('..'); 3 | 4 | const now = performance.now(); 5 | libc.familySync(); 6 | 7 | console.log(`[family] Time Spent ${performance.now() - now}ms`); 8 | -------------------------------------------------------------------------------- /benchmark/call-isNonGlibcLinuxSync.js: -------------------------------------------------------------------------------- 1 | const performance = require('perf_hooks').performance; 2 | const libc = require('..'); 3 | 4 | const now = performance.now(); 5 | libc.isNonGlibcLinuxSync(); 6 | 7 | console.log(`[isNonGlibcLinux] Time Spent ${performance.now() - now}ms`); 8 | -------------------------------------------------------------------------------- /benchmark/call-versionSync.js: -------------------------------------------------------------------------------- 1 | const performance = require('perf_hooks').performance; 2 | const libc = require('..'); 3 | 4 | const now = performance.now(); 5 | libc.versionSync(); 6 | 7 | console.log(`[versionSync] Time Spent ${performance.now() - now}ms`); 8 | -------------------------------------------------------------------------------- /benchmark/detect-libc.js: -------------------------------------------------------------------------------- 1 | const Benchmark = require('benchmark'); 2 | const suite = new Benchmark.Suite(); 3 | const libc = require('../'); 4 | 5 | suite.add('family', async function () { 6 | await libc.family(); 7 | }); 8 | 9 | suite.add('familySync', function () { 10 | libc.familySync(); 11 | }); 12 | 13 | suite.add('version', async function () { 14 | await libc.version(); 15 | }); 16 | 17 | suite.add('versionSync', function () { 18 | libc.versionSync(); 19 | }); 20 | 21 | suite.add('isNonGlibcLinux', async function () { 22 | await libc.isNonGlibcLinux(); 23 | }); 24 | 25 | suite.add('isNonGlibcLinuxSync', function () { 26 | libc.isNonGlibcLinuxSync(); 27 | }); 28 | 29 | suite 30 | // add listeners 31 | .on('cycle', function (event) { 32 | console.log(String(event.target)); 33 | }) 34 | .on('complete', function () { 35 | console.log('Fastest operation is ' + this.filter('fastest').map('name')); 36 | }) 37 | .run({ 38 | async: true 39 | }); 40 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Lovell Fuller and others. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export const GLIBC: 'glibc'; 5 | export const MUSL: 'musl'; 6 | 7 | export function family(): Promise; 8 | export function familySync(): string | null; 9 | 10 | export function isNonGlibcLinux(): Promise; 11 | export function isNonGlibcLinuxSync(): boolean; 12 | 13 | export function version(): Promise; 14 | export function versionSync(): string | null; 15 | -------------------------------------------------------------------------------- /lib/detect-libc.js: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Lovell Fuller and others. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 'use strict'; 5 | 6 | const childProcess = require('child_process'); 7 | const { isLinux, getReport } = require('./process'); 8 | const { LDD_PATH, readFile, readFileSync } = require('./filesystem'); 9 | 10 | let cachedFamilyFilesystem; 11 | let cachedVersionFilesystem; 12 | 13 | const command = 'getconf GNU_LIBC_VERSION 2>&1 || true; ldd --version 2>&1 || true'; 14 | let commandOut = ''; 15 | 16 | const safeCommand = () => { 17 | if (!commandOut) { 18 | return new Promise((resolve) => { 19 | childProcess.exec(command, (err, out) => { 20 | commandOut = err ? ' ' : out; 21 | resolve(commandOut); 22 | }); 23 | }); 24 | } 25 | return commandOut; 26 | }; 27 | 28 | const safeCommandSync = () => { 29 | if (!commandOut) { 30 | try { 31 | commandOut = childProcess.execSync(command, { encoding: 'utf8' }); 32 | } catch (_err) { 33 | commandOut = ' '; 34 | } 35 | } 36 | return commandOut; 37 | }; 38 | 39 | /** 40 | * A String constant containing the value `glibc`. 41 | * @type {string} 42 | * @public 43 | */ 44 | const GLIBC = 'glibc'; 45 | 46 | /** 47 | * A Regexp constant to get the GLIBC Version. 48 | * @type {string} 49 | */ 50 | const RE_GLIBC_VERSION = /LIBC[a-z0-9 \-).]*?(\d+\.\d+)/i; 51 | 52 | /** 53 | * A String constant containing the value `musl`. 54 | * @type {string} 55 | * @public 56 | */ 57 | const MUSL = 'musl'; 58 | 59 | const isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-'); 60 | 61 | const familyFromReport = () => { 62 | const report = getReport(); 63 | if (report.header && report.header.glibcVersionRuntime) { 64 | return GLIBC; 65 | } 66 | if (Array.isArray(report.sharedObjects)) { 67 | if (report.sharedObjects.some(isFileMusl)) { 68 | return MUSL; 69 | } 70 | } 71 | return null; 72 | }; 73 | 74 | const familyFromCommand = (out) => { 75 | const [getconf, ldd1] = out.split(/[\r\n]+/); 76 | if (getconf && getconf.includes(GLIBC)) { 77 | return GLIBC; 78 | } 79 | if (ldd1 && ldd1.includes(MUSL)) { 80 | return MUSL; 81 | } 82 | return null; 83 | }; 84 | 85 | const getFamilyFromLddContent = (content) => { 86 | if (content.includes('musl')) { 87 | return MUSL; 88 | } 89 | if (content.includes('GNU C Library')) { 90 | return GLIBC; 91 | } 92 | return null; 93 | }; 94 | 95 | const familyFromFilesystem = async () => { 96 | if (cachedFamilyFilesystem !== undefined) { 97 | return cachedFamilyFilesystem; 98 | } 99 | cachedFamilyFilesystem = null; 100 | try { 101 | const lddContent = await readFile(LDD_PATH); 102 | cachedFamilyFilesystem = getFamilyFromLddContent(lddContent); 103 | } catch (e) {} 104 | return cachedFamilyFilesystem; 105 | }; 106 | 107 | const familyFromFilesystemSync = () => { 108 | if (cachedFamilyFilesystem !== undefined) { 109 | return cachedFamilyFilesystem; 110 | } 111 | cachedFamilyFilesystem = null; 112 | try { 113 | const lddContent = readFileSync(LDD_PATH); 114 | cachedFamilyFilesystem = getFamilyFromLddContent(lddContent); 115 | } catch (e) {} 116 | return cachedFamilyFilesystem; 117 | }; 118 | 119 | /** 120 | * Resolves with the libc family when it can be determined, `null` otherwise. 121 | * @returns {Promise} 122 | */ 123 | const family = async () => { 124 | let family = null; 125 | if (isLinux()) { 126 | family = await familyFromFilesystem(); 127 | if (!family) { 128 | family = familyFromReport(); 129 | } 130 | if (!family) { 131 | const out = await safeCommand(); 132 | family = familyFromCommand(out); 133 | } 134 | } 135 | return family; 136 | }; 137 | 138 | /** 139 | * Returns the libc family when it can be determined, `null` otherwise. 140 | * @returns {?string} 141 | */ 142 | const familySync = () => { 143 | let family = null; 144 | if (isLinux()) { 145 | family = familyFromFilesystemSync(); 146 | if (!family) { 147 | family = familyFromReport(); 148 | } 149 | if (!family) { 150 | const out = safeCommandSync(); 151 | family = familyFromCommand(out); 152 | } 153 | } 154 | return family; 155 | }; 156 | 157 | /** 158 | * Resolves `true` only when the platform is Linux and the libc family is not `glibc`. 159 | * @returns {Promise} 160 | */ 161 | const isNonGlibcLinux = async () => isLinux() && await family() !== GLIBC; 162 | 163 | /** 164 | * Returns `true` only when the platform is Linux and the libc family is not `glibc`. 165 | * @returns {boolean} 166 | */ 167 | const isNonGlibcLinuxSync = () => isLinux() && familySync() !== GLIBC; 168 | 169 | const versionFromFilesystem = async () => { 170 | if (cachedVersionFilesystem !== undefined) { 171 | return cachedVersionFilesystem; 172 | } 173 | cachedVersionFilesystem = null; 174 | try { 175 | const lddContent = await readFile(LDD_PATH); 176 | const versionMatch = lddContent.match(RE_GLIBC_VERSION); 177 | if (versionMatch) { 178 | cachedVersionFilesystem = versionMatch[1]; 179 | } 180 | } catch (e) {} 181 | return cachedVersionFilesystem; 182 | }; 183 | 184 | const versionFromFilesystemSync = () => { 185 | if (cachedVersionFilesystem !== undefined) { 186 | return cachedVersionFilesystem; 187 | } 188 | cachedVersionFilesystem = null; 189 | try { 190 | const lddContent = readFileSync(LDD_PATH); 191 | const versionMatch = lddContent.match(RE_GLIBC_VERSION); 192 | if (versionMatch) { 193 | cachedVersionFilesystem = versionMatch[1]; 194 | } 195 | } catch (e) {} 196 | return cachedVersionFilesystem; 197 | }; 198 | 199 | const versionFromReport = () => { 200 | const report = getReport(); 201 | if (report.header && report.header.glibcVersionRuntime) { 202 | return report.header.glibcVersionRuntime; 203 | } 204 | return null; 205 | }; 206 | 207 | const versionSuffix = (s) => s.trim().split(/\s+/)[1]; 208 | 209 | const versionFromCommand = (out) => { 210 | const [getconf, ldd1, ldd2] = out.split(/[\r\n]+/); 211 | if (getconf && getconf.includes(GLIBC)) { 212 | return versionSuffix(getconf); 213 | } 214 | if (ldd1 && ldd2 && ldd1.includes(MUSL)) { 215 | return versionSuffix(ldd2); 216 | } 217 | return null; 218 | }; 219 | 220 | /** 221 | * Resolves with the libc version when it can be determined, `null` otherwise. 222 | * @returns {Promise} 223 | */ 224 | const version = async () => { 225 | let version = null; 226 | if (isLinux()) { 227 | version = await versionFromFilesystem(); 228 | if (!version) { 229 | version = versionFromReport(); 230 | } 231 | if (!version) { 232 | const out = await safeCommand(); 233 | version = versionFromCommand(out); 234 | } 235 | } 236 | return version; 237 | }; 238 | 239 | /** 240 | * Returns the libc version when it can be determined, `null` otherwise. 241 | * @returns {?string} 242 | */ 243 | const versionSync = () => { 244 | let version = null; 245 | if (isLinux()) { 246 | version = versionFromFilesystemSync(); 247 | if (!version) { 248 | version = versionFromReport(); 249 | } 250 | if (!version) { 251 | const out = safeCommandSync(); 252 | version = versionFromCommand(out); 253 | } 254 | } 255 | return version; 256 | }; 257 | 258 | module.exports = { 259 | GLIBC, 260 | MUSL, 261 | family, 262 | familySync, 263 | isNonGlibcLinux, 264 | isNonGlibcLinuxSync, 265 | version, 266 | versionSync 267 | }; 268 | -------------------------------------------------------------------------------- /lib/filesystem.js: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Lovell Fuller and others. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 'use strict'; 5 | 6 | const fs = require('fs'); 7 | 8 | /** 9 | * The path where we can find the ldd 10 | */ 11 | const LDD_PATH = '/usr/bin/ldd'; 12 | 13 | /** 14 | * Read the content of a file synchronous 15 | * 16 | * @param {string} path 17 | * @returns {string} 18 | */ 19 | const readFileSync = (path) => fs.readFileSync(path, 'utf-8'); 20 | 21 | /** 22 | * Read the content of a file 23 | * 24 | * @param {string} path 25 | * @returns {Promise} 26 | */ 27 | const readFile = (path) => new Promise((resolve, reject) => { 28 | fs.readFile(path, 'utf-8', (err, data) => { 29 | if (err) { 30 | reject(err); 31 | } else { 32 | resolve(data); 33 | } 34 | }); 35 | }); 36 | 37 | module.exports = { 38 | LDD_PATH, 39 | readFileSync, 40 | readFile 41 | }; 42 | -------------------------------------------------------------------------------- /lib/process.js: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Lovell Fuller and others. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 'use strict'; 5 | 6 | const isLinux = () => process.platform === 'linux'; 7 | 8 | let report = null; 9 | const getReport = () => { 10 | if (!report) { 11 | /* istanbul ignore next */ 12 | if (isLinux() && process.report) { 13 | const orig = process.report.excludeNetwork; 14 | process.report.excludeNetwork = true; 15 | report = process.report.getReport(); 16 | process.report.excludeNetwork = orig; 17 | } else { 18 | report = {}; 19 | } 20 | } 21 | return report; 22 | }; 23 | 24 | module.exports = { isLinux, getReport }; 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "detect-libc", 3 | "version": "2.0.4", 4 | "description": "Node.js module to detect the C standard library (libc) implementation family and version", 5 | "main": "lib/detect-libc.js", 6 | "files": [ 7 | "lib/", 8 | "index.d.ts" 9 | ], 10 | "scripts": { 11 | "test": "semistandard && nyc --reporter=text --check-coverage --branches=100 ava test/unit.js", 12 | "bench": "node benchmark/detect-libc", 13 | "bench:calls": "node benchmark/call-familySync.js && sleep 1 && node benchmark/call-isNonGlibcLinuxSync.js && sleep 1 && node benchmark/call-versionSync.js" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git://github.com/lovell/detect-libc" 18 | }, 19 | "keywords": [ 20 | "libc", 21 | "glibc", 22 | "musl" 23 | ], 24 | "author": "Lovell Fuller ", 25 | "contributors": [ 26 | "Niklas Salmoukas ", 27 | "Vinícius Lourenço " 28 | ], 29 | "license": "Apache-2.0", 30 | "devDependencies": { 31 | "ava": "^2.4.0", 32 | "benchmark": "^2.1.4", 33 | "nyc": "^15.1.0", 34 | "proxyquire": "^2.1.3", 35 | "semistandard": "^14.2.3" 36 | }, 37 | "engines": { 38 | "node": ">=8" 39 | }, 40 | "types": "index.d.ts" 41 | } 42 | -------------------------------------------------------------------------------- /test/integration.js: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Lovell Fuller and others. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 'use strict'; 5 | 6 | const { familySync, versionSync } = require('../'); 7 | 8 | const family = familySync() || 'unknown-family'; 9 | const version = versionSync() || 'unknown-version'; 10 | 11 | process.stdout.write(`${family} ${version}\n`); 12 | -------------------------------------------------------------------------------- /test/test-fixture.txt: -------------------------------------------------------------------------------- 1 | 1 -------------------------------------------------------------------------------- /test/unit.js: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Lovell Fuller and others. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 'use strict'; 5 | 6 | const test = require('ava'); 7 | const path = require('path'); 8 | const proxyquire = require('proxyquire') 9 | .noCallThru() 10 | .noPreserveCache(); 11 | 12 | const filePermissionError = new Error('Read error'); 13 | filePermissionError.code = 'ERR_ACCESS_DENIED'; 14 | 15 | test('filesystem - file found', async (t) => { 16 | t.plan(2); 17 | 18 | const filesystem = require('../lib/filesystem'); 19 | const notExistFile = path.join(__dirname, './non-exist.txt'); 20 | 21 | try { 22 | await filesystem.readFile(notExistFile); 23 | } catch (e) { 24 | t.true(e instanceof Error); 25 | } 26 | 27 | try { 28 | filesystem.readFileSync(notExistFile); 29 | } catch (e) { 30 | t.true(e instanceof Error); 31 | } 32 | }); 33 | 34 | test('filesystem - file not found', async (t) => { 35 | t.plan(2); 36 | 37 | const filesystem = require('../lib/filesystem'); 38 | const testFixtureFilePath = path.join(__dirname, './test-fixture.txt'); 39 | 40 | t.is(await filesystem.readFile(testFixtureFilePath), '1'); 41 | t.is(filesystem.readFileSync(testFixtureFilePath), '1'); 42 | }); 43 | 44 | test('constants', (t) => { 45 | t.plan(2); 46 | 47 | const libc = require('../'); 48 | 49 | t.is(libc.GLIBC, 'glibc'); 50 | t.is(libc.MUSL, 'musl'); 51 | }); 52 | 53 | test('linux - glibc family detected via ldd', async (t) => { 54 | t.plan(2); 55 | 56 | const libc = proxyquire('../', { 57 | './process': { 58 | isLinux: () => true 59 | }, 60 | './filesystem': { 61 | readFile: () => Promise.resolve('# This file is part of the GNU C Library.') 62 | } 63 | }); 64 | 65 | t.is(await libc.family(), libc.GLIBC); 66 | t.false(await libc.isNonGlibcLinux()); 67 | }); 68 | 69 | test('linux - glibc familySync detected via ldd', async (t) => { 70 | t.plan(2); 71 | 72 | const libc = proxyquire('../', { 73 | './process': { 74 | isLinux: () => true 75 | }, 76 | './filesystem': { 77 | readFileSync: () => '# The GNU C Library is free software; you can redistribute it and/or' 78 | } 79 | }); 80 | 81 | t.is(libc.familySync(), libc.GLIBC); 82 | t.false(libc.isNonGlibcLinuxSync()); 83 | }); 84 | 85 | test('linux - glibc family detected via ldd on error fallback', async (t) => { 86 | t.plan(2); 87 | 88 | const libc = proxyquire('../', { 89 | './process': { 90 | isLinux: () => true, 91 | getReport: () => ({ 92 | header: { 93 | glibcVersionRuntime: '1.23' 94 | } 95 | }) 96 | }, 97 | './filesystem': { 98 | readFile: () => Promise.reject(filePermissionError) 99 | } 100 | }); 101 | 102 | t.is(await libc.family(), libc.GLIBC); 103 | t.false(await libc.isNonGlibcLinux()); 104 | }); 105 | 106 | test('linux - glibc familySync detected via ldd on error fallback', async (t) => { 107 | t.plan(2); 108 | 109 | const libc = proxyquire('../', { 110 | './process': { 111 | isLinux: () => true, 112 | getReport: () => ({ 113 | header: { 114 | glibcVersionRuntime: '1.23' 115 | } 116 | }) 117 | }, 118 | './filesystem': { 119 | readFileSync: () => { 120 | throw filePermissionError; 121 | } 122 | } 123 | }); 124 | 125 | t.is(libc.familySync(), libc.GLIBC); 126 | t.false(libc.isNonGlibcLinuxSync()); 127 | }); 128 | 129 | test('linux - glibc family and version detected via report', async (t) => { 130 | t.plan(3); 131 | 132 | const libc = proxyquire('../', { 133 | './process': { 134 | isLinux: () => true, 135 | getReport: () => ({ 136 | header: { 137 | glibcVersionRuntime: '1.23' 138 | } 139 | }) 140 | }, 141 | './filesystem': { 142 | readFile: () => Promise.resolve('bunch-of-text') 143 | } 144 | }); 145 | 146 | t.is(await libc.family(), libc.GLIBC); 147 | t.is(await libc.version(), '1.23'); 148 | t.false(await libc.isNonGlibcLinux()); 149 | }); 150 | 151 | test('linux - glibc familySync and version detected via report', async (t) => { 152 | t.plan(3); 153 | 154 | const libc = proxyquire('../', { 155 | './process': { 156 | isLinux: () => true, 157 | getReport: () => ({ 158 | header: { 159 | glibcVersionRuntime: '1.23' 160 | } 161 | }) 162 | }, 163 | './filesystem': { 164 | readFileSync: () => 'bunch-of-text' 165 | } 166 | }); 167 | 168 | t.is(libc.familySync(), libc.GLIBC); 169 | t.is(libc.versionSync(), '1.23'); 170 | t.false(libc.isNonGlibcLinuxSync()); 171 | }); 172 | 173 | test('linux - musl family detected via ldd', async (t) => { 174 | t.plan(2); 175 | 176 | const libc = proxyquire('../', { 177 | './process': { 178 | isLinux: () => true 179 | }, 180 | './filesystem': { 181 | readFile: () => Promise.resolve('bunch-of-text-musl') 182 | } 183 | }); 184 | 185 | t.is(await libc.family(), libc.MUSL); 186 | t.true(await libc.isNonGlibcLinux()); 187 | }); 188 | 189 | test('linux - musl familySync detected via ldd', async (t) => { 190 | t.plan(2); 191 | 192 | const libc = proxyquire('../', { 193 | './process': { 194 | isLinux: () => true 195 | }, 196 | './filesystem': { 197 | readFileSync: () => 'bunch-of-text-musl' 198 | } 199 | }); 200 | 201 | t.is(libc.familySync(), libc.MUSL); 202 | t.true(libc.isNonGlibcLinuxSync()); 203 | }); 204 | 205 | test('linux - musl family fallback when not found ldd', async (t) => { 206 | t.plan(2); 207 | 208 | const someError = new Error('Some error'); 209 | 210 | const libc = proxyquire('../', { 211 | './process': { 212 | isLinux: () => true, 213 | getReport: () => ({ 214 | sharedObjects: ['/lib/ld-musl-x86_64.so.1'] 215 | }) 216 | }, 217 | './filesystem': { 218 | readFile: () => Promise.reject(someError) 219 | } 220 | }); 221 | 222 | t.is(await libc.family(), libc.MUSL); 223 | t.true(await libc.isNonGlibcLinux()); 224 | }); 225 | 226 | test('linux - musl familySync fallback when not found ldd', async (t) => { 227 | t.plan(2); 228 | 229 | const someError = new Error('Some error'); 230 | 231 | const libc = proxyquire('../', { 232 | './process': { 233 | isLinux: () => true, 234 | getReport: () => ({ 235 | sharedObjects: ['/lib/ld-musl-x86_64.so.1'] 236 | }) 237 | }, 238 | './filesystem': { 239 | readFileSync: () => { 240 | throw someError; 241 | } 242 | } 243 | }); 244 | 245 | t.is(libc.familySync(), libc.MUSL); 246 | t.true(libc.isNonGlibcLinuxSync()); 247 | }); 248 | 249 | test('linux - musl family detected via report', async (t) => { 250 | t.plan(4); 251 | 252 | const libc = proxyquire('../', { 253 | './process': { 254 | isLinux: () => true, 255 | getReport: () => ({ 256 | sharedObjects: ['/lib/ld-musl-x86_64.so.1'] 257 | }) 258 | }, 259 | './filesystem': { 260 | readFile: () => Promise.reject(filePermissionError), 261 | readFileSync: () => { 262 | throw filePermissionError; 263 | } 264 | } 265 | }); 266 | 267 | t.is(await libc.family(), libc.MUSL); 268 | t.true(await libc.isNonGlibcLinux()); 269 | 270 | t.is(libc.familySync(), libc.MUSL); 271 | t.true(libc.isNonGlibcLinuxSync()); 272 | }); 273 | 274 | test('linux - glibc family detected via async child process', async (t) => { 275 | t.plan(2); 276 | 277 | const out = 'glibc 1.23\nldd (GLIBC) 1.23\nCopyright\netc'; 278 | const libc = proxyquire('../', { 279 | './process': { 280 | isLinux: () => true, 281 | getReport: () => ({}) 282 | }, 283 | child_process: { 284 | exec: (_c, cb) => cb(null, out) 285 | }, 286 | './filesystem': { 287 | readFile: () => Promise.reject(filePermissionError), 288 | readFileSync: () => { 289 | throw filePermissionError; 290 | } 291 | } 292 | }); 293 | 294 | t.is(await libc.family(), libc.GLIBC); 295 | t.false(await libc.isNonGlibcLinux()); 296 | }); 297 | 298 | test('linux - glibc family detected via sync child process', async (t) => { 299 | t.plan(2); 300 | 301 | const out = 'glibc 1.23\nldd (GLIBC) 1.23\nCopyright\netc'; 302 | const libc = proxyquire('../', { 303 | './process': { 304 | isLinux: () => true, 305 | getReport: () => ({}) 306 | }, 307 | child_process: { 308 | execSync: () => out 309 | }, 310 | './filesystem': { 311 | readFile: () => Promise.reject(filePermissionError), 312 | readFileSync: () => { 313 | throw filePermissionError; 314 | } 315 | } 316 | }); 317 | 318 | t.is(libc.familySync(), libc.GLIBC); 319 | t.false(libc.isNonGlibcLinuxSync()); 320 | }); 321 | 322 | test('linux - musl family detected via async child process', async (t) => { 323 | t.plan(2); 324 | 325 | const out = 'getconf: GNU_LIBC_VERSION: unknown variable\nmusl libc\nVersion 1.2.3\netc'; 326 | const libc = proxyquire('../', { 327 | './process': { 328 | isLinux: () => true, 329 | getReport: () => ({ 330 | sharedObjects: [] 331 | }) 332 | }, 333 | child_process: { 334 | exec: (_c, cb) => cb(null, out) 335 | }, 336 | './filesystem': { 337 | readFile: () => Promise.reject(filePermissionError), 338 | readFileSync: () => { 339 | throw filePermissionError; 340 | } 341 | } 342 | }); 343 | 344 | t.is(await libc.family(), libc.MUSL); 345 | t.true(await libc.isNonGlibcLinux()); 346 | }); 347 | 348 | test('linux - musl family detected via sync child process', async (t) => { 349 | t.plan(2); 350 | 351 | const out = 'getconf: GNU_LIBC_VERSION: unknown variable\nmusl libc\nVersion 1.2.3\netc'; 352 | const libc = proxyquire('../', { 353 | './process': { 354 | isLinux: () => true, 355 | getReport: () => ({ 356 | sharedObjects: [] 357 | }) 358 | }, 359 | child_process: { 360 | execSync: () => out 361 | }, 362 | './filesystem': { 363 | readFile: () => Promise.reject(filePermissionError), 364 | readFileSync: () => { 365 | throw filePermissionError; 366 | } 367 | } 368 | }); 369 | 370 | t.is(libc.familySync(), libc.MUSL); 371 | t.true(libc.isNonGlibcLinuxSync()); 372 | }); 373 | 374 | test('linux - unknown family', async (t) => { 375 | t.plan(4); 376 | 377 | const out = 'unknown'; 378 | const libc = proxyquire('../', { 379 | './process': { 380 | isLinux: () => true, 381 | getReport: () => ({}) 382 | }, 383 | child_process: { 384 | exec: (_c, cb) => cb(null, out), 385 | execSync: () => out 386 | }, 387 | './filesystem': { 388 | readFile: () => Promise.reject(filePermissionError), 389 | readFileSync: () => { 390 | throw filePermissionError; 391 | } 392 | } 393 | }); 394 | 395 | t.is(await libc.family(), null); 396 | t.true(await libc.isNonGlibcLinux()); 397 | 398 | t.is(libc.familySync(), null); 399 | t.true(libc.isNonGlibcLinuxSync()); 400 | }); 401 | 402 | test('linux - unknown family (exec fails)', async (t) => { 403 | t.plan(2); 404 | 405 | const libc = proxyquire('../', { 406 | './process': { 407 | isLinux: () => true, 408 | getReport: () => ({}) 409 | }, 410 | child_process: { 411 | exec: (_c, cb) => cb(new Error()) 412 | }, 413 | './filesystem': { 414 | readFile: () => Promise.reject(filePermissionError), 415 | readFileSync: () => { 416 | throw filePermissionError; 417 | } 418 | } 419 | }); 420 | 421 | t.is(await libc.family(), null); 422 | t.true(await libc.isNonGlibcLinux()); 423 | }); 424 | 425 | test('linux - unknown family (execSync fails)', async (t) => { 426 | t.plan(2); 427 | 428 | const libc = proxyquire('../', { 429 | './process': { 430 | isLinux: () => true, 431 | getReport: () => ({}) 432 | }, 433 | child_process: { 434 | execSync: () => { throw new Error(); } 435 | }, 436 | './filesystem': { 437 | readFileSync: () => { 438 | throw filePermissionError; 439 | } 440 | } 441 | }); 442 | 443 | t.is(libc.familySync(), null); 444 | t.true(libc.isNonGlibcLinuxSync()); 445 | }); 446 | 447 | test('non-linux - unknown family', async (t) => { 448 | t.plan(2); 449 | 450 | const libc = proxyquire('../', { 451 | './process': { 452 | isLinux: () => false 453 | } 454 | }); 455 | 456 | t.is(await libc.family(), null); 457 | t.false(await libc.isNonGlibcLinux()); 458 | }); 459 | 460 | test('non-linux - unknown familySync', async (t) => { 461 | t.plan(2); 462 | 463 | const libc = proxyquire('../', { 464 | './process': { 465 | isLinux: () => false 466 | } 467 | }); 468 | 469 | t.is(libc.familySync(), null); 470 | t.false(libc.isNonGlibcLinuxSync()); 471 | }); 472 | 473 | // version 474 | 475 | test('linux - glibc version detected via filesystem', async (t) => { 476 | t.plan(1); 477 | 478 | const out = '--vers | --versi | --versio | --version)\necho \'ldd (Ubuntu GLIBC 1.23-0ubuntu9.9) 1.23\''; 479 | const libc = proxyquire('../', { 480 | './process': { 481 | isLinux: () => true 482 | }, 483 | './filesystem': { 484 | readFile: () => Promise.resolve(out) 485 | } 486 | }); 487 | 488 | t.is(await libc.version(), '1.23'); 489 | }); 490 | 491 | test('linux - glibc version detected via filesystem (libc)', async (t) => { 492 | t.plan(1); 493 | 494 | const out = '--vers | --versi | --versio | --version)\necho \'ldd (GNU libc) 2.39\''; 495 | const libc = proxyquire('../', { 496 | './process': { 497 | isLinux: () => true 498 | }, 499 | './filesystem': { 500 | readFile: () => Promise.resolve(out) 501 | } 502 | }); 503 | 504 | t.is(await libc.version(), '2.39'); 505 | }); 506 | 507 | test('linux - libc version not detected via filesystem (void linux musl)', async (t) => { 508 | t.plan(1); 509 | 510 | const out = 'startlibc_startGNU AS 2.35.1'; 511 | const libc = proxyquire('../', { 512 | './process': { 513 | isLinux: () => true, 514 | getReport: () => ({}) 515 | }, 516 | './filesystem': { 517 | readFile: () => Promise.resolve(out) 518 | }, 519 | child_process: { 520 | exec: (_c, cb) => cb(null, out), 521 | execSync: () => out 522 | } 523 | }); 524 | 525 | t.is(await libc.version(), null); 526 | }); 527 | 528 | test('linux - glibc version detected via filesystemSync', async (t) => { 529 | t.plan(1); 530 | 531 | const out = '--vers | --versi | --versio | --version)\necho \'ldd (Ubuntu GLIBC 1.23-0ubuntu9.9) 1.23\''; 532 | const libc = proxyquire('../', { 533 | './process': { 534 | isLinux: () => true 535 | }, 536 | './filesystem': { 537 | readFileSync: () => out 538 | } 539 | }); 540 | 541 | t.is(libc.versionSync(), '1.23'); 542 | }); 543 | 544 | test('linux - glibc version detected via filesystemSync (libc)', async (t) => { 545 | t.plan(1); 546 | 547 | const out = '--vers | --versi | --versio | --version)\necho \'ldd (GNU libc) 2.39\''; 548 | const libc = proxyquire('../', { 549 | './process': { 550 | isLinux: () => true 551 | }, 552 | './filesystem': { 553 | readFileSync: () => out 554 | } 555 | }); 556 | 557 | t.is(libc.versionSync(), '2.39'); 558 | }); 559 | 560 | test('linux - libc version not detected via filesystemSync (void linux musl)', (t) => { 561 | t.plan(1); 562 | 563 | const out = 'startlibc_startGNU AS 2.35.1'; 564 | const libc = proxyquire('../', { 565 | './process': { 566 | isLinux: () => true, 567 | getReport: () => ({}) 568 | }, 569 | './filesystem': { 570 | readFile: () => Promise.resolve(out) 571 | }, 572 | child_process: { 573 | exec: (_c, cb) => cb(null, out), 574 | execSync: () => out 575 | } 576 | }); 577 | 578 | t.is(libc.versionSync(), null); 579 | }); 580 | 581 | test('linux - glibc version detected via child process', async (t) => { 582 | t.plan(1); 583 | 584 | const out = 'glibc 1.23\nldd (GLIBC) 1.23\nCopyright\netc'; 585 | const libc = proxyquire('../', { 586 | './process': { 587 | isLinux: () => true, 588 | getReport: () => ({}) 589 | }, 590 | child_process: { 591 | exec: (_c, cb) => cb(null, out), 592 | execSync: () => out 593 | }, 594 | './filesystem': { 595 | readFile: () => Promise.reject(filePermissionError) 596 | } 597 | }); 598 | 599 | t.is(await libc.version(), '1.23'); 600 | }); 601 | 602 | test('linux - glibc version detected via child process sync', async (t) => { 603 | t.plan(1); 604 | 605 | const out = 'glibc 1.23\nldd (GLIBC) 1.23\nCopyright\netc'; 606 | const libc = proxyquire('../', { 607 | './process': { 608 | isLinux: () => true, 609 | getReport: () => ({}) 610 | }, 611 | child_process: { 612 | execSync: () => out 613 | }, 614 | './filesystem': { 615 | readFileSync: () => { 616 | throw filePermissionError; 617 | } 618 | } 619 | }); 620 | 621 | t.is(libc.versionSync(), '1.23'); 622 | }); 623 | 624 | test('linux - musl version detected via child process', async (t) => { 625 | t.plan(4); 626 | 627 | const out = 'getconf: GNU_LIBC_VERSION: unknown variable\nmusl libc\nVersion 1.2.3\netc'; 628 | const libc = proxyquire('../', { 629 | './process': { 630 | isLinux: () => true, 631 | getReport: () => ({}) 632 | }, 633 | child_process: { 634 | exec: (_c, cb) => cb(null, out), 635 | execSync: () => out 636 | }, 637 | './filesystem': { 638 | readFile: () => Promise.resolve('does not have version') 639 | } 640 | }); 641 | 642 | t.is(await libc.version(), '1.2.3'); 643 | t.is(libc.versionSync(), '1.2.3'); 644 | 645 | // calling twice to check the cache 646 | t.is(await libc.version(), '1.2.3'); 647 | t.is(libc.versionSync(), '1.2.3'); 648 | }); 649 | 650 | test('linux - unknown version', async (t) => { 651 | t.plan(2); 652 | 653 | const out = 'unknown'; 654 | const libc = proxyquire('../', { 655 | './process': { 656 | isLinux: () => true, 657 | getReport: () => ({}) 658 | }, 659 | child_process: { 660 | exec: (_c, cb) => cb(null, out), 661 | execSync: () => out 662 | }, 663 | './filesystem': { 664 | readFile: () => Promise.resolve('does not have version') 665 | } 666 | }); 667 | 668 | t.is(await libc.version(), null); 669 | t.is(libc.versionSync(), null); 670 | }); 671 | 672 | test('linux - unknown version (exec fails)', async (t) => { 673 | t.plan(2); 674 | 675 | const libc = proxyquire('../', { 676 | './process': { 677 | isLinux: () => true, 678 | getReport: () => ({}) 679 | }, 680 | child_process: { 681 | exec: (_c, cb) => cb(new Error()), 682 | execSync: () => { throw new Error(); } 683 | }, 684 | './filesystem': { 685 | readFile: () => Promise.resolve('does not have version') 686 | } 687 | }); 688 | 689 | t.is(await libc.version(), null); 690 | t.is(libc.versionSync(), null); 691 | }); 692 | 693 | test('non-linux - unknown version', async (t) => { 694 | t.plan(2); 695 | 696 | const libc = proxyquire('../', { 697 | './process': { 698 | isLinux: () => false 699 | } 700 | }); 701 | 702 | t.is(await libc.version(), null); 703 | t.is(libc.versionSync(), null); 704 | }); 705 | 706 | test('process (internal)', (t) => { 707 | t.plan(3); 708 | 709 | const process = require('../lib/process'); 710 | 711 | t.is(typeof process.isLinux(), 'boolean'); 712 | t.is(typeof process.getReport(), 'object'); 713 | t.is(process.getReport(), process.getReport()); 714 | }); 715 | --------------------------------------------------------------------------------