├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── build.rs ├── renovate.json ├── src ├── bin │ ├── hostname.rs │ └── uudoc.rs └── uu │ └── hostname │ ├── Cargo.toml │ ├── hostname.md │ └── src │ ├── change.rs │ ├── change │ ├── unix.rs │ └── windows.rs │ ├── errors.rs │ ├── hostname.rs │ ├── main.rs │ ├── net.rs │ ├── net │ ├── unix.rs │ └── windows.rs │ ├── print.rs │ ├── print │ ├── unix.rs │ └── windows.rs │ └── utils.rs └── tests ├── by-util └── test_hostname.rs └── tests.rs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: Basic CI 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | 8 | jobs: 9 | check: 10 | name: cargo check 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | matrix: 14 | os: [ubuntu-latest, macOS-latest, windows-latest] 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: dtolnay/rust-toolchain@stable 18 | - run: cargo check 19 | 20 | test: 21 | name: cargo test 22 | runs-on: ${{ matrix.os }} 23 | strategy: 24 | matrix: 25 | os: [ubuntu-latest, macOS-latest, windows-latest] 26 | steps: 27 | - uses: actions/checkout@v4 28 | - uses: dtolnay/rust-toolchain@stable 29 | - run: cargo test 30 | 31 | fmt: 32 | name: cargo fmt --all -- --check 33 | runs-on: ubuntu-latest 34 | steps: 35 | - uses: actions/checkout@v4 36 | - uses: dtolnay/rust-toolchain@stable 37 | - run: rustup component add rustfmt 38 | - run: cargo fmt --all -- --check 39 | 40 | clippy: 41 | name: cargo clippy -- -D warnings 42 | runs-on: ${{ matrix.os }} 43 | strategy: 44 | matrix: 45 | os: [ubuntu-latest, macOS-latest, windows-latest] 46 | steps: 47 | - uses: actions/checkout@v4 48 | - uses: dtolnay/rust-toolchain@stable 49 | - run: rustup component add clippy 50 | - run: cargo clippy -- -D warnings 51 | 52 | coverage: 53 | name: Code Coverage 54 | runs-on: ${{ matrix.job.os }} 55 | strategy: 56 | fail-fast: true 57 | matrix: 58 | job: 59 | - { os: ubuntu-latest , features: unix } 60 | - { os: macos-latest , features: macos } 61 | - { os: windows-latest , features: windows } 62 | steps: 63 | - uses: actions/checkout@v4 64 | - name: Initialize workflow variables 65 | id: vars 66 | shell: bash 67 | run: | 68 | ## VARs setup 69 | outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; } 70 | # toolchain 71 | TOOLCHAIN="nightly" ## default to "nightly" toolchain (required for certain required unstable compiler flags) ## !maint: refactor when stable channel has needed support 72 | # * specify gnu-type TOOLCHAIN for windows; `grcov` requires gnu-style code coverage data files 73 | case ${{ matrix.job.os }} in windows-*) TOOLCHAIN="$TOOLCHAIN-x86_64-pc-windows-gnu" ;; esac; 74 | # * use requested TOOLCHAIN if specified 75 | if [ -n "${{ matrix.job.toolchain }}" ]; then TOOLCHAIN="${{ matrix.job.toolchain }}" ; fi 76 | outputs TOOLCHAIN 77 | # target-specific options 78 | # * CODECOV_FLAGS 79 | CODECOV_FLAGS=$( echo "${{ matrix.job.os }}" | sed 's/[^[:alnum:]]/_/g' ) 80 | outputs CODECOV_FLAGS 81 | 82 | - name: rust toolchain ~ install 83 | uses: dtolnay/rust-toolchain@nightly 84 | with: 85 | components: llvm-tools-preview 86 | - name: Test 87 | run: cargo test --no-fail-fast 88 | env: 89 | CARGO_INCREMENTAL: "0" 90 | RUSTC_WRAPPER: "" 91 | RUSTFLAGS: "-Cinstrument-coverage -Zcoverage-options=branch -Ccodegen-units=1 -Copt-level=0 -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort" 92 | RUSTDOCFLAGS: "-Cpanic=abort" 93 | LLVM_PROFILE_FILE: "hostname-%p-%m.profraw" 94 | - name: "`grcov` ~ install" 95 | id: build_grcov 96 | shell: bash 97 | run: | 98 | git clone https://github.com/mozilla/grcov.git ~/grcov/ 99 | cd ~/grcov 100 | # Hardcode the version of crossbeam-epoch. See 101 | # https://github.com/uutils/coreutils/issues/3680 102 | sed -i -e "s|tempfile =|crossbeam-epoch = \"=0.9.8\"\ntempfile =|" Cargo.toml 103 | cargo install --path . 104 | cd - 105 | # Uncomment when the upstream issue 106 | # https://github.com/mozilla/grcov/issues/849 is fixed 107 | # uses: actions-rs/install@v0.1 108 | # with: 109 | # crate: grcov 110 | # version: latest 111 | # use-tool-cache: false 112 | - name: Generate coverage data (via `grcov`) 113 | id: coverage 114 | shell: bash 115 | run: | 116 | ## Generate coverage data 117 | COVERAGE_REPORT_DIR="target/debug" 118 | COVERAGE_REPORT_FILE="${COVERAGE_REPORT_DIR}/lcov.info" 119 | mkdir -p "${COVERAGE_REPORT_DIR}" 120 | # display coverage files 121 | grcov . --binary-path="${COVERAGE_REPORT_DIR}" --output-type files --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()" | sort --unique 122 | # generate coverage report 123 | grcov . --binary-path="${COVERAGE_REPORT_DIR}" --output-type lcov --output-path "${COVERAGE_REPORT_FILE}" --branch --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()" 124 | echo "report=${COVERAGE_REPORT_FILE}" >> $GITHUB_OUTPUT 125 | - name: Upload coverage results (to Codecov.io) 126 | uses: codecov/codecov-action@v5 127 | with: 128 | token: ${{ secrets.CODECOV_TOKEN }} 129 | files: ${{ steps.coverage.outputs.report }} 130 | ## flags: IntegrationTests, UnitTests, ${{ steps.vars.outputs.CODECOV_FLAGS }} 131 | flags: ${{ steps.vars.outputs.CODECOV_FLAGS }} 132 | name: codecov-umbrella 133 | fail_ci_if_error: false 134 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | 3 | /target/ 4 | /.vscode/ 5 | /*.code-workspace 6 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.1.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "anstream" 16 | version = "0.6.18" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" 19 | dependencies = [ 20 | "anstyle", 21 | "anstyle-parse", 22 | "anstyle-query", 23 | "anstyle-wincon", 24 | "colorchoice", 25 | "is_terminal_polyfill", 26 | "utf8parse", 27 | ] 28 | 29 | [[package]] 30 | name = "anstyle" 31 | version = "1.0.10" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" 34 | 35 | [[package]] 36 | name = "anstyle-parse" 37 | version = "0.2.6" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" 40 | dependencies = [ 41 | "utf8parse", 42 | ] 43 | 44 | [[package]] 45 | name = "anstyle-query" 46 | version = "1.1.2" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" 49 | dependencies = [ 50 | "windows-sys 0.59.0", 51 | ] 52 | 53 | [[package]] 54 | name = "anstyle-wincon" 55 | version = "3.0.7" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" 58 | dependencies = [ 59 | "anstyle", 60 | "once_cell", 61 | "windows-sys 0.59.0", 62 | ] 63 | 64 | [[package]] 65 | name = "bitflags" 66 | version = "2.9.0" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" 69 | 70 | [[package]] 71 | name = "cfg-if" 72 | version = "1.0.0" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 75 | 76 | [[package]] 77 | name = "cfg_aliases" 78 | version = "0.2.1" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 81 | 82 | [[package]] 83 | name = "clap" 84 | version = "4.5.40" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" 87 | dependencies = [ 88 | "clap_builder", 89 | ] 90 | 91 | [[package]] 92 | name = "clap_builder" 93 | version = "4.5.40" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" 96 | dependencies = [ 97 | "anstream", 98 | "anstyle", 99 | "clap_lex", 100 | "strsim", 101 | "terminal_size", 102 | ] 103 | 104 | [[package]] 105 | name = "clap_complete" 106 | version = "4.5.54" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "aad5b1b4de04fead402672b48897030eec1f3bfe1550776322f59f6d6e6a5677" 109 | dependencies = [ 110 | "clap", 111 | ] 112 | 113 | [[package]] 114 | name = "clap_lex" 115 | version = "0.7.4" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" 118 | 119 | [[package]] 120 | name = "clap_mangen" 121 | version = "0.2.27" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "fc33c849748320656a90832f54a5eeecaa598e92557fb5dedebc3355746d31e4" 124 | dependencies = [ 125 | "clap", 126 | "roff", 127 | ] 128 | 129 | [[package]] 130 | name = "colorchoice" 131 | version = "1.0.3" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" 134 | 135 | [[package]] 136 | name = "ctor" 137 | version = "0.4.2" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "a4735f265ba6a1188052ca32d461028a7d1125868be18e287e756019da7607b5" 140 | dependencies = [ 141 | "ctor-proc-macro", 142 | "dtor", 143 | ] 144 | 145 | [[package]] 146 | name = "ctor-proc-macro" 147 | version = "0.0.5" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "4f211af61d8efdd104f96e57adf5e426ba1bc3ed7a4ead616e15e5881fd79c4d" 150 | 151 | [[package]] 152 | name = "deranged" 153 | version = "0.4.0" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" 156 | dependencies = [ 157 | "powerfmt", 158 | ] 159 | 160 | [[package]] 161 | name = "diff" 162 | version = "0.1.13" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" 165 | 166 | [[package]] 167 | name = "displaydoc" 168 | version = "0.2.5" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 171 | dependencies = [ 172 | "proc-macro2", 173 | "quote", 174 | "syn", 175 | ] 176 | 177 | [[package]] 178 | name = "dns-lookup" 179 | version = "2.0.4" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "e5766087c2235fec47fafa4cfecc81e494ee679d0fd4a59887ea0919bfb0e4fc" 182 | dependencies = [ 183 | "cfg-if", 184 | "libc", 185 | "socket2", 186 | "windows-sys 0.48.0", 187 | ] 188 | 189 | [[package]] 190 | name = "dtor" 191 | version = "0.0.6" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "97cbdf2ad6846025e8e25df05171abfb30e3ababa12ee0a0e44b9bbe570633a8" 194 | dependencies = [ 195 | "dtor-proc-macro", 196 | ] 197 | 198 | [[package]] 199 | name = "dtor-proc-macro" 200 | version = "0.0.5" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | checksum = "7454e41ff9012c00d53cf7f475c5e3afa3b91b7c90568495495e8d9bf47a1055" 203 | 204 | [[package]] 205 | name = "errno" 206 | version = "0.3.12" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" 209 | dependencies = [ 210 | "libc", 211 | "windows-sys 0.52.0", 212 | ] 213 | 214 | [[package]] 215 | name = "fastrand" 216 | version = "2.3.0" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 219 | 220 | [[package]] 221 | name = "fluent" 222 | version = "0.17.0" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "8137a6d5a2c50d6b0ebfcb9aaa91a28154e0a70605f112d30cb0cd4a78670477" 225 | dependencies = [ 226 | "fluent-bundle", 227 | "unic-langid", 228 | ] 229 | 230 | [[package]] 231 | name = "fluent-bundle" 232 | version = "0.16.0" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "01203cb8918f5711e73891b347816d932046f95f54207710bda99beaeb423bf4" 235 | dependencies = [ 236 | "fluent-langneg", 237 | "fluent-syntax", 238 | "intl-memoizer", 239 | "intl_pluralrules", 240 | "rustc-hash", 241 | "self_cell", 242 | "smallvec", 243 | "unic-langid", 244 | ] 245 | 246 | [[package]] 247 | name = "fluent-langneg" 248 | version = "0.13.0" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "2c4ad0989667548f06ccd0e306ed56b61bd4d35458d54df5ec7587c0e8ed5e94" 251 | dependencies = [ 252 | "unic-langid", 253 | ] 254 | 255 | [[package]] 256 | name = "fluent-syntax" 257 | version = "0.12.0" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "54f0d287c53ffd184d04d8677f590f4ac5379785529e5e08b1c8083acdd5c198" 260 | dependencies = [ 261 | "memchr", 262 | "thiserror", 263 | ] 264 | 265 | [[package]] 266 | name = "getrandom" 267 | version = "0.3.2" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" 270 | dependencies = [ 271 | "cfg-if", 272 | "libc", 273 | "r-efi", 274 | "wasi", 275 | ] 276 | 277 | [[package]] 278 | name = "glob" 279 | version = "0.3.2" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" 282 | 283 | [[package]] 284 | name = "hex" 285 | version = "0.4.3" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 288 | 289 | [[package]] 290 | name = "hostname" 291 | version = "0.0.1" 292 | dependencies = [ 293 | "clap", 294 | "clap_complete", 295 | "clap_mangen", 296 | "ctor", 297 | "libc", 298 | "phf", 299 | "phf_codegen", 300 | "pretty_assertions", 301 | "procfs", 302 | "rand 0.9.1", 303 | "regex", 304 | "rlimit", 305 | "tempfile", 306 | "textwrap", 307 | "uu_hostname", 308 | "uucore", 309 | "uutests", 310 | "xattr", 311 | ] 312 | 313 | [[package]] 314 | name = "intl-memoizer" 315 | version = "0.5.3" 316 | source = "registry+https://github.com/rust-lang/crates.io-index" 317 | checksum = "310da2e345f5eb861e7a07ee182262e94975051db9e4223e909ba90f392f163f" 318 | dependencies = [ 319 | "type-map", 320 | "unic-langid", 321 | ] 322 | 323 | [[package]] 324 | name = "intl_pluralrules" 325 | version = "7.0.2" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | checksum = "078ea7b7c29a2b4df841a7f6ac8775ff6074020c6776d48491ce2268e068f972" 328 | dependencies = [ 329 | "unic-langid", 330 | ] 331 | 332 | [[package]] 333 | name = "is_terminal_polyfill" 334 | version = "1.70.1" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 337 | 338 | [[package]] 339 | name = "itoa" 340 | version = "1.0.15" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 343 | 344 | [[package]] 345 | name = "libc" 346 | version = "0.2.172" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" 349 | 350 | [[package]] 351 | name = "linux-raw-sys" 352 | version = "0.4.15" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" 355 | 356 | [[package]] 357 | name = "linux-raw-sys" 358 | version = "0.9.4" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" 361 | 362 | [[package]] 363 | name = "memchr" 364 | version = "2.7.4" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 367 | 368 | [[package]] 369 | name = "nix" 370 | version = "0.30.1" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" 373 | dependencies = [ 374 | "bitflags", 375 | "cfg-if", 376 | "cfg_aliases", 377 | "libc", 378 | ] 379 | 380 | [[package]] 381 | name = "num-conv" 382 | version = "0.1.0" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 385 | 386 | [[package]] 387 | name = "num_threads" 388 | version = "0.1.7" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" 391 | dependencies = [ 392 | "libc", 393 | ] 394 | 395 | [[package]] 396 | name = "number_prefix" 397 | version = "0.4.0" 398 | source = "registry+https://github.com/rust-lang/crates.io-index" 399 | checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" 400 | 401 | [[package]] 402 | name = "once_cell" 403 | version = "1.21.3" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 406 | 407 | [[package]] 408 | name = "os_display" 409 | version = "0.1.4" 410 | source = "registry+https://github.com/rust-lang/crates.io-index" 411 | checksum = "ad5fd71b79026fb918650dde6d125000a233764f1c2f1659a1c71118e33ea08f" 412 | dependencies = [ 413 | "unicode-width", 414 | ] 415 | 416 | [[package]] 417 | name = "phf" 418 | version = "0.11.3" 419 | source = "registry+https://github.com/rust-lang/crates.io-index" 420 | checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" 421 | dependencies = [ 422 | "phf_shared", 423 | ] 424 | 425 | [[package]] 426 | name = "phf_codegen" 427 | version = "0.11.3" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" 430 | dependencies = [ 431 | "phf_generator", 432 | "phf_shared", 433 | ] 434 | 435 | [[package]] 436 | name = "phf_generator" 437 | version = "0.11.3" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" 440 | dependencies = [ 441 | "phf_shared", 442 | "rand 0.8.5", 443 | ] 444 | 445 | [[package]] 446 | name = "phf_shared" 447 | version = "0.11.3" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" 450 | dependencies = [ 451 | "siphasher", 452 | ] 453 | 454 | [[package]] 455 | name = "powerfmt" 456 | version = "0.2.0" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 459 | 460 | [[package]] 461 | name = "ppv-lite86" 462 | version = "0.2.21" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" 465 | dependencies = [ 466 | "zerocopy", 467 | ] 468 | 469 | [[package]] 470 | name = "pretty_assertions" 471 | version = "1.4.1" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" 474 | dependencies = [ 475 | "diff", 476 | "yansi", 477 | ] 478 | 479 | [[package]] 480 | name = "proc-macro2" 481 | version = "1.0.94" 482 | source = "registry+https://github.com/rust-lang/crates.io-index" 483 | checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" 484 | dependencies = [ 485 | "unicode-ident", 486 | ] 487 | 488 | [[package]] 489 | name = "procfs" 490 | version = "0.17.0" 491 | source = "registry+https://github.com/rust-lang/crates.io-index" 492 | checksum = "cc5b72d8145275d844d4b5f6d4e1eef00c8cd889edb6035c21675d1bb1f45c9f" 493 | dependencies = [ 494 | "bitflags", 495 | "hex", 496 | "procfs-core", 497 | "rustix 0.38.44", 498 | ] 499 | 500 | [[package]] 501 | name = "procfs-core" 502 | version = "0.17.0" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "239df02d8349b06fc07398a3a1697b06418223b1c7725085e801e7c0fc6a12ec" 505 | dependencies = [ 506 | "bitflags", 507 | "hex", 508 | ] 509 | 510 | [[package]] 511 | name = "quote" 512 | version = "1.0.40" 513 | source = "registry+https://github.com/rust-lang/crates.io-index" 514 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 515 | dependencies = [ 516 | "proc-macro2", 517 | ] 518 | 519 | [[package]] 520 | name = "r-efi" 521 | version = "5.2.0" 522 | source = "registry+https://github.com/rust-lang/crates.io-index" 523 | checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" 524 | 525 | [[package]] 526 | name = "rand" 527 | version = "0.8.5" 528 | source = "registry+https://github.com/rust-lang/crates.io-index" 529 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 530 | dependencies = [ 531 | "rand_core 0.6.4", 532 | ] 533 | 534 | [[package]] 535 | name = "rand" 536 | version = "0.9.1" 537 | source = "registry+https://github.com/rust-lang/crates.io-index" 538 | checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" 539 | dependencies = [ 540 | "rand_chacha", 541 | "rand_core 0.9.3", 542 | ] 543 | 544 | [[package]] 545 | name = "rand_chacha" 546 | version = "0.9.0" 547 | source = "registry+https://github.com/rust-lang/crates.io-index" 548 | checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" 549 | dependencies = [ 550 | "ppv-lite86", 551 | "rand_core 0.9.3", 552 | ] 553 | 554 | [[package]] 555 | name = "rand_core" 556 | version = "0.6.4" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 559 | 560 | [[package]] 561 | name = "rand_core" 562 | version = "0.9.3" 563 | source = "registry+https://github.com/rust-lang/crates.io-index" 564 | checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" 565 | dependencies = [ 566 | "getrandom", 567 | ] 568 | 569 | [[package]] 570 | name = "regex" 571 | version = "1.11.1" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 574 | dependencies = [ 575 | "aho-corasick", 576 | "memchr", 577 | "regex-automata", 578 | "regex-syntax", 579 | ] 580 | 581 | [[package]] 582 | name = "regex-automata" 583 | version = "0.4.9" 584 | source = "registry+https://github.com/rust-lang/crates.io-index" 585 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 586 | dependencies = [ 587 | "aho-corasick", 588 | "memchr", 589 | "regex-syntax", 590 | ] 591 | 592 | [[package]] 593 | name = "regex-syntax" 594 | version = "0.8.5" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 597 | 598 | [[package]] 599 | name = "rlimit" 600 | version = "0.10.2" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "7043b63bd0cd1aaa628e476b80e6d4023a3b50eb32789f2728908107bd0c793a" 603 | dependencies = [ 604 | "libc", 605 | ] 606 | 607 | [[package]] 608 | name = "roff" 609 | version = "0.2.2" 610 | source = "registry+https://github.com/rust-lang/crates.io-index" 611 | checksum = "88f8660c1ff60292143c98d08fc6e2f654d722db50410e3f3797d40baaf9d8f3" 612 | 613 | [[package]] 614 | name = "rustc-hash" 615 | version = "2.1.1" 616 | source = "registry+https://github.com/rust-lang/crates.io-index" 617 | checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" 618 | 619 | [[package]] 620 | name = "rustix" 621 | version = "0.38.44" 622 | source = "registry+https://github.com/rust-lang/crates.io-index" 623 | checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" 624 | dependencies = [ 625 | "bitflags", 626 | "errno", 627 | "libc", 628 | "linux-raw-sys 0.4.15", 629 | "windows-sys 0.52.0", 630 | ] 631 | 632 | [[package]] 633 | name = "rustix" 634 | version = "1.0.5" 635 | source = "registry+https://github.com/rust-lang/crates.io-index" 636 | checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" 637 | dependencies = [ 638 | "bitflags", 639 | "errno", 640 | "libc", 641 | "linux-raw-sys 0.9.4", 642 | "windows-sys 0.52.0", 643 | ] 644 | 645 | [[package]] 646 | name = "self_cell" 647 | version = "1.2.0" 648 | source = "registry+https://github.com/rust-lang/crates.io-index" 649 | checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749" 650 | 651 | [[package]] 652 | name = "serde" 653 | version = "1.0.219" 654 | source = "registry+https://github.com/rust-lang/crates.io-index" 655 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 656 | dependencies = [ 657 | "serde_derive", 658 | ] 659 | 660 | [[package]] 661 | name = "serde_derive" 662 | version = "1.0.219" 663 | source = "registry+https://github.com/rust-lang/crates.io-index" 664 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 665 | dependencies = [ 666 | "proc-macro2", 667 | "quote", 668 | "syn", 669 | ] 670 | 671 | [[package]] 672 | name = "siphasher" 673 | version = "1.0.1" 674 | source = "registry+https://github.com/rust-lang/crates.io-index" 675 | checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" 676 | 677 | [[package]] 678 | name = "smallvec" 679 | version = "1.15.0" 680 | source = "registry+https://github.com/rust-lang/crates.io-index" 681 | checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" 682 | 683 | [[package]] 684 | name = "smawk" 685 | version = "0.3.2" 686 | source = "registry+https://github.com/rust-lang/crates.io-index" 687 | checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" 688 | 689 | [[package]] 690 | name = "socket2" 691 | version = "0.5.9" 692 | source = "registry+https://github.com/rust-lang/crates.io-index" 693 | checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" 694 | dependencies = [ 695 | "libc", 696 | "windows-sys 0.52.0", 697 | ] 698 | 699 | [[package]] 700 | name = "strsim" 701 | version = "0.11.1" 702 | source = "registry+https://github.com/rust-lang/crates.io-index" 703 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 704 | 705 | [[package]] 706 | name = "syn" 707 | version = "2.0.100" 708 | source = "registry+https://github.com/rust-lang/crates.io-index" 709 | checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" 710 | dependencies = [ 711 | "proc-macro2", 712 | "quote", 713 | "unicode-ident", 714 | ] 715 | 716 | [[package]] 717 | name = "tempfile" 718 | version = "3.20.0" 719 | source = "registry+https://github.com/rust-lang/crates.io-index" 720 | checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" 721 | dependencies = [ 722 | "fastrand", 723 | "getrandom", 724 | "once_cell", 725 | "rustix 1.0.5", 726 | "windows-sys 0.52.0", 727 | ] 728 | 729 | [[package]] 730 | name = "terminal_size" 731 | version = "0.4.2" 732 | source = "registry+https://github.com/rust-lang/crates.io-index" 733 | checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" 734 | dependencies = [ 735 | "rustix 1.0.5", 736 | "windows-sys 0.59.0", 737 | ] 738 | 739 | [[package]] 740 | name = "textwrap" 741 | version = "0.16.2" 742 | source = "registry+https://github.com/rust-lang/crates.io-index" 743 | checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" 744 | dependencies = [ 745 | "smawk", 746 | "terminal_size", 747 | "unicode-linebreak", 748 | "unicode-width", 749 | ] 750 | 751 | [[package]] 752 | name = "thiserror" 753 | version = "2.0.12" 754 | source = "registry+https://github.com/rust-lang/crates.io-index" 755 | checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" 756 | dependencies = [ 757 | "thiserror-impl", 758 | ] 759 | 760 | [[package]] 761 | name = "thiserror-impl" 762 | version = "2.0.12" 763 | source = "registry+https://github.com/rust-lang/crates.io-index" 764 | checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" 765 | dependencies = [ 766 | "proc-macro2", 767 | "quote", 768 | "syn", 769 | ] 770 | 771 | [[package]] 772 | name = "time" 773 | version = "0.3.41" 774 | source = "registry+https://github.com/rust-lang/crates.io-index" 775 | checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" 776 | dependencies = [ 777 | "deranged", 778 | "itoa", 779 | "libc", 780 | "num-conv", 781 | "num_threads", 782 | "powerfmt", 783 | "serde", 784 | "time-core", 785 | "time-macros", 786 | ] 787 | 788 | [[package]] 789 | name = "time-core" 790 | version = "0.1.4" 791 | source = "registry+https://github.com/rust-lang/crates.io-index" 792 | checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" 793 | 794 | [[package]] 795 | name = "time-macros" 796 | version = "0.2.22" 797 | source = "registry+https://github.com/rust-lang/crates.io-index" 798 | checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" 799 | dependencies = [ 800 | "num-conv", 801 | "time-core", 802 | ] 803 | 804 | [[package]] 805 | name = "tinystr" 806 | version = "0.8.1" 807 | source = "registry+https://github.com/rust-lang/crates.io-index" 808 | checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" 809 | dependencies = [ 810 | "displaydoc", 811 | "zerovec", 812 | ] 813 | 814 | [[package]] 815 | name = "type-map" 816 | version = "0.5.1" 817 | source = "registry+https://github.com/rust-lang/crates.io-index" 818 | checksum = "cb30dbbd9036155e74adad6812e9898d03ec374946234fbcebd5dfc7b9187b90" 819 | dependencies = [ 820 | "rustc-hash", 821 | ] 822 | 823 | [[package]] 824 | name = "unic-langid" 825 | version = "0.9.6" 826 | source = "registry+https://github.com/rust-lang/crates.io-index" 827 | checksum = "a28ba52c9b05311f4f6e62d5d9d46f094bd6e84cb8df7b3ef952748d752a7d05" 828 | dependencies = [ 829 | "unic-langid-impl", 830 | ] 831 | 832 | [[package]] 833 | name = "unic-langid-impl" 834 | version = "0.9.6" 835 | source = "registry+https://github.com/rust-lang/crates.io-index" 836 | checksum = "dce1bf08044d4b7a94028c93786f8566047edc11110595914de93362559bc658" 837 | dependencies = [ 838 | "tinystr", 839 | ] 840 | 841 | [[package]] 842 | name = "unicode-ident" 843 | version = "1.0.18" 844 | source = "registry+https://github.com/rust-lang/crates.io-index" 845 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 846 | 847 | [[package]] 848 | name = "unicode-linebreak" 849 | version = "0.1.5" 850 | source = "registry+https://github.com/rust-lang/crates.io-index" 851 | checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" 852 | 853 | [[package]] 854 | name = "unicode-width" 855 | version = "0.2.0" 856 | source = "registry+https://github.com/rust-lang/crates.io-index" 857 | checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" 858 | 859 | [[package]] 860 | name = "utf8parse" 861 | version = "0.2.2" 862 | source = "registry+https://github.com/rust-lang/crates.io-index" 863 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 864 | 865 | [[package]] 866 | name = "uu_hostname" 867 | version = "0.0.1" 868 | dependencies = [ 869 | "clap", 870 | "errno", 871 | "libc", 872 | "uucore", 873 | "windows-sys 0.59.0", 874 | ] 875 | 876 | [[package]] 877 | name = "uucore" 878 | version = "0.1.0" 879 | source = "registry+https://github.com/rust-lang/crates.io-index" 880 | checksum = "9032bf981784f22fcc5ddc7e74b7cf3bae3d5f44a48d2054138ed38068b9f4e0" 881 | dependencies = [ 882 | "clap", 883 | "dns-lookup", 884 | "fluent", 885 | "fluent-bundle", 886 | "libc", 887 | "nix", 888 | "number_prefix", 889 | "os_display", 890 | "thiserror", 891 | "time", 892 | "unic-langid", 893 | "uucore_procs", 894 | "wild", 895 | ] 896 | 897 | [[package]] 898 | name = "uucore_procs" 899 | version = "0.1.0" 900 | source = "registry+https://github.com/rust-lang/crates.io-index" 901 | checksum = "c933945fdac5b7779eae1fc746146e61f5b0298deb6ede002ce0b6e93e1b3bfc" 902 | dependencies = [ 903 | "proc-macro2", 904 | "quote", 905 | "uuhelp_parser", 906 | ] 907 | 908 | [[package]] 909 | name = "uuhelp_parser" 910 | version = "0.1.0" 911 | source = "registry+https://github.com/rust-lang/crates.io-index" 912 | checksum = "beda381dd5c7927f8682f50b055b0903bb694ba5a4b27fad1b4934bc4fbf7b8d" 913 | 914 | [[package]] 915 | name = "uutests" 916 | version = "0.1.0" 917 | source = "registry+https://github.com/rust-lang/crates.io-index" 918 | checksum = "12453ee52c9cffa6bf2c74f9f35ed0c824b846cb0b4bdee829d8150332e7204c" 919 | dependencies = [ 920 | "ctor", 921 | "glob", 922 | "libc", 923 | "nix", 924 | "pretty_assertions", 925 | "rand 0.9.1", 926 | "regex", 927 | "rlimit", 928 | "tempfile", 929 | "time", 930 | "uucore", 931 | "xattr", 932 | ] 933 | 934 | [[package]] 935 | name = "wasi" 936 | version = "0.14.2+wasi-0.2.4" 937 | source = "registry+https://github.com/rust-lang/crates.io-index" 938 | checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" 939 | dependencies = [ 940 | "wit-bindgen-rt", 941 | ] 942 | 943 | [[package]] 944 | name = "wild" 945 | version = "2.2.1" 946 | source = "registry+https://github.com/rust-lang/crates.io-index" 947 | checksum = "a3131afc8c575281e1e80f36ed6a092aa502c08b18ed7524e86fbbb12bb410e1" 948 | dependencies = [ 949 | "glob", 950 | ] 951 | 952 | [[package]] 953 | name = "windows-sys" 954 | version = "0.48.0" 955 | source = "registry+https://github.com/rust-lang/crates.io-index" 956 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 957 | dependencies = [ 958 | "windows-targets 0.48.5", 959 | ] 960 | 961 | [[package]] 962 | name = "windows-sys" 963 | version = "0.52.0" 964 | source = "registry+https://github.com/rust-lang/crates.io-index" 965 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 966 | dependencies = [ 967 | "windows-targets 0.52.6", 968 | ] 969 | 970 | [[package]] 971 | name = "windows-sys" 972 | version = "0.59.0" 973 | source = "registry+https://github.com/rust-lang/crates.io-index" 974 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 975 | dependencies = [ 976 | "windows-targets 0.52.6", 977 | ] 978 | 979 | [[package]] 980 | name = "windows-targets" 981 | version = "0.48.5" 982 | source = "registry+https://github.com/rust-lang/crates.io-index" 983 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 984 | dependencies = [ 985 | "windows_aarch64_gnullvm 0.48.5", 986 | "windows_aarch64_msvc 0.48.5", 987 | "windows_i686_gnu 0.48.5", 988 | "windows_i686_msvc 0.48.5", 989 | "windows_x86_64_gnu 0.48.5", 990 | "windows_x86_64_gnullvm 0.48.5", 991 | "windows_x86_64_msvc 0.48.5", 992 | ] 993 | 994 | [[package]] 995 | name = "windows-targets" 996 | version = "0.52.6" 997 | source = "registry+https://github.com/rust-lang/crates.io-index" 998 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 999 | dependencies = [ 1000 | "windows_aarch64_gnullvm 0.52.6", 1001 | "windows_aarch64_msvc 0.52.6", 1002 | "windows_i686_gnu 0.52.6", 1003 | "windows_i686_gnullvm", 1004 | "windows_i686_msvc 0.52.6", 1005 | "windows_x86_64_gnu 0.52.6", 1006 | "windows_x86_64_gnullvm 0.52.6", 1007 | "windows_x86_64_msvc 0.52.6", 1008 | ] 1009 | 1010 | [[package]] 1011 | name = "windows_aarch64_gnullvm" 1012 | version = "0.48.5" 1013 | source = "registry+https://github.com/rust-lang/crates.io-index" 1014 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 1015 | 1016 | [[package]] 1017 | name = "windows_aarch64_gnullvm" 1018 | version = "0.52.6" 1019 | source = "registry+https://github.com/rust-lang/crates.io-index" 1020 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 1021 | 1022 | [[package]] 1023 | name = "windows_aarch64_msvc" 1024 | version = "0.48.5" 1025 | source = "registry+https://github.com/rust-lang/crates.io-index" 1026 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 1027 | 1028 | [[package]] 1029 | name = "windows_aarch64_msvc" 1030 | version = "0.52.6" 1031 | source = "registry+https://github.com/rust-lang/crates.io-index" 1032 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 1033 | 1034 | [[package]] 1035 | name = "windows_i686_gnu" 1036 | version = "0.48.5" 1037 | source = "registry+https://github.com/rust-lang/crates.io-index" 1038 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 1039 | 1040 | [[package]] 1041 | name = "windows_i686_gnu" 1042 | version = "0.52.6" 1043 | source = "registry+https://github.com/rust-lang/crates.io-index" 1044 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 1045 | 1046 | [[package]] 1047 | name = "windows_i686_gnullvm" 1048 | version = "0.52.6" 1049 | source = "registry+https://github.com/rust-lang/crates.io-index" 1050 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 1051 | 1052 | [[package]] 1053 | name = "windows_i686_msvc" 1054 | version = "0.48.5" 1055 | source = "registry+https://github.com/rust-lang/crates.io-index" 1056 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 1057 | 1058 | [[package]] 1059 | name = "windows_i686_msvc" 1060 | version = "0.52.6" 1061 | source = "registry+https://github.com/rust-lang/crates.io-index" 1062 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 1063 | 1064 | [[package]] 1065 | name = "windows_x86_64_gnu" 1066 | version = "0.48.5" 1067 | source = "registry+https://github.com/rust-lang/crates.io-index" 1068 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 1069 | 1070 | [[package]] 1071 | name = "windows_x86_64_gnu" 1072 | version = "0.52.6" 1073 | source = "registry+https://github.com/rust-lang/crates.io-index" 1074 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 1075 | 1076 | [[package]] 1077 | name = "windows_x86_64_gnullvm" 1078 | version = "0.48.5" 1079 | source = "registry+https://github.com/rust-lang/crates.io-index" 1080 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 1081 | 1082 | [[package]] 1083 | name = "windows_x86_64_gnullvm" 1084 | version = "0.52.6" 1085 | source = "registry+https://github.com/rust-lang/crates.io-index" 1086 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 1087 | 1088 | [[package]] 1089 | name = "windows_x86_64_msvc" 1090 | version = "0.48.5" 1091 | source = "registry+https://github.com/rust-lang/crates.io-index" 1092 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 1093 | 1094 | [[package]] 1095 | name = "windows_x86_64_msvc" 1096 | version = "0.52.6" 1097 | source = "registry+https://github.com/rust-lang/crates.io-index" 1098 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1099 | 1100 | [[package]] 1101 | name = "wit-bindgen-rt" 1102 | version = "0.39.0" 1103 | source = "registry+https://github.com/rust-lang/crates.io-index" 1104 | checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" 1105 | dependencies = [ 1106 | "bitflags", 1107 | ] 1108 | 1109 | [[package]] 1110 | name = "xattr" 1111 | version = "1.5.0" 1112 | source = "registry+https://github.com/rust-lang/crates.io-index" 1113 | checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e" 1114 | dependencies = [ 1115 | "libc", 1116 | "rustix 1.0.5", 1117 | ] 1118 | 1119 | [[package]] 1120 | name = "yansi" 1121 | version = "1.0.1" 1122 | source = "registry+https://github.com/rust-lang/crates.io-index" 1123 | checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" 1124 | 1125 | [[package]] 1126 | name = "zerocopy" 1127 | version = "0.8.24" 1128 | source = "registry+https://github.com/rust-lang/crates.io-index" 1129 | checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" 1130 | dependencies = [ 1131 | "zerocopy-derive", 1132 | ] 1133 | 1134 | [[package]] 1135 | name = "zerocopy-derive" 1136 | version = "0.8.24" 1137 | source = "registry+https://github.com/rust-lang/crates.io-index" 1138 | checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" 1139 | dependencies = [ 1140 | "proc-macro2", 1141 | "quote", 1142 | "syn", 1143 | ] 1144 | 1145 | [[package]] 1146 | name = "zerofrom" 1147 | version = "0.1.6" 1148 | source = "registry+https://github.com/rust-lang/crates.io-index" 1149 | checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" 1150 | 1151 | [[package]] 1152 | name = "zerovec" 1153 | version = "0.11.2" 1154 | source = "registry+https://github.com/rust-lang/crates.io-index" 1155 | checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" 1156 | dependencies = [ 1157 | "zerofrom", 1158 | ] 1159 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | # hostname (uutils) 2 | # * see the repository LICENSE, README, and CONTRIBUTING files for more information 3 | 4 | # spell-checker:ignore (libs) mangen procfs 5 | 6 | [package] 7 | name = "hostname" 8 | version = "0.0.1" 9 | authors = ["uutils developers"] 10 | license = "MIT" 11 | description = "hostname ~ implemented as universal (cross-platform) utils, written in Rust" 12 | default-run = "hostname" 13 | 14 | homepage = "https://github.com/uutils/hostname" 15 | repository = "https://github.com/uutils/hostname" 16 | readme = "README.md" 17 | keywords = ["hostname", "uutils", "cross-platform", "cli", "utility"] 18 | categories = ["command-line-utilities"] 19 | edition = "2024" 20 | 21 | build = "build.rs" 22 | 23 | [features] 24 | default = ["feat_common_core"] 25 | uudoc = [] 26 | 27 | feat_common_core = [ 28 | "hostname", 29 | ] 30 | 31 | [workspace.dependencies] 32 | uucore = "0.1.0" 33 | uutests = "0.1.0" 34 | ctor = "0.4.1" 35 | clap = { version = "4.5.4", features = ["wrap_help", "cargo"] } 36 | clap_complete = "4.5.2" 37 | clap_mangen = "0.2.20" 38 | regex = "1.10.4" 39 | sysinfo = "0.35.0" 40 | libc = "0.2.154" 41 | errno = "0.3" 42 | phf = "0.11.2" 43 | phf_codegen = "0.11.2" 44 | textwrap = { version = "0.16.1", features = ["terminal_size"] } 45 | xattr = "1.3.1" 46 | tempfile = "3.10.1" 47 | rand = { version = "0.9.0", features = ["small_rng"] } 48 | utmpx = "0.2" 49 | windows-sys = { version = "0.59", features = [ 50 | "Win32_Globalization", 51 | "Win32_Networking_WinSock", 52 | "Win32_NetworkManagement_IpHelper", 53 | "Win32_NetworkManagement_Ndis", 54 | "Win32_System_SystemInformation", 55 | "Win32_Foundation", 56 | ] } 57 | 58 | [dependencies] 59 | clap = { workspace = true } 60 | clap_complete = { workspace = true } 61 | clap_mangen = { workspace = true } 62 | uucore = { workspace = true } 63 | phf = { workspace = true } 64 | textwrap = { workspace = true } 65 | 66 | 67 | # 68 | hostname = { optional = true, version = "0.0.1", package = "uu_hostname", path = "src/uu/hostname" } 69 | 70 | [dev-dependencies] 71 | pretty_assertions = "1.4.0" 72 | regex = { workspace = true } 73 | tempfile = { workspace = true } 74 | libc = { workspace = true } 75 | rand = { workspace = true } 76 | uutests = { workspace = true } 77 | ctor = { workspace = true } 78 | uucore = { workspace = true, features = ["entries", "process", "signals"] } 79 | 80 | [target.'cfg(unix)'.dev-dependencies] 81 | xattr = { workspace = true } 82 | 83 | [target.'cfg(any(target_os = "linux", target_os = "android"))'.dev-dependencies] 84 | procfs = { version = "0.17", default-features = false } 85 | rlimit = "0.10.1" 86 | 87 | [build-dependencies] 88 | phf_codegen = { workspace = true } 89 | 90 | 91 | [[bin]] 92 | name = "hostname" 93 | path = "src/bin/hostname.rs" 94 | 95 | [[bin]] 96 | name = "uudoc" 97 | path = "src/bin/uudoc.rs" 98 | required-features = ["uudoc"] 99 | 100 | # The default release profile. It contains all optimizations, without 101 | # sacrificing debug info. With this profile (like in the standard 102 | # release profile), the debug info and the stack traces will still be available. 103 | [profile.release] 104 | lto = true 105 | 106 | # A release-like profile that is tuned to be fast, even when being fast 107 | # compromises on binary size. This includes aborting on panic. 108 | [profile.release-fast] 109 | inherits = "release" 110 | panic = "abort" 111 | 112 | # A release-like profile that is as small as possible. 113 | [profile.release-small] 114 | inherits = "release" 115 | opt-level = "z" 116 | panic = "abort" 117 | strip = true 118 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 uutils 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | hostname 2 | ======== 3 | 4 | 5 | hostname provides commands which can be used to display the system's 6 | DNS name, and to display or set its hostname or NIS domain name. 7 | 8 | Reimplement the following commands in Rust: 9 | * /bin/hostname 10 | * /bin/dnsdomainname 11 | * /bin/domainname 12 | * /bin/nisdomainname 13 | * /bin/ypdomainname 14 | 15 | Upstream: 16 | TODO 17 | 18 | ## License 19 | 20 | hostname is licensed under the MIT License - see the `LICENSE` file for details 21 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils hostname package. 2 | // 3 | // For the full copyright and license information, please view the LICENSE 4 | // file that was distributed with this source code. 5 | 6 | // spell-checker:ignore (vars) krate 7 | 8 | use std::env; 9 | use std::fs::File; 10 | use std::io::Write; 11 | use std::path::Path; 12 | 13 | pub fn main() { 14 | if let Ok(profile) = env::var("PROFILE") { 15 | println!("cargo:rustc-cfg=build={profile:?}"); 16 | } 17 | 18 | const ENV_FEATURE_PREFIX: &str = "CARGO_FEATURE_"; 19 | const FEATURE_PREFIX: &str = "feat_"; 20 | const OVERRIDE_PREFIX: &str = "uu_"; 21 | 22 | let out_dir = env::var("OUT_DIR").unwrap(); 23 | 24 | let mut crates = Vec::new(); 25 | for (key, val) in env::vars() { 26 | if val == "1" && key.starts_with(ENV_FEATURE_PREFIX) { 27 | let krate = key[ENV_FEATURE_PREFIX.len()..].to_lowercase(); 28 | // Allow this as we have a bunch of info in the comments 29 | #[allow(clippy::match_same_arms)] 30 | match krate.as_ref() { 31 | "default" | "macos" | "unix" | "windows" | "selinux" | "zip" => continue, // common/standard feature names 32 | "nightly" | "test_unimplemented" => continue, // crate-local custom features 33 | "uudoc" => continue, // is not a utility 34 | "test" => continue, // over-ridden with 'uu_test' to avoid collision with rust core crate 'test' 35 | s if s.starts_with(FEATURE_PREFIX) => continue, // crate feature sets 36 | _ => {} // util feature name 37 | } 38 | crates.push(krate); 39 | } 40 | } 41 | crates.sort(); 42 | 43 | let mut mf = File::create(Path::new(&out_dir).join("uutils_map.rs")).unwrap(); 44 | 45 | mf.write_all( 46 | "type UtilityMap = phf::OrderedMap<&'static str, (fn(T) -> i32, fn() -> Command)>;\n\ 47 | \n\ 48 | #[allow(clippy::too_many_lines)] 49 | fn util_map() -> UtilityMap {\n" 50 | .as_bytes(), 51 | ) 52 | .unwrap(); 53 | 54 | let mut phf_map = phf_codegen::OrderedMap::<&str>::new(); 55 | for krate in &crates { 56 | let map_value = format!("({krate}::uumain, {krate}::uu_app)"); 57 | match krate.as_ref() { 58 | // 'test' is named uu_test to avoid collision with rust core crate 'test'. 59 | // It can also be invoked by name '[' for the '[ expr ] syntax'. 60 | "uu_test" => { 61 | phf_map.entry("test", &map_value); 62 | phf_map.entry("[", &map_value); 63 | } 64 | k if k.starts_with(OVERRIDE_PREFIX) => { 65 | phf_map.entry(&k[OVERRIDE_PREFIX.len()..], &map_value); 66 | } 67 | "false" | "true" => { 68 | phf_map.entry(krate, &format!("(r#{krate}::uumain, r#{krate}::uu_app)")); 69 | } 70 | "hashsum" => { 71 | phf_map.entry(krate, &format!("({krate}::uumain, {krate}::uu_app_custom)")); 72 | 73 | let map_value = format!("({krate}::uumain, {krate}::uu_app_common)"); 74 | let map_value_bits = format!("({krate}::uumain, {krate}::uu_app_bits)"); 75 | let map_value_b3sum = format!("({krate}::uumain, {krate}::uu_app_b3sum)"); 76 | phf_map.entry("md5sum", &map_value); 77 | phf_map.entry("sha1sum", &map_value); 78 | phf_map.entry("sha224sum", &map_value); 79 | phf_map.entry("sha256sum", &map_value); 80 | phf_map.entry("sha384sum", &map_value); 81 | phf_map.entry("sha512sum", &map_value); 82 | phf_map.entry("sha3sum", &map_value_bits); 83 | phf_map.entry("sha3-224sum", &map_value); 84 | phf_map.entry("sha3-256sum", &map_value); 85 | phf_map.entry("sha3-384sum", &map_value); 86 | phf_map.entry("sha3-512sum", &map_value); 87 | phf_map.entry("shake128sum", &map_value_bits); 88 | phf_map.entry("shake256sum", &map_value_bits); 89 | phf_map.entry("b2sum", &map_value); 90 | phf_map.entry("b3sum", &map_value_b3sum); 91 | } 92 | _ => { 93 | phf_map.entry(krate, &map_value); 94 | } 95 | } 96 | } 97 | write!(mf, "{}", phf_map.build()).unwrap(); 98 | mf.write_all(b"\n}\n").unwrap(); 99 | 100 | mf.flush().unwrap(); 101 | } 102 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /src/bin/hostname.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils hostname package. 2 | // 3 | // For the full copyright and license information, please view the LICENSE 4 | // file that was distributed with this source code. 5 | 6 | // spell-checker:ignore manpages mangen 7 | 8 | use clap::{Arg, Command}; 9 | use clap_complete::Shell; 10 | use std::cmp; 11 | use std::ffi::OsStr; 12 | use std::ffi::OsString; 13 | use std::io::{self, Write}; 14 | use std::path::{Path, PathBuf}; 15 | use std::process; 16 | use uucore::display::Quotable; 17 | 18 | const VERSION: &str = env!("CARGO_PKG_VERSION"); 19 | 20 | include!(concat!(env!("OUT_DIR"), "/uutils_map.rs")); 21 | 22 | fn usage(utils: &UtilityMap, name: &str) { 23 | println!("{name} {VERSION} (multi-call binary)\n"); 24 | println!("Usage: {name} [function [arguments...]]\n"); 25 | println!("Currently defined functions:\n"); 26 | #[allow(clippy::map_clone)] 27 | let mut utils: Vec<&str> = utils.keys().map(|&s| s).collect(); 28 | utils.sort_unstable(); 29 | let display_list = utils.join(", "); 30 | let width = cmp::min(textwrap::termwidth(), 100) - 4 * 2; // (opinion/heuristic) max 100 chars wide with 4 character side indentions 31 | println!( 32 | "{}", 33 | textwrap::indent(&textwrap::fill(&display_list, width), " ") 34 | ); 35 | } 36 | 37 | fn binary_path(args: &mut impl Iterator) -> PathBuf { 38 | match args.next() { 39 | Some(ref s) if !s.is_empty() => PathBuf::from(s), 40 | _ => std::env::current_exe().unwrap(), 41 | } 42 | } 43 | 44 | fn name(binary_path: &Path) -> Option<&str> { 45 | binary_path.file_stem()?.to_str() 46 | } 47 | 48 | #[allow(clippy::cognitive_complexity)] 49 | fn main() { 50 | uucore::panic::mute_sigpipe_panic(); 51 | 52 | let utils = util_map(); 53 | let mut args = uucore::args_os(); 54 | 55 | let binary = binary_path(&mut args); 56 | let binary_as_util = name(&binary).unwrap_or_else(|| { 57 | usage(&utils, ""); 58 | process::exit(0); 59 | }); 60 | 61 | // binary name equals util name? 62 | if let Some(&(uumain, _)) = utils.get(binary_as_util) { 63 | process::exit(uumain((vec![binary.into()].into_iter()).chain(args))); 64 | } 65 | 66 | // binary name equals prefixed util name? 67 | // * prefix/stem may be any string ending in a non-alphanumeric character 68 | let util_name = if let Some(util) = utils.keys().find(|util| { 69 | binary_as_util.ends_with(*util) 70 | && !binary_as_util[..binary_as_util.len() - (*util).len()] 71 | .ends_with(char::is_alphanumeric) 72 | }) { 73 | // prefixed util => replace 0th (aka, executable name) argument 74 | Some(OsString::from(*util)) 75 | } else { 76 | // unmatched binary name => regard as multi-binary container and advance argument list 77 | uucore::set_utility_is_second_arg(); 78 | args.next() 79 | }; 80 | 81 | // 0th argument equals util name? 82 | if let Some(util_os) = util_name { 83 | fn not_found(util: &OsStr) -> ! { 84 | println!("{}: function/utility not found", util.maybe_quote()); 85 | process::exit(1); 86 | } 87 | 88 | let util = match util_os.to_str() { 89 | Some(util) => util, 90 | None => not_found(&util_os), 91 | }; 92 | 93 | if util == "completion" { 94 | gen_completions(args, &utils); 95 | } 96 | 97 | if util == "manpage" { 98 | gen_manpage(args, &utils); 99 | } 100 | 101 | match utils.get(util) { 102 | Some(&(uumain, _)) => { 103 | process::exit(uumain((vec![util_os].into_iter()).chain(args))); 104 | } 105 | None => { 106 | if util == "--help" || util == "-h" { 107 | // see if they want help on a specific util 108 | if let Some(util_os) = args.next() { 109 | let util = match util_os.to_str() { 110 | Some(util) => util, 111 | None => not_found(&util_os), 112 | }; 113 | 114 | match utils.get(util) { 115 | Some(&(uumain, _)) => { 116 | let code = uumain( 117 | (vec![util_os, OsString::from("--help")].into_iter()) 118 | .chain(args), 119 | ); 120 | io::stdout().flush().expect("could not flush stdout"); 121 | process::exit(code); 122 | } 123 | None => not_found(&util_os), 124 | } 125 | } 126 | usage(&utils, binary_as_util); 127 | process::exit(0); 128 | } else { 129 | not_found(&util_os); 130 | } 131 | } 132 | } 133 | } else { 134 | // no arguments provided 135 | usage(&utils, binary_as_util); 136 | process::exit(0); 137 | } 138 | } 139 | 140 | /// Prints completions for the utility in the first parameter for the shell in the second parameter to stdout 141 | fn gen_completions( 142 | args: impl Iterator, 143 | util_map: &UtilityMap, 144 | ) -> ! { 145 | let all_utilities: Vec<_> = std::iter::once("hostname") 146 | .chain(util_map.keys().copied()) 147 | .collect(); 148 | 149 | let matches = Command::new("completion") 150 | .about("Prints completions to stdout") 151 | .arg( 152 | Arg::new("utility") 153 | .value_parser(clap::builder::PossibleValuesParser::new(all_utilities)) 154 | .required(true), 155 | ) 156 | .arg( 157 | Arg::new("shell") 158 | .value_parser(clap::builder::EnumValueParser::::new()) 159 | .required(true), 160 | ) 161 | .get_matches_from(std::iter::once(OsString::from("completion")).chain(args)); 162 | 163 | let utility = matches.get_one::("utility").unwrap(); 164 | let shell = *matches.get_one::("shell").unwrap(); 165 | 166 | let mut command = if utility == "hostname" { 167 | gen_hostname_app(util_map) 168 | } else { 169 | util_map.get(utility).unwrap().1() 170 | }; 171 | let bin_name = std::env::var("PROG_PREFIX").unwrap_or_default() + utility; 172 | 173 | clap_complete::generate(shell, &mut command, bin_name, &mut io::stdout()); 174 | io::stdout().flush().unwrap(); 175 | process::exit(0); 176 | } 177 | 178 | /// Generate the manpage for the utility in the first parameter 179 | fn gen_manpage( 180 | args: impl Iterator, 181 | util_map: &UtilityMap, 182 | ) -> ! { 183 | let all_utilities: Vec<_> = std::iter::once("hostname") 184 | .chain(util_map.keys().copied()) 185 | .collect(); 186 | 187 | let matches = Command::new("manpage") 188 | .about("Prints manpage to stdout") 189 | .arg( 190 | Arg::new("utility") 191 | .value_parser(clap::builder::PossibleValuesParser::new(all_utilities)) 192 | .required(true), 193 | ) 194 | .get_matches_from(std::iter::once(OsString::from("manpage")).chain(args)); 195 | 196 | let utility = matches.get_one::("utility").unwrap(); 197 | 198 | let command = if utility == "hostname" { 199 | gen_hostname_app(util_map) 200 | } else { 201 | util_map.get(utility).unwrap().1() 202 | }; 203 | 204 | let man = clap_mangen::Man::new(command); 205 | man.render(&mut io::stdout()) 206 | .expect("Man page generation failed"); 207 | io::stdout().flush().unwrap(); 208 | process::exit(0); 209 | } 210 | 211 | fn gen_hostname_app(util_map: &UtilityMap) -> Command { 212 | let mut command = Command::new("hostname"); 213 | for (name, (_, sub_app)) in util_map { 214 | // Recreate a small subcommand with only the relevant info 215 | // (name & short description) 216 | let about = sub_app() 217 | .get_about() 218 | .expect("Could not get the 'about'") 219 | .to_string(); 220 | let sub_app = Command::new(name).about(about); 221 | command = command.subcommand(sub_app); 222 | } 223 | command 224 | } 225 | -------------------------------------------------------------------------------- /src/bin/uudoc.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils hostname package. 2 | // 3 | // For the full copyright and license information, please view the LICENSE 4 | // file that was distributed with this source code. 5 | // spell-checker:ignore tldr uuhelp 6 | 7 | use clap::Command; 8 | use std::collections::HashMap; 9 | use std::ffi::OsString; 10 | use std::fs::File; 11 | use std::io::{self, Read, Seek, Write}; 12 | use zip::ZipArchive; 13 | 14 | include!(concat!(env!("OUT_DIR"), "/uutils_map.rs")); 15 | 16 | fn main() -> io::Result<()> { 17 | let mut tldr_zip = File::open("docs/tldr.zip") 18 | .ok() 19 | .and_then(|f| ZipArchive::new(f).ok()); 20 | 21 | if tldr_zip.is_none() { 22 | println!("Warning: No tldr archive found, so the documentation will not include examples."); 23 | println!( 24 | "To include examples in the documentation, download the tldr archive and put it in the docs/ folder." 25 | ); 26 | println!(); 27 | println!(" curl https://tldr.sh/assets/tldr.zip -o docs/tldr.zip"); 28 | println!(); 29 | } 30 | 31 | let utils = util_map::>>(); 32 | match std::fs::create_dir("docs/src/utils/") { 33 | Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => Ok(()), 34 | x => x, 35 | }?; 36 | 37 | println!("Writing initial info to SUMMARY.md"); 38 | let mut summary = File::create("docs/src/SUMMARY.md")?; 39 | 40 | let _ = write!( 41 | summary, 42 | "# Summary\n\ 43 | \n\ 44 | [Introduction](index.md)\n\ 45 | * [Installation](installation.md)\n\ 46 | * [Build from source](build.md)\n\ 47 | * [Platform support](platforms.md)\n\ 48 | * [Contributing](contributing.md)\n\ 49 | * [GNU test coverage](test_coverage.md)\n\ 50 | * [Extensions](extensions.md)\n\ 51 | \n\ 52 | # Reference\n\ 53 | * [Multi-call binary](multicall.md)\n", 54 | ); 55 | 56 | println!("Gathering utils per platform"); 57 | let utils_per_platform = { 58 | let mut map = HashMap::new(); 59 | for platform in ["unix", "macos", "windows", "unix_android"] { 60 | let platform_utils: Vec = String::from_utf8( 61 | std::process::Command::new("./util/show-utils.sh") 62 | .arg(format!("--features=feat_os_{}", platform)) 63 | .output()? 64 | .stdout, 65 | ) 66 | .unwrap() 67 | .trim() 68 | .split(' ') 69 | .map(ToString::to_string) 70 | .collect(); 71 | map.insert(platform, platform_utils); 72 | } 73 | 74 | // Linux is a special case because it can support selinux 75 | let platform_utils: Vec = String::from_utf8( 76 | std::process::Command::new("./util/show-utils.sh") 77 | .arg("--features=feat_os_unix feat_selinux") 78 | .output()? 79 | .stdout, 80 | ) 81 | .unwrap() 82 | .trim() 83 | .split(' ') 84 | .map(ToString::to_string) 85 | .collect(); 86 | map.insert("linux", platform_utils); 87 | 88 | map 89 | }; 90 | 91 | let mut utils = utils.entries().collect::>(); 92 | utils.sort(); 93 | 94 | println!("Writing util per platform table"); 95 | { 96 | let mut platform_table_file = File::create("docs/src/platform_table.md").unwrap(); 97 | 98 | // sum, cksum, b2sum, etc. are all available on all platforms, but not in the data structure 99 | // otherwise, we check the map for the util name. 100 | let check_supported = |name: &str, platform: &str| { 101 | if name.ends_with("sum") || utils_per_platform[platform].iter().any(|u| u == name) { 102 | "✓" 103 | } else { 104 | " " 105 | } 106 | }; 107 | writeln!( 108 | platform_table_file, 109 | "| util | Linux | macOS | Windows | FreeBSD | Android |\n\ 110 | | ---------------- | ----- | ----- | ------- | ------- | ------- |" 111 | )?; 112 | for (&name, _) in &utils { 113 | if name == "[" { 114 | continue; 115 | } 116 | // The alignment is not necessary, but makes the output a bit more 117 | // pretty when viewed as plain markdown. 118 | writeln!( 119 | platform_table_file, 120 | "| {:<16} | {:<5} | {:<5} | {:<7} | {:<7} | {:<7} |", 121 | format!("**{name}**"), 122 | check_supported(name, "linux"), 123 | check_supported(name, "macos"), 124 | check_supported(name, "windows"), 125 | check_supported(name, "unix"), 126 | check_supported(name, "unix_android"), 127 | )?; 128 | } 129 | } 130 | 131 | println!("Writing to utils"); 132 | for (&name, (_, command)) in utils { 133 | if name == "[" { 134 | continue; 135 | } 136 | let p = format!("docs/src/utils/{}.md", name); 137 | 138 | let markdown = File::open(format!("src/uu/{name}/{name}.md")) 139 | .and_then(|mut f: File| { 140 | let mut s = String::new(); 141 | f.read_to_string(&mut s)?; 142 | Ok(s) 143 | }) 144 | .ok(); 145 | 146 | if let Ok(f) = File::create(&p) { 147 | MDWriter { 148 | w: Box::new(f), 149 | command: command(), 150 | name, 151 | tldr_zip: &mut tldr_zip, 152 | utils_per_platform: &utils_per_platform, 153 | markdown, 154 | } 155 | .markdown()?; 156 | println!("Wrote to '{}'", p); 157 | } else { 158 | println!("Error writing to {}", p); 159 | } 160 | writeln!(summary, "* [{0}](utils/{0}.md)", name)?; 161 | } 162 | Ok(()) 163 | } 164 | 165 | struct MDWriter<'a, 'b> { 166 | w: Box, 167 | command: Command, 168 | name: &'a str, 169 | tldr_zip: &'b mut Option>, 170 | utils_per_platform: &'b HashMap<&'b str, Vec>, 171 | markdown: Option, 172 | } 173 | 174 | impl<'a, 'b> MDWriter<'a, 'b> { 175 | fn markdown(&mut self) -> io::Result<()> { 176 | write!(self.w, "# {}\n\n", self.name)?; 177 | self.additional()?; 178 | self.usage()?; 179 | self.about()?; 180 | self.options()?; 181 | self.after_help()?; 182 | self.examples() 183 | } 184 | 185 | fn additional(&mut self) -> io::Result<()> { 186 | writeln!(self.w, "
")?; 187 | self.platforms()?; 188 | self.version()?; 189 | writeln!(self.w, "
") 190 | } 191 | 192 | fn platforms(&mut self) -> io::Result<()> { 193 | writeln!(self.w, "
")?; 194 | for (feature, icon) in [ 195 | ("linux", "linux"), 196 | // freebsd is disabled for now because mdbook does not use font-awesome 5 yet. 197 | // ("unix", "freebsd"), 198 | ("macos", "apple"), 199 | ("windows", "windows"), 200 | ] { 201 | if self.name.contains("sum") 202 | || self.utils_per_platform[feature] 203 | .iter() 204 | .any(|u| u == self.name) 205 | { 206 | writeln!(self.w, "", icon)?; 207 | } 208 | } 209 | writeln!(self.w, "
")?; 210 | 211 | Ok(()) 212 | } 213 | 214 | fn version(&mut self) -> io::Result<()> { 215 | writeln!( 216 | self.w, 217 | "
v{}
", 218 | self.command.render_version().split_once(' ').unwrap().1 219 | ) 220 | } 221 | 222 | fn usage(&mut self) -> io::Result<()> { 223 | if let Some(markdown) = &self.markdown { 224 | let usage = uuhelp_parser::parse_usage(markdown); 225 | let usage = usage.replace("{}", self.name); 226 | 227 | writeln!(self.w, "\n```")?; 228 | writeln!(self.w, "{}", usage)?; 229 | writeln!(self.w, "```") 230 | } else { 231 | Ok(()) 232 | } 233 | } 234 | 235 | fn about(&mut self) -> io::Result<()> { 236 | if let Some(markdown) = &self.markdown { 237 | writeln!(self.w, "{}", uuhelp_parser::parse_about(markdown)) 238 | } else { 239 | Ok(()) 240 | } 241 | } 242 | 243 | fn after_help(&mut self) -> io::Result<()> { 244 | if let Some(markdown) = &self.markdown { 245 | if let Some(after_help) = uuhelp_parser::parse_section("after help", markdown) { 246 | return writeln!(self.w, "\n\n{after_help}"); 247 | } 248 | } 249 | 250 | Ok(()) 251 | } 252 | 253 | fn examples(&mut self) -> io::Result<()> { 254 | if let Some(zip) = self.tldr_zip { 255 | let content = if let Some(f) = 256 | get_zip_content(zip, &format!("pages/common/{}.md", self.name)) 257 | { 258 | f 259 | } else if let Some(f) = get_zip_content(zip, &format!("pages/linux/{}.md", self.name)) { 260 | f 261 | } else { 262 | println!( 263 | "Warning: Could not find tldr examples for page '{}'", 264 | self.name 265 | ); 266 | return Ok(()); 267 | }; 268 | 269 | writeln!(self.w, "## Examples")?; 270 | writeln!(self.w)?; 271 | for line in content.lines().skip_while(|l| !l.starts_with('-')) { 272 | if let Some(l) = line.strip_prefix("- ") { 273 | writeln!(self.w, "{}", l)?; 274 | } else if line.starts_with('`') { 275 | writeln!(self.w, "```shell\n{}\n```", line.trim_matches('`'))?; 276 | } else if line.is_empty() { 277 | writeln!(self.w)?; 278 | } else { 279 | println!("Not sure what to do with this line:"); 280 | println!("{}", line); 281 | } 282 | } 283 | writeln!(self.w)?; 284 | writeln!( 285 | self.w, 286 | "> The examples are provided by the [tldr-pages project](https://tldr.sh) under the [CC BY 4.0 License](https://github.com/tldr-pages/tldr/blob/main/LICENSE.md)." 287 | )?; 288 | writeln!(self.w, ">")?; 289 | writeln!( 290 | self.w, 291 | "> Please note that, as uutils is a work in progress, some examples might fail." 292 | )?; 293 | } 294 | Ok(()) 295 | } 296 | 297 | fn options(&mut self) -> io::Result<()> { 298 | writeln!(self.w, "

Options

")?; 299 | write!(self.w, "
")?; 300 | for arg in self.command.get_arguments() { 301 | write!(self.w, "
")?; 302 | let mut first = true; 303 | for l in arg.get_long_and_visible_aliases().unwrap_or_default() { 304 | if first { 305 | first = false; 306 | } else { 307 | write!(self.w, ", ")?; 308 | } 309 | write!(self.w, "")?; 310 | write!(self.w, "--{}", l)?; 311 | if let Some(names) = arg.get_value_names() { 312 | write!( 313 | self.w, 314 | "={}", 315 | names 316 | .iter() 317 | .map(|x| format!("<{}>", x)) 318 | .collect::>() 319 | .join(" ") 320 | )?; 321 | } 322 | write!(self.w, "")?; 323 | } 324 | for s in arg.get_short_and_visible_aliases().unwrap_or_default() { 325 | if first { 326 | first = false; 327 | } else { 328 | write!(self.w, ", ")?; 329 | } 330 | write!(self.w, "")?; 331 | write!(self.w, "-{}", s)?; 332 | if let Some(names) = arg.get_value_names() { 333 | write!( 334 | self.w, 335 | " {}", 336 | names 337 | .iter() 338 | .map(|x| format!("<{}>", x)) 339 | .collect::>() 340 | .join(" ") 341 | )?; 342 | } 343 | write!(self.w, "")?; 344 | } 345 | writeln!(self.w, "
")?; 346 | writeln!( 347 | self.w, 348 | "
\n\n{}\n\n
", 349 | arg.get_help() 350 | .unwrap_or_default() 351 | .to_string() 352 | .replace('\n', "
") 353 | )?; 354 | } 355 | writeln!(self.w, "
\n") 356 | } 357 | } 358 | 359 | fn get_zip_content(archive: &mut ZipArchive, name: &str) -> Option { 360 | let mut s = String::new(); 361 | archive.by_name(name).ok()?.read_to_string(&mut s).unwrap(); 362 | Some(s) 363 | } 364 | -------------------------------------------------------------------------------- /src/uu/hostname/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uu_hostname" 3 | version = "0.0.1" 4 | edition = "2024" 5 | authors = ["uutils developers"] 6 | license = "MIT" 7 | description = "hostname ~ (uutils) Execute a program periodically, showing output fullscreen" 8 | 9 | homepage = "https://github.com/uutils/hostname" 10 | repository = "https://github.com/uutils/hostname/tree/main/src/uu/hostname" 11 | keywords = ["acl", "uutils", "cross-platform", "cli", "utility"] 12 | categories = ["command-line-utilities"] 13 | 14 | [dependencies] 15 | uucore = { workspace = true } 16 | clap = { workspace = true } 17 | 18 | [target.'cfg(target_os = "windows")'.dependencies] 19 | windows-sys = { workspace = true } 20 | 21 | [target.'cfg(not(target_os = "windows"))'.dependencies] 22 | errno = { workspace = true } 23 | libc = { workspace = true } 24 | 25 | [lib] 26 | path = "src/hostname.rs" 27 | 28 | [[bin]] 29 | name = "hostname" 30 | path = "src/main.rs" 31 | -------------------------------------------------------------------------------- /src/uu/hostname/hostname.md: -------------------------------------------------------------------------------- 1 | # hostname 2 | 3 | ``` 4 | hostname [-a|--alias|-d|--domain|-f|--fqdn|--long|-A|--all-fqdns|-i|--ip-address|-I|--all-ip-addresses|-s|--short|-y|--yp|--nis] 5 | hostname [-b|--boot] {-F filename|--file filename|hostname} 6 | hostname {-h|--help} 7 | hostname {-V|--version} 8 | ``` 9 | 10 | show or set the system's host name 11 | -------------------------------------------------------------------------------- /src/uu/hostname/src/change.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils hostname package. 2 | // 3 | // For the full copyright and license information, please view the LICENSE 4 | // file that was distributed with this source code. 5 | 6 | #[cfg(not(target_family = "windows"))] 7 | pub(crate) mod unix; 8 | #[cfg(target_family = "windows")] 9 | pub(crate) mod windows; 10 | 11 | #[cfg(not(target_family = "windows"))] 12 | pub(crate) use unix::{from_argument, from_file}; 13 | #[cfg(target_family = "windows")] 14 | pub(crate) use windows::{from_argument, from_file}; 15 | -------------------------------------------------------------------------------- /src/uu/hostname/src/change/unix.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils hostname package. 2 | // 3 | // For the full copyright and license information, please view the LICENSE 4 | // file that was distributed with this source code. 5 | 6 | use std::borrow::Cow; 7 | use std::ffi::{CString, OsStr}; 8 | use std::path::Path; 9 | 10 | use uucore::error::UResult; 11 | 12 | use crate::errors::HostNameError; 13 | use crate::net::set_host_name; 14 | use crate::utils::parse_host_name_file; 15 | 16 | pub(crate) fn from_file(path: &Path) -> UResult<()> { 17 | parse_host_name_file(path).map(Cow::Owned).and_then(run) 18 | } 19 | 20 | pub(crate) fn from_argument(host_name: &OsStr) -> UResult<()> { 21 | #[cfg(target_family = "unix")] 22 | let host_name = { 23 | use std::os::unix::ffi::OsStrExt; 24 | Cow::Borrowed(host_name.as_bytes()) 25 | }; 26 | 27 | #[cfg(target_family = "wasm")] 28 | let host_name = { 29 | use std::os::wasm::ffi::OsStrExt; 30 | Cow::Borrowed(host_name.as_bytes()) 31 | }; 32 | 33 | run(host_name) 34 | } 35 | 36 | fn run(mut host_name: Cow<[u8]>) -> UResult<()> { 37 | // Trim white space. 38 | match &mut host_name { 39 | Cow::Borrowed(name) => *name = name.trim_ascii(), 40 | 41 | Cow::Owned(name) => { 42 | while name.first().is_some_and(u8::is_ascii_whitespace) { 43 | name.remove(0); 44 | } 45 | 46 | while name.last().is_some_and(u8::is_ascii_whitespace) { 47 | name.pop(); 48 | } 49 | } 50 | }; 51 | 52 | let host_name = validate_host_name(host_name)?; 53 | 54 | set_host_name(&host_name) 55 | } 56 | 57 | fn validate_host_name(host_name: Cow<[u8]>) -> Result { 58 | // Rules: 59 | // - The only allowed prefix and suffix characters are alphanumeric. 60 | // - The only allowed characters inside are alphanumeric, '-' and '.'. 61 | // - The following sequences are disallowed: "..", ".-" and "-.". 62 | // 63 | // Reference: RFC 1035: Domain Names - Implementation And Specification, 64 | // section 2.3.1. Preferred name syntax. 65 | 66 | let (Some(first_byte), Some(last_byte)) = (host_name.first(), host_name.last()) else { 67 | return Err(HostNameError::InvalidHostName); // Empty name. 68 | }; 69 | 70 | let is_disallowed_byte = move |b: &u8| !b.is_ascii_alphanumeric() && *b != b'-' && *b != b'.'; 71 | let is_disallowed_seq = move |seq: &[u8]| seq == b".." || seq == b".-" || seq == b"-."; 72 | 73 | if !first_byte.is_ascii_alphanumeric() 74 | || !last_byte.is_ascii_alphanumeric() 75 | || host_name.iter().any(is_disallowed_byte) 76 | || host_name.windows(2).any(is_disallowed_seq) 77 | { 78 | return Err(HostNameError::InvalidHostName); 79 | } 80 | 81 | let mut host_name = host_name.into_owned(); 82 | host_name.push(0_u8); 83 | Ok(unsafe { CString::from_vec_with_nul_unchecked(host_name) }) 84 | } 85 | -------------------------------------------------------------------------------- /src/uu/hostname/src/change/windows.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils hostname package. 2 | // 3 | // For the full copyright and license information, please view the LICENSE 4 | // file that was distributed with this source code. 5 | 6 | use std::ffi::OsStr; 7 | use std::os::windows::ffi::OsStrExt; 8 | use std::path::Path; 9 | 10 | use uucore::error::UResult; 11 | 12 | use crate::errors::HostNameError; 13 | use crate::net::set_host_name; 14 | use crate::utils::parse_host_name_file; 15 | 16 | pub(crate) fn from_file(path: &Path) -> UResult<()> { 17 | let host_name = parse_host_name_file(path)?; 18 | let host_name = std::str::from_utf8(&host_name).map_err(|_r| HostNameError::InvalidHostName)?; 19 | run(host_name.encode_utf16().collect()) 20 | } 21 | 22 | pub(crate) fn from_argument(host_name: &OsStr) -> UResult<()> { 23 | run(host_name.encode_wide().collect()) 24 | } 25 | 26 | fn run(mut host_name: Vec) -> UResult<()> { 27 | // Trim white space. 28 | while host_name.first().is_some_and(u16_is_ascii_whitespace) { 29 | host_name.remove(0); 30 | } 31 | 32 | while host_name.last().is_some_and(u16_is_ascii_whitespace) { 33 | host_name.pop(); 34 | } 35 | 36 | validate_host_name(&host_name)?; 37 | 38 | host_name.push(0); // Null-terminate. 39 | set_host_name(&host_name) 40 | } 41 | 42 | fn u16_is_ascii_whitespace(ch: &u16) -> bool { 43 | u8::try_from(*ch).is_ok_and(|b| b.is_ascii_whitespace()) 44 | } 45 | 46 | fn u16_is_ascii_alphanumeric(ch: &u16) -> bool { 47 | u8::try_from(*ch).is_ok_and(|b| b.is_ascii_alphanumeric()) 48 | } 49 | 50 | fn validate_host_name(host_name: &[u16]) -> Result<(), HostNameError> { 51 | // Rules: 52 | // - The only allowed prefix and suffix characters are alphanumeric. 53 | // - The only allowed characters inside are alphanumeric, '-' and '.'. 54 | // - The following sequences are disallowed: "..", ".-" and "-.". 55 | // 56 | // Reference: RFC 1035: Domain Names - Implementation And Specification, 57 | // section 2.3.1. Preferred name syntax. 58 | 59 | const DOT_DOT: [u16; 2] = [b'.' as u16, b'.' as u16]; 60 | const DOT_DASH: [u16; 2] = [b'.' as u16, b'-' as u16]; 61 | const DASH_DOT: [u16; 2] = [b'-' as u16, b'.' as u16]; 62 | 63 | let (Some(first_byte), Some(last_byte)) = (host_name.first(), host_name.last()) else { 64 | return Err(HostNameError::InvalidHostName); // Empty name. 65 | }; 66 | 67 | let is_disallowed_byte = move |ch: &u16| { 68 | !u16_is_ascii_alphanumeric(ch) && *ch != (b'-' as u16) && *ch != (b'.' as u16) 69 | }; 70 | let is_disallowed_seq = move |seq: &[u16]| seq == DOT_DOT || seq == DOT_DASH || seq == DASH_DOT; 71 | 72 | if !u16_is_ascii_alphanumeric(first_byte) 73 | || !u16_is_ascii_alphanumeric(last_byte) 74 | || host_name.iter().any(is_disallowed_byte) 75 | || host_name.windows(2).any(is_disallowed_seq) 76 | { 77 | Err(HostNameError::InvalidHostName) 78 | } else { 79 | Ok(()) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/uu/hostname/src/errors.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils hostname package. 2 | // 3 | // For the full copyright and license information, please view the LICENSE 4 | // file that was distributed with this source code. 5 | 6 | use std::fmt; 7 | 8 | use uucore::error::UError; 9 | 10 | #[derive(Debug, PartialEq, Eq)] 11 | pub enum HostNameError { 12 | InvalidHostName, 13 | HostNameTooLong, 14 | NoLocalDomainName, 15 | SetHostNameDenied, 16 | #[cfg(not(target_family = "windows"))] 17 | GetNameOrAddrInfo(GetNameOrAddrInfoError), 18 | } 19 | 20 | impl fmt::Display for HostNameError { 21 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 22 | match self { 23 | Self::InvalidHostName => write!(f, "the specified hostname is invalid"), 24 | Self::HostNameTooLong => write!(f, "name too long"), 25 | Self::NoLocalDomainName => write!(f, "local domain name not set"), 26 | Self::SetHostNameDenied => write!(f, "you must be root to change the host name"), 27 | #[cfg(not(target_family = "windows"))] 28 | Self::GetNameOrAddrInfo(r) => write!(f, "{r}"), 29 | } 30 | } 31 | } 32 | 33 | impl UError for HostNameError { 34 | fn code(&self) -> i32 { 35 | 1 36 | } 37 | 38 | fn usage(&self) -> bool { 39 | false 40 | } 41 | } 42 | 43 | impl std::error::Error for HostNameError {} 44 | 45 | #[cfg(not(target_family = "windows"))] 46 | #[derive(Debug, PartialEq, Eq)] 47 | pub struct GetNameOrAddrInfoError(pub(crate) std::ffi::c_int); 48 | 49 | #[cfg(not(target_family = "windows"))] 50 | impl fmt::Display for GetNameOrAddrInfoError { 51 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 52 | let message = unsafe { libc::gai_strerror(self.0) }; 53 | if message.is_null() { 54 | write!(f, "domain name resolution failed: error {}", self.0) 55 | } else { 56 | let message = unsafe { std::ffi::CStr::from_ptr(message.cast()) }; 57 | if let Ok(message) = message.to_str() { 58 | write!(f, "{message}") 59 | } else { 60 | write!(f, "{message:?}") 61 | } 62 | } 63 | } 64 | } 65 | 66 | #[cfg(not(target_family = "windows"))] 67 | impl std::error::Error for GetNameOrAddrInfoError {} 68 | -------------------------------------------------------------------------------- /src/uu/hostname/src/hostname.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils hostname package. 2 | // 3 | // For the full copyright and license information, please view the LICENSE 4 | // file that was distributed with this source code. 5 | 6 | mod change; 7 | mod errors; 8 | mod net; 9 | mod print; 10 | mod utils; 11 | 12 | use std::ffi::OsString; 13 | use std::path::PathBuf; 14 | 15 | use clap::{Arg, ArgAction, ArgGroup, Command, crate_version, value_parser}; 16 | use uucore::{error::UResult, format_usage, help_about, help_usage}; 17 | 18 | const ABOUT: &str = help_about!("hostname.md"); 19 | const USAGE: &str = help_usage!("hostname.md"); 20 | 21 | pub mod options { 22 | pub static ALIAS: &str = "alias"; 23 | pub static ALL_FQDNS: &str = "all-fqdns"; 24 | pub static ALL_IP_ADDRESSES: &str = "all-ip-addresses"; 25 | pub static BOOT: &str = "boot"; 26 | pub static DOMAIN: &str = "domain"; 27 | pub static FILE: &str = "file"; 28 | pub static FILENAME: &str = "filename"; 29 | pub static FQDN: &str = "fqdn"; 30 | pub static HOSTNAME: &str = "hostname"; 31 | pub static IP_ADDRESS: &str = "ip-address"; 32 | pub static LONG: &str = "long"; 33 | pub static NIS: &str = "nis"; 34 | pub static SHORT: &str = "short"; 35 | pub static YP: &str = "yp"; 36 | } 37 | 38 | #[uucore::main] 39 | pub fn uumain(args: impl uucore::Args) -> UResult<()> { 40 | let args = uu_app().try_get_matches_from(args)?; 41 | 42 | let _net_lib_guard = net::LibraryGuard::load()?; 43 | 44 | if args.contains_id("set-group") { 45 | if let Some(path) = args.get_one::(options::FILE) { 46 | change::from_file(path) 47 | } else { 48 | let host_name = args 49 | .get_one::(options::HOSTNAME) 50 | .expect("hostname must be specified"); 51 | 52 | change::from_argument(host_name) 53 | } 54 | } else { 55 | let host_name: &mut dyn print::PrintHostName = if args.get_flag(options::ALIAS) { 56 | &mut print::AliasHostName 57 | } else if args.get_flag(options::DOMAIN) { 58 | &mut print::DomainHostName 59 | } else if args.get_flag(options::FQDN) { 60 | &mut print::FqdnHostName 61 | } else if args.get_flag(options::ALL_FQDNS) { 62 | &mut print::AllFqdnHostName 63 | } else if args.get_flag(options::IP_ADDRESS) { 64 | &mut print::IpAddressHostName 65 | } else if args.get_flag(options::ALL_IP_ADDRESSES) { 66 | &mut print::AllIpAddressesHostName 67 | } else if args.get_flag(options::SHORT) { 68 | &mut print::ShortHostName 69 | } else if args.get_flag(options::NIS) { 70 | &mut print::NisHostName 71 | } else { 72 | &mut print::DefaultHostName 73 | }; 74 | 75 | let mut stdout = std::io::stdout(); 76 | host_name.print_host_name(&mut stdout) 77 | } 78 | } 79 | 80 | #[must_use] 81 | pub fn uu_app() -> Command { 82 | Command::new(uucore::util_name()) 83 | .version(crate_version!()) 84 | .about(ABOUT) 85 | .override_usage(format_usage(USAGE)) 86 | .infer_long_args(true) 87 | .arg( 88 | Arg::new(options::ALIAS) 89 | .short('a') 90 | .long(options::ALIAS) 91 | .action(ArgAction::SetTrue) 92 | .help("alias names"), 93 | ) 94 | .arg( 95 | Arg::new(options::DOMAIN) 96 | .short('d') 97 | .long(options::DOMAIN) 98 | .action(ArgAction::SetTrue) 99 | .help("DNS domain name"), 100 | ) 101 | .arg( 102 | Arg::new(options::FQDN) 103 | .short('f') 104 | .long(options::FQDN) 105 | .visible_alias(options::LONG) 106 | .action(ArgAction::SetTrue) 107 | .help("long host name (FQDN)"), 108 | ) 109 | .arg( 110 | Arg::new(options::ALL_FQDNS) 111 | .short('A') 112 | .long(options::ALL_FQDNS) 113 | .action(ArgAction::SetTrue) 114 | .help("all long host names (FQDNs)"), 115 | ) 116 | .arg( 117 | Arg::new(options::IP_ADDRESS) 118 | .short('i') 119 | .long(options::IP_ADDRESS) 120 | .action(ArgAction::SetTrue) 121 | .help("addresses for the host name"), 122 | ) 123 | .arg( 124 | Arg::new(options::ALL_IP_ADDRESSES) 125 | .short('I') 126 | .long(options::ALL_IP_ADDRESSES) 127 | .action(ArgAction::SetTrue) 128 | .help("all addresses for the host"), 129 | ) 130 | .arg( 131 | Arg::new(options::SHORT) 132 | .short('s') 133 | .long(options::SHORT) 134 | .action(ArgAction::SetTrue) 135 | .help("short host name"), 136 | ) 137 | .arg( 138 | Arg::new(options::NIS) 139 | .short('y') 140 | .long(options::YP) 141 | .visible_alias(options::NIS) 142 | .action(ArgAction::SetTrue) 143 | .help("NIS/YP domain name"), 144 | ) 145 | .arg( 146 | Arg::new(options::BOOT) 147 | .short('b') 148 | .long(options::BOOT) 149 | .action(ArgAction::SetTrue) 150 | .help("set default hostname if none available"), 151 | ) 152 | .arg( 153 | Arg::new(options::FILE) 154 | .short('F') 155 | .long(options::FILE) 156 | .value_name(options::FILENAME) 157 | .value_parser(value_parser!(PathBuf)) 158 | .action(ArgAction::Set) 159 | .conflicts_with(options::HOSTNAME) 160 | .help("read host name or NIS domain name from given file"), 161 | ) 162 | .arg( 163 | Arg::new(options::HOSTNAME) 164 | .value_parser(value_parser!(OsString)) 165 | .conflicts_with(options::FILE), 166 | ) 167 | .group( 168 | ArgGroup::new("get-group") 169 | .args([ 170 | options::ALIAS, 171 | options::DOMAIN, 172 | options::FQDN, 173 | options::ALL_FQDNS, 174 | options::IP_ADDRESS, 175 | options::ALL_IP_ADDRESSES, 176 | options::SHORT, 177 | options::NIS, 178 | ]) 179 | .multiple(false) 180 | .conflicts_with("set-group"), 181 | ) 182 | .group( 183 | ArgGroup::new("set-group") 184 | .args([options::BOOT, options::FILE, options::HOSTNAME]) 185 | .multiple(true) 186 | .requires("source-group") 187 | .conflicts_with("get-group"), 188 | ) 189 | .group( 190 | ArgGroup::new("source-group") 191 | .args([options::FILE, options::HOSTNAME]) 192 | .multiple(false), 193 | ) 194 | } 195 | -------------------------------------------------------------------------------- /src/uu/hostname/src/main.rs: -------------------------------------------------------------------------------- 1 | uucore::bin!(uu_hostname); 2 | -------------------------------------------------------------------------------- /src/uu/hostname/src/net.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(target_family = "windows"))] 2 | mod unix; 3 | #[cfg(target_family = "windows")] 4 | mod windows; 5 | 6 | #[cfg(not(target_family = "windows"))] 7 | pub(crate) use unix::*; 8 | #[cfg(target_family = "windows")] 9 | pub(crate) use windows::*; 10 | 11 | pub(crate) struct LibraryGuard; 12 | -------------------------------------------------------------------------------- /src/uu/hostname/src/net/unix.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils hostname package. 2 | // 3 | // For the full copyright and license information, please view the LICENSE 4 | // file that was distributed with this source code. 5 | 6 | use std::ffi::{CStr, CString, c_int}; 7 | use std::ptr; 8 | use std::ptr::NonNull; 9 | 10 | use uucore::error::UResult; 11 | 12 | use crate::errors::{GetNameOrAddrInfoError, HostNameError}; 13 | 14 | impl crate::net::LibraryGuard { 15 | pub(crate) fn load() -> std::io::Result { 16 | Ok(Self) 17 | } 18 | } 19 | 20 | fn in6_is_addr_multicast(addr: &libc::in6_addr) -> bool { 21 | addr.s6_addr[0] == 0xff 22 | } 23 | 24 | pub(crate) fn in6_is_addr_linklocal(addr: &libc::in6_addr) -> bool { 25 | addr.s6_addr[0] == 0xfe && ((addr.s6_addr[1] & 0xc0) == 0x80) 26 | } 27 | 28 | pub(crate) fn in6_is_addr_mc_linklocal(addr: &libc::in6_addr) -> bool { 29 | in6_is_addr_multicast(addr) && ((addr.s6_addr[1] & 0xf) == 0x2) 30 | } 31 | 32 | fn max_host_name_size() -> usize { 33 | const _POSIX_HOST_NAME_MAX: usize = 255; 34 | 35 | usize::try_from(unsafe { libc::sysconf(libc::_SC_HOST_NAME_MAX) }) 36 | .unwrap_or(_POSIX_HOST_NAME_MAX) 37 | .max(_POSIX_HOST_NAME_MAX) 38 | .saturating_add(1) 39 | } 40 | 41 | pub(crate) fn host_name() -> std::io::Result { 42 | let mut buffer: Vec = vec![0_u8; max_host_name_size()]; 43 | loop { 44 | errno::set_errno(errno::Errno(0)); 45 | 46 | if unsafe { libc::gethostname(buffer.as_mut_ptr().cast(), buffer.len()) } == -1 { 47 | let err = std::io::Error::last_os_error(); 48 | if err.raw_os_error() != Some(libc::ENAMETOOLONG) { 49 | break Err(err); 50 | } 51 | // else an error happened because a bigger buffer is needed. 52 | } else if let Some(index) = buffer.iter().position(|&b| b == 0_u8) { 53 | buffer.truncate(index + 1); 54 | break Ok(unsafe { CString::from_vec_with_nul_unchecked(buffer) }); 55 | } 56 | // else truncation happened because a bigger buffer is needed. 57 | 58 | buffer.resize_with(buffer.len() + 4096, Default::default); 59 | } 60 | } 61 | 62 | pub(crate) fn domain_name() -> UResult> { 63 | let mut buffer: Vec = vec![0_u8; 256]; 64 | loop { 65 | #[cfg(any( 66 | target_os = "dragonfly", 67 | target_os = "freebsd", 68 | target_os = "illumos", 69 | target_os = "ios", 70 | target_os = "macos", 71 | target_os = "solaris", 72 | ))] 73 | let Ok(buffer_len) = c_int::try_from(buffer.len()) else { 74 | break Err(Box::new(HostNameError::HostNameTooLong)); 75 | }; 76 | 77 | #[cfg(not(any( 78 | target_os = "dragonfly", 79 | target_os = "freebsd", 80 | target_os = "illumos", 81 | target_os = "ios", 82 | target_os = "macos", 83 | target_os = "solaris", 84 | )))] 85 | let buffer_len = buffer.len(); 86 | 87 | errno::set_errno(errno::Errno(0)); 88 | 89 | if unsafe { libc::getdomainname(buffer.as_mut_ptr().cast(), buffer_len) } == -1 { 90 | let err = std::io::Error::last_os_error(); 91 | if err.raw_os_error() != Some(libc::ENAMETOOLONG) { 92 | break Err(err.into()); 93 | } 94 | // else an error happened because a bigger buffer is needed. 95 | } else if let Some(index) = buffer.iter().position(|&b| b == 0_u8) { 96 | buffer.truncate(index + 1); 97 | break Ok((buffer != c"(none)".to_bytes_with_nul()) 98 | .then(|| unsafe { CString::from_vec_with_nul_unchecked(buffer) })); 99 | } 100 | // else truncation happened because a bigger buffer is needed. 101 | 102 | buffer.resize_with(buffer.len() + 4096, Default::default); 103 | } 104 | } 105 | 106 | pub(crate) fn short_host_name() -> std::io::Result { 107 | let mut bytes = host_name()?.into_bytes_with_nul(); 108 | if let Some(index) = bytes.iter().position(|&byte| byte == b'.') { 109 | bytes.truncate(index); 110 | bytes.push(0_u8); 111 | } 112 | 113 | Ok(unsafe { CString::from_vec_with_nul_unchecked(bytes) }) 114 | } 115 | 116 | pub(crate) fn set_host_name(host_name: &CStr) -> UResult<()> { 117 | use std::io::{Error, ErrorKind}; 118 | 119 | #[cfg(any( 120 | target_os = "dragonfly", 121 | target_os = "freebsd", 122 | target_os = "illumos", 123 | target_os = "ios", 124 | target_os = "macos", 125 | target_os = "solaris", 126 | ))] 127 | let Ok(host_name_len) = c_int::try_from(host_name.count_bytes()) else { 128 | return Err(Box::new(HostNameError::HostNameTooLong)); 129 | }; 130 | 131 | #[cfg(not(any( 132 | target_os = "dragonfly", 133 | target_os = "freebsd", 134 | target_os = "illumos", 135 | target_os = "ios", 136 | target_os = "macos", 137 | target_os = "solaris", 138 | )))] 139 | let host_name_len = host_name.count_bytes(); 140 | 141 | if unsafe { libc::sethostname(host_name.as_ptr(), host_name_len) } != -1 { 142 | return Ok(()); 143 | } 144 | 145 | let err = Error::last_os_error(); 146 | match err.kind() { 147 | ErrorKind::PermissionDenied => Err(Box::new(HostNameError::SetHostNameDenied)), 148 | ErrorKind::InvalidInput => Err(Box::new(HostNameError::HostNameTooLong)), 149 | _ => Err(err.into()), 150 | } 151 | } 152 | 153 | #[allow(clippy::cast_possible_truncation, clippy::as_conversions)] 154 | pub(crate) fn get_name_info( 155 | address: *const libc::sockaddr, 156 | address_size: libc::socklen_t, 157 | flags: c_int, 158 | ) -> Result { 159 | #[cfg(any( 160 | target_os = "ios", 161 | target_os = "linux", 162 | target_os = "macos", 163 | target_os = "netbsd" 164 | ))] 165 | let initial_size = max_host_name_size().min(libc::socklen_t::MAX as usize); 166 | 167 | #[cfg(not(any( 168 | target_os = "ios", 169 | target_os = "linux", 170 | target_os = "macos", 171 | target_os = "netbsd" 172 | )))] 173 | let initial_size = max_host_name_size(); 174 | 175 | let mut buffer: Vec = vec![0_u8; initial_size]; 176 | 177 | loop { 178 | #[cfg(any( 179 | target_os = "ios", 180 | target_os = "linux", 181 | target_os = "macos", 182 | target_os = "netbsd" 183 | ))] 184 | let Ok(buffer_len) = libc::socklen_t::try_from(buffer.len()) else { 185 | return Err(HostNameError::HostNameTooLong); 186 | }; 187 | 188 | #[cfg(not(any( 189 | target_os = "ios", 190 | target_os = "linux", 191 | target_os = "macos", 192 | target_os = "netbsd" 193 | )))] 194 | let buffer_len = buffer.len(); 195 | 196 | let r = unsafe { 197 | libc::getnameinfo( 198 | address, 199 | address_size, 200 | buffer.as_mut_ptr().cast(), 201 | buffer_len, 202 | ptr::null_mut(), 203 | 0, 204 | flags, 205 | ) 206 | }; 207 | 208 | match r { 209 | libc::EAI_OVERFLOW => {} 210 | 211 | 0 => { 212 | if let Some(index) = buffer.iter().position(|&byte| byte == 0_u8) { 213 | buffer.truncate(index + 1); 214 | break Ok(unsafe { CString::from_vec_with_nul_unchecked(buffer) }); 215 | } 216 | } 217 | 218 | _ => break Err(HostNameError::GetNameOrAddrInfo(GetNameOrAddrInfoError(r))), 219 | } 220 | 221 | buffer.resize_with(buffer.len() + 4096, Default::default); 222 | } 223 | } 224 | 225 | #[repr(transparent)] 226 | pub(crate) struct InterfaceAddresses(NonNull); 227 | 228 | impl InterfaceAddresses { 229 | pub(crate) fn new() -> std::io::Result { 230 | use std::io::{Error, ErrorKind}; 231 | 232 | let mut ptr: *mut libc::ifaddrs = ptr::null_mut(); 233 | 234 | if unsafe { libc::getifaddrs(&mut ptr) } == -1 { 235 | Err(Error::last_os_error()) 236 | } else { 237 | NonNull::new(ptr) 238 | .map(Self) 239 | .ok_or_else(|| Error::from(ErrorKind::InvalidData)) 240 | } 241 | } 242 | 243 | pub(crate) fn iter(&self) -> InterfaceAddressesIter { 244 | InterfaceAddressesIter { 245 | _ia: self, 246 | ptr: Some(self.0), 247 | } 248 | } 249 | } 250 | 251 | impl Drop for InterfaceAddresses { 252 | fn drop(&mut self) { 253 | unsafe { libc::freeifaddrs(self.0.as_ptr()) } 254 | } 255 | } 256 | 257 | pub(crate) struct InterfaceAddressesIter<'ia> { 258 | _ia: &'ia InterfaceAddresses, 259 | ptr: Option>, 260 | } 261 | 262 | impl<'ia> Iterator for InterfaceAddressesIter<'ia> { 263 | type Item = &'ia libc::ifaddrs; 264 | 265 | fn next(&mut self) -> Option { 266 | let element = unsafe { self.ptr?.as_ref() }; 267 | self.ptr = NonNull::new(element.ifa_next); 268 | Some(element) 269 | } 270 | } 271 | 272 | #[repr(transparent)] 273 | pub(crate) struct AddressInfo(NonNull); 274 | 275 | impl AddressInfo { 276 | pub(crate) fn new( 277 | host_name: &CStr, 278 | hint_family: c_int, 279 | hint_socktype: c_int, 280 | hint_protocol: c_int, 281 | hint_flags: c_int, 282 | ) -> UResult { 283 | let mut c_hints: libc::addrinfo = unsafe { std::mem::zeroed() }; 284 | c_hints.ai_family = hint_family; 285 | c_hints.ai_socktype = hint_socktype; 286 | c_hints.ai_protocol = hint_protocol; 287 | c_hints.ai_flags = hint_flags; 288 | 289 | let mut ptr: *mut libc::addrinfo = ptr::null_mut(); 290 | 291 | let r = unsafe { libc::getaddrinfo(host_name.as_ptr(), ptr::null(), &c_hints, &mut ptr) }; 292 | if r == 0 { 293 | NonNull::new(ptr) 294 | .map(Self) 295 | .ok_or_else(|| std::io::Error::from(std::io::ErrorKind::InvalidData).into()) 296 | } else { 297 | Err(Box::new(HostNameError::GetNameOrAddrInfo( 298 | GetNameOrAddrInfoError(r), 299 | ))) 300 | } 301 | } 302 | 303 | pub(crate) fn first(&self) -> &libc::addrinfo { 304 | unsafe { self.0.as_ref() } 305 | } 306 | 307 | pub(crate) fn iter(&self) -> AddressInfoIter { 308 | AddressInfoIter { 309 | _ia: self, 310 | ptr: Some(self.0), 311 | } 312 | } 313 | } 314 | 315 | impl Drop for AddressInfo { 316 | fn drop(&mut self) { 317 | unsafe { libc::freeaddrinfo(self.0.as_ptr()) } 318 | } 319 | } 320 | 321 | pub(crate) struct AddressInfoIter<'ia> { 322 | _ia: &'ia AddressInfo, 323 | ptr: Option>, 324 | } 325 | 326 | impl<'ia> Iterator for AddressInfoIter<'ia> { 327 | type Item = &'ia libc::addrinfo; 328 | 329 | fn next(&mut self) -> Option { 330 | let element = unsafe { self.ptr?.as_ref() }; 331 | self.ptr = NonNull::new(element.ai_next); 332 | Some(element) 333 | } 334 | } 335 | -------------------------------------------------------------------------------- /src/uu/hostname/src/net/windows.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils hostname package. 2 | // 3 | // For the full copyright and license information, please view the LICENSE 4 | // file that was distributed with this source code. 5 | 6 | use std::ffi::{OsStr, OsString, c_int}; 7 | use std::os::windows::ffi::{OsStrExt, OsStringExt}; 8 | use std::ptr::NonNull; 9 | use std::{mem, ptr}; 10 | 11 | use uucore::error::UResult; 12 | use windows_sys::Win32::Foundation::{ 13 | ERROR_ACCESS_DENIED, ERROR_BUFFER_OVERFLOW, ERROR_INVALID_NAME, ERROR_INVALID_PARAMETER, 14 | ERROR_MORE_DATA, ERROR_NO_DATA, ERROR_SUCCESS, 15 | }; 16 | use windows_sys::Win32::NetworkManagement::IpHelper::{ 17 | GAA_FLAG_SKIP_DNS_SERVER, GAA_FLAG_SKIP_FRIENDLY_NAME, GetAdaptersAddresses, 18 | IP_ADAPTER_ADDRESSES_LH, IP_ADAPTER_UNICAST_ADDRESS_LH, 19 | }; 20 | use windows_sys::Win32::Networking::WinSock::{ 21 | ADDRINFOW, AF_UNSPEC, FreeAddrInfoW, GetAddrInfoW, GetNameInfoW, IN6_ADDR, SOCKADDR, 22 | WSACleanup, WSADATA, WSAENAMETOOLONG, WSAENOBUFS, WSAGetLastError, WSAStartup, socklen_t, 23 | }; 24 | use windows_sys::Win32::System::SystemInformation::{ 25 | ComputerNamePhysicalDnsDomain, ComputerNamePhysicalDnsFullyQualified, 26 | ComputerNamePhysicalDnsHostname, GetComputerNameExW, SetComputerNameExW, 27 | }; 28 | 29 | use crate::errors::HostNameError; 30 | 31 | impl crate::net::LibraryGuard { 32 | pub(crate) fn load() -> std::io::Result { 33 | let mut init_data: WSADATA = unsafe { mem::zeroed() }; 34 | let r = unsafe { WSAStartup(make_word(2, 2), &mut init_data) }; 35 | if r == 0 { 36 | Ok(Self) 37 | } else { 38 | Err(std::io::Error::from_raw_os_error(r)) 39 | } 40 | } 41 | } 42 | 43 | impl Drop for crate::net::LibraryGuard { 44 | fn drop(&mut self) { 45 | unsafe { WSACleanup() }; 46 | } 47 | } 48 | 49 | fn make_word(low: u8, high: u8) -> u16 { 50 | (u16::from(high) << 8) | u16::from(low) 51 | } 52 | 53 | fn in6_is_addr_multicast(addr: &IN6_ADDR) -> bool { 54 | unsafe { addr.u.Byte[0] == 0xff } 55 | } 56 | 57 | pub(crate) fn in6_is_addr_linklocal(addr: &IN6_ADDR) -> bool { 58 | unsafe { addr.u.Byte[0] == 0xfe && ((addr.u.Byte[1] & 0xc0) == 0x80) } 59 | } 60 | 61 | pub(crate) fn in6_is_addr_mc_linklocal(addr: &IN6_ADDR) -> bool { 62 | in6_is_addr_multicast(addr) && unsafe { (addr.u.Byte[1] & 0xf) == 0x2 } 63 | } 64 | 65 | fn get_computer_name_ex_w(kind: c_int) -> std::io::Result { 66 | use std::io::Error; 67 | 68 | let mut buffer: Vec = Vec::with_capacity(256); 69 | loop { 70 | let Ok(mut buffer_capacity) = u32::try_from(buffer.capacity()) else { 71 | break Err(Error::from_raw_os_error(WSAENAMETOOLONG)); 72 | }; 73 | 74 | let buffer_ptr = buffer.spare_capacity_mut().as_mut_ptr().cast(); 75 | 76 | if unsafe { GetComputerNameExW(kind, buffer_ptr, &mut buffer_capacity) } == 0 { 77 | let err = Error::last_os_error(); 78 | if err.raw_os_error() == Some(ERROR_MORE_DATA as i32) { 79 | // An error happened because a bigger buffer is needed. 80 | buffer.reserve((buffer_capacity as usize).saturating_sub(buffer.capacity())); 81 | } else { 82 | break Err(err); 83 | } 84 | } else { 85 | unsafe { buffer.set_len(buffer_capacity as usize) }; 86 | break Ok(OsString::from_wide(&buffer)); 87 | } 88 | } 89 | } 90 | 91 | pub(crate) fn host_name() -> std::io::Result { 92 | // TODO: Should we specify ComputerNameDnsHostname instead? 93 | get_computer_name_ex_w(ComputerNamePhysicalDnsHostname) 94 | } 95 | 96 | pub(crate) fn fully_qualified_dns_name() -> std::io::Result { 97 | // TODO: Should we specify ComputerNameDnsFullyQualified instead? 98 | get_computer_name_ex_w(ComputerNamePhysicalDnsFullyQualified) 99 | } 100 | 101 | pub(crate) fn domain_name() -> std::io::Result> { 102 | // TODO: Should we specify ComputerNameDnsDomain instead? 103 | // TODO: Should we call NetGetJoinInformation() instead? 104 | let name = get_computer_name_ex_w(ComputerNamePhysicalDnsDomain)?; 105 | Ok((!name.is_empty()).then_some(name)) 106 | } 107 | 108 | pub(crate) fn short_host_name() -> std::io::Result { 109 | let host_name = host_name()?; 110 | let bytes = host_name.encode_wide(); 111 | if bytes.clone().any(|ch| ch == (b'.' as u16)) { 112 | let bytes: Vec = bytes.take_while(|&ch| ch != (b'.' as u16)).collect(); 113 | Ok(OsString::from_wide(&bytes)) 114 | } else { 115 | Ok(host_name) 116 | } 117 | } 118 | 119 | pub(crate) fn set_host_name(host_name: &[u16]) -> UResult<()> { 120 | if unsafe { SetComputerNameExW(ComputerNamePhysicalDnsHostname, host_name.as_ptr()) } != 0 { 121 | return Ok(()); 122 | } 123 | 124 | let err = std::io::Error::last_os_error(); 125 | match err.raw_os_error().map(|n| n as u32) { 126 | Some(ERROR_ACCESS_DENIED) => Err(Box::new(HostNameError::SetHostNameDenied)), 127 | 128 | Some(ERROR_INVALID_PARAMETER | ERROR_INVALID_NAME) => { 129 | Err(Box::new(HostNameError::HostNameTooLong)) 130 | } 131 | 132 | _ => Err(err.into()), 133 | } 134 | } 135 | 136 | pub(crate) fn get_name_info( 137 | address: *const SOCKADDR, 138 | address_size: usize, 139 | flags: c_int, 140 | ) -> std::io::Result { 141 | use std::io::{Error, ErrorKind}; 142 | 143 | let Ok(address_size) = socklen_t::try_from(address_size) else { 144 | return Err(Error::from(ErrorKind::InvalidInput)); 145 | }; 146 | 147 | let mut buffer: Vec = vec![0_u16; 1025]; 148 | 149 | loop { 150 | let Ok(buffer_len) = u32::try_from(buffer.len()) else { 151 | return Err(Error::from_raw_os_error(WSAENAMETOOLONG)); 152 | }; 153 | 154 | let r = unsafe { 155 | GetNameInfoW( 156 | address, 157 | address_size, 158 | buffer.as_mut_ptr(), 159 | buffer_len, 160 | ptr::null_mut(), 161 | 0, 162 | flags, 163 | ) 164 | }; 165 | 166 | match r { 167 | WSAENOBUFS | WSAENAMETOOLONG => {} 168 | 169 | 0 => { 170 | if let Some(index) = buffer.iter().position(|&ch| ch == 0_u16) { 171 | buffer.truncate(index); 172 | break Ok(OsString::from_wide(&buffer)); 173 | } 174 | } 175 | 176 | _ => break Err(Error::from_raw_os_error(unsafe { WSAGetLastError() })), 177 | } 178 | 179 | buffer.resize_with(buffer.len() + 4096, Default::default); 180 | } 181 | } 182 | 183 | fn get_adapters_addresses() -> std::io::Result>> { 184 | let mut buffer: Vec = Vec::with_capacity(32768); 185 | 186 | loop { 187 | let mut buffer_size = buffer.capacity().min(u32::MAX as usize) as u32; 188 | 189 | let r = unsafe { 190 | GetAdaptersAddresses( 191 | AF_UNSPEC as u32, 192 | GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_DNS_SERVER, 193 | ptr::null(), 194 | buffer.spare_capacity_mut().as_mut_ptr().cast(), 195 | &mut buffer_size, 196 | ) 197 | }; 198 | 199 | match r { 200 | ERROR_NO_DATA => return Ok(None), // No interface addresses were found. 201 | 202 | ERROR_BUFFER_OVERFLOW => { 203 | if buffer_size == 0 { 204 | return Ok(None); // No interface addresses were found. 205 | } 206 | 207 | buffer.reserve((buffer_size as usize).saturating_sub(buffer.capacity())); 208 | // Loop to try again. 209 | } 210 | 211 | ERROR_SUCCESS => { 212 | if buffer_size == 0 { 213 | return Ok(None); // No interface addresses were found. 214 | } 215 | 216 | unsafe { buffer.set_len(buffer_size as usize) }; 217 | return Ok(Some(buffer.into_boxed_slice())); 218 | } 219 | 220 | _ => return Err(std::io::Error::from_raw_os_error(r as i32)), 221 | }; 222 | } 223 | } 224 | 225 | pub(crate) struct InterfaceAddresses { 226 | list: *const IP_ADAPTER_ADDRESSES_LH, 227 | buffer: *mut [u8], 228 | } 229 | 230 | impl InterfaceAddresses { 231 | pub(crate) fn new() -> std::io::Result { 232 | if let Some(buffer) = get_adapters_addresses()? { 233 | let list = (*buffer).as_ptr().cast(); 234 | let buffer = Box::into_raw(buffer); 235 | Ok(Self { list, buffer }) 236 | } else { 237 | Ok(Self { 238 | list: ptr::null(), 239 | buffer: ptr::null_mut::<[u8; 0]>(), 240 | }) 241 | } 242 | } 243 | 244 | pub(crate) fn iter(&self) -> InterfaceAddressesIter { 245 | InterfaceAddressesIter { 246 | _ia: self, 247 | ptr: self.list, 248 | } 249 | } 250 | } 251 | 252 | impl Drop for InterfaceAddresses { 253 | fn drop(&mut self) { 254 | if !self.buffer.is_null() { 255 | drop(unsafe { Box::from_raw(self.buffer) }); 256 | } 257 | } 258 | } 259 | 260 | pub(crate) struct InterfaceAddressesIter<'ia> { 261 | _ia: &'ia InterfaceAddresses, 262 | ptr: *const IP_ADAPTER_ADDRESSES_LH, 263 | } 264 | 265 | impl<'ia> Iterator for InterfaceAddressesIter<'ia> { 266 | type Item = &'ia IP_ADAPTER_ADDRESSES_LH; 267 | 268 | fn next(&mut self) -> Option { 269 | if self.ptr.is_null() { 270 | return None; 271 | } 272 | 273 | let element = unsafe { &*self.ptr }; 274 | self.ptr = element.Next; 275 | Some(element) 276 | } 277 | } 278 | 279 | pub(crate) struct AdapterUnicastAddressIter<'aa> { 280 | _aa: &'aa IP_ADAPTER_ADDRESSES_LH, 281 | ptr: *const IP_ADAPTER_UNICAST_ADDRESS_LH, 282 | } 283 | 284 | impl<'aa> AdapterUnicastAddressIter<'aa> { 285 | pub(crate) fn new(adapter_addresses: &'aa IP_ADAPTER_ADDRESSES_LH) -> Self { 286 | Self { 287 | _aa: adapter_addresses, 288 | ptr: adapter_addresses.FirstUnicastAddress, 289 | } 290 | } 291 | } 292 | 293 | impl<'ia> Iterator for AdapterUnicastAddressIter<'ia> { 294 | type Item = &'ia IP_ADAPTER_UNICAST_ADDRESS_LH; 295 | 296 | fn next(&mut self) -> Option { 297 | if self.ptr.is_null() { 298 | return None; 299 | } 300 | 301 | let element = unsafe { &*self.ptr }; 302 | self.ptr = element.Next; 303 | Some(element) 304 | } 305 | } 306 | 307 | #[repr(transparent)] 308 | pub(crate) struct AddressInfo(NonNull); 309 | 310 | impl AddressInfo { 311 | pub(crate) fn new( 312 | host_name: &OsStr, 313 | hint_family: c_int, 314 | hint_socktype: c_int, 315 | hint_protocol: c_int, 316 | hint_flags: c_int, 317 | ) -> std::io::Result { 318 | use std::io::{Error, ErrorKind}; 319 | 320 | let mut host_name: Vec = host_name.encode_wide().collect(); 321 | host_name.push(0); 322 | 323 | let mut c_hints: ADDRINFOW = unsafe { std::mem::zeroed() }; 324 | c_hints.ai_family = hint_family; 325 | c_hints.ai_socktype = hint_socktype; 326 | c_hints.ai_protocol = hint_protocol; 327 | c_hints.ai_flags = hint_flags; 328 | 329 | let mut ptr: *mut ADDRINFOW = ptr::null_mut(); 330 | 331 | if unsafe { GetAddrInfoW(host_name.as_ptr(), ptr::null(), &c_hints, &mut ptr) } == 0 { 332 | NonNull::new(ptr) 333 | .map(Self) 334 | .ok_or_else(|| Error::from(ErrorKind::InvalidData)) 335 | } else { 336 | Err(Error::from_raw_os_error(unsafe { WSAGetLastError() })) 337 | } 338 | } 339 | 340 | pub(crate) fn iter(&self) -> AddressInfoIter { 341 | AddressInfoIter { 342 | _ia: self, 343 | ptr: Some(self.0), 344 | } 345 | } 346 | } 347 | 348 | impl Drop for AddressInfo { 349 | fn drop(&mut self) { 350 | unsafe { FreeAddrInfoW(self.0.as_ptr()) } 351 | } 352 | } 353 | 354 | pub(crate) struct AddressInfoIter<'ia> { 355 | _ia: &'ia AddressInfo, 356 | ptr: Option>, 357 | } 358 | 359 | impl<'ia> Iterator for AddressInfoIter<'ia> { 360 | type Item = &'ia ADDRINFOW; 361 | 362 | fn next(&mut self) -> Option { 363 | let element = unsafe { self.ptr?.as_ref() }; 364 | self.ptr = NonNull::new(element.ai_next); 365 | Some(element) 366 | } 367 | } 368 | -------------------------------------------------------------------------------- /src/uu/hostname/src/print.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils hostname package. 2 | // 3 | // For the full copyright and license information, please view the LICENSE 4 | // file that was distributed with this source code. 5 | 6 | #[cfg(not(target_family = "windows"))] 7 | mod unix; 8 | #[cfg(target_family = "windows")] 9 | mod windows; 10 | 11 | use uucore::error::UResult; 12 | 13 | pub(crate) trait PrintHostName { 14 | fn print_host_name(&self, out: &mut dyn std::io::Write) -> UResult<()>; 15 | } 16 | 17 | pub(crate) struct DefaultHostName; 18 | pub(crate) struct AliasHostName; 19 | pub(crate) struct DomainHostName; 20 | pub(crate) struct FqdnHostName; 21 | pub(crate) struct AllFqdnHostName; 22 | pub(crate) struct IpAddressHostName; 23 | pub(crate) struct AllIpAddressesHostName; 24 | pub(crate) struct ShortHostName; 25 | pub(crate) struct NisHostName; 26 | -------------------------------------------------------------------------------- /src/uu/hostname/src/print/unix.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils hostname package. 2 | // 3 | // For the full copyright and license information, please view the LICENSE 4 | // file that was distributed with this source code. 5 | 6 | use std::ffi::{CStr, c_int, c_uint}; 7 | use std::ptr::NonNull; 8 | 9 | use libc::{ 10 | AF_UNSPEC, AI_CANONNAME, IFF_LOOPBACK, IFF_UP, NI_NAMEREQD, NI_NUMERICHOST, SOCK_DGRAM, 11 | sockaddr, sockaddr_in, sockaddr_in6, socklen_t, 12 | }; 13 | use uucore::error::UResult; 14 | 15 | use crate::errors::{GetNameOrAddrInfoError, HostNameError}; 16 | use crate::net::{ 17 | AddressInfo, InterfaceAddresses, domain_name, get_name_info, host_name, in6_is_addr_linklocal, 18 | in6_is_addr_mc_linklocal, short_host_name, 19 | }; 20 | use crate::print::{ 21 | AliasHostName, AllFqdnHostName, AllIpAddressesHostName, DefaultHostName, DomainHostName, 22 | FqdnHostName, IpAddressHostName, NisHostName, PrintHostName, ShortHostName, 23 | }; 24 | 25 | impl PrintHostName for DefaultHostName { 26 | fn print_host_name(&self, out: &mut dyn std::io::Write) -> UResult<()> { 27 | let host_name = host_name()?; 28 | out.write_all(host_name.as_bytes())?; 29 | out.write_all(b"\n").map_err(From::from) 30 | } 31 | } 32 | 33 | impl PrintHostName for AliasHostName { 34 | fn print_host_name(&self, out: &mut dyn std::io::Write) -> UResult<()> { 35 | // This is intended to reproduce the behavior of calling gethostbyname() and then printing 36 | // the elements of hostent::h_aliases[]. 37 | // 38 | // However, gethostbyname() is deprecated. Replacing its usage with getaddrinfo() 39 | // and getnameinfo() does NOT produce the same result, but it is the best approach I can 40 | // think of, that is still portable. 41 | 42 | let host_name = host_name()?; 43 | let address_info = AddressInfo::new(&host_name, AF_UNSPEC, SOCK_DGRAM, 0, 0)?; 44 | 45 | out.write_all(host_name.as_bytes())?; 46 | 47 | address_info 48 | .iter() 49 | .map(|ai| get_name_info(ai.ai_addr, ai.ai_addrlen, NI_NAMEREQD)) 50 | .try_for_each(|name| -> UResult<()> { 51 | out.write_all(b" ")?; 52 | out.write_all(name?.as_bytes()).map_err(From::from) 53 | })?; 54 | 55 | out.write_all(b"\n").map_err(From::from) 56 | } 57 | } 58 | 59 | impl PrintHostName for DomainHostName { 60 | fn print_host_name(&self, out: &mut dyn std::io::Write) -> UResult<()> { 61 | let address_info = AddressInfo::new(&host_name()?, AF_UNSPEC, SOCK_DGRAM, 0, AI_CANONNAME)?; 62 | 63 | let canonical_name = address_info.first().ai_canonname; 64 | if canonical_name.is_null() { 65 | return Ok(()); // No canonical name set. 66 | }; 67 | 68 | let Some(domain_name) = unsafe { CStr::from_ptr(canonical_name) } 69 | .to_bytes() 70 | .splitn(2, |&byte| byte == b'.') 71 | .nth(1) 72 | else { 73 | return Ok(()); // Canonical name contains zero dots. 74 | }; 75 | 76 | out.write_all(domain_name)?; 77 | out.write_all(b"\n").map_err(From::from) 78 | } 79 | } 80 | 81 | impl PrintHostName for FqdnHostName { 82 | fn print_host_name(&self, out: &mut dyn std::io::Write) -> UResult<()> { 83 | let address_info = AddressInfo::new(&host_name()?, AF_UNSPEC, SOCK_DGRAM, 0, AI_CANONNAME)?; 84 | 85 | let canonical_name = address_info.first().ai_canonname; 86 | if canonical_name.is_null() { 87 | return Ok(()); // No canonical name set. 88 | }; 89 | 90 | out.write_all(unsafe { CStr::from_ptr(canonical_name) }.to_bytes())?; 91 | out.write_all(b"\n").map_err(From::from) 92 | } 93 | } 94 | 95 | #[allow(clippy::cast_possible_truncation, clippy::as_conversions)] 96 | fn filter_map_interface_addresses( 97 | interface_address: &libc::ifaddrs, 98 | ) -> Option<(NonNull, socklen_t)> { 99 | // Ensure the interface has a configured address. 100 | let addr = NonNull::new(interface_address.ifa_addr)?; 101 | 102 | if (interface_address.ifa_flags & (IFF_UP as c_uint)) == 0 { 103 | return None; // Interface is down. 104 | } 105 | 106 | if (interface_address.ifa_flags & (IFF_LOOPBACK as c_uint)) != 0 { 107 | return None; // This is the loop back interface. 108 | } 109 | 110 | match c_int::from(unsafe { addr.as_ref() }.sa_family) { 111 | libc::AF_INET => Some((addr, size_of::() as socklen_t)), 112 | 113 | libc::AF_INET6 => { 114 | let ipv6_addr = unsafe { &addr.cast::().as_ref().sin6_addr }; 115 | // Ensure ipv6_addr is not an IPv6 link-local address. 116 | (!in6_is_addr_linklocal(ipv6_addr) && !in6_is_addr_mc_linklocal(ipv6_addr)) 117 | .then_some((addr, size_of::() as socklen_t)) 118 | } 119 | 120 | _ => None, // Unsupported address family. 121 | } 122 | } 123 | 124 | impl PrintHostName for AllFqdnHostName { 125 | fn print_host_name(&self, out: &mut dyn std::io::Write) -> UResult<()> { 126 | let mut separator: &[u8] = &[]; 127 | 128 | InterfaceAddresses::new()? 129 | .iter() 130 | .filter_map(filter_map_interface_addresses) 131 | // Skip addresses whose translation fails. 132 | .filter_map(|(addr, size)| get_name_info(addr.as_ptr(), size, NI_NAMEREQD).ok()) 133 | .try_for_each(|name| { 134 | out.write_all(separator)?; 135 | separator = b" "; 136 | out.write_all(name.as_bytes()) 137 | })?; 138 | 139 | out.write_all(b"\n").map_err(From::from) 140 | } 141 | } 142 | 143 | impl PrintHostName for IpAddressHostName { 144 | fn print_host_name(&self, out: &mut dyn std::io::Write) -> UResult<()> { 145 | let mut separator: &[u8] = &[]; 146 | 147 | AddressInfo::new(&host_name()?, AF_UNSPEC, SOCK_DGRAM, 0, 0)? 148 | .iter() 149 | .map(|ai| get_name_info(ai.ai_addr, ai.ai_addrlen, NI_NUMERICHOST)) 150 | .try_for_each(|name| -> UResult<()> { 151 | out.write_all(separator)?; 152 | separator = b" "; 153 | out.write_all(name?.as_bytes()).map_err(From::from) 154 | })?; 155 | 156 | out.write_all(b"\n").map_err(From::from) 157 | } 158 | } 159 | 160 | impl PrintHostName for AllIpAddressesHostName { 161 | fn print_host_name(&self, out: &mut dyn std::io::Write) -> UResult<()> { 162 | const NONAME: HostNameError = 163 | HostNameError::GetNameOrAddrInfo(GetNameOrAddrInfoError(libc::EAI_NONAME)); 164 | 165 | let mut separator: &[u8] = &[]; 166 | 167 | InterfaceAddresses::new()? 168 | .iter() 169 | .filter_map(filter_map_interface_addresses) 170 | .map(|(addr, addr_size)| get_name_info(addr.as_ptr(), addr_size, NI_NUMERICHOST)) 171 | .filter(|result| *result != Err(NONAME)) 172 | .try_for_each(|name| -> UResult<()> { 173 | out.write_all(separator)?; 174 | separator = b" "; 175 | out.write_all(name?.as_bytes()).map_err(From::from) 176 | })?; 177 | 178 | out.write_all(b"\n").map_err(From::from) 179 | } 180 | } 181 | 182 | impl PrintHostName for ShortHostName { 183 | fn print_host_name(&self, out: &mut dyn std::io::Write) -> UResult<()> { 184 | let host_name = short_host_name()?; 185 | out.write_all(host_name.as_bytes())?; 186 | out.write_all(b"\n").map_err(From::from) 187 | } 188 | } 189 | 190 | impl PrintHostName for NisHostName { 191 | fn print_host_name(&self, out: &mut dyn std::io::Write) -> UResult<()> { 192 | if let Some(domain_name) = domain_name()? { 193 | out.write_all(domain_name.as_bytes())?; 194 | out.write_all(b"\n").map_err(From::from) 195 | } else { 196 | Err(Box::new(HostNameError::NoLocalDomainName)) 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/uu/hostname/src/print/windows.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils hostname package. 2 | // 3 | // For the full copyright and license information, please view the LICENSE 4 | // file that was distributed with this source code. 5 | 6 | use std::ffi::c_int; 7 | 8 | use uucore::error::UResult; 9 | use windows_sys::Win32::NetworkManagement::IpHelper::{ 10 | IF_TYPE_SOFTWARE_LOOPBACK, IP_ADAPTER_ADDRESSES_LH, IP_ADAPTER_IPV4_ENABLED, 11 | IP_ADAPTER_IPV6_ENABLED, IP_ADAPTER_UNICAST_ADDRESS_LH, 12 | }; 13 | use windows_sys::Win32::NetworkManagement::Ndis::IfOperStatusUp; 14 | use windows_sys::Win32::Networking::WinSock::{ 15 | AF_INET, AF_INET6, AF_UNSPEC, NI_NAMEREQD, NI_NUMERICHOST, SOCK_DGRAM, SOCKADDR, SOCKADDR_IN6, 16 | WSAHOST_NOT_FOUND, 17 | }; 18 | 19 | use crate::errors::HostNameError; 20 | use crate::net::{ 21 | AdapterUnicastAddressIter, AddressInfo, InterfaceAddresses, domain_name, 22 | fully_qualified_dns_name, get_name_info, host_name, in6_is_addr_linklocal, 23 | in6_is_addr_mc_linklocal, short_host_name, 24 | }; 25 | use crate::print::{ 26 | AliasHostName, AllFqdnHostName, AllIpAddressesHostName, DefaultHostName, DomainHostName, 27 | FqdnHostName, IpAddressHostName, NisHostName, PrintHostName, ShortHostName, 28 | }; 29 | 30 | impl PrintHostName for DefaultHostName { 31 | fn print_host_name(&self, out: &mut dyn std::io::Write) -> UResult<()> { 32 | let host_name = host_name()?; 33 | out.write_all(host_name.as_encoded_bytes())?; 34 | out.write_all(b"\n").map_err(From::from) 35 | } 36 | } 37 | 38 | impl PrintHostName for AliasHostName { 39 | fn print_host_name(&self, out: &mut dyn std::io::Write) -> UResult<()> { 40 | // This is intended to reproduce the behavior of calling gethostbyname() and then printing 41 | // the elements of hostent::h_aliases[]. 42 | // 43 | // However, gethostbyname() is deprecated. Replacing its usage with GetAddrInfoW() 44 | // and GetNameInfoW() does NOT produce the same result, but it is the best approach I can 45 | // think of, that is still portable. 46 | 47 | let host_name = host_name()?; 48 | out.write_all(host_name.as_encoded_bytes())?; 49 | 50 | AddressInfo::new(&host_name, AF_UNSPEC as c_int, SOCK_DGRAM, 0, 0)? 51 | .iter() 52 | .map(|ai| get_name_info(ai.ai_addr, ai.ai_addrlen, NI_NAMEREQD as c_int)) 53 | .try_for_each(|name| -> UResult<()> { 54 | out.write_all(b" ")?; 55 | out.write_all(name?.as_encoded_bytes()).map_err(From::from) 56 | })?; 57 | 58 | out.write_all(b"\n").map_err(From::from) 59 | } 60 | } 61 | 62 | impl PrintHostName for DomainHostName { 63 | fn print_host_name(&self, out: &mut dyn std::io::Write) -> UResult<()> { 64 | if let Some(domain_name) = domain_name()? { 65 | out.write_all(domain_name.as_encoded_bytes())?; 66 | out.write_all(b"\n").map_err(From::from) 67 | } else { 68 | Ok(()) 69 | } 70 | } 71 | } 72 | 73 | impl PrintHostName for FqdnHostName { 74 | fn print_host_name(&self, out: &mut dyn std::io::Write) -> UResult<()> { 75 | let host_name = fully_qualified_dns_name()?; 76 | out.write_all(host_name.as_encoded_bytes())?; 77 | out.write_all(b"\n").map_err(From::from) 78 | } 79 | } 80 | 81 | fn filter_interface_addresses(ai: &&IP_ADAPTER_ADDRESSES_LH) -> bool { 82 | // Interface is up. 83 | ai.OperStatus == IfOperStatusUp && 84 | // This is NOT the loop back interface. 85 | (ai.IfType & IF_TYPE_SOFTWARE_LOOPBACK) == 0 && 86 | // Ensure the interface has a configured address. 87 | !ai.FirstUnicastAddress.is_null() && 88 | // Ensure the interface has an IPv4 or IPv6 address. 89 | // Windows Vista or later is required for this check. 90 | (unsafe { ai.Anonymous2.Flags } & (IP_ADAPTER_IPV4_ENABLED | IP_ADAPTER_IPV6_ENABLED)) != 0 91 | } 92 | 93 | fn filter_map_interface_addresses( 94 | addr: &IP_ADAPTER_UNICAST_ADDRESS_LH, 95 | ) -> Option<(*mut SOCKADDR, usize)> { 96 | let Ok(addr_size) = usize::try_from(addr.Address.iSockaddrLength) else { 97 | return None; 98 | }; 99 | 100 | match unsafe { *addr.Address.lpSockaddr }.sa_family { 101 | AF_INET => Some((addr.Address.lpSockaddr, addr_size)), 102 | 103 | AF_INET6 => { 104 | let ipv6_addr = unsafe { &(*addr.Address.lpSockaddr.cast::()).sin6_addr }; 105 | // Ensure ipv6_addr is not an IPv6 link-local address. 106 | (!in6_is_addr_linklocal(ipv6_addr) && !in6_is_addr_mc_linklocal(ipv6_addr)) 107 | .then_some((addr.Address.lpSockaddr, addr_size)) 108 | } 109 | 110 | _ => None, // Unsupported address family. 111 | } 112 | } 113 | 114 | impl PrintHostName for AllFqdnHostName { 115 | fn print_host_name(&self, out: &mut dyn std::io::Write) -> UResult<()> { 116 | let mut separator: &[u8] = &[]; 117 | 118 | InterfaceAddresses::new()? 119 | .iter() 120 | .filter(filter_interface_addresses) 121 | .flat_map(AdapterUnicastAddressIter::new) 122 | .filter_map(filter_map_interface_addresses) 123 | // Skip addresses whose translation fails. 124 | .filter_map(|(addr, size)| get_name_info(addr, size, NI_NAMEREQD as c_int).ok()) 125 | .try_for_each(|name| { 126 | out.write_all(separator)?; 127 | separator = b" "; 128 | out.write_all(name.as_encoded_bytes()) 129 | })?; 130 | 131 | out.write_all(b"\n").map_err(From::from) 132 | } 133 | } 134 | 135 | impl PrintHostName for IpAddressHostName { 136 | fn print_host_name(&self, out: &mut dyn std::io::Write) -> UResult<()> { 137 | let mut separator: &[u8] = &[]; 138 | 139 | AddressInfo::new(&host_name()?, AF_UNSPEC as c_int, SOCK_DGRAM, 0, 0)? 140 | .iter() 141 | .map(|ai| get_name_info(ai.ai_addr, ai.ai_addrlen, NI_NUMERICHOST as c_int)) 142 | .try_for_each(|name| -> UResult<()> { 143 | out.write_all(separator)?; 144 | separator = b" "; 145 | out.write_all(name?.as_encoded_bytes()).map_err(From::from) 146 | })?; 147 | 148 | out.write_all(b"\n").map_err(From::from) 149 | } 150 | } 151 | 152 | impl PrintHostName for AllIpAddressesHostName { 153 | fn print_host_name(&self, out: &mut dyn std::io::Write) -> UResult<()> { 154 | let mut separator: &[u8] = &[]; 155 | 156 | InterfaceAddresses::new()? 157 | .iter() 158 | .filter(filter_interface_addresses) 159 | .flat_map(AdapterUnicastAddressIter::new) 160 | .filter_map(filter_map_interface_addresses) 161 | .map(|(addr, size)| get_name_info(addr, size, NI_NUMERICHOST as c_int)) 162 | .filter(|r| !matches!(r, Err(err) if err.raw_os_error() == Some(WSAHOST_NOT_FOUND))) 163 | .try_for_each(|name| { 164 | out.write_all(separator)?; 165 | separator = b" "; 166 | out.write_all(name?.as_encoded_bytes()) 167 | })?; 168 | 169 | out.write_all(b"\n").map_err(From::from) 170 | } 171 | } 172 | 173 | impl PrintHostName for ShortHostName { 174 | fn print_host_name(&self, out: &mut dyn std::io::Write) -> UResult<()> { 175 | let host_name = short_host_name()?; 176 | out.write_all(host_name.as_encoded_bytes())?; 177 | out.write_all(b"\n").map_err(From::from) 178 | } 179 | } 180 | 181 | impl PrintHostName for NisHostName { 182 | fn print_host_name(&self, out: &mut dyn std::io::Write) -> UResult<()> { 183 | if let Some(domain_name) = domain_name()? { 184 | out.write_all(domain_name.as_encoded_bytes())?; 185 | out.write_all(b"\n").map_err(From::from) 186 | } else { 187 | Err(Box::new(HostNameError::NoLocalDomainName)) 188 | } 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/uu/hostname/src/utils.rs: -------------------------------------------------------------------------------- 1 | use std::io::{BufRead, BufReader, Read}; 2 | use std::path::Path; 3 | 4 | use uucore::error::UResult; 5 | 6 | pub(crate) fn parse_host_name_file(path: &Path) -> UResult> { 7 | let mut file = std::fs::File::open(path).map(BufReader::new)?; 8 | 9 | let first_byte = loop { 10 | let mut first_byte = [0_u8; 1]; 11 | if let Err(err) = file.read_exact(&mut first_byte) { 12 | if err.kind() == std::io::ErrorKind::UnexpectedEof { 13 | return Ok(Vec::default()); // Empty name. 14 | } 15 | } 16 | 17 | match first_byte[0] { 18 | b'\r' | b'\n' => {} // Empty line. Skip. 19 | 20 | b'#' => { 21 | file.skip_until(b'\n')?; // Comment line. Skip. 22 | } 23 | 24 | first_byte => break first_byte, 25 | } 26 | }; 27 | 28 | let mut buffer = Vec::with_capacity(256); 29 | buffer.push(first_byte); 30 | file.read_until(b'\n', &mut buffer)?; 31 | while matches!(buffer.last().copied(), Some(b'\r' | b'\n')) { 32 | buffer.pop(); 33 | } 34 | Ok(buffer) 35 | } 36 | -------------------------------------------------------------------------------- /tests/by-util/test_hostname.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils hostname package. 2 | // 3 | // For the full copyright and license information, please view the LICENSE 4 | // file that was distributed with this source code. 5 | 6 | use uutests::new_ucmd; 7 | use uutests::util::TestScenario; 8 | use uutests::util_name; 9 | 10 | #[test] 11 | fn test_invalid_arg() { 12 | new_ucmd!().arg("--definitely-invalid").fails().code_is(1); 13 | } 14 | -------------------------------------------------------------------------------- /tests/tests.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils hostname package. 2 | // 3 | // For the full copyright and license information, please view the LICENSE 4 | // file that was distributed with this source code. 5 | use std::env; 6 | 7 | pub const TESTS_BINARY: &str = env!("CARGO_BIN_EXE_hostname"); 8 | 9 | // Use the ctor attribute to run this function before any tests 10 | #[ctor::ctor] 11 | fn init() { 12 | unsafe { 13 | // Necessary for uutests to be able to find the binary 14 | std::env::set_var("UUTESTS_BINARY_PATH", TESTS_BINARY); 15 | } 16 | } 17 | 18 | #[cfg(feature = "hostname")] 19 | #[path = "by-util/test_hostname.rs"] 20 | mod test_hostname; 21 | --------------------------------------------------------------------------------