├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── build.rs ├── renovate.json ├── src ├── bin │ ├── util-linux.rs │ └── uudoc.rs └── uu │ ├── blockdev │ ├── Cargo.toml │ ├── blockdev.md │ └── src │ │ ├── blockdev.rs │ │ └── main.rs │ ├── chcpu │ ├── Cargo.toml │ ├── chcpu.md │ └── src │ │ ├── chcpu.rs │ │ ├── errors.rs │ │ ├── main.rs │ │ └── sysfs.rs │ ├── ctrlaltdel │ ├── Cargo.toml │ ├── ctrlaltdel.md │ └── src │ │ ├── ctrlaltdel.rs │ │ └── main.rs │ ├── dmesg │ ├── Cargo.toml │ ├── dmesg.md │ └── src │ │ ├── dmesg.rs │ │ ├── json.rs │ │ ├── main.rs │ │ └── time_formatter.rs │ ├── fsfreeze │ ├── Cargo.toml │ ├── fsfreeze.md │ └── src │ │ ├── fsfreeze.rs │ │ └── main.rs │ ├── last │ ├── Cargo.toml │ ├── last.md │ └── src │ │ ├── last.rs │ │ ├── main.rs │ │ └── platform │ │ ├── macos.rs │ │ ├── mod.rs │ │ ├── openbsd.rs │ │ ├── unix.rs │ │ └── windows.rs │ ├── lscpu │ ├── Cargo.toml │ ├── lscpu.md │ └── src │ │ ├── lscpu.rs │ │ ├── main.rs │ │ └── sysfs.rs │ ├── lsipc │ ├── Cargo.toml │ ├── after-help.txt │ ├── lsipc.md │ └── src │ │ ├── column.rs │ │ ├── display.rs │ │ ├── errors.rs │ │ ├── lsipc.rs │ │ ├── main.rs │ │ ├── message_queue.rs │ │ ├── semaphore.rs │ │ ├── shared_memory.rs │ │ ├── smartcols.rs │ │ └── utils.rs │ ├── lslocks │ ├── Cargo.toml │ ├── columns.json │ ├── columns.raw │ ├── columns.txt │ ├── lslocks.md │ └── src │ │ ├── column.rs │ │ ├── display.rs │ │ ├── errors.rs │ │ ├── lslocks.rs │ │ ├── main.rs │ │ ├── smartcols.rs │ │ └── utils.rs │ ├── lsmem │ ├── Cargo.toml │ ├── lsmem.md │ └── src │ │ ├── lsmem.rs │ │ ├── main.rs │ │ └── utils.rs │ ├── mcookie │ ├── Cargo.toml │ ├── mcookie.md │ └── src │ │ ├── main.rs │ │ ├── mcookie.rs │ │ └── size.rs │ ├── mesg │ ├── Cargo.toml │ ├── mesg.md │ └── src │ │ ├── main.rs │ │ └── mesg.rs │ ├── mountpoint │ ├── Cargo.toml │ ├── mountpoint.md │ └── src │ │ ├── main.rs │ │ └── mountpoint.rs │ ├── renice │ ├── Cargo.toml │ ├── renice.md │ └── src │ │ ├── main.rs │ │ └── renice.rs │ ├── rev │ ├── Cargo.toml │ ├── rev.md │ └── src │ │ ├── main.rs │ │ └── rev.rs │ ├── setsid │ ├── Cargo.toml │ ├── setsid.md │ └── src │ │ ├── main.rs │ │ └── setsid.rs │ └── uuidgen │ ├── Cargo.toml │ ├── src │ ├── main.rs │ └── uuidgen.rs │ └── uuidgen.md └── tests ├── by-util ├── test_blockdev.rs ├── test_ctrlaltdel.rs ├── test_dmesg.rs ├── test_fsfreeze.rs ├── test_last.rs ├── test_lscpu.rs ├── test_lslocks.rs ├── test_lsmem.rs ├── test_mcookie.rs ├── test_mesg.rs ├── test_mountpoint.rs ├── test_renice.rs ├── test_rev.rs ├── test_setsid.rs └── test_uuidgen.rs ├── common ├── macros.rs ├── mod.rs ├── random.rs └── util.rs ├── fixtures ├── dmesg │ ├── kmsg.input │ ├── kmsg.input.1 │ ├── test_kmsg_json.expected │ ├── test_kmsg_time_format_ctime.expected │ ├── test_kmsg_time_format_delta.expected │ ├── test_kmsg_time_format_iso.expected │ ├── test_kmsg_time_format_notime.expected │ ├── test_kmsg_time_format_raw.expected │ ├── test_kmsg_time_format_reltime.expected │ └── test_since_until.expected ├── last │ └── last.input.1 ├── lsmem │ ├── test_lsmem_columns_json.expected │ ├── test_lsmem_columns_pairs.expected │ ├── test_lsmem_columns_raw.expected │ ├── test_lsmem_columns_table.expected │ ├── test_lsmem_json.expected │ ├── test_lsmem_json_all.expected │ ├── test_lsmem_json_bytes.expected │ ├── test_lsmem_json_noheadings.expected │ ├── test_lsmem_pairs.expected │ ├── test_lsmem_pairs_all.expected │ ├── test_lsmem_pairs_bytes.expected │ ├── test_lsmem_pairs_noheadings.expected │ ├── test_lsmem_raw.expected │ ├── test_lsmem_raw_all.expected │ ├── test_lsmem_raw_bytes.expected │ ├── test_lsmem_raw_noheadings.expected │ ├── test_lsmem_split_node.expected │ ├── test_lsmem_split_output_default.expected │ ├── test_lsmem_split_removable.expected │ ├── test_lsmem_split_state.expected │ ├── test_lsmem_split_zones.expected │ ├── test_lsmem_summary_always.expected │ ├── test_lsmem_summary_empty.expected │ ├── test_lsmem_summary_never.expected │ ├── test_lsmem_summary_only.expected │ ├── test_lsmem_table.expected │ ├── test_lsmem_table_all.expected │ ├── test_lsmem_table_bytes.expected │ └── test_lsmem_table_noheadings.expected └── util │ └── is_atty.sh └── 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 | - name: Install dependencies 19 | shell: bash 20 | run: case ${{ matrix.os }} in ubuntu-*) sudo apt-get update --quiet && sudo apt-get install --quiet --no-install-recommends --assume-yes libclang-dev libc6-dev libsmartcols-dev libmount-dev ;; esac; 21 | - run: cargo check 22 | 23 | test: 24 | name: cargo test 25 | runs-on: ${{ matrix.os }} 26 | strategy: 27 | matrix: 28 | os: [ubuntu-latest, macOS-latest, windows-latest] 29 | steps: 30 | - uses: actions/checkout@v4 31 | - uses: dtolnay/rust-toolchain@stable 32 | - name: Install dependencies 33 | shell: bash 34 | run: case ${{ matrix.os }} in ubuntu-*) sudo apt-get update --quiet && sudo apt-get install --quiet --no-install-recommends --assume-yes libclang-dev libc6-dev libsmartcols-dev libmount-dev ;; esac; 35 | - run: cargo test 36 | 37 | fmt: 38 | name: cargo fmt --all -- --check 39 | runs-on: ubuntu-latest 40 | steps: 41 | - uses: actions/checkout@v4 42 | - uses: dtolnay/rust-toolchain@stable 43 | - run: sudo apt-get update --quiet 44 | - run: sudo apt-get install --quiet --no-install-recommends --assume-yes libclang-dev libc6-dev libsmartcols-dev libmount-dev 45 | - run: rustup component add rustfmt 46 | - run: cargo fmt --all -- --check 47 | 48 | clippy: 49 | name: cargo clippy -- -D warnings 50 | runs-on: ${{ matrix.os }} 51 | strategy: 52 | matrix: 53 | os: [ubuntu-latest, macOS-latest, windows-latest] 54 | steps: 55 | - uses: actions/checkout@v4 56 | - uses: dtolnay/rust-toolchain@stable 57 | - name: Install dependencies 58 | shell: bash 59 | run: case ${{ matrix.os }} in ubuntu-*) sudo apt-get update --quiet && sudo apt-get install --quiet --no-install-recommends --assume-yes libclang-dev libc6-dev libsmartcols-dev libmount-dev ;; esac; 60 | - run: rustup component add clippy 61 | - run: cargo clippy -- -D warnings 62 | 63 | coverage: 64 | name: Code Coverage 65 | runs-on: ${{ matrix.job.os }} 66 | strategy: 67 | fail-fast: true 68 | matrix: 69 | job: 70 | - { os: ubuntu-latest , features: unix } 71 | - { os: macos-latest , features: macos } 72 | - { os: windows-latest , features: windows } 73 | steps: 74 | - uses: actions/checkout@v4 75 | - name: Initialize workflow variables 76 | id: vars 77 | shell: bash 78 | run: | 79 | ## VARs setup 80 | outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; } 81 | # toolchain 82 | TOOLCHAIN="nightly" ## default to "nightly" toolchain (required for certain required unstable compiler flags) ## !maint: refactor when stable channel has needed support 83 | # * specify gnu-type TOOLCHAIN for windows; `grcov` requires gnu-style code coverage data files 84 | case ${{ matrix.job.os }} in windows-*) TOOLCHAIN="$TOOLCHAIN-x86_64-pc-windows-gnu" ;; esac; 85 | # * use requested TOOLCHAIN if specified 86 | if [ -n "${{ matrix.job.toolchain }}" ]; then TOOLCHAIN="${{ matrix.job.toolchain }}" ; fi 87 | outputs TOOLCHAIN 88 | # target-specific options 89 | # * CODECOV_FLAGS 90 | CODECOV_FLAGS=$( echo "${{ matrix.job.os }}" | sed 's/[^[:alnum:]]/_/g' ) 91 | outputs CODECOV_FLAGS 92 | 93 | - name: Install dependencies 94 | shell: bash 95 | run: case ${{ matrix.job.os }} in ubuntu-*) sudo apt-get update --quiet && sudo apt-get install --quiet --no-install-recommends --assume-yes libclang-dev libc6-dev libsmartcols-dev libmount-dev ;; esac; 96 | 97 | - name: rust toolchain ~ install 98 | uses: dtolnay/rust-toolchain@nightly 99 | - name: Install llvm-tools-preview 100 | run: rustup component add llvm-tools-preview 101 | - name: Test 102 | run: cargo test --no-fail-fast 103 | env: 104 | CARGO_INCREMENTAL: "0" 105 | RUSTC_WRAPPER: "" 106 | RUSTFLAGS: "-Cinstrument-coverage -Zcoverage-options=branch -Ccodegen-units=1 -Copt-level=0 -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort" 107 | RUSTDOCFLAGS: "-Cpanic=abort" 108 | LLVM_PROFILE_FILE: "util-linux-%p-%m.profraw" 109 | - name: "`grcov` ~ install" 110 | id: build_grcov 111 | shell: bash 112 | run: | 113 | git clone https://github.com/mozilla/grcov.git ~/grcov/ 114 | cd ~/grcov 115 | # Hardcode the version of crossbeam-epoch. See 116 | # https://github.com/uutils/coreutils/issues/3680 117 | sed -i -e "s|tempfile =|crossbeam-epoch = \"=0.9.8\"\ntempfile =|" Cargo.toml 118 | cargo install --path . 119 | cd - 120 | # Uncomment when the upstream issue 121 | # https://github.com/mozilla/grcov/issues/849 is fixed 122 | # uses: actions-rs/install@v0.1 123 | # with: 124 | # crate: grcov 125 | # version: latest 126 | # use-tool-cache: false 127 | - name: Generate coverage data (via `grcov`) 128 | id: coverage 129 | shell: bash 130 | run: | 131 | ## Generate coverage data 132 | COVERAGE_REPORT_DIR="target/debug" 133 | COVERAGE_REPORT_FILE="${COVERAGE_REPORT_DIR}/lcov.info" 134 | mkdir -p "${COVERAGE_REPORT_DIR}" 135 | # display coverage files 136 | 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 137 | # generate coverage report 138 | 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\()" 139 | echo "report=${COVERAGE_REPORT_FILE}" >> $GITHUB_OUTPUT 140 | - name: Upload coverage results (to Codecov.io) 141 | uses: codecov/codecov-action@v5 142 | with: 143 | token: ${{ secrets.CODECOV_TOKEN }} 144 | files: ${{ steps.coverage.outputs.report }} 145 | ## flags: IntegrationTests, UnitTests, ${{ steps.vars.outputs.CODECOV_FLAGS }} 146 | flags: ${{ steps.vars.outputs.CODECOV_FLAGS }} 147 | name: codecov-umbrella 148 | fail_ci_if_error: false 149 | 150 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | 3 | /target/ 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | # util-linux (uutils) 2 | # * see the repository LICENSE, README, and CONTRIBUTING files for more information 3 | 4 | # spell-checker:ignore (libs) bigdecimal datetime fundu gethostid kqueue libselinux mangen memmap procfs uuhelp 5 | 6 | [package] 7 | name = "util-linux" 8 | version = "0.0.1" 9 | authors = ["uutils developers"] 10 | license = "MIT" 11 | description = "util-linux ~ GNU util-linux (updated); implemented as universal (cross-platform) utils, written in Rust" 12 | default-run = "util-linux" 13 | 14 | homepage = "https://github.com/uutils/util-linux" 15 | repository = "https://github.com/uutils/util-linux" 16 | readme = "README.md" 17 | keywords = ["util-linux", "uutils", "cross-platform", "cli", "utility"] 18 | categories = ["command-line-utilities"] 19 | rust-version = "1.70.0" 20 | edition = "2021" 21 | 22 | build = "build.rs" 23 | 24 | [features] 25 | default = ["feat_common_core"] 26 | uudoc = [] 27 | 28 | feat_common_core = [ 29 | "blockdev", 30 | "chcpu", 31 | "ctrlaltdel", 32 | "dmesg", 33 | "fsfreeze", 34 | "last", 35 | "lscpu", 36 | "lsipc", 37 | "lslocks", 38 | "lsmem", 39 | "mcookie", 40 | "mesg", 41 | "mountpoint", 42 | "renice", 43 | "rev", 44 | "setsid", 45 | "uuidgen", 46 | ] 47 | 48 | [workspace.dependencies] 49 | clap = { version = "4.4", features = ["wrap_help", "cargo"] } 50 | clap_complete = "4.4" 51 | clap_mangen = "0.2" 52 | dns-lookup = "2.0.4" 53 | errno = "0.3" 54 | libc = "0.2.171" 55 | libmount-sys = "0.1.1" 56 | linux-raw-sys = { version = "0.9.0", features = ["ioctl"] } 57 | md-5 = "0.10.6" 58 | nix = { version = "0.30", default-features = false } 59 | phf = "0.11.2" 60 | phf_codegen = "0.11.2" 61 | rand = { version = "0.9.0", features = ["small_rng"] } 62 | rangemap = "1.5.1" 63 | regex = "1.10.2" 64 | serde = { version = "1.0", features = ["derive"] } 65 | serde_json = { version = "1.0.122", features = ["preserve_order"] } 66 | smartcols-sys = "0.1" 67 | syscall-numbers = "4.0.2" 68 | sysinfo = "0.35" 69 | tempfile = "3.9.0" 70 | textwrap = { version = "0.16.0", features = ["terminal_size"] } 71 | thiserror = "2.0" 72 | uucore = "0.1.0" 73 | uuid = { version = "1.16.0", features = ["rng-rand"] } 74 | windows = { version = "0.61.1" } 75 | xattr = "1.3.1" 76 | 77 | [dependencies] 78 | clap = { workspace = true } 79 | clap_complete = { workspace = true } 80 | clap_mangen = { workspace = true } 81 | dns-lookup = { workspace = true } 82 | phf = { workspace = true } 83 | serde = { workspace = true } 84 | serde_json = { workspace = true } 85 | textwrap = { workspace = true } 86 | uucore = { workspace = true } 87 | 88 | # 89 | blockdev = { optional = true, version = "0.0.1", package = "uu_blockdev", path = "src/uu/blockdev" } 90 | chcpu = { optional = true, version = "0.0.1", package = "uu_chcpu", path = "src/uu/chcpu" } 91 | ctrlaltdel = { optional = true, version = "0.0.1", package = "uu_ctrlaltdel", path = "src/uu/ctrlaltdel" } 92 | dmesg = { optional = true, version = "0.0.1", package = "uu_dmesg", path = "src/uu/dmesg" } 93 | fsfreeze = { optional = true, version = "0.0.1", package = "uu_fsfreeze", path = "src/uu/fsfreeze" } 94 | last = { optional = true, version = "0.0.1", package = "uu_last", path = "src/uu/last" } 95 | lscpu = { optional = true, version = "0.0.1", package = "uu_lscpu", path = "src/uu/lscpu" } 96 | lsipc = { optional = true, version = "0.0.1", package = "uu_lsipc", path = "src/uu/lsipc" } 97 | lslocks = { optional = true, version = "0.0.1", package = "uu_lslocks", path = "src/uu/lslocks" } 98 | lsmem = { optional = true, version = "0.0.1", package = "uu_lsmem", path = "src/uu/lsmem" } 99 | mcookie = { optional = true, version = "0.0.1", package = "uu_mcookie", path = "src/uu/mcookie" } 100 | mesg = { optional = true, version = "0.0.1", package = "uu_mesg", path = "src/uu/mesg" } 101 | mountpoint = { optional = true, version = "0.0.1", package = "uu_mountpoint", path = "src/uu/mountpoint" } 102 | renice = { optional = true, version = "0.0.1", package = "uu_renice", path = "src/uu/renice" } 103 | rev = { optional = true, version = "0.0.1", package = "uu_rev", path = "src/uu/rev" } 104 | setsid = { optional = true, version = "0.0.1", package = "uu_setsid", path ="src/uu/setsid" } 105 | uuidgen = { optional = true, version = "0.0.1", package = "uu_uuidgen", path ="src/uu/uuidgen" } 106 | 107 | [dev-dependencies] 108 | # dmesg test require fixed-boot-time feature turned on. 109 | dmesg = { version = "0.0.1", package = "uu_dmesg", path = "src/uu/dmesg", features = ["fixed-boot-time"] } 110 | libc = { workspace = true } 111 | pretty_assertions = "1" 112 | rand = { workspace = true } 113 | regex = { workspace = true } 114 | tempfile = { workspace = true } 115 | uucore = { workspace = true, features = ["entries", "process", "signals"] } 116 | uuid = { workspace = true } 117 | 118 | [target.'cfg(unix)'.dev-dependencies] 119 | nix = { workspace = true, features = ["term"] } 120 | xattr = { workspace = true } 121 | 122 | [target.'cfg(any(target_os = "linux", target_os = "android"))'.dev-dependencies] 123 | procfs = { version = "0.17", default-features = false } 124 | rlimit = "0.10.1" 125 | 126 | [build-dependencies] 127 | phf_codegen = { workspace = true } 128 | 129 | 130 | [[bin]] 131 | name = "util-linux" 132 | path = "src/bin/util-linux.rs" 133 | 134 | [[bin]] 135 | name = "uudoc" 136 | path = "src/bin/uudoc.rs" 137 | required-features = ["uudoc"] 138 | 139 | # The default release profile. It contains all optimizations, without 140 | # sacrificing debug info. With this profile (like in the standard 141 | # release profile), the debug info and the stack traces will still be available. 142 | [profile.release] 143 | lto = true 144 | 145 | # A release-like profile that is tuned to be fast, even when being fast 146 | # compromises on binary size. This includes aborting on panic. 147 | [profile.release-fast] 148 | inherits = "release" 149 | panic = "abort" 150 | 151 | # A release-like profile that is as small as possible. 152 | [profile.release-small] 153 | inherits = "release" 154 | opt-level = "z" 155 | panic = "abort" 156 | strip = true 157 | -------------------------------------------------------------------------------- /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 | [![Crates.io](https://img.shields.io/crates/v/util-linux.svg)](https://crates.io/crates/util-linux) 2 | [![Discord](https://img.shields.io/badge/discord-join-7289DA.svg?logo=discord&longCache=true&style=flat)](https://discord.gg/wQVJbvJ) 3 | [![License](http://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/uutils/util-linux/blob/main/LICENSE) 4 | [![dependency status](https://deps.rs/repo/github/uutils/util-linux/status.svg)](https://deps.rs/repo/github/uutils/util-linux) 5 | 6 | [![CodeCov](https://codecov.io/gh/uutils/util-linux/branch/master/graph/badge.svg)](https://codecov.io/gh/uutils/util-linux) 7 | 8 | # util-linux 9 | 10 | This projects aims at doing the same as https://github.com/uutils/coreutils for util-linux. 11 | 12 | We are rewriting [these tools](https://github.com/util-linux/util-linux) in Rust as drop-in replacements. 13 | 14 | First, reimplement the most important tools from util-linux: 15 | 16 | ## System Information 17 | - `dmesg`: Displays kernel messages. 18 | - `lscpu`: Shows CPU architecture information. 19 | Started 20 | - `lsipc`: Lists IPC facilities. 21 | - `lslocks`: Lists system locks. 22 | - `lsmem`: Lists memory ranges and status. 23 | - `lsns`: Lists namespaces. 24 | 25 | ## Hardware Management 26 | - `chcpu`: Manages CPU state. 27 | - `rtcwake`: Manages system sleep states. 28 | - `zramctl`: Manages zram devices. 29 | - `wdctl`: Shows watchdog status. 30 | - `chmem`: Manages kernel memory usage. 31 | 32 | ## Filesystem Tools 33 | - `findmnt`: Lists mounted filesystems. 34 | - `mountpoint`: Checks if a directory is a mountpoint. 35 | Started 36 | - `fsck`: Checks and repairs filesystems. 37 | - `fsfreeze`: Freezes/unfreezes filesystems. 38 | Done 39 | - `fstrim`: Discards unused blocks on filesystems. 40 | - `wipefs`: Wipes filesystem signatures. 41 | 42 | ## Partition Management 43 | - `blkdiscard`: Discards sectors on a device. 44 | - `blkid`: Identifies block device attributes. 45 | - `blkzone`: Manages zoned block device parameters. 46 | - `blockdev`: Performs block device operations. 47 | - `mkswap`: Sets up swap space. 48 | - `swaplabel`: Manages swap space labels. 49 | - `addpart`: Adds a partition. 50 | - `delpart`: Deletes a partition. 51 | - `partx`: Manages partition entries. 52 | - `resizepart`: Resizes a partition. 53 | 54 | ## Process and Resource Management 55 | - `runuser`: Runs a shell with different user/group IDs. 56 | - `sulogin`: Provides single-user mode login. 57 | - `chrt`: Manages real-time process attributes. 58 | - `ionice`: Sets process I/O scheduling class/priority. 59 | - `kill`: Sends signals to processes. 60 | - `renice`: Alters process priority. 61 | - `prlimit`: Sets/gets process resource limits. 62 | - `taskset`: Sets/gets process CPU affinity. 63 | - `uclampset`: Manages process utilization clamping. 64 | 65 | ## User and Session Management 66 | - `su`: Changes user ID or becomes superuser. 67 | - `agetty`: Manages TTYs for login prompts. 68 | - `ctrlaltdel`: Configures Ctrl-Alt-Del action. 69 | - `pivot_root`: Changes the root filesystem. 70 | - `switch_root`: Switches to a different root filesystem. 71 | - `last`: Lists last logged-in users. 72 | - `lslogins`: Displays user information. 73 | - `mesg`: Controls write access to terminal. 74 | - `setsid`: Runs a program in a new session. 75 | - `setterm`: Sets terminal attributes. 76 | - `getty`: Manages virtual console login prompts. 77 | 78 | ## Networking and IPC 79 | - `ipcmk`: Creates IPC resources. 80 | - `ipcrm`: Removes IPC resources. 81 | - `ipcs`: Shows IPC facilities status. 82 | - `nsenter`: Enters different namespaces. 83 | 84 | ## Utility Tools 85 | - `lsblk`: Lists block devices. 86 | - `fallocate`: Preallocates file space. 87 | - `flock`: Manages file locks. 88 | - `getopt`: Parses command options. 89 | - `hardlink`: Creates hard links. 90 | - `mcookie`: Generates random numbers. 91 | - `namei`: Follows a pathname to its endpoint. 92 | - `rename`: Renames files. 93 | - `rev`: Reverses lines in a file. 94 | - `setarch`: Sets architecture emulation. 95 | - `setpriv`: Runs a program with different privileges. 96 | - `unshare`: Runs a program with unshared namespaces. 97 | - `utmpdump`: Dumps UTMP/WTMP files. 98 | - `whereis`: Locates binaries, sources, and manuals. 99 | - `ldattach`: Attaches line discipline to a serial line. 100 | - `readprofile`: Reads kernel profiling info. 101 | - `i386, linux32, linux64, x86_64`: Set personality flags for execution environment. 102 | - `uuidgen`: Generate different types of UUID. 103 | 104 | Note: 105 | * /bin/more is already implemented in https://github.com/uutils/coreutils 106 | 107 | Project: 108 | http://www.kernel.org/pub/linux/utils/util-linux/ 109 | 110 | ## Installation 111 | 112 | Ensure you have Rust installed on your system. You can install Rust through [rustup](https://rustup.rs/). 113 | 114 | Clone the repository and build the project using Cargo: 115 | 116 | ```bash 117 | git clone https://github.com/uutils/util-linux.git 118 | cd util-linux 119 | cargo build --release 120 | cargo run --release 121 | ``` 122 | 123 | ## License 124 | 125 | util-linux is licensed under the MIT License - see the `LICENSE` file for details 126 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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 | const ENV_FEATURE_PREFIX: &str = "CARGO_FEATURE_"; 15 | const FEATURE_PREFIX: &str = "feat_"; 16 | 17 | if let Ok(profile) = env::var("PROFILE") { 18 | println!("cargo:rustc-cfg=build={profile:?}"); 19 | } 20 | 21 | let out_dir = env::var("OUT_DIR").unwrap(); 22 | 23 | let mut crates = Vec::new(); 24 | for (key, val) in env::vars() { 25 | if val == "1" && key.starts_with(ENV_FEATURE_PREFIX) { 26 | let krate = key[ENV_FEATURE_PREFIX.len()..].to_lowercase(); 27 | // Allow this as we have a bunch of info in the comments 28 | #[allow(clippy::match_same_arms)] 29 | match krate.as_ref() { 30 | "default" | "macos" | "unix" | "windows" | "selinux" | "zip" => continue, // common/standard feature names 31 | "nightly" | "test_unimplemented" => continue, // crate-local custom features 32 | "uudoc" => continue, // is not a utility 33 | "test" => continue, // over-ridden with 'uu_test' to avoid collision with rust core crate 'test' 34 | s if s.starts_with(FEATURE_PREFIX) => continue, // crate feature sets 35 | _ => {} // util feature name 36 | } 37 | crates.push(krate); 38 | } 39 | } 40 | crates.sort(); 41 | 42 | let mut mf = File::create(Path::new(&out_dir).join("uutils_map.rs")).unwrap(); 43 | 44 | mf.write_all( 45 | "type UtilityMap = phf::OrderedMap<&'static str, (fn(T) -> i32, fn() -> Command)>;\n\ 46 | \n\ 47 | #[allow(clippy::too_many_lines)] 48 | fn util_map() -> UtilityMap {\n" 49 | .as_bytes(), 50 | ) 51 | .unwrap(); 52 | 53 | let mut phf_map = phf_codegen::OrderedMap::<&str>::new(); 54 | for krate in &crates { 55 | let map_value = format!("({krate}::uumain, {krate}::uu_app)"); 56 | phf_map.entry(krate, &map_value); 57 | } 58 | write!(mf, "{}", phf_map.build()).unwrap(); 59 | mf.write_all(b"\n}\n").unwrap(); 60 | 61 | mf.flush().unwrap(); 62 | } 63 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /src/bin/util-linux.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils coreutils 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("coreutils") 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 == "coreutils" { 167 | gen_coreutils_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("coreutils") 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 == "coreutils" { 199 | gen_coreutils_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_coreutils_app(util_map: &UtilityMap) -> Command { 212 | let mut command = Command::new("coreutils"); 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/uu/blockdev/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uu_blockdev" 3 | version = "0.0.1" 4 | edition = "2021" 5 | description = "blockdev ~ Get or set various block device attributes." 6 | 7 | [lib] 8 | path = "src/blockdev.rs" 9 | 10 | [[bin]] 11 | name = "blockdev" 12 | path = "src/main.rs" 13 | 14 | [dependencies] 15 | clap = { workspace = true } 16 | linux-raw-sys = { workspace = true } 17 | regex = { workspace = true } 18 | sysinfo = { workspace = true } 19 | uucore = { workspace = true } 20 | -------------------------------------------------------------------------------- /src/uu/blockdev/blockdev.md: -------------------------------------------------------------------------------- 1 | # blockdev 2 | 3 | ``` 4 | blockdev 5 | blockdev --report 6 | ``` 7 | 8 | Get or set various block device attributes. 9 | -------------------------------------------------------------------------------- /src/uu/blockdev/src/main.rs: -------------------------------------------------------------------------------- 1 | uucore::bin!(uu_blockdev); 2 | -------------------------------------------------------------------------------- /src/uu/chcpu/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uu_chcpu" 3 | version = "0.0.1" 4 | edition = "2024" 5 | 6 | [lib] 7 | path = "src/chcpu.rs" 8 | 9 | [[bin]] 10 | name = "chcpu" 11 | path = "src/main.rs" 12 | 13 | [dependencies] 14 | clap = { workspace = true } 15 | libc = { workspace = true } 16 | rangemap = { workspace = true } 17 | syscall-numbers = { workspace = true } 18 | thiserror = { workspace = true } 19 | uucore = { workspace = true } 20 | -------------------------------------------------------------------------------- /src/uu/chcpu/chcpu.md: -------------------------------------------------------------------------------- 1 | # chcpu 2 | 3 | ``` 4 | chcpu {-e|--enable|-d|--disable|-c|--configure|-g|--deconfigure} cpu-list 5 | chcpu {-p|--dispatch} mode 6 | chcpu {-r|--rescan} 7 | chcpu {-V|--version} 8 | chcpu {-h|--help} 9 | ``` 10 | 11 | configure CPUs in a multi-processor system. 12 | -------------------------------------------------------------------------------- /src/uu/chcpu/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::path::PathBuf; 7 | 8 | #[derive(Debug, thiserror::Error)] 9 | pub enum ChCpuError { 10 | #[error("CPU {0} is enabled")] 11 | CpuIsEnabled(usize), 12 | 13 | #[error("CPU {0} is not configurable")] 14 | CpuNotConfigurable(usize), 15 | 16 | #[error("CPU {0} is not hot pluggable")] 17 | CpuNotHotPluggable(usize), 18 | 19 | #[error("this system does not support rescanning of CPUs")] 20 | CpuRescanUnsupported, 21 | 22 | #[error("first element of CPU list range is greater than its last element")] 23 | CpuSpecFirstAfterLast, 24 | 25 | #[error("CPU list element is not a positive number")] 26 | CpuSpecNotPositiveInteger, 27 | 28 | #[error("CPU list is empty")] 29 | EmptyCpuList, 30 | 31 | #[error("CPU {0} does not exist")] 32 | InvalidCpuIndex(usize), 33 | 34 | #[error("{0}: {1}")] 35 | IO0(String, std::io::Error), 36 | 37 | #[error("{0} '{path}': {2}", path = .1.display())] 38 | IO1(String, PathBuf, std::io::Error), 39 | 40 | #[error("only one CPU is enabled")] 41 | OneCpuIsEnabled, 42 | 43 | #[error("data is not an integer '{0}'")] 44 | NotInteger(String), 45 | 46 | #[error("this system does not support setting the dispatching mode of CPUs")] 47 | SetCpuDispatchUnsupported, 48 | } 49 | 50 | impl ChCpuError { 51 | pub(crate) fn io0(message: impl Into, error: std::io::Error) -> Self { 52 | Self::IO0(message.into(), error) 53 | } 54 | 55 | pub(crate) fn io1( 56 | message: impl Into, 57 | path: impl Into, 58 | error: std::io::Error, 59 | ) -> Self { 60 | Self::IO1(message.into(), path.into(), error) 61 | } 62 | 63 | pub(crate) fn with_io_message(self, message: impl Into) -> Self { 64 | match self { 65 | Self::IO0(_, err) => Self::IO0(message.into(), err), 66 | 67 | Self::IO1(_, path, err) => Self::IO1(message.into(), path, err), 68 | 69 | Self::CpuIsEnabled(_) 70 | | Self::CpuNotConfigurable(_) 71 | | Self::CpuNotHotPluggable(_) 72 | | Self::CpuRescanUnsupported 73 | | Self::CpuSpecFirstAfterLast 74 | | Self::CpuSpecNotPositiveInteger 75 | | Self::EmptyCpuList 76 | | Self::InvalidCpuIndex(_) 77 | | Self::OneCpuIsEnabled 78 | | Self::NotInteger(_) 79 | | Self::SetCpuDispatchUnsupported => self, 80 | } 81 | } 82 | } 83 | 84 | impl uucore::error::UError for ChCpuError { 85 | fn code(&self) -> i32 { 86 | 1 87 | } 88 | 89 | fn usage(&self) -> bool { 90 | false 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/uu/chcpu/src/main.rs: -------------------------------------------------------------------------------- 1 | uucore::bin!(uu_chcpu); 2 | -------------------------------------------------------------------------------- /src/uu/ctrlaltdel/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uu_ctrlaltdel" 3 | version = "0.0.1" 4 | edition = "2021" 5 | 6 | [lib] 7 | path = "src/ctrlaltdel.rs" 8 | 9 | [[bin]] 10 | name = "ctrlaltdel" 11 | path = "src/main.rs" 12 | 13 | [dependencies] 14 | uucore = { workspace = true } 15 | clap = { workspace = true } 16 | -------------------------------------------------------------------------------- /src/uu/ctrlaltdel/ctrlaltdel.md: -------------------------------------------------------------------------------- 1 | # ctrlaltdel 2 | 3 | ``` 4 | ctrlaltdel hard|soft 5 | ``` 6 | 7 | set the function of the ctrl-alt-del combination -------------------------------------------------------------------------------- /src/uu/ctrlaltdel/src/ctrlaltdel.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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 clap::{crate_version, Arg, ArgAction, Command}; 7 | use uucore::{error::UResult, format_usage, help_about, help_usage}; 8 | 9 | const ABOUT: &str = help_about!("ctrlaltdel.md"); 10 | const USAGE: &str = help_usage!("ctrlaltdel.md"); 11 | 12 | #[cfg(target_os = "linux")] 13 | const CTRL_ALT_DEL_PATH: &str = "/proc/sys/kernel/ctrl-alt-del"; 14 | 15 | #[cfg(target_os = "linux")] 16 | #[uucore::main] 17 | pub fn uumain(args: impl uucore::Args) -> UResult<()> { 18 | let matches: clap::ArgMatches = uu_app().try_get_matches_from(args)?; 19 | let pattern = matches.get_one::("pattern"); 20 | match pattern { 21 | Some(x) if x == "hard" => { 22 | set_ctrlaltdel(CtrlAltDel::Hard)?; 23 | } 24 | Some(x) if x == "soft" => { 25 | set_ctrlaltdel(CtrlAltDel::Soft)?; 26 | } 27 | Some(x) => { 28 | Err(Error::UnknownArgument(x.clone()))?; 29 | } 30 | None => { 31 | println!("{}", get_ctrlaltdel()?); 32 | } 33 | } 34 | 35 | Ok(()) 36 | } 37 | 38 | #[cfg(not(target_os = "linux"))] 39 | #[uucore::main] 40 | pub fn uumain(args: impl uucore::Args) -> UResult<()> { 41 | let _matches: clap::ArgMatches = uu_app().try_get_matches_from(args)?; 42 | 43 | Err(uucore::error::USimpleError::new( 44 | 1, 45 | "`ctrlaltdel` is unavailable on current platform.", 46 | )) 47 | } 48 | 49 | #[cfg(target_os = "linux")] 50 | fn get_ctrlaltdel() -> UResult { 51 | let value: i32 = std::fs::read_to_string(CTRL_ALT_DEL_PATH)? 52 | .trim() 53 | .parse() 54 | .map_err(|_| Error::UnknownData)?; 55 | 56 | Ok(CtrlAltDel::from_sysctl(value)) 57 | } 58 | 59 | #[cfg(target_os = "linux")] 60 | fn set_ctrlaltdel(ctrlaltdel: CtrlAltDel) -> UResult<()> { 61 | std::fs::write(CTRL_ALT_DEL_PATH, format!("{}\n", ctrlaltdel.to_sysctl())) 62 | .map_err(|_| Error::NotRoot)?; 63 | 64 | Ok(()) 65 | } 66 | 67 | #[cfg(target_os = "linux")] 68 | #[derive(Clone, Copy)] 69 | enum CtrlAltDel { 70 | Soft, 71 | Hard, 72 | } 73 | #[cfg(target_os = "linux")] 74 | impl CtrlAltDel { 75 | /// # Panics 76 | /// Panics if value of the parameter `value` is neither `0` nor `1`. 77 | fn from_sysctl(value: i32) -> Self { 78 | match value { 79 | 0 => Self::Soft, 80 | 1 => Self::Hard, 81 | _ => unreachable!(), 82 | } 83 | } 84 | 85 | fn to_sysctl(self) -> i32 { 86 | match self { 87 | Self::Soft => 0, 88 | Self::Hard => 1, 89 | } 90 | } 91 | } 92 | #[cfg(target_os = "linux")] 93 | impl std::fmt::Display for CtrlAltDel { 94 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 95 | match self { 96 | Self::Soft => write!(f, "soft"), 97 | Self::Hard => write!(f, "hard"), 98 | } 99 | } 100 | } 101 | 102 | #[cfg(target_os = "linux")] 103 | #[derive(Debug)] 104 | enum Error { 105 | NotRoot, 106 | UnknownArgument(String), 107 | UnknownData, 108 | } 109 | #[cfg(target_os = "linux")] 110 | impl std::fmt::Display for Error { 111 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 112 | match self { 113 | Self::NotRoot => write!(f, "You must be root to set the Ctrl-Alt-Del behavior"), 114 | Self::UnknownArgument(x) => write!(f, "unknown argument: {x}"), 115 | Self::UnknownData => write!(f, "unknown data"), 116 | } 117 | } 118 | } 119 | #[cfg(target_os = "linux")] 120 | impl std::error::Error for Error {} 121 | #[cfg(target_os = "linux")] 122 | impl uucore::error::UError for Error { 123 | fn code(&self) -> i32 { 124 | 1 125 | } 126 | 127 | fn usage(&self) -> bool { 128 | false 129 | } 130 | } 131 | 132 | pub fn uu_app() -> Command { 133 | Command::new(uucore::util_name()) 134 | .version(crate_version!()) 135 | .about(ABOUT) 136 | .override_usage(format_usage(USAGE)) 137 | .infer_long_args(true) 138 | .arg(Arg::new("pattern").action(ArgAction::Set)) 139 | } 140 | -------------------------------------------------------------------------------- /src/uu/ctrlaltdel/src/main.rs: -------------------------------------------------------------------------------- 1 | uucore::bin!(uu_ctrlaltdel); 2 | -------------------------------------------------------------------------------- /src/uu/dmesg/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uu_dmesg" 3 | version = "0.0.1" 4 | edition = "2021" 5 | 6 | [lib] 7 | path = "src/dmesg.rs" 8 | 9 | [[bin]] 10 | name = "dmesg" 11 | path = "src/main.rs" 12 | 13 | [dependencies] 14 | clap = { workspace = true } 15 | uucore = { workspace = true } 16 | regex = { workspace = true } 17 | serde_json = { workspace = true } 18 | serde = { workspace = true } 19 | chrono = "0.4.38" 20 | parse_datetime = "0.10.0" 21 | 22 | [features] 23 | fixed-boot-time = [] 24 | -------------------------------------------------------------------------------- /src/uu/dmesg/dmesg.md: -------------------------------------------------------------------------------- 1 | # dmesg 2 | 3 | ``` 4 | dmesg [options] 5 | ``` 6 | 7 | Display or control the kernel ring buffer. 8 | -------------------------------------------------------------------------------- /src/uu/dmesg/src/json.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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 serde::Serialize; 7 | use std::io; 8 | 9 | pub fn serialize_records(records: &Vec) -> String { 10 | let json = Dmesg::from(records); 11 | let formatter = DmesgFormatter::new(); 12 | let mut buf = vec![]; 13 | let mut serializer = serde_json::Serializer::with_formatter(&mut buf, formatter); 14 | json.serialize(&mut serializer).unwrap(); 15 | String::from_utf8_lossy(&buf).to_string() 16 | } 17 | 18 | #[derive(serde::Serialize)] 19 | struct Dmesg<'a> { 20 | dmesg: Vec>, 21 | } 22 | 23 | #[derive(serde::Serialize)] 24 | struct Record<'a> { 25 | pri: u32, 26 | time: i64, 27 | msg: &'a str, 28 | } 29 | 30 | impl<'a> From<&'a Vec> for Dmesg<'a> { 31 | fn from(value: &'a Vec) -> Self { 32 | let mut dmesg_json = Dmesg { dmesg: vec![] }; 33 | for record in value { 34 | let record_json = Record { 35 | pri: record.priority_facility, 36 | time: record.timestamp_us, 37 | msg: &record.message, 38 | }; 39 | dmesg_json.dmesg.push(record_json); 40 | } 41 | dmesg_json 42 | } 43 | } 44 | 45 | struct DmesgFormatter { 46 | nesting_depth: i32, 47 | } 48 | 49 | impl DmesgFormatter { 50 | const SINGLE_INDENTATION: &[u8] = b" "; 51 | 52 | fn new() -> Self { 53 | DmesgFormatter { nesting_depth: 0 } 54 | } 55 | 56 | fn write_indentation(&mut self, writer: &mut W) -> io::Result<()> 57 | where 58 | W: ?Sized + io::Write, 59 | { 60 | for _ in 0..self.nesting_depth { 61 | writer.write_all(Self::SINGLE_INDENTATION)?; 62 | } 63 | Ok(()) 64 | } 65 | } 66 | 67 | impl serde_json::ser::Formatter for DmesgFormatter { 68 | fn begin_object(&mut self, writer: &mut W) -> io::Result<()> 69 | where 70 | W: ?Sized + io::Write, 71 | { 72 | self.nesting_depth += 1; 73 | writer.write_all(b"{\n") 74 | } 75 | 76 | fn end_object(&mut self, writer: &mut W) -> io::Result<()> 77 | where 78 | W: ?Sized + io::Write, 79 | { 80 | writer.write_all(b"\n")?; 81 | self.nesting_depth -= 1; 82 | self.write_indentation(writer)?; 83 | writer.write_all(b"}") 84 | } 85 | 86 | fn begin_array(&mut self, writer: &mut W) -> io::Result<()> 87 | where 88 | W: ?Sized + io::Write, 89 | { 90 | self.nesting_depth += 1; 91 | writer.write_all(b"[\n") 92 | } 93 | 94 | fn end_array(&mut self, writer: &mut W) -> io::Result<()> 95 | where 96 | W: ?Sized + io::Write, 97 | { 98 | writer.write_all(b"\n")?; 99 | self.nesting_depth -= 1; 100 | self.write_indentation(writer)?; 101 | writer.write_all(b"]") 102 | } 103 | 104 | fn begin_object_key(&mut self, writer: &mut W, first: bool) -> io::Result<()> 105 | where 106 | W: ?Sized + io::Write, 107 | { 108 | if !first { 109 | writer.write_all(b",\n")?; 110 | } 111 | self.write_indentation(writer) 112 | } 113 | 114 | fn begin_object_value(&mut self, writer: &mut W) -> io::Result<()> 115 | where 116 | W: ?Sized + io::Write, 117 | { 118 | writer.write_all(b": ") 119 | } 120 | 121 | fn begin_array_value(&mut self, writer: &mut W, first: bool) -> io::Result<()> 122 | where 123 | W: ?Sized + io::Write, 124 | { 125 | if first { 126 | self.write_indentation(writer) 127 | } else { 128 | writer.write_all(b",") 129 | } 130 | } 131 | 132 | fn write_i64(&mut self, writer: &mut W, value: i64) -> io::Result<()> 133 | where 134 | W: ?Sized + io::Write, 135 | { 136 | // The only i64 field in Dmesg is time, which requires a specific format 137 | let seconds = value / 1000000; 138 | let sub_seconds = value % 1000000; 139 | let repr = format!("{:>5}.{:0>6}", seconds, sub_seconds); 140 | writer.write_all(repr.as_bytes()) 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/uu/dmesg/src/main.rs: -------------------------------------------------------------------------------- 1 | uucore::bin!(uu_dmesg); 2 | -------------------------------------------------------------------------------- /src/uu/dmesg/src/time_formatter.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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 chrono::{DateTime, FixedOffset, TimeDelta}; 7 | #[cfg(feature = "fixed-boot-time")] 8 | use chrono::{NaiveDate, NaiveTime}; 9 | use std::sync::OnceLock; 10 | use uucore::error::{UResult, USimpleError}; 11 | 12 | pub fn raw(timestamp_us: i64) -> String { 13 | let seconds = timestamp_us / 1000000; 14 | let sub_seconds = timestamp_us % 1000000; 15 | format!("{:>5}.{:0>6}", seconds, sub_seconds) 16 | } 17 | 18 | pub fn ctime(timestamp_us: i64) -> String { 19 | let date_time = boot_time() 20 | .checked_add_signed(TimeDelta::microseconds(timestamp_us)) 21 | .unwrap(); 22 | date_time.format("%a %b %d %H:%M:%S %Y").to_string() 23 | } 24 | 25 | pub fn iso(timestamp_us: i64) -> String { 26 | let date_time = boot_time() 27 | .checked_add_signed(TimeDelta::microseconds(timestamp_us)) 28 | .unwrap(); 29 | date_time.format("%Y-%m-%dT%H:%M:%S,%6f%:z").to_string() 30 | } 31 | 32 | pub struct ReltimeFormatter { 33 | state: State, 34 | prev_timestamp_us: i64, 35 | previous_unix_timestamp: i64, 36 | } 37 | 38 | pub struct DeltaFormatter { 39 | state: State, 40 | prev_timestamp_us: i64, 41 | } 42 | 43 | pub enum State { 44 | Initial, 45 | AfterBoot, 46 | Delta, 47 | } 48 | 49 | impl ReltimeFormatter { 50 | pub fn new() -> Self { 51 | ReltimeFormatter { 52 | state: State::Initial, 53 | prev_timestamp_us: 0, 54 | previous_unix_timestamp: 0, 55 | } 56 | } 57 | 58 | pub fn format(&mut self, timestamp_us: i64) -> String { 59 | let date_time = boot_time() 60 | .checked_add_signed(TimeDelta::microseconds(timestamp_us)) 61 | .unwrap(); 62 | let unix_timestamp = date_time.timestamp(); 63 | let minute_changes = (unix_timestamp / 60) != (self.previous_unix_timestamp / 60); 64 | let format_res = match self.state { 65 | State::Initial => date_time.format("%b%d %H:%M").to_string(), 66 | _ if minute_changes => date_time.format("%b%d %H:%M").to_string(), 67 | State::AfterBoot => Self::delta(0), 68 | State::Delta => Self::delta(timestamp_us - self.prev_timestamp_us), 69 | }; 70 | self.prev_timestamp_us = timestamp_us; 71 | self.previous_unix_timestamp = unix_timestamp; 72 | self.state = match self.state { 73 | State::Initial if timestamp_us == 0 => State::AfterBoot, 74 | _ => State::Delta, 75 | }; 76 | format_res 77 | } 78 | 79 | fn delta(delta_us: i64) -> String { 80 | let seconds = i64::abs(delta_us / 1000000); 81 | let sub_seconds = i64::abs(delta_us % 1000000); 82 | let sign = if delta_us >= 0 { '+' } else { '-' }; 83 | let res = format!("{}{}.{:0>6}", sign, seconds, sub_seconds); 84 | format!("{:>11}", res) 85 | } 86 | } 87 | 88 | impl DeltaFormatter { 89 | pub fn new() -> Self { 90 | DeltaFormatter { 91 | state: State::Initial, 92 | prev_timestamp_us: 0, 93 | } 94 | } 95 | 96 | pub fn format(&mut self, timestamp_us: i64) -> String { 97 | let format_res = match self.state { 98 | State::Delta => Self::delta(timestamp_us - self.prev_timestamp_us), 99 | _ => Self::delta(0), 100 | }; 101 | self.prev_timestamp_us = timestamp_us; 102 | self.state = match self.state { 103 | State::Initial if timestamp_us == 0 => State::AfterBoot, 104 | _ => State::Delta, 105 | }; 106 | format_res 107 | } 108 | 109 | fn delta(delta_us: i64) -> String { 110 | let seconds = i64::abs(delta_us / 1000000); 111 | let sub_seconds = i64::abs(delta_us % 1000000); 112 | let mut res = format!("{}.{:0>6}", seconds, sub_seconds); 113 | if delta_us < 0 { 114 | res.insert(0, '-'); 115 | } 116 | format!("<{:>12}>", res) 117 | } 118 | } 119 | 120 | pub fn parse_datetime(s: &str) -> UResult> { 121 | parse_datetime::parse_datetime(s) 122 | .map_err(|_| USimpleError::new(1, format!("invalid time value \"{s}\""))) 123 | } 124 | 125 | pub fn datetime_from_microseconds_since_boot(microseconds: i64) -> DateTime { 126 | boot_time() 127 | .checked_add_signed(TimeDelta::microseconds(microseconds)) 128 | .unwrap() 129 | } 130 | 131 | static BOOT_TIME: OnceLock> = OnceLock::new(); 132 | 133 | #[cfg(feature = "fixed-boot-time")] 134 | fn boot_time() -> DateTime { 135 | *BOOT_TIME.get_or_init(|| { 136 | let date = NaiveDate::from_ymd_opt(2024, 11, 18).unwrap(); 137 | let time = NaiveTime::from_hms_micro_opt(19, 34, 12, 866807).unwrap(); 138 | let tz = FixedOffset::east_opt(7 * 3600).unwrap(); 139 | chrono::NaiveDateTime::new(date, time) 140 | .and_local_timezone(tz) 141 | .unwrap() 142 | }) 143 | } 144 | 145 | #[cfg(not(feature = "fixed-boot-time"))] 146 | #[cfg(unix)] 147 | #[cfg(not(target_os = "openbsd"))] 148 | fn boot_time() -> DateTime { 149 | *BOOT_TIME.get_or_init(|| boot_time_from_utmpx().unwrap()) 150 | } 151 | 152 | #[cfg(not(feature = "fixed-boot-time"))] 153 | #[cfg(windows)] 154 | fn boot_time() -> DateTime { 155 | // TODO: get windows boot time 156 | *BOOT_TIME.get_or_init(|| chrono::DateTime::from_timestamp(0, 0).unwrap().into()) 157 | } 158 | 159 | #[cfg(not(feature = "fixed-boot-time"))] 160 | #[cfg(target_os = "openbsd")] 161 | fn boot_time() -> DateTime { 162 | // TODO: get openbsd boot time 163 | *BOOT_TIME.get_or_init(|| chrono::DateTime::from_timestamp(0, 0).unwrap().into()) 164 | } 165 | 166 | #[cfg(not(feature = "fixed-boot-time"))] 167 | #[cfg(unix)] 168 | #[cfg(not(target_os = "openbsd"))] 169 | fn boot_time_from_utmpx() -> Option> { 170 | for record in uucore::utmpx::Utmpx::iter_all_records() { 171 | if record.record_type() == uucore::utmpx::BOOT_TIME { 172 | let t = record.login_time(); 173 | return Some( 174 | chrono::DateTime::from_timestamp(t.unix_timestamp(), t.nanosecond()) 175 | .unwrap() 176 | .with_timezone(&chrono::Local) 177 | .into(), 178 | ); 179 | } 180 | } 181 | None 182 | } 183 | -------------------------------------------------------------------------------- /src/uu/fsfreeze/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uu_fsfreeze" 3 | version = "0.0.1" 4 | edition = "2021" 5 | 6 | [lib] 7 | path = "src/fsfreeze.rs" 8 | 9 | [[bin]] 10 | name = "fsfreeze" 11 | path = "src/main.rs" 12 | 13 | [dependencies] 14 | clap = { workspace = true } 15 | linux-raw-sys = { workspace = true } 16 | regex = { workspace = true } 17 | sysinfo = { workspace = true } 18 | uucore = { workspace = true } 19 | -------------------------------------------------------------------------------- /src/uu/fsfreeze/fsfreeze.md: -------------------------------------------------------------------------------- 1 | # fsfreeze 2 | 3 | ``` 4 | fsfreeze <--freeze|--unfreeze> 5 | ``` 6 | 7 | suspends or resumes modifications to a mounted filesystem 8 | -------------------------------------------------------------------------------- /src/uu/fsfreeze/src/fsfreeze.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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 clap::{crate_version, Arg, ArgAction, ArgGroup, Command}; 7 | #[cfg(target_os = "linux")] 8 | use std::{fs::File, io, os::fd::AsRawFd}; 9 | #[cfg(target_os = "linux")] 10 | use uucore::{error::UIoError, libc}; 11 | use uucore::{error::UResult, format_usage, help_about, help_usage}; 12 | 13 | const ABOUT: &str = help_about!("fsfreeze.md"); 14 | const USAGE: &str = help_usage!("fsfreeze.md"); 15 | 16 | #[cfg(target_os = "linux")] 17 | #[uucore::main] 18 | pub fn uumain(args: impl uucore::Args) -> UResult<()> { 19 | let matches: clap::ArgMatches = uu_app().try_get_matches_from(args)?; 20 | let mountpoint = matches.get_one::("mountpoint").unwrap(); 21 | let file = File::open(mountpoint)?; 22 | let metadata = file.metadata()?; 23 | if !metadata.is_dir() { 24 | return Err(UIoError::new(io::ErrorKind::InvalidData, "not a directory")); 25 | } 26 | 27 | let (op_name, op_code) = if matches.get_flag("freeze") { 28 | ("freeze", linux_raw_sys::ioctl::FIFREEZE) 29 | } else { 30 | ("unfreeze", linux_raw_sys::ioctl::FITHAW) 31 | }; 32 | 33 | if unsafe { libc::ioctl(file.as_raw_fd(), op_code.into(), 0) } < 0 { 34 | uucore::show_error!( 35 | "failed to {} the filesystem: {}", 36 | op_name, 37 | UIoError::from(io::Error::last_os_error()) 38 | ); 39 | } 40 | Ok(()) 41 | } 42 | 43 | pub fn uu_app() -> Command { 44 | Command::new(uucore::util_name()) 45 | .version(crate_version!()) 46 | .about(ABOUT) 47 | .override_usage(format_usage(USAGE)) 48 | .infer_long_args(true) 49 | .arg( 50 | Arg::new("freeze") 51 | .short('f') 52 | .long("freeze") 53 | .help("freeze the filesystem") 54 | .action(ArgAction::SetTrue), 55 | ) 56 | .arg( 57 | Arg::new("unfreeze") 58 | .short('u') 59 | .long("unfreeze") 60 | .help("unfreeze the filesystem") 61 | .action(ArgAction::SetTrue), 62 | ) 63 | .arg( 64 | Arg::new("mountpoint") 65 | .help("mountpoint of the filesystem") 66 | .required(true) 67 | .action(ArgAction::Set), 68 | ) 69 | .group( 70 | ArgGroup::new("action") 71 | .required(true) 72 | .args(["freeze", "unfreeze"]), 73 | ) 74 | } 75 | 76 | #[cfg(not(target_os = "linux"))] 77 | #[uucore::main] 78 | pub fn uumain(args: impl uucore::Args) -> UResult<()> { 79 | let _matches: clap::ArgMatches = uu_app().try_get_matches_from(args)?; 80 | 81 | Err(uucore::error::USimpleError::new( 82 | 1, 83 | "`fsfreeze` is available only on Linux.", 84 | )) 85 | } 86 | -------------------------------------------------------------------------------- /src/uu/fsfreeze/src/main.rs: -------------------------------------------------------------------------------- 1 | uucore::bin!(uu_fsfreeze); 2 | -------------------------------------------------------------------------------- /src/uu/last/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uu_last" 3 | version = "0.0.1" 4 | edition = "2021" 5 | 6 | [lib] 7 | path = "src/last.rs" 8 | 9 | [dependencies] 10 | uucore = { workspace = true, features = ["utmpx"] } 11 | clap = { workspace = true} 12 | dns-lookup = { workspace = true } 13 | -------------------------------------------------------------------------------- /src/uu/last/last.md: -------------------------------------------------------------------------------- 1 | # last 2 | 3 | ``` 4 | Usage: 5 | [options] [...] [...] 6 | ``` 7 | 8 | Show a listing of last logged in users. 9 | -------------------------------------------------------------------------------- /src/uu/last/src/last.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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 clap::{crate_version, Arg, ArgAction, Command}; 7 | use uucore::{format_usage, help_about, help_usage}; 8 | 9 | mod platform; 10 | 11 | mod options { 12 | pub const SYSTEM: &str = "system"; 13 | pub const HOSTLAST: &str = "hostlast"; 14 | pub const NO_HOST: &str = "nohostname"; 15 | pub const LIMIT: &str = "limit"; 16 | pub const DNS: &str = "dns"; 17 | pub const TIME_FORMAT: &str = "time-format"; 18 | pub const USER_TTY: &str = "username"; 19 | pub const FILE: &str = "file"; 20 | } 21 | 22 | const ABOUT: &str = help_about!("last.md"); 23 | const USAGE: &str = help_usage!("last.md"); 24 | 25 | #[uucore::main] 26 | use platform::uumain; 27 | 28 | pub fn uu_app() -> Command { 29 | Command::new(uucore::util_name()) 30 | .version(crate_version!()) 31 | .about(ABOUT) 32 | .override_usage(format_usage(USAGE)) 33 | .infer_long_args(true) 34 | .arg( 35 | Arg::new(options::FILE) 36 | .short('f') 37 | .long("file") 38 | .action(ArgAction::Set) 39 | .default_value("/var/log/wtmp") 40 | .help("use a specific file instead of /var/log/wtmp") 41 | .required(false), 42 | ) 43 | .arg( 44 | Arg::new(options::SYSTEM) 45 | .short('x') 46 | .long(options::SYSTEM) 47 | .action(ArgAction::SetTrue) 48 | .required(false) 49 | .help("display system shutdown entries and run level changes"), 50 | ) 51 | .arg( 52 | Arg::new(options::DNS) 53 | .short('d') 54 | .long(options::DNS) 55 | .action(ArgAction::SetTrue) 56 | .required(false) 57 | .help("translate the IP number back into a hostname"), 58 | ) 59 | .arg( 60 | Arg::new(options::HOSTLAST) 61 | .short('a') 62 | .long(options::HOSTLAST) 63 | .action(ArgAction::SetTrue) 64 | .required(false) 65 | .help("display hostnames in the last column"), 66 | ) 67 | .arg( 68 | Arg::new(options::NO_HOST) 69 | .short('R') 70 | .long(options::NO_HOST) 71 | .action(ArgAction::SetTrue) 72 | .required(false) 73 | .help("don't display the hostname field"), 74 | ) 75 | .arg( 76 | Arg::new(options::LIMIT) 77 | .short('n') 78 | .long(options::LIMIT) 79 | .action(ArgAction::Set) 80 | .required(false) 81 | .help("how many lines to show") 82 | .value_parser(clap::value_parser!(i32)) 83 | .allow_negative_numbers(true), 84 | ) 85 | .arg( 86 | Arg::new(options::TIME_FORMAT) 87 | .long(options::TIME_FORMAT) 88 | .action(ArgAction::Set) 89 | .required(false) 90 | .help("show timestamps in the specified : notime|short|full|iso") 91 | .default_value("short"), 92 | ) 93 | .arg(Arg::new(options::USER_TTY).action(ArgAction::Append)) 94 | } 95 | -------------------------------------------------------------------------------- /src/uu/last/src/main.rs: -------------------------------------------------------------------------------- 1 | uucore::bin!(uu_last); 2 | -------------------------------------------------------------------------------- /src/uu/last/src/platform/macos.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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 | // Specific implementation for OpenBSD: tool unsupported (utmpx not supported) 7 | 8 | use crate::uu_app; 9 | 10 | use uucore::error::UResult; 11 | 12 | pub fn uumain(args: impl uucore::Args) -> UResult<()> { 13 | let _matches = uu_app().try_get_matches_from(args)?; 14 | 15 | println!("unsupported command on macOS"); 16 | Ok(()) 17 | } 18 | -------------------------------------------------------------------------------- /src/uu/last/src/platform/mod.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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(all(unix, not(target_os = "macos")))] 7 | mod unix; 8 | #[cfg(all(unix, not(target_os = "macos")))] 9 | pub use self::unix::*; 10 | 11 | #[cfg(target_os = "openbsd")] 12 | mod openbsd; 13 | #[cfg(target_os = "openbsd")] 14 | pub use self::openbsd::*; 15 | 16 | #[cfg(windows)] 17 | mod windows; 18 | #[cfg(windows)] 19 | pub use self::windows::*; 20 | 21 | #[cfg(target_os = "macos")] 22 | mod macos; 23 | #[cfg(target_os = "macos")] 24 | pub use self::macos::*; 25 | -------------------------------------------------------------------------------- /src/uu/last/src/platform/openbsd.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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 | // Specific implementation for OpenBSD: tool unsupported (utmpx not supported) 7 | 8 | use crate::uu_app; 9 | 10 | use uucore::error::UResult; 11 | 12 | pub fn uumain(args: impl uucore::Args) -> UResult<()> { 13 | let _matches = uu_app().try_get_matches_from(args)?; 14 | 15 | println!("unsupported command on OpenBSD"); 16 | Ok(()) 17 | } 18 | -------------------------------------------------------------------------------- /src/uu/last/src/platform/windows.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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 | // Specific implementation for Windows: tool unsupported (utmpx not supported) 7 | 8 | use crate::uu_app; 9 | 10 | use uucore::error::UResult; 11 | 12 | pub fn uumain(args: impl uucore::Args) -> UResult<()> { 13 | let _matches = uu_app().try_get_matches_from(args)?; 14 | 15 | println!("unsupported command on Windows"); 16 | Ok(()) 17 | } 18 | -------------------------------------------------------------------------------- /src/uu/lscpu/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uu_lscpu" 3 | version = "0.0.1" 4 | edition = "2021" 5 | 6 | [lib] 7 | path = "src/lscpu.rs" 8 | 9 | [[bin]] 10 | name = "lscpu" 11 | path = "src/main.rs" 12 | 13 | [dependencies] 14 | regex = { workspace = true } 15 | sysinfo = { workspace = true } 16 | uucore = { workspace = true } 17 | clap = { workspace = true } 18 | serde = { workspace = true } 19 | serde_json = { workspace = true } 20 | -------------------------------------------------------------------------------- /src/uu/lscpu/lscpu.md: -------------------------------------------------------------------------------- 1 | # lscpu 2 | 3 | ``` 4 | lscpu [OPTION]... 5 | ``` 6 | 7 | display information about the CPU architecture -------------------------------------------------------------------------------- /src/uu/lscpu/src/main.rs: -------------------------------------------------------------------------------- 1 | uucore::bin!(uu_lscpu); 2 | -------------------------------------------------------------------------------- /src/uu/lsipc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uu_lsipc" 3 | version = "0.0.1" 4 | edition = "2024" 5 | 6 | [lib] 7 | path = "src/lsipc.rs" 8 | 9 | [[bin]] 10 | name = "lsipc" 11 | path = "src/main.rs" 12 | 13 | [dependencies] 14 | uucore = { workspace = true } 15 | clap = { workspace = true } 16 | libc = { workspace = true } 17 | errno = { workspace = true } 18 | smartcols-sys = { workspace = true } 19 | -------------------------------------------------------------------------------- /src/uu/lsipc/after-help.txt: -------------------------------------------------------------------------------- 1 | Generic columns: 2 | KEY Resource key 3 | ID Resource ID 4 | OWNER Owner's username or UID 5 | PERMS Permissions 6 | CUID Creator UID 7 | CUSER Creator user 8 | CGID Creator GID 9 | CGROUP Creator group 10 | UID User ID 11 | USER User name 12 | GID Group ID 13 | GROUP Group name 14 | CTIME Time of the last change 15 | 16 | Shared-memory columns (--shmems): 17 | SIZE Segment size 18 | NATTCH Number of attached processes 19 | STATUS Status 20 | ATTACH Attach time 21 | DETACH Detach time 22 | COMMAND Creator command line 23 | CPID PID of the creator 24 | LPID PID of last user 25 | 26 | Message-queue columns (--queues): 27 | USEDBYTES Bytes used 28 | MSGS Number of messages 29 | SEND Time of last msg sent 30 | RECV Time of last msg received 31 | LSPID PID of the last msg sender 32 | LRPID PID of the last msg receiver 33 | 34 | Semaphore columns (--semaphores): 35 | NSEMS Number of semaphores 36 | OTIME Time of the last operation 37 | 38 | Summary columns (--global): 39 | RESOURCE Resource name 40 | DESCRIPTION Resource description 41 | LIMIT System-wide limit 42 | USED Currently used 43 | USE% Currently use percentage 44 | -------------------------------------------------------------------------------- /src/uu/lsipc/lsipc.md: -------------------------------------------------------------------------------- 1 | # lsipc 2 | 3 | ``` 4 | lsipc [-g|--global] [{-m|--shmems|-q|--queues|-s|--semaphores} [-c|--creator] [-t|--time]] [-e|--export|-J|--json|-l|--list|-n|--newline|-r|--raw] [-b|--bytes] [--noheadings] [--notruncate] [-o list|--output list] [-P|--numeric-perms] [--time-format type] [-y|--shell] 5 | lsipc {-i id|--id id} {-m|--shmems|-q|--queues|-s|--semaphores} [-e|--export|-J|--json|-l|--list|-n|--newline|-r|--raw] [-b|--bytes] [-c|--creator] [--noheadings] [--notruncate] [-o list|--output list] [-P|--numeric-perms] [-t|--time] [--time-format type] [-y|--shell] 6 | lsipc {-V|--version} 7 | lsipc {-h|--help} 8 | ``` 9 | 10 | show information on IPC facilities currently employed in the system. 11 | -------------------------------------------------------------------------------- /src/uu/lsipc/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::ffi::c_int; 7 | use std::fmt; 8 | use std::path::PathBuf; 9 | 10 | use uucore::error::UError; 11 | 12 | #[derive(Debug)] 13 | pub enum LsIpcError { 14 | InvalidColumnName(String), 15 | InvalidColumnSequence(String), 16 | InvalidTimeFormat(String), 17 | IO0(String, std::io::Error), 18 | IO1(String, PathBuf, std::io::Error), 19 | } 20 | 21 | impl LsIpcError { 22 | pub(crate) fn io0(message: impl Into, error: impl Into) -> Self { 23 | Self::IO0(message.into(), error.into()) 24 | } 25 | 26 | pub(crate) fn last_io0(message: impl Into) -> Self { 27 | let err = std::io::Error::last_os_error(); 28 | Self::IO0(message.into(), err) 29 | } 30 | 31 | pub(crate) fn io1( 32 | message: impl Into, 33 | path: impl Into, 34 | error: impl Into, 35 | ) -> Self { 36 | Self::IO1(message.into(), path.into(), error.into()) 37 | } 38 | 39 | pub(crate) fn io_from_neg_errno( 40 | message: impl Into, 41 | result: c_int, 42 | ) -> Result { 43 | if let Ok(result) = usize::try_from(result) { 44 | Ok(result) 45 | } else { 46 | let err = std::io::Error::from_raw_os_error(-result); 47 | Err(Self::IO0(message.into(), err)) 48 | } 49 | } 50 | } 51 | 52 | impl fmt::Display for LsIpcError { 53 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 54 | match self { 55 | Self::IO0(message, err) => write!(f, "{message}: {err}"), 56 | Self::IO1(message, path, err) => write!(f, "{message} '{}': {err}", path.display()), 57 | Self::InvalidColumnName(name) => write!(f, "invalid column name: {name}"), 58 | Self::InvalidColumnSequence(seq) => write!(f, "invalid column sequence: {seq}"), 59 | Self::InvalidTimeFormat(mode) => write!(f, "invalid time format: {mode}"), 60 | } 61 | } 62 | } 63 | 64 | impl UError for LsIpcError { 65 | fn code(&self) -> i32 { 66 | 1 67 | } 68 | 69 | fn usage(&self) -> bool { 70 | false 71 | } 72 | } 73 | 74 | impl std::error::Error for LsIpcError {} 75 | -------------------------------------------------------------------------------- /src/uu/lsipc/src/main.rs: -------------------------------------------------------------------------------- 1 | uucore::bin!(uu_lsipc); 2 | -------------------------------------------------------------------------------- /src/uu/lsipc/src/utils.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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}; 7 | use std::fs::File; 8 | use std::io::{BufRead, BufReader}; 9 | use std::path::Path; 10 | use std::str::FromStr; 11 | use std::sync::atomic::AtomicU64; 12 | use std::{io, ptr}; 13 | 14 | use crate::errors::LsIpcError; 15 | 16 | pub(crate) fn get_page_size() -> Result { 17 | use std::sync::atomic::Ordering; 18 | 19 | static VALUE: AtomicU64 = AtomicU64::new(0); 20 | 21 | let mut page_size = VALUE.load(Ordering::Acquire); 22 | 23 | if page_size == 0 { 24 | let value = unsafe { libc::sysconf(libc::_SC_PAGE_SIZE) }; 25 | if value == -1 || value == 0 { 26 | return Err(LsIpcError::last_io0("sysconf(_SC_PAGE_SIZE)")); 27 | } 28 | 29 | let value = value as u64; 30 | 31 | match VALUE.compare_exchange(0, value, Ordering::AcqRel, Ordering::Acquire) { 32 | Ok(_previous_value) => page_size = value, 33 | Err(previous_value) => page_size = previous_value, 34 | } 35 | } 36 | 37 | Ok(page_size) 38 | } 39 | 40 | pub(crate) fn local_time(time: libc::time_t) -> Result { 41 | let mut detailed_time: libc::tm; 42 | 43 | let r = unsafe { 44 | detailed_time = std::mem::zeroed(); 45 | libc::localtime_r(&time, &mut detailed_time) 46 | }; 47 | 48 | if r.is_null() { 49 | Err(LsIpcError::last_io0("localtime_r")) 50 | } else { 51 | Ok(detailed_time) 52 | } 53 | } 54 | 55 | pub(crate) fn time_of_day() -> Result { 56 | let mut time: libc::timeval; 57 | let r = unsafe { 58 | time = std::mem::zeroed(); 59 | libc::gettimeofday(&mut time, ptr::null_mut()) 60 | }; 61 | 62 | if r == -1 { 63 | Err(LsIpcError::last_io0("gettimeofday")) 64 | } else { 65 | Ok(time) 66 | } 67 | } 68 | 69 | pub(crate) fn pid_command_line(pid: libc::pid_t) -> Result { 70 | let path = Path::new("/proc").join(pid.to_string()).join("cmdline"); 71 | 72 | let mut contents = 73 | std::fs::read(&path).map_err(|err| LsIpcError::io1("reading file", path, err))?; 74 | if contents.last().copied() == Some(0_u8) { 75 | contents.pop(); 76 | } 77 | find_replace_in_vec(0_u8, b' ', &mut contents); 78 | contents.push(0_u8); 79 | 80 | Ok(unsafe { CString::from_vec_with_nul_unchecked(contents) }) 81 | } 82 | 83 | pub(crate) fn find_replace_in_vec(needle: T, replacement: T, data: &mut [T]) 84 | where 85 | T: Copy + Eq, 86 | { 87 | let mut start = 0; 88 | while let Some((index, found)) = data[start..] 89 | .iter_mut() 90 | .enumerate() 91 | .find(|&(_index, &mut element)| element == needle) 92 | { 93 | *found = replacement; 94 | 95 | start += index + 1; 96 | if start >= data.len() { 97 | break; 98 | } 99 | } 100 | } 101 | 102 | pub(crate) fn read_value(path: impl AsRef) -> Result 103 | where 104 | T: FromStr, 105 | { 106 | let path = path.as_ref(); 107 | 108 | let mut file = File::open(path) 109 | .map(BufReader::new) 110 | .map_err(|err| LsIpcError::io1("opening file", path, err))?; 111 | 112 | let mut line = String::default(); 113 | file.read_line(&mut line) 114 | .map_err(|err| LsIpcError::io1("reading file", path, err))?; 115 | 116 | line.trim() 117 | .parse() 118 | .map_err(|_| LsIpcError::io1("invalid data", path, io::ErrorKind::InvalidData)) 119 | } 120 | 121 | pub(crate) struct UserDbRecordRef(*const libc::passwd); 122 | 123 | impl Default for UserDbRecordRef { 124 | fn default() -> Self { 125 | Self(ptr::null()) 126 | } 127 | } 128 | 129 | impl UserDbRecordRef { 130 | pub(crate) fn for_id(&mut self, uid: libc::uid_t) -> &Self { 131 | if unsafe { self.0.as_ref() }.is_none_or(|record| record.pw_uid != uid) { 132 | self.0 = unsafe { libc::getpwuid(uid) }; 133 | } 134 | self 135 | } 136 | 137 | pub(crate) fn name(&self) -> Option<&CStr> { 138 | unsafe { self.0.as_ref() } 139 | .and_then(|record| (!record.pw_name.is_null()).then_some(record.pw_name)) 140 | .map(|name| unsafe { CStr::from_ptr(name) }) 141 | } 142 | } 143 | 144 | pub(crate) struct GroupDbRecordRef(*const libc::group); 145 | 146 | impl Default for GroupDbRecordRef { 147 | fn default() -> Self { 148 | Self(ptr::null()) 149 | } 150 | } 151 | 152 | impl GroupDbRecordRef { 153 | pub(crate) fn for_id(&mut self, gid: libc::gid_t) -> &Self { 154 | if unsafe { self.0.as_ref() }.is_none_or(|record| record.gr_gid != gid) { 155 | self.0 = unsafe { libc::getgrgid(gid) }; 156 | } 157 | self 158 | } 159 | 160 | pub(crate) fn name(&self) -> Option<&CStr> { 161 | unsafe { self.0.as_ref() } 162 | .and_then(|record| (!record.gr_name.is_null()).then_some(record.gr_name)) 163 | .map(|name| unsafe { CStr::from_ptr(name) }) 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/uu/lslocks/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uu_lslocks" 3 | version = "0.0.1" 4 | edition = "2024" 5 | 6 | [lib] 7 | path = "src/lslocks.rs" 8 | 9 | [[bin]] 10 | name = "lslocks" 11 | path = "src/main.rs" 12 | 13 | [dependencies] 14 | uucore = { workspace = true } 15 | clap = { workspace = true } 16 | libc = { workspace = true } 17 | smartcols-sys = { workspace = true } 18 | libmount-sys = { workspace = true } 19 | -------------------------------------------------------------------------------- /src/uu/lslocks/columns.json: -------------------------------------------------------------------------------- 1 | { 2 | "lslocks-columns": [ 3 | { 4 | "holder": "COMMAND", 5 | "type": "", 6 | "description": "command of the process holding the lock" 7 | }, 8 | { 9 | "holder": "PID", 10 | "type": "", 11 | "description": "PID of the process holding the lock" 12 | }, 13 | { 14 | "holder": "TYPE", 15 | "type": "", 16 | "description": "kind of lock" 17 | }, 18 | { 19 | "holder": "SIZE", 20 | "type": "", 21 | "description": "size of the lock, use if --bytes is given" 22 | }, 23 | { 24 | "holder": "INODE", 25 | "type": "", 26 | "description": "inode number" 27 | }, 28 | { 29 | "holder": "MAJ:MIN", 30 | "type": "", 31 | "description": "major:minor device number" 32 | }, 33 | { 34 | "holder": "MODE", 35 | "type": "", 36 | "description": "lock access mode" 37 | }, 38 | { 39 | "holder": "M", 40 | "type": "", 41 | "description": "mandatory state of the lock: 0 (none), 1 (set)" 42 | }, 43 | { 44 | "holder": "START", 45 | "type": "", 46 | "description": "relative byte offset of the lock" 47 | }, 48 | { 49 | "holder": "END", 50 | "type": "", 51 | "description": "ending offset of the lock" 52 | }, 53 | { 54 | "holder": "PATH", 55 | "type": "", 56 | "description": "path of the locked file" 57 | }, 58 | { 59 | "holder": "BLOCKER", 60 | "type": "", 61 | "description": "PID of the process blocking the lock" 62 | }, 63 | { 64 | "holder": "HOLDERS", 65 | "type": "", 66 | "description": "holders of the lock" 67 | } 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /src/uu/lslocks/columns.raw: -------------------------------------------------------------------------------- 1 | COMMAND command\x20of\x20the\x20process\x20holding\x20the\x20lock 2 | PID PID\x20of\x20the\x20process\x20holding\x20the\x20lock 3 | TYPE kind\x20of\x20lock 4 | SIZE size\x20of\x20the\x20lock,\x20use\x20\x20if\x20--bytes\x20is\x20given 5 | INODE inode\x20number 6 | MAJ:MIN major:minor\x20device\x20number 7 | MODE lock\x20access\x20mode 8 | M mandatory\x20state\x20of\x20the\x20lock:\x200\x20(none),\x201\x20(set) 9 | START relative\x20byte\x20offset\x20of\x20the\x20lock 10 | END ending\x20offset\x20of\x20the\x20lock 11 | PATH path\x20of\x20the\x20locked\x20file 12 | BLOCKER PID\x20of\x20the\x20process\x20blocking\x20the\x20lock 13 | HOLDERS holders\x20of\x20the\x20lock 14 | -------------------------------------------------------------------------------- /src/uu/lslocks/columns.txt: -------------------------------------------------------------------------------- 1 | COMMAND command of the process holding the lock 2 | PID PID of the process holding the lock 3 | TYPE kind of lock 4 | SIZE size of the lock, use if --bytes is given 5 | INODE inode number 6 | MAJ:MIN major:minor device number 7 | MODE lock access mode 8 | M mandatory state of the lock: 0 (none), 1 (set) 9 | START relative byte offset of the lock 10 | END ending offset of the lock 11 | PATH path of the locked file 12 | BLOCKER PID of the process blocking the lock 13 | HOLDERS holders of the lock 14 | -------------------------------------------------------------------------------- /src/uu/lslocks/lslocks.md: -------------------------------------------------------------------------------- 1 | # lslocks 2 | 3 | ``` 4 | lslocks [-b|--bytes] [-i|--noinaccessible] [-J|--json|-r|--raw] [-n|--noheadings] [-o list|--output list] [--output-all] [-p pid|--pid pid] [-u|--notruncate] 5 | lslocks {-H|--list-columns} [-J|--json|-r|--raw] 6 | lslocks {-V|--version} 7 | lslocks {-h|--help} 8 | ``` 9 | 10 | list local system locks. 11 | -------------------------------------------------------------------------------- /src/uu/lslocks/src/column.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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_uint}; 7 | use std::str::FromStr; 8 | 9 | use smartcols_sys::{ 10 | SCOLS_FL_RIGHT, SCOLS_FL_TRUNC, SCOLS_FL_WRAP, SCOLS_JSON_ARRAY_STRING, SCOLS_JSON_BOOLEAN, 11 | SCOLS_JSON_NUMBER, SCOLS_JSON_STRING, 12 | }; 13 | 14 | use crate::errors::LsLocksError; 15 | 16 | #[derive(Debug, Copy, Clone)] 17 | pub(crate) struct ColumnInfo { 18 | pub(crate) id: &'static CStr, 19 | pub(crate) width_hint: f64, 20 | pub(crate) flags: c_uint, 21 | pub(crate) default_json_type: c_uint, 22 | } 23 | 24 | impl ColumnInfo { 25 | const fn new( 26 | id: &'static CStr, 27 | width_hint: f64, 28 | flags: c_uint, 29 | default_json_type: c_uint, 30 | ) -> Self { 31 | Self { 32 | id, 33 | width_hint, 34 | flags, 35 | default_json_type, 36 | } 37 | } 38 | 39 | pub(crate) fn json_type(&self, in_bytes: bool) -> c_uint { 40 | if in_bytes && self.id.to_bytes() == b"SIZE" { 41 | SCOLS_JSON_NUMBER 42 | } else { 43 | self.default_json_type 44 | } 45 | } 46 | } 47 | 48 | pub(crate) static COLUMN_INFOS: [ColumnInfo; 13] = [ 49 | ColumnInfo::new(c"COMMAND", 15.0, 0, SCOLS_JSON_STRING), 50 | ColumnInfo::new(c"PID", 5.0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER), 51 | ColumnInfo::new(c"TYPE", 5.0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING), 52 | ColumnInfo::new(c"SIZE", 4.0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING), 53 | ColumnInfo::new(c"INODE", 5.0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER), 54 | ColumnInfo::new(c"MAJ:MIN", 6.0, 0, SCOLS_JSON_STRING), 55 | ColumnInfo::new(c"MODE", 5.0, 0, SCOLS_JSON_STRING), 56 | ColumnInfo::new(c"M", 1.0, 0, SCOLS_JSON_BOOLEAN), 57 | ColumnInfo::new(c"START", 10.0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER), 58 | ColumnInfo::new(c"END", 10.0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER), 59 | ColumnInfo::new(c"PATH", 0.0, SCOLS_FL_TRUNC, SCOLS_JSON_STRING), 60 | ColumnInfo::new(c"BLOCKER", 0.0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER), 61 | ColumnInfo::new(c"HOLDERS", 0.0, SCOLS_FL_WRAP, SCOLS_JSON_ARRAY_STRING), 62 | ]; 63 | 64 | pub(crate) static ALL: [&str; 13] = [ 65 | "COMMAND", "PID", "TYPE", "SIZE", "INODE", "MAJ:MIN", "MODE", "M", "START", "END", "PATH", 66 | "BLOCKER", "HOLDERS", 67 | ]; 68 | 69 | pub(crate) static DEFAULT: [&str; 9] = [ 70 | "COMMAND", "PID", "TYPE", "SIZE", "MODE", "M", "START", "END", "PATH", 71 | ]; 72 | 73 | #[derive(Debug, Clone)] 74 | pub(crate) struct OutputColumns { 75 | pub(crate) append: bool, 76 | pub(crate) list: Vec<&'static ColumnInfo>, 77 | } 78 | 79 | impl Default for OutputColumns { 80 | fn default() -> Self { 81 | Self { 82 | append: true, 83 | list: Vec::default(), 84 | } 85 | } 86 | } 87 | 88 | impl FromStr for OutputColumns { 89 | type Err = LsLocksError; 90 | 91 | fn from_str(s: &str) -> Result { 92 | let suffix = s.strip_prefix('+'); 93 | let append = suffix.is_some(); 94 | 95 | let list: Vec<_> = suffix 96 | .unwrap_or(s) 97 | .split(',') 98 | .map(|name| { 99 | COLUMN_INFOS 100 | .iter() 101 | .find(|&column| column.id.to_str().unwrap() == name) 102 | .ok_or_else(|| LsLocksError::InvalidColumnName(name.into())) 103 | }) 104 | .collect::>()?; 105 | 106 | if list.is_empty() { 107 | Err(LsLocksError::InvalidColumnSequence(s.into())) 108 | } else { 109 | Ok(Self { append, list }) 110 | } 111 | } 112 | } 113 | 114 | impl From<&'_ clap::ArgMatches> for OutputColumns { 115 | fn from(args: &clap::ArgMatches) -> Self { 116 | args.get_one::(crate::options::OUTPUT) 117 | .map_or_else(Self::default, |columns| Self { 118 | append: columns.append, 119 | list: columns.list.clone(), 120 | }) 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/uu/lslocks/src/display.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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::{CStr, CString}; 8 | use std::sync::atomic::AtomicPtr; 9 | use std::{io, ptr}; 10 | 11 | use crate::errors::LsLocksError; 12 | use crate::utils::LockInfo; 13 | 14 | fn decimal_point() -> &'static str { 15 | use std::sync::atomic::Ordering; 16 | 17 | static DEFAULT: &CStr = c"."; 18 | static VALUE: AtomicPtr = AtomicPtr::new(ptr::null_mut()); 19 | 20 | let mut decimal_point = VALUE.load(Ordering::Acquire); 21 | 22 | if decimal_point.is_null() { 23 | decimal_point = unsafe { libc::localeconv().as_ref() } 24 | .and_then(|lc| (!lc.decimal_point.is_null()).then_some(lc.decimal_point)) 25 | .unwrap_or(DEFAULT.as_ptr().cast_mut()) 26 | .cast(); 27 | 28 | match VALUE.compare_exchange( 29 | ptr::null_mut(), 30 | decimal_point, 31 | Ordering::AcqRel, 32 | Ordering::Acquire, 33 | ) { 34 | Ok(_previous_value) => {} 35 | Err(previous_value) => decimal_point = previous_value, 36 | } 37 | } 38 | 39 | unsafe { CStr::from_ptr(decimal_point.cast()) } 40 | .to_str() 41 | .unwrap() 42 | } 43 | 44 | // returns exponent (2^x=n) in range KiB..EiB (2^10..2^60). 45 | fn bytes_exponent(bytes: u64) -> u64 { 46 | for shift in (10..=60).step_by(10) { 47 | if bytes < (1 << shift) { 48 | return shift - 10; 49 | } 50 | } 51 | 60 52 | } 53 | 54 | fn size_to_human_string(bytes: u64) -> String { 55 | static LETTERS: [char; 7] = ['B', 'K', 'M', 'G', 'T', 'P', 'E']; 56 | 57 | let exp = bytes_exponent(bytes); 58 | let unit = LETTERS[if exp == 0 { 0 } else { (exp / 10) as usize }]; 59 | let mut decimal = if exp == 0 { bytes } else { bytes / (1 << exp) }; 60 | let mut fractional = if exp == 0 { 0 } else { bytes % (1 << exp) }; 61 | 62 | if fractional != 0 { 63 | fractional = if fractional >= (u64::MAX / 1000) { 64 | ((fractional / 1024) * 1000) / (1 << (exp - 10)) 65 | } else { 66 | (fractional * 1000) / (1 << exp) 67 | }; 68 | 69 | fractional = ((fractional + 50) / 100) * 10; 70 | 71 | if fractional == 100 { 72 | decimal += 1; 73 | fractional = 0; 74 | } 75 | } 76 | 77 | if fractional == 0 { 78 | format!("{decimal}{unit}") 79 | } else { 80 | format!("{decimal}{}{fractional:02}{unit}", decimal_point()) 81 | } 82 | } 83 | 84 | pub(crate) fn describe_integer(n: T) -> Option> { 85 | Some(Cow::Owned(CString::new(n.to_string()).unwrap())) 86 | } 87 | 88 | pub(crate) fn describe_size(size: u64, in_bytes: bool) -> Option> { 89 | let value = if in_bytes { 90 | size.to_string() 91 | } else { 92 | size_to_human_string(size) 93 | }; 94 | 95 | Some(Cow::Owned(CString::new(value).unwrap())) 96 | } 97 | 98 | pub(crate) fn describe_holders( 99 | proc_lock: &LockInfo, 100 | pid_locks: &[LockInfo], 101 | ) -> Result { 102 | let lock_compare = move |lock: &&LockInfo| { 103 | lock.range == proc_lock.range 104 | && lock.inode == proc_lock.inode 105 | && lock.device_id == proc_lock.device_id 106 | && lock.mandatory == proc_lock.mandatory 107 | && lock.blocked == proc_lock.blocked 108 | && lock.kind == proc_lock.kind 109 | && lock.mode == proc_lock.mode 110 | }; 111 | 112 | let mut separator: &[u8] = &[]; 113 | 114 | let append_holder = move |mut buffer: Vec, lock: &LockInfo| { 115 | buffer.extend(separator); 116 | separator = b"\n"; 117 | 118 | buffer.extend(lock.process_id.to_string().into_bytes()); 119 | buffer.push(b','); 120 | 121 | if let Some(command_line) = lock.command_name.as_deref().map(CStr::to_bytes) { 122 | buffer.extend(command_line); 123 | } 124 | 125 | buffer.push(b','); 126 | buffer.extend(lock.file_descriptor.to_string().into_bytes()); 127 | buffer 128 | }; 129 | 130 | let buffer = pid_locks 131 | .iter() 132 | .filter(lock_compare) 133 | .fold(Vec::default(), append_holder); 134 | 135 | CString::new(buffer).map_err(|_| LsLocksError::io0("invalid data", io::ErrorKind::InvalidData)) 136 | } 137 | -------------------------------------------------------------------------------- /src/uu/lslocks/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::ffi::c_int; 7 | use std::fmt; 8 | use std::path::PathBuf; 9 | 10 | use uucore::error::UError; 11 | 12 | #[derive(Debug)] 13 | pub enum LsLocksError { 14 | InvalidColumnName(String), 15 | InvalidColumnSequence(String), 16 | IO0(String, std::io::Error), 17 | IO1(String, PathBuf, std::io::Error), 18 | } 19 | 20 | impl LsLocksError { 21 | pub(crate) fn io0(message: impl Into, error: impl Into) -> Self { 22 | Self::IO0(message.into(), error.into()) 23 | } 24 | 25 | pub(crate) fn io1( 26 | message: impl Into, 27 | path: impl Into, 28 | error: impl Into, 29 | ) -> Self { 30 | Self::IO1(message.into(), path.into(), error.into()) 31 | } 32 | 33 | pub(crate) fn io_from_neg_errno( 34 | message: impl Into, 35 | result: c_int, 36 | ) -> Result { 37 | if let Ok(result) = usize::try_from(result) { 38 | Ok(result) 39 | } else { 40 | let err = std::io::Error::from_raw_os_error(-result); 41 | Err(Self::IO0(message.into(), err)) 42 | } 43 | } 44 | } 45 | 46 | impl fmt::Display for LsLocksError { 47 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 48 | match self { 49 | Self::IO0(message, err) => write!(f, "{message}: {err}"), 50 | Self::IO1(message, path, err) => write!(f, "{message} '{}': {err}", path.display()), 51 | Self::InvalidColumnName(name) => write!(f, "invalid column name: {name}"), 52 | Self::InvalidColumnSequence(seq) => write!(f, "invalid column sequence: {seq}"), 53 | } 54 | } 55 | } 56 | 57 | impl UError for LsLocksError { 58 | fn code(&self) -> i32 { 59 | 1 60 | } 61 | 62 | fn usage(&self) -> bool { 63 | false 64 | } 65 | } 66 | 67 | impl std::error::Error for LsLocksError {} 68 | -------------------------------------------------------------------------------- /src/uu/lslocks/src/main.rs: -------------------------------------------------------------------------------- 1 | uucore::bin!(uu_lslocks); 2 | -------------------------------------------------------------------------------- /src/uu/lslocks/src/smartcols.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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_char, c_int, c_uint, c_void}; 7 | use std::ptr::NonNull; 8 | use std::{io, ptr}; 9 | 10 | use smartcols_sys::{ 11 | libscols_column, libscols_line, libscols_table, scols_column_set_json_type, 12 | scols_column_set_safechars, scols_column_set_wrapfunc, scols_init_debug, scols_line_set_data, 13 | scols_new_table, scols_print_table, scols_table_enable_json, scols_table_enable_noheadings, 14 | scols_table_enable_raw, scols_table_new_column, scols_table_new_line, scols_table_set_name, 15 | scols_unref_table, 16 | }; 17 | 18 | use crate::errors::LsLocksError; 19 | 20 | pub(crate) fn initialize() { 21 | unsafe { scols_init_debug(0) }; 22 | } 23 | 24 | #[repr(transparent)] 25 | pub(crate) struct Table(NonNull); 26 | 27 | impl Table { 28 | pub(crate) fn new() -> Result { 29 | NonNull::new(unsafe { scols_new_table() }) 30 | .ok_or_else(|| LsLocksError::io0("scols_new_table", io::ErrorKind::OutOfMemory)) 31 | .map(Self) 32 | } 33 | } 34 | 35 | impl TableOperations for Table { 36 | fn as_ptr(&self) -> *mut libscols_table { 37 | self.0.as_ptr() 38 | } 39 | } 40 | 41 | impl Drop for Table { 42 | fn drop(&mut self) { 43 | unsafe { scols_unref_table(self.0.as_ptr()) } 44 | } 45 | } 46 | 47 | pub(crate) trait TableOperations: Sized { 48 | fn as_ptr(&self) -> *mut libscols_table; 49 | 50 | fn enable_headings(&mut self, enable: bool) -> Result<(), LsLocksError> { 51 | let no_headings = c_int::from(!enable); 52 | let r = unsafe { scols_table_enable_noheadings(self.as_ptr(), no_headings) }; 53 | LsLocksError::io_from_neg_errno("scols_table_enable_noheadings", r).map(|_| ()) 54 | } 55 | 56 | fn enable_raw(&mut self, enable: bool) -> Result<(), LsLocksError> { 57 | let r = unsafe { scols_table_enable_raw(self.as_ptr(), c_int::from(enable)) }; 58 | LsLocksError::io_from_neg_errno("scols_table_enable_raw", r).map(|_| ()) 59 | } 60 | 61 | fn enable_json(&mut self, enable: bool) -> Result<(), LsLocksError> { 62 | let r = unsafe { scols_table_enable_json(self.as_ptr(), c_int::from(enable)) }; 63 | LsLocksError::io_from_neg_errno("scols_table_enable_json", r).map(|_| ()) 64 | } 65 | 66 | fn new_column( 67 | &mut self, 68 | name: &CStr, 69 | width_hint: f64, 70 | flags: c_uint, 71 | ) -> Result { 72 | NonNull::new(unsafe { 73 | scols_table_new_column(self.as_ptr(), name.as_ptr(), width_hint, flags as c_int) 74 | }) 75 | .ok_or_else(|| LsLocksError::io0("scols_table_new_column", io::ErrorKind::OutOfMemory)) 76 | .map(ColumnRef) 77 | } 78 | 79 | fn new_line(&mut self, parent: Option<&mut LineRef>) -> Result { 80 | let parent = parent.map_or(ptr::null_mut(), |parent| parent.0.as_ptr()); 81 | 82 | NonNull::new(unsafe { scols_table_new_line(self.as_ptr(), parent) }) 83 | .ok_or_else(|| LsLocksError::io0("scols_table_new_line", io::ErrorKind::OutOfMemory)) 84 | .map(LineRef) 85 | } 86 | 87 | fn set_name(&mut self, name: &CStr) -> Result<(), LsLocksError> { 88 | let r = unsafe { scols_table_set_name(self.as_ptr(), name.as_ptr()) }; 89 | LsLocksError::io_from_neg_errno("scols_table_set_name", r).map(|_| ()) 90 | } 91 | 92 | fn print(&self) -> Result<(), LsLocksError> { 93 | let r = unsafe { scols_print_table(self.as_ptr()) }; 94 | LsLocksError::io_from_neg_errno("scols_print_table", r).map(|_| ()) 95 | } 96 | } 97 | 98 | #[repr(transparent)] 99 | pub(crate) struct LineRef(NonNull); 100 | 101 | impl LineRef { 102 | pub(crate) fn set_data(&mut self, cell_index: usize, data: &CStr) -> Result<(), LsLocksError> { 103 | let r = unsafe { scols_line_set_data(self.0.as_ptr(), cell_index, data.as_ptr()) }; 104 | LsLocksError::io_from_neg_errno("scols_line_set_data", r).map(|_| ()) 105 | } 106 | } 107 | 108 | #[repr(transparent)] 109 | pub(crate) struct ColumnRef(NonNull); 110 | 111 | impl ColumnRef { 112 | pub(crate) fn set_json_type(&mut self, json_type: c_uint) -> Result<(), LsLocksError> { 113 | let r = unsafe { scols_column_set_json_type(self.0.as_ptr(), json_type as c_int) }; 114 | LsLocksError::io_from_neg_errno("scols_column_set_json_type", r).map(|_| ()) 115 | } 116 | 117 | pub(crate) fn set_safe_chars(&mut self, safe: &CStr) -> Result<(), LsLocksError> { 118 | let r = unsafe { scols_column_set_safechars(self.0.as_ptr(), safe.as_ptr()) }; 119 | LsLocksError::io_from_neg_errno("scols_column_set_safechars", r).map(|_| ()) 120 | } 121 | 122 | pub(crate) fn set_wrap_func( 123 | &mut self, 124 | wrap_chunk_size: Option< 125 | unsafe extern "C" fn(*const libscols_column, *const c_char, *mut c_void) -> usize, 126 | >, 127 | wrap_next_chunk: Option< 128 | unsafe extern "C" fn(*const libscols_column, *mut c_char, *mut c_void) -> *mut c_char, 129 | >, 130 | user_data: *mut c_void, 131 | ) -> Result<(), LsLocksError> { 132 | let r = unsafe { 133 | scols_column_set_wrapfunc(self.0.as_ptr(), wrap_chunk_size, wrap_next_chunk, user_data) 134 | }; 135 | LsLocksError::io_from_neg_errno("scols_column_set_wrapfunc", r).map(|_| ()) 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/uu/lsmem/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uu_lsmem" 3 | version = "0.0.1" 4 | edition = "2021" 5 | 6 | [lib] 7 | path = "src/lsmem.rs" 8 | 9 | [[bin]] 10 | name = "lsmem" 11 | path = "src/main.rs" 12 | 13 | [dependencies] 14 | uucore = { workspace = true } 15 | clap = { workspace = true } 16 | serde = { workspace = true } 17 | serde_json = { workspace = true } 18 | -------------------------------------------------------------------------------- /src/uu/lsmem/lsmem.md: -------------------------------------------------------------------------------- 1 | # lsmem 2 | 3 | ``` 4 | lsmem [OPTION]... 5 | ``` 6 | 7 | List the ranges of available memory with their online status. 8 | -------------------------------------------------------------------------------- /src/uu/lsmem/src/main.rs: -------------------------------------------------------------------------------- 1 | uucore::bin!(uu_lsmem); 2 | -------------------------------------------------------------------------------- /src/uu/lsmem/src/utils.rs: -------------------------------------------------------------------------------- 1 | pub fn size_to_human_string(bytes: u64) -> String { 2 | let mut buf = String::with_capacity(32); 3 | let mut dec; 4 | let mut frac; 5 | let letters = "BKMGTPE"; 6 | let mut suffix = String::with_capacity(4); 7 | 8 | let exp = get_exp(bytes); 9 | let c = letters 10 | .chars() 11 | .nth(if exp != 0 { exp / 10 } else { 0 }) 12 | .unwrap_or('B'); 13 | dec = if exp != 0 { 14 | bytes / (1_u64 << exp) 15 | } else { 16 | bytes 17 | }; 18 | frac = if exp != 0 { bytes % (1_u64 << exp) } else { 0 }; 19 | 20 | suffix.push(c); 21 | 22 | // Rounding logic 23 | if frac != 0 { 24 | if frac >= u64::MAX / 1000 { 25 | frac = ((frac / 1024) * 1000) / (1 << (exp - 10)); 26 | } else { 27 | frac = (frac * 1000) / (1 << exp); 28 | } 29 | 30 | // Round to 1 decimal place 31 | frac = ((frac + 50) / 100) * 10; 32 | 33 | // Check for overflow due to rounding 34 | if frac == 100 { 35 | dec += 1; 36 | frac = 0; 37 | } 38 | } 39 | 40 | // Format the result 41 | if frac != 0 { 42 | let decimal_point = "."; 43 | buf = format!("{}{}{:02}", dec, decimal_point, frac); 44 | if buf.ends_with('0') { 45 | buf.pop(); // Remove extraneous zero 46 | } 47 | buf += &suffix; 48 | } else { 49 | buf += &format!("{dec}"); 50 | buf += &suffix; 51 | } 52 | 53 | buf 54 | } 55 | 56 | fn get_exp(n: u64) -> usize { 57 | let mut shft = 10; 58 | while shft <= 60 { 59 | if n < (1 << shft) { 60 | break; 61 | } 62 | shft += 10; 63 | } 64 | shft - 10 65 | } 66 | 67 | #[test] 68 | fn test_size_to_human_string() { 69 | assert_eq!("11.7K", size_to_human_string(12000)); 70 | assert_eq!("11.4M", size_to_human_string(12000000)); 71 | assert_eq!("11.2G", size_to_human_string(12000000000)); 72 | } 73 | -------------------------------------------------------------------------------- /src/uu/mcookie/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uu_mcookie" 3 | version = "0.0.1" 4 | edition = "2021" 5 | 6 | [lib] 7 | path = "src/mcookie.rs" 8 | 9 | [[bin]] 10 | name = "mcookie" 11 | path = "src/main.rs" 12 | 13 | [dependencies] 14 | uucore = { workspace = true } 15 | clap = { workspace = true } 16 | md-5 = { workspace = true } 17 | rand = { workspace = true } 18 | -------------------------------------------------------------------------------- /src/uu/mcookie/mcookie.md: -------------------------------------------------------------------------------- 1 | # mcookie 2 | 3 | ``` 4 | mcookie [OPTION]... 5 | ``` 6 | 7 | Generate magic cookies for xauth. 8 | -------------------------------------------------------------------------------- /src/uu/mcookie/src/main.rs: -------------------------------------------------------------------------------- 1 | uucore::bin!(uu_mcookie); 2 | -------------------------------------------------------------------------------- /src/uu/mcookie/src/mcookie.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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::{ 7 | fs::File, 8 | io::{stdin, Read}, 9 | }; 10 | 11 | use clap::{crate_version, Arg, ArgAction, Command}; 12 | use md5::{Digest, Md5}; 13 | use rand::RngCore; 14 | use uucore::{ 15 | error::{UResult, USimpleError}, 16 | format_usage, help_about, help_usage, 17 | }; 18 | mod size; 19 | use size::Size; 20 | 21 | mod options { 22 | pub const FILE: &str = "file"; 23 | pub const MAX_SIZE: &str = "max-size"; 24 | pub const VERBOSE: &str = "verbose"; 25 | } 26 | 27 | const ABOUT: &str = help_about!("mcookie.md"); 28 | const USAGE: &str = help_usage!("mcookie.md"); 29 | 30 | const RANDOM_BYTES: usize = 128; 31 | const MAX_DEFAULT: u64 = 4096; 32 | 33 | #[uucore::main] 34 | pub fn uumain(args: impl uucore::Args) -> UResult<()> { 35 | let matches: clap::ArgMatches = uu_app().try_get_matches_from(args)?; 36 | 37 | let verbose = matches.get_flag(options::VERBOSE); 38 | 39 | let seed_files: Vec<&str> = matches 40 | .get_many::(options::FILE) 41 | .unwrap_or_default() 42 | .map(|v| v.as_str()) 43 | .collect(); 44 | 45 | let max_size = if let Some(size_str) = matches.get_one::(options::MAX_SIZE) { 46 | match Size::parse(size_str) { 47 | Ok(size) => { 48 | let mut s = size.size_bytes(); 49 | if s == 0 { 50 | s = MAX_DEFAULT; 51 | } 52 | s 53 | } 54 | Err(_) => { 55 | return Err(USimpleError::new(1, "Failed to parse max-size value")); 56 | } 57 | } 58 | } else { 59 | MAX_DEFAULT 60 | }; 61 | 62 | let mut hasher = Md5::new(); 63 | 64 | for file_path in seed_files { 65 | let mut buffer: Vec = Vec::new(); 66 | let input_name: &str; 67 | 68 | if file_path == "-" { 69 | input_name = "stdin"; 70 | let stdin_handle = stdin(); 71 | 72 | let mut limited_reader = stdin_handle.take(max_size); 73 | limited_reader.read_to_end(&mut buffer)?; 74 | } else { 75 | input_name = file_path; 76 | let open_result = File::open(file_path); 77 | if let Err(err) = open_result { 78 | eprintln!("mcookie: cannot open {file_path}: {err}"); 79 | continue; 80 | } 81 | 82 | let f = open_result.unwrap(); 83 | let mut limited_reader = f.take(max_size); 84 | limited_reader.read_to_end(&mut buffer)?; 85 | } 86 | 87 | if verbose { 88 | eprintln!("Got {} bytes from {}", buffer.len(), input_name); 89 | } 90 | 91 | hasher.update(&buffer); 92 | } 93 | 94 | let mut rng = rand::rng(); 95 | let mut rand_bytes = [0u8; RANDOM_BYTES]; 96 | rng.fill_bytes(&mut rand_bytes); 97 | 98 | hasher.update(rand_bytes); 99 | 100 | if verbose { 101 | eprintln!("Got {} bytes from randomness source", RANDOM_BYTES); 102 | } 103 | 104 | let result = hasher.finalize(); 105 | let output = result 106 | .iter() 107 | .map(|byte| format!("{:02x}", byte)) 108 | .collect::>() 109 | .join(""); 110 | 111 | println!("{}", output); 112 | 113 | Ok(()) 114 | } 115 | 116 | pub fn uu_app() -> Command { 117 | Command::new(uucore::util_name()) 118 | .version(crate_version!()) 119 | .about(ABOUT) 120 | .override_usage(format_usage(USAGE)) 121 | .infer_long_args(true) 122 | .arg( 123 | Arg::new(options::FILE) 124 | .short('f') 125 | .long("file") 126 | .value_name("file") 127 | .action(ArgAction::Append) 128 | .help("use file as a cookie seed"), 129 | ) 130 | .arg( 131 | Arg::new(options::MAX_SIZE) 132 | .short('m') 133 | .long("max-size") 134 | .value_name("num") 135 | .action(ArgAction::Set) 136 | .help("limit how much is read from seed files (supports B suffix or binary units: KiB, MiB, GiB, TiB)"), 137 | ) 138 | .arg( 139 | Arg::new(options::VERBOSE) 140 | .short('v') 141 | .long("verbose") 142 | .action(ArgAction::SetTrue) 143 | .help("explain what is being done"), 144 | ) 145 | } 146 | -------------------------------------------------------------------------------- /src/uu/mcookie/src/size.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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::error::Error; 7 | use std::fmt; 8 | 9 | #[derive(Debug)] 10 | pub struct ParseSizeError(String); 11 | 12 | impl fmt::Display for ParseSizeError { 13 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 14 | write!(f, "Invalid size format: {}", self.0) 15 | } 16 | } 17 | 18 | impl Error for ParseSizeError {} 19 | 20 | pub struct Size(u64); 21 | 22 | impl Size { 23 | pub fn parse(s: &str) -> Result { 24 | let s = s.trim(); 25 | 26 | // Handle bytes with "B" suffix 27 | if s.ends_with('B') && !s.ends_with("iB") { 28 | if let Some(nums) = s.strip_suffix('B') { 29 | return nums 30 | .trim() 31 | .parse::() 32 | .map(Self) 33 | .map_err(|_| ParseSizeError(s.to_string())); 34 | } 35 | } 36 | 37 | // Handle binary units (KiB, MiB, GiB, TiB) 38 | for (suffix, exponent) in [("KiB", 1), ("MiB", 2), ("GiB", 3), ("TiB", 4)] { 39 | if let Some(nums) = s.strip_suffix(suffix) { 40 | return nums 41 | .trim() 42 | .parse::() 43 | .map(|n| Self(n * 1024_u64.pow(exponent))) 44 | .map_err(|_| ParseSizeError(s.to_string())); 45 | } 46 | } 47 | 48 | // If no suffix, treat as bytes 49 | s.parse::() 50 | .map(Self) 51 | .map_err(|_| ParseSizeError(s.to_string())) 52 | } 53 | 54 | pub fn size_bytes(&self) -> u64 { 55 | self.0 56 | } 57 | } 58 | 59 | #[cfg(test)] 60 | mod tests { 61 | use super::*; 62 | 63 | #[test] 64 | fn test_parse_numeric() { 65 | assert_eq!(Size::parse("1234").unwrap().size_bytes(), 1234); 66 | } 67 | 68 | #[test] 69 | fn test_parse_with_suffix() { 70 | assert_eq!(Size::parse("1024B").unwrap().size_bytes(), 1024); 71 | assert_eq!(Size::parse("1KiB").unwrap().size_bytes(), 1024); 72 | assert_eq!(Size::parse("1MiB").unwrap().size_bytes(), 1024 * 1024); 73 | assert_eq!( 74 | Size::parse("1GiB").unwrap().size_bytes(), 75 | 1024 * 1024 * 1024 76 | ); 77 | assert_eq!( 78 | Size::parse("1TiB").unwrap().size_bytes(), 79 | 1024 * 1024 * 1024 * 1024 80 | ); 81 | } 82 | 83 | #[test] 84 | fn test_invalid_input() { 85 | // Invalid format 86 | assert!(Size::parse("invalid").is_err()); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/uu/mesg/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uu_mesg" 3 | version = "0.0.1" 4 | edition = "2021" 5 | 6 | [lib] 7 | path = "src/mesg.rs" 8 | 9 | [[bin]] 10 | name = "mesg" 11 | path = "src/main.rs" 12 | 13 | [dependencies] 14 | clap = { workspace = true } 15 | nix = { workspace = true, features = ["fs"] } 16 | uucore = { workspace = true } 17 | -------------------------------------------------------------------------------- /src/uu/mesg/mesg.md: -------------------------------------------------------------------------------- 1 | # mesg 2 | 3 | ``` 4 | mesg [option] [y|n] 5 | ``` 6 | 7 | enables or disables displaying messages from other users 8 | -------------------------------------------------------------------------------- /src/uu/mesg/src/main.rs: -------------------------------------------------------------------------------- 1 | uucore::bin!(uu_mesg); 2 | -------------------------------------------------------------------------------- /src/uu/mesg/src/mesg.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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 clap::{builder::PossibleValuesParser, crate_version, Arg, ArgAction, ArgMatches, Command}; 7 | #[cfg(target_family = "unix")] 8 | use uucore::error::{set_exit_code, UIoError, USimpleError}; 9 | use uucore::{error::UResult, format_usage, help_about, help_usage}; 10 | 11 | const ABOUT: &str = help_about!("mesg.md"); 12 | const USAGE: &str = help_usage!("mesg.md"); 13 | 14 | #[cfg(target_family = "unix")] 15 | pub fn do_mesg(matches: &ArgMatches) -> UResult<()> { 16 | use nix::sys::stat::{fchmod, fstat, Mode}; 17 | use std::io; 18 | use std::{io::IsTerminal, os::fd::AsFd}; 19 | 20 | for fd in &[ 21 | std::io::stdin().as_fd(), 22 | std::io::stdout().as_fd(), 23 | std::io::stderr().as_fd(), 24 | ] { 25 | if fd.is_terminal() { 26 | let st = fstat(fd.as_fd()).map_err(|e| USimpleError::new(1, e.desc()))?; 27 | if let Some(enable) = matches.get_one::("enable") { 28 | // 'mesg y' on the GNU version seems to only modify the group write bit, 29 | // but 'mesg n' modifies both group and others write bits. 30 | let new_mode = if enable == "y" { 31 | st.st_mode | 0o020 32 | } else { 33 | st.st_mode & !0o022 34 | }; 35 | fchmod(fd.as_fd(), Mode::from_bits_retain(new_mode)) 36 | .map_err(|e| USimpleError::new(1, e.desc()))?; 37 | if enable == "n" { 38 | set_exit_code(1); 39 | } 40 | if matches.get_flag("verbose") { 41 | println!( 42 | "write access to your terminal is {}", 43 | if enable == "y" { "allowed" } else { "denied" } 44 | ); 45 | } 46 | } else if st.st_mode & 0o022 != 0 { 47 | println!("is y"); 48 | } else { 49 | set_exit_code(1); 50 | println!("is n"); 51 | } 52 | return Ok(()); 53 | } 54 | } 55 | Err(UIoError::new( 56 | io::ErrorKind::Other, 57 | "stdin/stdout/stderr is not a terminal", 58 | )) 59 | } 60 | 61 | #[cfg(target_family = "unix")] 62 | #[uucore::main] 63 | pub fn uumain(args: impl uucore::Args) -> UResult<()> { 64 | let matches = uu_app().try_get_matches_from(args)?; 65 | if let Err(e) = do_mesg(&matches) { 66 | set_exit_code(2); 67 | uucore::show_error!("{}", e); 68 | }; 69 | Ok(()) 70 | } 71 | 72 | pub fn uu_app() -> Command { 73 | Command::new(uucore::util_name()) 74 | .version(crate_version!()) 75 | .about(ABOUT) 76 | .override_usage(format_usage(USAGE)) 77 | .infer_long_args(true) 78 | .arg( 79 | Arg::new("verbose") 80 | .short('v') 81 | .long("verbose") 82 | .help("Explain what is being done") 83 | .action(ArgAction::SetTrue), 84 | ) 85 | .arg( 86 | Arg::new("enable") 87 | .help("Whether to allow or disallow messages") 88 | .value_parser(PossibleValuesParser::new(["y", "n"])) 89 | .action(ArgAction::Set), 90 | ) 91 | } 92 | 93 | #[cfg(not(target_family = "unix"))] 94 | #[uucore::main] 95 | pub fn uumain(args: impl uucore::Args) -> UResult<()> { 96 | let _matches: ArgMatches = uu_app().try_get_matches_from(args)?; 97 | 98 | Err(uucore::error::USimpleError::new( 99 | 1, 100 | "`mesg` is available only on Unix platforms.", 101 | )) 102 | } 103 | -------------------------------------------------------------------------------- /src/uu/mountpoint/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uu_mountpoint" 3 | version = "0.0.1" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | uucore = { workspace = true } 10 | clap = { workspace = true } 11 | 12 | [lib] 13 | path = "src/mountpoint.rs" 14 | 15 | [[bin]] 16 | name = "mountpoint" 17 | path = "src/main.rs" 18 | -------------------------------------------------------------------------------- /src/uu/mountpoint/mountpoint.md: -------------------------------------------------------------------------------- 1 | # mountpoint 2 | 3 | ``` 4 | mountpoint [-d|-q] directory|file 5 | 6 | mountpoint -x device 7 | ``` 8 | 9 | See if a directory or file is a mountpoint -------------------------------------------------------------------------------- /src/uu/mountpoint/src/main.rs: -------------------------------------------------------------------------------- 1 | uucore::bin!(uu_mountpoint); 2 | -------------------------------------------------------------------------------- /src/uu/mountpoint/src/mountpoint.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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 clap::Arg; 7 | use clap::{crate_version, Command}; 8 | use std::env; 9 | #[cfg(not(windows))] 10 | use std::fs; 11 | #[cfg(not(windows))] 12 | use std::os::unix::fs::MetadataExt; 13 | use std::process; 14 | use uucore::{error::UResult, format_usage, help_about, help_usage}; 15 | 16 | const ABOUT: &str = help_about!("mountpoint.md"); 17 | const USAGE: &str = help_usage!("mountpoint.md"); 18 | 19 | #[uucore::main] 20 | pub fn uumain(args: impl uucore::Args) -> UResult<()> { 21 | let matches: clap::ArgMatches = uu_app().try_get_matches_from(args)?; 22 | let path = matches.get_one::("path"); 23 | 24 | if let Some(path) = path { 25 | if is_mountpoint(path) { 26 | println!("{} is a mountpoint", path); 27 | } else { 28 | println!("{} is not a mountpoint", path); 29 | } 30 | } else { 31 | // Handle the case where path is not provided 32 | eprintln!("Error: Path argument is required"); 33 | process::exit(1); 34 | } 35 | Ok(()) 36 | } 37 | 38 | #[cfg(not(windows))] 39 | fn is_mountpoint(path: &str) -> bool { 40 | let metadata = match fs::metadata(path) { 41 | Ok(metadata) => metadata, 42 | Err(_) => return false, 43 | }; 44 | 45 | let dev = metadata.dev(); 46 | let inode = metadata.ino(); 47 | 48 | // Root inode (typically 2 in most Unix filesystems) indicates a mount point 49 | inode == 2 50 | || match fs::metadata("..") { 51 | Ok(parent_metadata) => parent_metadata.dev() != dev, 52 | Err(_) => false, 53 | } 54 | } 55 | 56 | // TODO: implement for windows 57 | #[cfg(windows)] 58 | fn is_mountpoint(_path: &str) -> bool { 59 | false 60 | } 61 | 62 | pub fn uu_app() -> Command { 63 | Command::new(uucore::util_name()) 64 | .version(crate_version!()) 65 | .about(ABOUT) 66 | .override_usage(format_usage(USAGE)) 67 | .infer_long_args(true) 68 | .arg( 69 | Arg::new("path") 70 | .value_name("PATH") 71 | .help("Path to check for mountpoint") 72 | .required(true) 73 | .index(1), 74 | ) 75 | } 76 | -------------------------------------------------------------------------------- /src/uu/renice/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uu_renice" 3 | version = "0.0.1" 4 | edition = "2021" 5 | description = "renice ~ (uutils) Alter priority of running processes" 6 | 7 | [lib] 8 | path = "src/renice.rs" 9 | 10 | [[bin]] 11 | name = "renice" 12 | path = "src/main.rs" 13 | 14 | [dependencies] 15 | clap = { workspace = true } 16 | libc = { workspace = true } 17 | uucore = { workspace = true } 18 | -------------------------------------------------------------------------------- /src/uu/renice/renice.md: -------------------------------------------------------------------------------- 1 | # renice 2 | 3 | ``` 4 | renice [--priority|--relative] priority [-g|-p|-u] identifier... 5 | ``` 6 | 7 | Alter priority of running processes -------------------------------------------------------------------------------- /src/uu/renice/src/main.rs: -------------------------------------------------------------------------------- 1 | uucore::bin!(uu_renice); 2 | -------------------------------------------------------------------------------- /src/uu/renice/src/renice.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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 clap::{crate_version, Arg, Command}; 7 | #[cfg(not(windows))] 8 | use libc::PRIO_PROCESS; 9 | use std::env; 10 | #[cfg(not(windows))] 11 | use std::io::Error; 12 | use std::process; 13 | use uucore::{error::UResult, format_usage, help_about, help_usage}; 14 | 15 | const ABOUT: &str = help_about!("renice.md"); 16 | const USAGE: &str = help_usage!("renice.md"); 17 | 18 | #[uucore::main] 19 | pub fn uumain(args: impl uucore::Args) -> UResult<()> { 20 | let matches = uu_app().try_get_matches_from(args)?; 21 | 22 | let nice_value_str = matches.get_one::("nice_value").unwrap(); // Retrieve as String 23 | let nice_value = nice_value_str.parse::().unwrap_or_else(|_| { 24 | eprintln!("Invalid nice value"); 25 | process::exit(1); 26 | }); 27 | 28 | let pid_str = matches.get_one::("pid").unwrap(); // Retrieve as String 29 | let pid = pid_str.parse::().unwrap_or_else(|_| { 30 | eprintln!("Invalid PID"); 31 | process::exit(1); 32 | }); 33 | 34 | // TODO: implement functionality on windows 35 | #[cfg(not(windows))] 36 | if unsafe { libc::setpriority(PRIO_PROCESS, pid.try_into().unwrap(), nice_value) } == -1 { 37 | eprintln!("Failed to set nice value: {}", Error::last_os_error()); 38 | process::exit(1); 39 | } 40 | 41 | println!("Nice value of process {} set to {}", pid, nice_value); 42 | Ok(()) 43 | } 44 | 45 | pub fn uu_app() -> Command { 46 | Command::new(uucore::util_name()) 47 | .version(crate_version!()) 48 | .about(ABOUT) 49 | .override_usage(format_usage(USAGE)) 50 | .infer_long_args(true) 51 | .arg( 52 | Arg::new("nice_value") 53 | .value_name("NICE_VALUE") 54 | .help("The new nice value for the process") 55 | .required(true) 56 | .index(1), 57 | ) 58 | .arg( 59 | Arg::new("pid") 60 | .value_name("PID") 61 | .help("The PID of the process") 62 | .required(true) 63 | .index(2), 64 | ) 65 | } 66 | -------------------------------------------------------------------------------- /src/uu/rev/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uu_rev" 3 | version = "0.0.1" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | uucore = { workspace = true } 10 | clap = { workspace = true } 11 | 12 | [lib] 13 | path = "src/rev.rs" 14 | 15 | [[bin]] 16 | name = "rev" 17 | path = "src/main.rs" 18 | -------------------------------------------------------------------------------- /src/uu/rev/rev.md: -------------------------------------------------------------------------------- 1 | # rev 2 | 3 | ``` 4 | rev [options] [file ...] 5 | ``` 6 | 7 | Reverse lines characterwise. -------------------------------------------------------------------------------- /src/uu/rev/src/main.rs: -------------------------------------------------------------------------------- 1 | uucore::bin!(uu_rev); 2 | -------------------------------------------------------------------------------- /src/uu/rev/src/rev.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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 clap::{crate_version, Command}; 7 | use clap::{Arg, ArgAction}; 8 | use std::env; 9 | use std::io::{BufRead, BufReader, Read, Write}; 10 | use uucore::{error::UResult, format_usage, help_about, help_usage}; 11 | 12 | const ABOUT: &str = help_about!("rev.md"); 13 | const USAGE: &str = help_usage!("rev.md"); 14 | 15 | mod options { 16 | pub const FILE: &str = "file"; 17 | pub const ZERO: &str = "zero"; 18 | } 19 | 20 | #[uucore::main] 21 | pub fn uumain(args: impl uucore::Args) -> UResult<()> { 22 | let matches: clap::ArgMatches = uu_app().try_get_matches_from(args)?; 23 | let files = matches.get_many::(options::FILE); 24 | let zero = matches.get_flag(options::ZERO); 25 | 26 | let sep = if zero { b'\0' } else { b'\n' }; 27 | 28 | if let Some(files) = files { 29 | for path in files { 30 | let Ok(file) = std::fs::File::open(path) else { 31 | uucore::error::set_exit_code(1); 32 | uucore::show_error!("cannot open {path}: No such file or directory"); 33 | continue; 34 | }; 35 | if let Err(err) = rev_stream(file, sep) { 36 | uucore::error::set_exit_code(1); 37 | uucore::show_error!("cannot read {path}: {err}"); 38 | } 39 | } 40 | } else { 41 | let stdin = std::io::stdin().lock(); 42 | let _ = rev_stream(stdin, sep); 43 | } 44 | 45 | Ok(()) 46 | } 47 | 48 | fn rev_stream(stream: impl Read, sep: u8) -> std::io::Result<()> { 49 | let mut stdout = std::io::stdout().lock(); 50 | let mut stream = BufReader::new(stream); 51 | let mut buf = Vec::with_capacity(4096); 52 | loop { 53 | buf.clear(); 54 | stream.read_until(sep, &mut buf)?; 55 | if buf.last().copied() == Some(sep) { 56 | buf.pop(); 57 | buf.reverse(); 58 | buf.push(sep); 59 | stdout.write_all(&buf)?; 60 | } else { 61 | buf.reverse(); 62 | stdout.write_all(&buf)?; 63 | break; 64 | } 65 | } 66 | Ok(()) 67 | } 68 | 69 | pub fn uu_app() -> Command { 70 | Command::new(uucore::util_name()) 71 | .version(crate_version!()) 72 | .about(ABOUT) 73 | .override_usage(format_usage(USAGE)) 74 | .infer_long_args(true) 75 | .arg( 76 | Arg::new(options::FILE) 77 | .value_name("FILE") 78 | .help("Paths of files to reverse") 79 | .index(1) 80 | .action(ArgAction::Set) 81 | .num_args(1..), 82 | ) 83 | .arg( 84 | Arg::new(options::ZERO) 85 | .short('0') 86 | .long("zero") 87 | .help("Zero termination. Use the byte '\\0' as line separator.") 88 | .action(ArgAction::SetTrue), 89 | ) 90 | } 91 | -------------------------------------------------------------------------------- /src/uu/setsid/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uu_setsid" 3 | version = "0.0.1" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | uucore = { workspace = true } 8 | libc = { workspace = true } 9 | clap = { workspace = true } 10 | 11 | [lib] 12 | path = "src/setsid.rs" 13 | 14 | [[bin]] 15 | name = "setsid" 16 | path = "src/main.rs" 17 | -------------------------------------------------------------------------------- /src/uu/setsid/setsid.md: -------------------------------------------------------------------------------- 1 | # setsid 2 | 3 | ``` 4 | setsid [options] [argument ...] 5 | ``` 6 | 7 | Run a program in a new session. 8 | -------------------------------------------------------------------------------- /src/uu/setsid/src/main.rs: -------------------------------------------------------------------------------- 1 | uucore::bin!(uu_setsid); 2 | -------------------------------------------------------------------------------- /src/uu/setsid/src/setsid.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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 clap::builder::ValueParser; 7 | use clap::{crate_version, Command}; 8 | use clap::{Arg, ArgAction}; 9 | use uucore::{error::UResult, format_usage, help_about, help_usage}; 10 | 11 | const ABOUT: &str = help_about!("setsid.md"); 12 | const USAGE: &str = help_usage!("setsid.md"); 13 | 14 | #[cfg(target_family = "unix")] 15 | mod unix { 16 | pub use std::ffi::{OsStr, OsString}; 17 | pub use std::os::unix::process::CommandExt; 18 | pub use std::{io, process}; 19 | pub use uucore::error::{FromIo, UIoError, UResult}; 20 | 21 | // The promise made by setsid(1) is that it forks if the process 22 | // is already a group leader, not session leader. 23 | pub fn already_group_leader() -> bool { 24 | let leader = unsafe { libc::getpgrp() }; 25 | leader == process::id() as i32 26 | } 27 | 28 | pub fn report_failure_to_exec(error: io::Error, executable: &OsStr, set_error: bool) { 29 | let kind = error.kind(); 30 | 31 | // FIXME: POSIX wants certain exit statuses for specific errors, should 32 | // these be handled by uucore::error? We should be able to just return 33 | // the UError here. 34 | uucore::show_error!( 35 | "failed to execute {}: {}", 36 | executable.to_string_lossy(), 37 | UIoError::from(error) 38 | ); 39 | 40 | if set_error { 41 | if kind == io::ErrorKind::NotFound { 42 | uucore::error::set_exit_code(127); 43 | } else if kind == io::ErrorKind::PermissionDenied { 44 | uucore::error::set_exit_code(126); 45 | } 46 | } 47 | } 48 | 49 | // This function will be potentially called after a fork(), so what it can do 50 | // is quite restricted. This is the meat of the program. 51 | pub fn prepare_child(take_controlling_tty: bool) -> io::Result<()> { 52 | // SAFETY: this is effectively a wrapper to the setsid syscall. 53 | let pid = unsafe { libc::setsid() }; 54 | 55 | // We fork if we are already a group leader, so an error 56 | // here should be impossible. 57 | assert_eq!(pid, process::id() as i32); 58 | 59 | // On some platforms (i.e. aarch64 Linux) TIOCSCTTY is the same type as the second argument, 60 | // but on some it is u64, while the expected type is u32. 61 | // SAFETY: the ioctl should not make any changes to memory, basically a wrapper 62 | // to the syscall. 63 | #[allow(clippy::useless_conversion)] 64 | if take_controlling_tty && unsafe { libc::ioctl(0, libc::TIOCSCTTY.into(), 1) } < 0 { 65 | // This is unfortunate, but we are bound by the Result type pre_exec requires, 66 | // as well as the limitations imposed by this being executed post-fork(). 67 | // Ideally we would return an io::Error of the Other kind so that we could handle 68 | // everything at the same place, but that would require an allocation. 69 | uucore::show_error!( 70 | "failed to set the controlling terminal: {}", 71 | UIoError::from(io::Error::last_os_error()) 72 | ); 73 | 74 | // SAFETY: this is actually safer than calling process::exit(), as that may 75 | // allocate, which is not safe post-fork. 76 | unsafe { libc::_exit(1) }; 77 | } 78 | Ok(()) 79 | } 80 | 81 | pub fn spawn_command( 82 | mut to_run: process::Command, 83 | executable: &OsStr, 84 | wait_child: bool, 85 | ) -> UResult<()> { 86 | let mut child = match to_run.spawn() { 87 | Ok(child) => child, 88 | Err(error) => { 89 | report_failure_to_exec(error, executable, wait_child); 90 | return Ok(()); 91 | } 92 | }; 93 | 94 | if !wait_child { 95 | return Ok(()); 96 | } 97 | 98 | match child.wait() { 99 | Ok(status) => { 100 | uucore::error::set_exit_code(status.code().unwrap()); 101 | Ok(()) 102 | } 103 | Err(error) => { 104 | Err(error.map_err_context(|| format!("failed to wait on PID {}", child.id()))) 105 | } 106 | } 107 | } 108 | } 109 | 110 | #[cfg(target_family = "unix")] 111 | use unix::*; 112 | 113 | #[cfg(target_family = "unix")] 114 | #[uucore::main] 115 | pub fn uumain(args: impl uucore::Args) -> UResult<()> { 116 | let matches: clap::ArgMatches = uu_app().try_get_matches_from(args)?; 117 | 118 | let force_fork = matches.get_flag("fork"); 119 | let wait_child = matches.get_flag("wait"); 120 | let take_controlling_tty = matches.get_flag("ctty"); 121 | 122 | let command: Vec<_> = match matches.get_many::("command") { 123 | Some(v) => v.collect(), 124 | None => return Err(uucore::error::USimpleError::new(1, "no command specified")), 125 | }; 126 | 127 | // We know we have at least one item, as none was 128 | // handled as an error on the match above. 129 | let executable = command[0]; 130 | let arguments = command.get(1..).unwrap_or(&[]); 131 | 132 | let mut to_run = process::Command::new(executable); 133 | to_run.args(arguments.iter()); 134 | 135 | // SAFETY: pre_exec() happens post-fork, so the process can potentially 136 | // be in a broken state; allocations are not safe, and we should exit 137 | // as soon as possible if we cannot go ahead. 138 | unsafe { 139 | to_run.pre_exec(move || prepare_child(take_controlling_tty)); 140 | }; 141 | 142 | if force_fork || already_group_leader() { 143 | spawn_command(to_run, executable, wait_child)?; 144 | } else { 145 | let error = to_run.exec(); 146 | report_failure_to_exec(error, executable, true); 147 | } 148 | 149 | Ok(()) 150 | } 151 | 152 | #[cfg(not(target_family = "unix"))] 153 | #[uucore::main] 154 | pub fn uumain(args: impl uucore::Args) -> UResult<()> { 155 | let _matches: clap::ArgMatches = uu_app().try_get_matches_from(args)?; 156 | 157 | Err(uucore::error::USimpleError::new( 158 | 1, 159 | "`setsid` is unavailable on non-UNIX-like platforms.", 160 | )) 161 | } 162 | 163 | pub fn uu_app() -> Command { 164 | Command::new(uucore::util_name()) 165 | .version(crate_version!()) 166 | .about(ABOUT) 167 | .override_usage(format_usage(USAGE)) 168 | .infer_long_args(true) 169 | .arg( 170 | Arg::new("ctty") 171 | .short('c') 172 | .action(ArgAction::SetTrue) 173 | .value_parser(ValueParser::bool()) 174 | .help("Take the current controlling terminal"), 175 | ) 176 | .arg( 177 | Arg::new("fork") 178 | .short('f') 179 | .action(ArgAction::SetTrue) 180 | .value_parser(ValueParser::bool()) 181 | .long_help("Always create a new process. By default this is only done if we are already a process group lead."), 182 | ) 183 | .arg( 184 | Arg::new("wait") 185 | .short('w') 186 | .action(ArgAction::SetTrue) 187 | .value_parser(ValueParser::bool()) 188 | .help("Wait for the command to finish and exit with its exit code."), 189 | ) 190 | .arg( 191 | Arg::new("command") 192 | .help("Program to be executed, followed by its arguments") 193 | .index(1) 194 | .action(ArgAction::Set) 195 | .trailing_var_arg(true) 196 | .value_parser(ValueParser::os_string()) 197 | .num_args(1..), 198 | ) 199 | } 200 | -------------------------------------------------------------------------------- /src/uu/uuidgen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uu_uuidgen" 3 | version = "0.0.1" 4 | edition = "2021" 5 | 6 | [lib] 7 | path = "src/uuidgen.rs" 8 | 9 | [[bin]] 10 | name = "uuidgen" 11 | path = "src/main.rs" 12 | 13 | [dependencies] 14 | clap = { workspace = true } 15 | rand = { workspace = true } 16 | thiserror = { workspace = true } 17 | uucore = { workspace = true } 18 | uuid = { workspace = true, features = ["v1", "v3", "v4", "v5"] } 19 | 20 | [target.'cfg(target_os = "windows")'.dependencies] 21 | windows = { workspace = true, features = ["Win32_NetworkManagement_IpHelper", "Win32_NetworkManagement_Ndis", "Win32_Networking_WinSock"] } 22 | 23 | [target.'cfg(all(target_family = "unix", not(target_os = "redox")))'.dependencies] 24 | nix = { workspace = true, features = ["net"] } 25 | -------------------------------------------------------------------------------- /src/uu/uuidgen/src/main.rs: -------------------------------------------------------------------------------- 1 | uucore::bin!(uu_uuidgen); 2 | -------------------------------------------------------------------------------- /src/uu/uuidgen/src/uuidgen.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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(target_os = "windows")] 7 | use windows::Win32::{ 8 | Foundation::{ERROR_BUFFER_OVERFLOW, ERROR_SUCCESS}, 9 | NetworkManagement::IpHelper::{ 10 | GetAdaptersAddresses, GAA_FLAG_SKIP_ANYCAST, GAA_FLAG_SKIP_DNS_SERVER, 11 | GAA_FLAG_SKIP_FRIENDLY_NAME, GAA_FLAG_SKIP_MULTICAST, GAA_FLAG_SKIP_UNICAST, 12 | IP_ADAPTER_ADDRESSES_LH, 13 | }, 14 | Networking::WinSock::AF_UNSPEC, 15 | }; 16 | 17 | use clap::{crate_version, Arg, ArgAction, ArgGroup, Command}; 18 | 19 | #[cfg(all(target_family = "unix", not(target_os = "redox")))] 20 | use nix::ifaddrs::getifaddrs; 21 | use uucore::{ 22 | error::{UResult, USimpleError}, 23 | format_usage, help_about, help_usage, 24 | }; 25 | use uuid::Uuid; 26 | 27 | const ABOUT: &str = help_about!("uuidgen.md"); 28 | const USAGE: &str = help_usage!("uuidgen.md"); 29 | 30 | #[uucore::main] 31 | pub fn uumain(args: impl uucore::Args) -> UResult<()> { 32 | let args = uu_app().try_get_matches_from_mut(args)?; 33 | 34 | let md5 = args.get_flag(options::MD5); 35 | let sha1 = args.get_flag(options::SHA1); 36 | 37 | let namespace = args.get_one(options::NAMESPACE); 38 | let name: Option<&String> = args.get_one(options::NAME); 39 | 40 | // https://github.com/clap-rs/clap/issues/1537 41 | if !(md5 || sha1) && (namespace.is_some() || name.is_some()) { 42 | return Err(USimpleError::new( 43 | 1, 44 | "--namespace and --name arguments require either --md5 or --sha1", 45 | )); 46 | } 47 | 48 | if args.get_flag(options::TIME) { 49 | let node_id = get_node_id().unwrap_or_else(|| { 50 | let mut default: [u8; 6] = rand::random(); 51 | default[0] |= 0x01; 52 | default 53 | }); 54 | println!("{:?}", Uuid::now_v1(&node_id)); 55 | } else if md5 || sha1 { 56 | let f = if md5 { Uuid::new_v3 } else { Uuid::new_v5 }; 57 | 58 | println!( 59 | "{:?}", 60 | f( 61 | namespace.expect("namespace arg to be set"), 62 | name.expect("name to be set").as_bytes() 63 | ) 64 | ); 65 | } else { 66 | // Random is the default 67 | println!("{}", Uuid::new_v4()); 68 | } 69 | 70 | Ok(()) 71 | } 72 | 73 | pub fn uu_app() -> Command { 74 | let all_uuid_types = [options::RANDOM, options::TIME, options::MD5, options::SHA1]; 75 | 76 | Command::new(uucore::util_name()) 77 | .version(crate_version!()) 78 | .about(ABOUT) 79 | .override_usage(format_usage(USAGE)) 80 | .infer_long_args(true) 81 | .arg( 82 | Arg::new(options::RANDOM) 83 | .short('r') 84 | .long(options::RANDOM) 85 | .action(ArgAction::SetTrue) 86 | .help("generate random UUID (v4)"), 87 | ) 88 | .arg( 89 | Arg::new(options::TIME) 90 | .short('t') 91 | .long(options::TIME) 92 | .action(ArgAction::SetTrue) 93 | .help("generate time UUID (v1)"), 94 | ) 95 | .arg( 96 | Arg::new(options::NAMESPACE) 97 | .short('n') 98 | .long(options::NAMESPACE) 99 | .action(ArgAction::Set) 100 | .value_parser(namespace_from_str) 101 | .help("namespace for md5/sha1 - one of: @dns @url @oid @x500"), 102 | ) 103 | .arg( 104 | Arg::new(options::NAME) 105 | .short('N') 106 | .long(options::NAME) 107 | .action(ArgAction::Set) 108 | .help("name for md5/sha1"), 109 | ) 110 | .arg( 111 | Arg::new(options::MD5) 112 | .short('m') 113 | .long(options::MD5) 114 | .action(ArgAction::SetTrue) 115 | .requires_all([options::NAMESPACE, options::NAME]) 116 | .help("generate md5 UUID (v3)"), 117 | ) 118 | .arg( 119 | Arg::new(options::SHA1) 120 | .short('s') 121 | .long(options::SHA1) 122 | .action(ArgAction::SetTrue) 123 | .requires_all([options::NAMESPACE, options::NAME]) 124 | .help("generate sha1 UUID (v5)"), 125 | ) 126 | .group(ArgGroup::new("mode").args(all_uuid_types).multiple(false)) 127 | } 128 | 129 | fn namespace_from_str(s: &str) -> Result { 130 | match s { 131 | "@dns" => Ok(Uuid::NAMESPACE_DNS), 132 | "@url" => Ok(Uuid::NAMESPACE_URL), 133 | "@oid" => Ok(Uuid::NAMESPACE_OID), 134 | "@x500" => Ok(Uuid::NAMESPACE_X500), 135 | _ => Err(USimpleError { 136 | code: 1, 137 | message: format!("Invalid namespace {}.", s), 138 | }), 139 | } 140 | } 141 | 142 | mod options { 143 | pub const RANDOM: &str = "random"; 144 | pub const TIME: &str = "time"; 145 | pub const MD5: &str = "md5"; 146 | pub const SHA1: &str = "sha1"; 147 | pub const NAMESPACE: &str = "namespace"; 148 | pub const NAME: &str = "name"; 149 | } 150 | 151 | #[cfg(target_os = "windows")] 152 | fn get_node_id() -> Option<[u8; 6]> { 153 | unsafe { 154 | // Skip everything we can - we are only interested in PhysicalAddress 155 | let flags = GAA_FLAG_SKIP_UNICAST 156 | | GAA_FLAG_SKIP_ANYCAST 157 | | GAA_FLAG_SKIP_MULTICAST 158 | | GAA_FLAG_SKIP_DNS_SERVER 159 | | GAA_FLAG_SKIP_FRIENDLY_NAME; 160 | 161 | let mut size = 0; 162 | let ret = GetAdaptersAddresses(AF_UNSPEC.0 as u32, flags, None, None, &mut size); 163 | if ret != ERROR_BUFFER_OVERFLOW.0 { 164 | return None; 165 | } 166 | 167 | let mut buf = vec![0u8; size as usize]; 168 | let ret = GetAdaptersAddresses( 169 | AF_UNSPEC.0 as u32, 170 | flags, 171 | None, 172 | Some(buf.as_mut_ptr() as *mut IP_ADAPTER_ADDRESSES_LH), 173 | &mut size, 174 | ); 175 | if ret != ERROR_SUCCESS.0 { 176 | return None; 177 | } 178 | 179 | // SAFETY: GetAdaptersAddresses returns ERROR_NO_DATA error if it's zero len 180 | let mut adapter_ptr = buf.as_ptr() as *const IP_ADAPTER_ADDRESSES_LH; 181 | while !adapter_ptr.is_null() { 182 | let adapter = adapter_ptr.read(); 183 | 184 | if adapter.PhysicalAddressLength == 6 { 185 | return Some(adapter.PhysicalAddress[0..6].try_into().unwrap()); 186 | } 187 | 188 | adapter_ptr = adapter.Next; 189 | } 190 | } 191 | None 192 | } 193 | 194 | #[cfg(all(target_family = "unix", not(target_os = "redox")))] 195 | fn get_node_id() -> Option<[u8; 6]> { 196 | getifaddrs().ok().and_then(|iflist| { 197 | iflist 198 | .filter_map(|intf| intf.address?.as_link_addr()?.addr()) 199 | .find(|mac| mac.iter().any(|x| *x != 0)) 200 | }) 201 | } 202 | 203 | #[cfg(not(any( 204 | target_os = "windows", 205 | all(target_family = "unix", not(target_os = "redox")) 206 | )))] 207 | fn get_node_id() -> Option<[u8; 6]> { 208 | None 209 | } 210 | -------------------------------------------------------------------------------- /src/uu/uuidgen/uuidgen.md: -------------------------------------------------------------------------------- 1 | # uuidgen 2 | ``` 3 | uuidgen [options] 4 | ``` 5 | 6 | 7 | Create UUIDs: 8 | - v1: time + MAC address (-t/--time) 9 | - v3: namespace + name, MD5 based (-m/--md5) 10 | - v4: random (-r/--random) 11 | - v5: namespace + name, SHA1 based (-s/--sha1) 12 | -------------------------------------------------------------------------------- /tests/by-util/test_blockdev.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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 crate::common::util::TestScenario; 7 | 8 | #[test] 9 | fn test_invalid_arg() { 10 | new_ucmd!().arg("--definitely-invalid").fails().code_is(1); 11 | } 12 | 13 | #[test] 14 | fn test_report_mutually_exclusive_with_others() { 15 | new_ucmd!() 16 | .arg("--report") 17 | .arg("--getalignoff") 18 | .arg("/foo") 19 | .fails() 20 | .code_is(1) 21 | .stderr_contains("the argument '--report' cannot be used with '--getalignoff'"); 22 | } 23 | 24 | #[cfg(target_os = "linux")] 25 | mod linux { 26 | use crate::common::util::TestScenario; 27 | use regex::Regex; 28 | 29 | #[test] 30 | fn test_fails_on_first_error() { 31 | new_ucmd!() 32 | .arg("-v") 33 | .arg("--getalignoff") 34 | .arg("--getbsz") 35 | .arg("/dev/null") 36 | .fails() 37 | .code_is(1) 38 | .stdout_is("get alignment offset in bytes failed.\n") 39 | .stderr_contains("Inappropriate ioctl for device"); 40 | } 41 | 42 | #[test] 43 | fn test_report_continues_on_errors() { 44 | new_ucmd!() 45 | .arg("--report") 46 | .arg("/dev/null") 47 | .arg("/non/existing") 48 | .fails() 49 | .code_is(1) 50 | .stderr_matches( 51 | &Regex::new("(?ms)Inappropriate ioctl for device.*No such file or directory") 52 | .unwrap(), 53 | ); 54 | } 55 | } 56 | 57 | #[cfg(not(target_os = "linux"))] 58 | mod non_linux { 59 | use crate::common::util::TestScenario; 60 | 61 | #[test] 62 | fn test_fails_on_unsupported_platforms() { 63 | new_ucmd!() 64 | .arg("--report") 65 | .arg("/dev/null") 66 | .fails() 67 | .code_is(1) 68 | .stderr_is("blockdev: `blockdev` is available only on Linux.\n"); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /tests/by-util/test_ctrlaltdel.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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(target_os = "linux")] 7 | use crate::common::util::TestScenario; 8 | 9 | #[test] 10 | #[cfg(target_os = "linux")] 11 | fn test_invalid_arg() { 12 | new_ucmd!().arg("--definitely-invalid").fails().code_is(1); 13 | } 14 | -------------------------------------------------------------------------------- /tests/by-util/test_dmesg.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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 crate::common::util::TestScenario; 6 | 7 | #[test] 8 | fn test_invalid_arg() { 9 | new_ucmd!().arg("--definitely-invalid").fails().code_is(1); 10 | } 11 | 12 | #[test] 13 | fn test_kmsg_nonexistent_file() { 14 | new_ucmd!() 15 | .arg("--kmsg-file") 16 | .arg("definitely-nonexistent-file") 17 | .fails() 18 | .code_is(1) 19 | .no_stdout() 20 | .stderr_is("dmesg: cannot open definitely-nonexistent-file: No such file or directory\n"); 21 | } 22 | 23 | #[test] 24 | fn test_kmsg_json() { 25 | new_ucmd!() 26 | .arg("--kmsg-file") 27 | .arg("kmsg.input") 28 | .arg("--json") 29 | .succeeds() 30 | .no_stderr() 31 | .stdout_is_templated_fixture("test_kmsg_json.expected", &[("\r\n", "\n")]); 32 | } 33 | 34 | #[test] 35 | fn test_kmsg_time_format_delta() { 36 | test_kmsg_time_format("delta"); 37 | } 38 | 39 | #[test] 40 | fn test_kmsg_time_format_reltime() { 41 | test_kmsg_time_format("reltime"); 42 | } 43 | 44 | #[test] 45 | fn test_kmsg_time_format_ctime() { 46 | test_kmsg_time_format("ctime"); 47 | } 48 | 49 | #[test] 50 | fn test_kmsg_time_format_notime() { 51 | test_kmsg_time_format("notime"); 52 | } 53 | 54 | #[test] 55 | fn test_kmsg_time_format_iso() { 56 | test_kmsg_time_format("iso"); 57 | } 58 | 59 | #[test] 60 | fn test_kmsg_time_format_raw() { 61 | test_kmsg_time_format("raw"); 62 | } 63 | 64 | fn test_kmsg_time_format(format: &str) { 65 | let time_format_arg = format!("--time-format={format}"); 66 | let expected_output = format!("test_kmsg_time_format_{format}.expected"); 67 | new_ucmd!() 68 | .arg("--kmsg-file") 69 | .arg("kmsg.input.1") 70 | .arg(time_format_arg) 71 | .succeeds() 72 | .no_stderr() 73 | .stdout_is_templated_fixture(expected_output, &[("\r\n", "\n")]); 74 | } 75 | 76 | #[test] 77 | fn test_invalid_time_format() { 78 | new_ucmd!() 79 | .arg("--time-format=definitely-invalid") 80 | .fails() 81 | .code_is(1) 82 | .stderr_only("dmesg: unknown time format: definitely-invalid\n"); 83 | } 84 | 85 | #[test] 86 | fn test_filter_facility() { 87 | let facilities = [ 88 | "kern", "user", "mail", "daemon", "auth", "syslog", "lpr", "news", "uucp", "cron", 89 | "authpriv", "ftp", "local0", "local1", "local2", "local3", "local4", "local5", "local6", 90 | "local7", 91 | ]; 92 | for facility in facilities { 93 | let facility_filter_arg = format!("--facility={facility}"); 94 | let mut cmd = new_ucmd!(); 95 | let result = cmd 96 | .arg("--kmsg-file") 97 | .arg("kmsg.input") 98 | .arg(facility_filter_arg) 99 | .succeeds(); 100 | let stdout = result.no_stderr().stdout_str(); 101 | assert_eq!(stdout.lines().count(), 8); 102 | let expected = format!("LOG_{}", facility.to_uppercase()); 103 | stdout 104 | .lines() 105 | .for_each(|line| assert!(line.contains(&expected))); 106 | } 107 | } 108 | 109 | #[test] 110 | fn test_filter_levels() { 111 | let levels = [ 112 | "emerg", "alert", "crit", "err", "warn", "notice", "info", "debug", 113 | ]; 114 | for level in levels { 115 | let level_filter_arg = format!("--level={level}"); 116 | let mut cmd = new_ucmd!(); 117 | let result = cmd 118 | .arg("--kmsg-file") 119 | .arg("kmsg.input") 120 | .arg(level_filter_arg) 121 | .succeeds(); 122 | let stdout = result.no_stderr().stdout_str(); 123 | assert_eq!(stdout.lines().count(), 20); 124 | let expected = format!("LOG_{}", level.to_uppercase()); 125 | stdout 126 | .lines() 127 | .for_each(|line| assert!(line.contains(&expected))); 128 | } 129 | } 130 | 131 | #[test] 132 | fn test_invalid_facility_argument() { 133 | new_ucmd!() 134 | .arg("--facility=definitely-invalid") 135 | .fails() 136 | .code_is(1) 137 | .stderr_only("dmesg: unknown facility 'definitely-invalid'\n"); 138 | } 139 | 140 | #[test] 141 | fn test_invalid_level_argument() { 142 | new_ucmd!() 143 | .arg("--level=definitely-invalid") 144 | .fails() 145 | .code_is(1) 146 | .stderr_only("dmesg: unknown level 'definitely-invalid'\n"); 147 | } 148 | 149 | #[test] 150 | fn test_filter_multiple() { 151 | let mut cmd = new_ucmd!(); 152 | let result = cmd 153 | .arg("--kmsg-file") 154 | .arg("kmsg.input") 155 | .arg("--facility=kern,user") 156 | .arg("--level=emerg,alert") 157 | .succeeds(); 158 | let stdout = result.no_stderr().stdout_str(); 159 | assert_eq!(stdout.lines().count(), 4); 160 | stdout.lines().for_each(|line| { 161 | assert!( 162 | (line.contains("LOG_KERN") || line.contains("LOG_USER")) 163 | && (line.contains("LOG_EMERG") || line.contains("LOG_ALERT")) 164 | ) 165 | }); 166 | } 167 | 168 | #[test] 169 | fn test_since_until() { 170 | new_ucmd!() 171 | .arg("--kmsg-file") 172 | .arg("kmsg.input") 173 | .arg("--since=2024-11-19 17:47:32 +0700") 174 | .arg("--until=2024-11-19 18:55:52 +0700") 175 | .succeeds() 176 | .no_stderr() 177 | .stdout_is_templated_fixture("test_since_until.expected", &[("\r\n", "\n")]); 178 | } 179 | 180 | #[test] 181 | fn test_since_until_invalid_time() { 182 | let options = ["--since", "--until"]; 183 | for option in options { 184 | new_ucmd!() 185 | .arg(format!("{option}=definitely-invalid")) 186 | .fails() 187 | .stderr_only("dmesg: invalid time value \"definitely-invalid\"\n"); 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /tests/by-util/test_fsfreeze.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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 crate::common::util::TestScenario; 7 | 8 | #[test] 9 | fn test_invalid_arg() { 10 | new_ucmd!().arg("--definitely-invalid").fails().code_is(1); 11 | } 12 | 13 | #[test] 14 | fn test_operations_mutually_exclusive() { 15 | new_ucmd!() 16 | .arg("--freeze") 17 | .arg("--unfreeze") 18 | .arg("/foo") 19 | .fails() 20 | .code_is(1) 21 | .stderr_contains("the argument '--freeze' cannot be used with '--unfreeze'"); 22 | } 23 | 24 | #[cfg(target_os = "linux")] 25 | mod linux { 26 | use crate::common::util::TestScenario; 27 | 28 | #[test] 29 | fn test_fails_on_non_existing_path() { 30 | new_ucmd!() 31 | .arg("--unfreeze") 32 | .arg("/non/existing") 33 | .fails() 34 | .code_is(1) 35 | .stderr_contains("No such file or directory"); 36 | } 37 | 38 | #[test] 39 | fn test_fails_on_non_directory() { 40 | new_ucmd!() 41 | .arg("--unfreeze") 42 | .arg("/dev/null") 43 | .fails() 44 | .code_is(1) 45 | .stderr_contains("not a directory"); 46 | } 47 | } 48 | 49 | #[cfg(not(target_os = "linux"))] 50 | mod non_linux { 51 | use crate::common::util::TestScenario; 52 | 53 | #[test] 54 | fn test_fails_on_unsupported_platforms() { 55 | new_ucmd!() 56 | .arg("--unfreeze") 57 | .arg("/non/existing") 58 | .fails() 59 | .code_is(1) 60 | .stderr_is("fsfreeze: `fsfreeze` is available only on Linux.\n"); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /tests/by-util/test_last.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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 (words) symdir somefakedir 6 | 7 | #[cfg(unix)] 8 | use crate::common::util::TestScenario; 9 | 10 | #[cfg(unix)] 11 | use regex::Regex; 12 | 13 | #[test] 14 | #[cfg(unix)] 15 | fn test_invalid_arg() { 16 | new_ucmd!().arg("--definitely-invalid").fails().code_is(1); 17 | } 18 | 19 | #[test] 20 | #[cfg(all(unix, not(target_os = "macos")))] 21 | fn test_last() { 22 | let regex = Regex::new("still running|still logged in").unwrap(); 23 | TestScenario::new(util_name!()) 24 | .ucmd() 25 | .succeeds() 26 | .stdout_matches(®ex); 27 | } 28 | 29 | #[test] 30 | #[cfg(all(unix, not(target_os = "macos")))] 31 | fn test_limit_arg() { 32 | let line_check = |input: &str| input.lines().count() == 3; 33 | new_ucmd!() 34 | .arg("--limit=1") 35 | .succeeds() 36 | .stdout_str_check(line_check); 37 | } 38 | 39 | #[test] 40 | // The -x flag generally adds two rows "shutdown" and "runlevel" 41 | // "shutdown" cannot be checked for since not every machine will have shutdown 42 | // "runlevel" only makes sense for Linux systems, so only Linux is included for 43 | // this test. 44 | #[cfg(target_os = "linux")] 45 | #[ignore = "fails on Arch Linux"] 46 | fn test_system_arg() { 47 | new_ucmd!().arg("-x").succeeds().stdout_contains("runlevel"); 48 | } 49 | 50 | #[test] 51 | #[cfg(unix)] 52 | fn test_timestamp_format_no_time() { 53 | let regex = Regex::new(" [0-9][0-9]:[0-9][0-9] ").unwrap(); 54 | new_ucmd!() 55 | .arg("--time-format=notime") 56 | .succeeds() 57 | .stdout_does_not_match(®ex); 58 | } 59 | 60 | #[test] 61 | #[cfg(all(unix, not(target_os = "macos")))] 62 | fn test_timestamp_format_short() { 63 | let regex = Regex::new(" [0-9][0-9]:[0-9][0-9] ").unwrap(); 64 | new_ucmd!() 65 | .arg("--time-format=short") 66 | .succeeds() 67 | .stdout_matches(®ex); 68 | } 69 | 70 | #[test] 71 | #[cfg(all(unix, not(target_os = "macos")))] 72 | fn test_timestamp_format_full() { 73 | let regex = Regex::new(" [0-9][0-9]:[0-9][0-9]:[0-9][0-9] ").unwrap(); 74 | new_ucmd!() 75 | .arg("--time-format=full") 76 | .succeeds() 77 | .stdout_matches(®ex); 78 | } 79 | 80 | // 2024-07-11T19:30:44+08:00 81 | #[test] 82 | #[cfg(all(unix, not(target_os = "macos")))] 83 | fn test_timestamp_format_iso() { 84 | let regex = 85 | Regex::new(" [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9]") 86 | .unwrap(); 87 | new_ucmd!() 88 | .arg("--time-format=iso") 89 | .succeeds() 90 | .stdout_matches(®ex); 91 | } 92 | 93 | #[test] 94 | #[cfg(all(unix, not(target_os = "macos")))] 95 | fn test_short_invalid_utmp_file() { 96 | let (at, mut ucmd) = at_and_ucmd!(); 97 | let file = "testfile"; 98 | // Random bytes 99 | let data = [ 100 | 4, 5, 6, 16, 8, 13, 2, 12, 5, 3, 11, 5, 1, 13, 1, 1, 0, 9, 5, 5, 2, 8, 4, 101 | ]; 102 | at.write_bytes(file, &data); 103 | 104 | let regex = Regex::new(r"\n\S*\sbegins\s*(Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s*(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s*[0-9][0-9]?\s*[0-9][0-9]:[0-9][0-9]:[0-9][0-9]\s*[0-9]*") 105 | .unwrap(); 106 | 107 | ucmd.arg(format!("--file={file}")) 108 | .succeeds() 109 | .stdout_matches(®ex); 110 | } 111 | 112 | #[test] 113 | #[cfg(all(unix, not(target_os = "macos"), not(target_os = "openbsd")))] 114 | fn test_display_hostname_last_column() { 115 | let output_expected = vec![ 116 | "ferris tty2 Sat Mar 8 16:29 still logged in :0", 117 | "ferris tty2 Sat Mar 8 16:24 - 16:29 (00:04) :0", 118 | "reboot system boot Sat Mar 8 16:24 still running 6.8.0-55-generic", 119 | ]; 120 | 121 | let hostlast_arg = "--hostlast"; 122 | let result = new_ucmd!() 123 | .arg("--file") 124 | .arg("last.input.1") 125 | .arg(hostlast_arg) 126 | .arg("-n") 127 | .arg("3") 128 | .succeeds(); 129 | 130 | // Keep only the three 1st lines to compare easier with the expected output (so without the information about the begin date of file) 131 | let output_result: Vec<_> = result.stdout_str().lines().take(3).collect(); 132 | 133 | assert_eq!(output_expected, output_result); 134 | } 135 | -------------------------------------------------------------------------------- /tests/by-util/test_lscpu.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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 crate::common::util::TestScenario; 7 | 8 | #[test] 9 | fn test_invalid_arg() { 10 | new_ucmd!().arg("--definitely-invalid").fails().code_is(1); 11 | } 12 | 13 | #[test] 14 | #[ignore = "not yet implemented"] 15 | fn test_hex() { 16 | new_ucmd!().arg("--hex").succeeds().stdout_contains("0x"); 17 | } 18 | 19 | #[test] 20 | #[cfg(target_os = "linux")] 21 | fn test_json() { 22 | let res = new_ucmd!().arg("--json").succeeds(); 23 | 24 | let stdout = res.no_stderr().stdout_str(); 25 | assert!(stdout.starts_with("{")); 26 | assert!(stdout.ends_with("}\n")); 27 | 28 | res.stdout_contains("\"lscpu\": [") 29 | .stdout_contains("\"field\": \"Architecture\"") 30 | .stdout_contains("\"field\": \"CPU(s)\"") 31 | .stdout_contains("\"children\": ["); 32 | } 33 | 34 | #[test] 35 | #[cfg(target_os = "linux")] 36 | fn test_output() { 37 | let res = new_ucmd!().succeeds(); 38 | let stdout = res.no_stderr().stdout_str(); 39 | 40 | // Non-exhaustive list of fields we expect 41 | // This also checks that fields which should be indented, are indeed indented as excepted 42 | assert!(stdout.contains("Architecture:")); 43 | assert!(stdout.contains("\n Address sizes:")); 44 | assert!(stdout.contains("\n Byte Order:")); 45 | assert!(stdout.contains("\nCPU(s):")); 46 | assert!(stdout.contains("\nVendor ID:")); 47 | assert!(stdout.contains("\n Model name:")); 48 | assert!(stdout.contains("\n CPU Family:")); 49 | } 50 | -------------------------------------------------------------------------------- /tests/by-util/test_lslocks.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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(target_os = "linux")] 7 | use crate::common::util::TestScenario; 8 | 9 | #[test] 10 | #[cfg(target_os = "linux")] 11 | fn test_column_headers() { 12 | let res = new_ucmd!().succeeds(); 13 | let stdout = res.no_stderr().stdout_str(); 14 | 15 | let header_line = stdout.lines().next().unwrap(); 16 | let cols: Vec<_> = header_line.split_whitespace().collect(); 17 | 18 | assert_eq!( 19 | cols, 20 | ["COMMAND", "PID", "TYPE", "SIZE", "MODE", "M", "START", "END", "PATH"] 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /tests/by-util/test_mcookie.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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::io::Write; 7 | 8 | use tempfile::NamedTempFile; 9 | 10 | use crate::common::util::TestScenario; 11 | 12 | #[test] 13 | fn test_invalid_arg() { 14 | new_ucmd!().arg("--definitely-invalid").fails().code_is(1); 15 | } 16 | 17 | #[test] 18 | fn test_basic_usage() { 19 | let res = new_ucmd!().succeeds(); 20 | 21 | let stdout = res.no_stderr().stdout_str(); 22 | 23 | // Expect 32 hex characters for the MD5 hash (after trimming the newline) 24 | assert_eq!(stdout.trim_end().len(), 32); 25 | assert!(stdout.trim_end().chars().all(|c| c.is_ascii_hexdigit())); 26 | } 27 | 28 | #[test] 29 | fn test_verbose() { 30 | let res = new_ucmd!().arg("--verbose").succeeds(); 31 | res.stderr_contains("Got 128 bytes from randomness source"); 32 | } 33 | 34 | #[test] 35 | fn test_seed_files_and_max_size_raw() { 36 | let mut file1 = NamedTempFile::new().unwrap(); 37 | const CONTENT1: &str = "Some seed data"; 38 | file1.write_all(CONTENT1.as_bytes()).unwrap(); 39 | 40 | let mut file2 = NamedTempFile::new().unwrap(); 41 | const CONTENT2: [u8; 2048] = [1; 2048]; 42 | file2.write_all(&CONTENT2).unwrap(); 43 | 44 | let res = new_ucmd!() 45 | .arg("--verbose") 46 | .arg("-f") 47 | .arg(file1.path()) 48 | .arg("-f") 49 | .arg(file2.path()) 50 | .arg("-m") 51 | .arg("1337") 52 | .succeeds(); 53 | 54 | res.stderr_contains(format!( 55 | "Got {} bytes from {}", 56 | CONTENT1.len(), 57 | file1.path().to_str().unwrap() 58 | )); 59 | 60 | // Ensure we only read up to the limit of bytes, despite the file being bigger 61 | res.stderr_contains(format!( 62 | "Got 1337 bytes from {}", 63 | file2.path().to_str().unwrap() 64 | )); 65 | } 66 | 67 | #[test] 68 | #[cfg(unix)] // Character devices like /dev/zero are a Unix concept 69 | fn test_char_device_input() { 70 | let res_no_limit = new_ucmd!().arg("-f").arg("/dev/zero").succeeds(); 71 | 72 | let stdout_no_limit = res_no_limit.no_stderr().stdout_str().trim_end(); 73 | assert_eq!(stdout_no_limit.len(), 32); 74 | assert!(stdout_no_limit.chars().all(|c| c.is_ascii_hexdigit())); 75 | 76 | let res_verbose = new_ucmd!() 77 | .arg("--verbose") 78 | .arg("-f") 79 | .arg("/dev/zero") 80 | .succeeds(); 81 | 82 | res_verbose.stderr_contains("Got 4096 bytes from /dev/zero"); 83 | res_verbose.stderr_contains("Got 128 bytes from randomness source"); // Ensure internal randomness is still added 84 | 85 | let stdout_verbose = res_verbose.stdout_str().trim_end(); 86 | assert_eq!(stdout_verbose.len(), 32); 87 | assert!(stdout_verbose.chars().all(|c| c.is_ascii_hexdigit())); 88 | 89 | assert_ne!(stdout_no_limit, stdout_verbose); 90 | } 91 | 92 | #[test] 93 | fn test_seed_files_and_max_size_human_readable() { 94 | let mut file = NamedTempFile::new().unwrap(); 95 | const CONTENT: [u8; 4096] = [1; 4096]; 96 | file.write_all(&CONTENT).unwrap(); 97 | 98 | let res = new_ucmd!() 99 | .arg("--verbose") 100 | .arg("-f") 101 | .arg(file.path()) 102 | .arg("-m") 103 | .arg("2KiB") 104 | .succeeds(); 105 | 106 | // Ensure we only read up to 2KiB (2048 bytes) 107 | res.stderr_contains(format!( 108 | "Got 2048 bytes from {}", 109 | file.path().to_str().unwrap() 110 | )); 111 | } 112 | 113 | #[test] 114 | fn test_invalid_size_format() { 115 | let file = NamedTempFile::new().unwrap(); 116 | 117 | let res = new_ucmd!() 118 | .arg("-f") 119 | .arg(file.path()) 120 | .arg("-m") 121 | .arg("invalid") 122 | .fails(); 123 | 124 | res.stderr_contains("Failed to parse max-size value"); 125 | } 126 | 127 | #[test] 128 | fn test_stdin_input() { 129 | const INPUT_DATA: &str = "some test data for stdin"; 130 | let res = new_ucmd!() 131 | .arg("--verbose") 132 | .arg("-f") 133 | .arg("-") 134 | .pipe_in(INPUT_DATA) 135 | .succeeds(); 136 | 137 | res.stderr_contains(format!("Got {} bytes from stdin", INPUT_DATA.len())); 138 | 139 | let stdout = res.stdout_str().trim_end(); 140 | assert_eq!(stdout.len(), 32); 141 | assert!(stdout.chars().all(|c| c.is_ascii_hexdigit())); 142 | } 143 | 144 | #[test] 145 | fn test_not_existing_file() { 146 | let mut file1 = NamedTempFile::new().unwrap(); 147 | const CONTENT1: &str = "Some seed data"; 148 | file1.write_all(CONTENT1.as_bytes()).unwrap(); 149 | 150 | let file_not_existing = file1.path().to_str().unwrap().to_owned() + "_extra"; 151 | 152 | let mut file2 = NamedTempFile::new().unwrap(); 153 | const CONTENT2: [u8; 2048] = [1; 2048]; 154 | file2.write_all(&CONTENT2).unwrap(); 155 | 156 | let res = new_ucmd!() 157 | .arg("--verbose") 158 | .arg("-f") 159 | .arg(file1.path()) 160 | .arg("-f") 161 | .arg(&file_not_existing) 162 | .arg("-f") 163 | .arg(file2.path()) 164 | .succeeds(); 165 | 166 | res.stderr_contains(format!( 167 | "Got {} bytes from {}", 168 | CONTENT1.len(), 169 | file1.path().to_str().unwrap() 170 | )); 171 | 172 | res.stderr_contains(format!("mcookie: cannot open {}", file_not_existing)); 173 | 174 | // Ensure we only read up to the limit of bytes, despite the file being bigger 175 | res.stderr_contains(format!( 176 | "Got 2048 bytes from {}", 177 | file2.path().to_str().unwrap() 178 | )); 179 | } 180 | 181 | #[test] 182 | fn test_max_size_limits() { 183 | let mut file = NamedTempFile::new().unwrap(); 184 | const CONTENT: [u8; 5500] = [1; 5500]; 185 | file.write_all(&CONTENT).unwrap(); 186 | 187 | let res_default = new_ucmd!() 188 | .arg("--verbose") 189 | .arg("-f") 190 | .arg(file.path()) 191 | .succeeds(); 192 | 193 | // Ensure we only read up to 4096 bytes 194 | res_default.stderr_contains(format!( 195 | "Got 4096 bytes from {}", 196 | file.path().to_str().unwrap() 197 | )); 198 | 199 | let res_zero = new_ucmd!() 200 | .arg("--verbose") 201 | .arg("-f") 202 | .arg(file.path()) 203 | .arg("-m") 204 | .arg("0") 205 | .succeeds(); 206 | 207 | // Ensure we read up 4096 bytes 208 | res_zero.stderr_contains(format!( 209 | "Got 4096 bytes from {}", 210 | file.path().to_str().unwrap() 211 | )); 212 | } 213 | -------------------------------------------------------------------------------- /tests/by-util/test_mesg.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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 crate::common::util::TestScenario; 7 | 8 | #[test] 9 | fn test_invalid_verb() { 10 | new_ucmd!().arg("foo").fails().code_is(1); 11 | } 12 | 13 | #[test] 14 | #[cfg(target_family = "unix")] 15 | fn test_no_terminal() { 16 | for args in &[vec![], vec!["y"], vec!["n"]] { 17 | new_ucmd!() 18 | .args(args) 19 | .fails() 20 | .code_is(2) 21 | .stderr_contains("stdin/stdout/stderr is not a terminal"); 22 | } 23 | } 24 | 25 | #[cfg(not(target_family = "unix"))] 26 | mod non_unix { 27 | use crate::common::util::TestScenario; 28 | 29 | #[test] 30 | fn test_fails_on_unsupported_platforms() { 31 | new_ucmd!() 32 | .fails() 33 | .code_is(1) 34 | .stderr_is("mesg: `mesg` is available only on Unix platforms.\n"); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/by-util/test_mountpoint.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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 crate::common::util::TestScenario; 7 | 8 | #[test] 9 | fn test_invalid_arg() { 10 | new_ucmd!().arg("--definitely-invalid").fails().code_is(1); 11 | } 12 | -------------------------------------------------------------------------------- /tests/by-util/test_renice.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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 (words) symdir somefakedir 6 | 7 | use crate::common::util::TestScenario; 8 | 9 | #[test] 10 | fn test_invalid_arg() { 11 | new_ucmd!().arg("--definitely-invalid").fails().code_is(1); 12 | } 13 | -------------------------------------------------------------------------------- /tests/by-util/test_rev.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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 crate::common::util::TestScenario; 7 | 8 | #[test] 9 | fn test_invalid_arg() { 10 | new_ucmd!().arg("--definitely-invalid").fails().code_is(1); 11 | } 12 | 13 | #[test] 14 | fn test_piped_in_data() { 15 | new_ucmd!().pipe_in("a test").succeeds().stdout_is("tset a"); 16 | } 17 | 18 | #[test] 19 | fn test_existing_file() { 20 | let (at, mut ucmd) = at_and_ucmd!(); 21 | 22 | at.write("a.txt", "line A\nline B"); 23 | 24 | ucmd.arg("a.txt").succeeds().stdout_is("A enil\nB enil"); 25 | } 26 | 27 | #[test] 28 | fn test_zero() { 29 | let (at, mut ucmd) = at_and_ucmd!(); 30 | 31 | at.write("a.txt", "line A\0line B"); 32 | 33 | ucmd.arg("a.txt") 34 | .arg("--zero") 35 | .succeeds() 36 | .stdout_is("A enil\0B enil"); 37 | } 38 | 39 | #[test] 40 | fn test_multiple_files() { 41 | let (at, mut ucmd) = at_and_ucmd!(); 42 | 43 | at.write("a.txt", "file A\n"); 44 | at.write("b.txt", "file B\n"); 45 | 46 | ucmd.args(&["a.txt", "b.txt"]) 47 | .succeeds() 48 | .stdout_is("A elif\nB elif\n"); 49 | } 50 | 51 | #[test] 52 | fn test_empty_file() { 53 | let (at, mut ucmd) = at_and_ucmd!(); 54 | 55 | at.touch("empty.txt"); 56 | 57 | ucmd.arg("empty.txt").succeeds().no_output(); 58 | } 59 | 60 | #[test] 61 | fn test_non_existing_file() { 62 | new_ucmd!() 63 | .arg("non_existing_file") 64 | .fails() 65 | .code_is(1) 66 | .no_stdout() 67 | .stderr_contains("cannot open non_existing_file: No such file or directory"); 68 | } 69 | 70 | #[test] 71 | fn test_non_existing_and_existing_file() { 72 | let (at, mut ucmd) = at_and_ucmd!(); 73 | 74 | at.write("a.txt", "file A"); 75 | 76 | ucmd.arg("non_existing_file") 77 | .arg("a.txt") 78 | .fails() 79 | .code_is(1) 80 | .stderr_contains("cannot open non_existing_file: No such file or directory") 81 | .stdout_is("A elif"); 82 | } 83 | -------------------------------------------------------------------------------- /tests/by-util/test_setsid.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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(target_family = "unix")] 7 | mod unix { 8 | use crate::common::util::{TestScenario, UCommand, TESTS_BINARY}; 9 | 10 | #[test] 11 | fn test_invalid_arg() { 12 | new_ucmd!().arg("--definitely-invalid").fails().code_is(1); 13 | 14 | new_ucmd!() 15 | .arg("-f") 16 | .fails() 17 | .code_is(1) 18 | .stderr_is("setsid: no command specified\n"); 19 | } 20 | 21 | #[test] 22 | fn fork_isolates_child_exit_code() { 23 | new_ucmd!() 24 | .arg("-f") 25 | .arg("/usr/bin/false") 26 | .succeeds() 27 | .no_output(); 28 | } 29 | 30 | #[test] 31 | fn non_fork_returns_child_exit_code() { 32 | new_ucmd!() 33 | .arg("/usr/bin/false") 34 | .fails() 35 | .code_is(1) 36 | .no_output(); 37 | } 38 | 39 | #[test] 40 | fn fork_wait_returns_child_exit_code() { 41 | new_ucmd!() 42 | .arg("-f") 43 | .arg("-w") 44 | .arg("/usr/bin/false") 45 | .fails() 46 | .code_is(1) 47 | .no_output(); 48 | } 49 | 50 | #[test] 51 | fn non_fork_returns_not_found_error() { 52 | new_ucmd!() 53 | .arg("/usr/bin/this-tool-does-not-exist-hopefully") 54 | .fails() 55 | .code_is(127) 56 | .stderr_is("setsid: failed to execute /usr/bin/this-tool-does-not-exist-hopefully: No such file or directory\n"); 57 | } 58 | 59 | #[test] 60 | fn non_fork_on_non_executable_returns_permission_denied_error() { 61 | new_ucmd!() 62 | .arg("/etc/passwd") 63 | .fails() 64 | .code_is(126) 65 | .stderr_is("setsid: failed to execute /etc/passwd: Permission denied\n"); 66 | } 67 | 68 | #[test] 69 | fn fork_isolates_not_found_error() { 70 | new_ucmd!() 71 | .arg("-f") 72 | .arg("/usr/bin/this-tool-does-not-exist-hopefully") 73 | .succeeds(); 74 | // no test for output, as it's a race whether the not found error gets printed 75 | // quickly enough, potential flakyness 76 | } 77 | 78 | #[test] 79 | fn unprivileged_user_cannot_steal_controlling_tty() { 80 | let shell_cmd = 81 | format!("{TESTS_BINARY} setsid -w -c {TESTS_BINARY} setsid -w -c /b/usrin/true"); 82 | UCommand::new() 83 | .terminal_simulation(true) 84 | .arg(&shell_cmd) 85 | .fails() 86 | .code_is(1) 87 | .no_stdout() 88 | .stderr_is("setsid: failed to set the controlling terminal: Permission denied\r\n"); 89 | } 90 | 91 | #[cfg(target_os = "linux")] 92 | #[test] 93 | fn unprivileged_user_can_take_new_controlling_tty() { 94 | let shell_cmd = format!( 95 | "/usr/bin/cat /proc/self/stat; {TESTS_BINARY} setsid -w -c /usr/bin/cat /proc/self/stat" 96 | ); 97 | 98 | let cmd_result = UCommand::new() 99 | .terminal_simulation(true) 100 | .arg(&shell_cmd) 101 | .succeeds(); 102 | 103 | let output = cmd_result.code_is(0).no_stderr().stdout_str(); 104 | 105 | // /proc/self/stat format has controlling terminal as the 7th 106 | // space-separated item; if we managed to change the controlling 107 | // terminal, we should see a difference there 108 | let (before, after) = output 109 | .split_once('\n') 110 | .expect("expected 2 lines of output at least"); 111 | let before = before 112 | .split_whitespace() 113 | .nth(6) 114 | .expect("unexpected stat format"); 115 | let after = after 116 | .split_whitespace() 117 | .nth(6) 118 | .expect("unexpected stat format"); 119 | 120 | assert_ne!(before, after); 121 | } 122 | 123 | #[cfg(target_os = "linux")] 124 | #[test] 125 | fn setsid_takes_session_leadership() { 126 | let shell_cmd = format!( 127 | "/usr/bin/cat /proc/self/stat; {TESTS_BINARY} setsid /usr/bin/cat /proc/self/stat" 128 | ); 129 | 130 | let cmd_result = UCommand::new() 131 | .terminal_simulation(true) 132 | .arg(&shell_cmd) 133 | .succeeds(); 134 | 135 | let output = cmd_result.no_stderr().stdout_str(); 136 | 137 | // /proc/self/stat format has session ID as the 6th space-separated 138 | // item; if we managed to get session leadership, we should see a 139 | // difference there... 140 | let (before, after) = output 141 | .split_once('\n') 142 | .expect("expected 2 lines of output at least"); 143 | let before = before 144 | .split_whitespace() 145 | .nth(5) 146 | .expect("unexpected stat format"); 147 | let after = after 148 | .split_whitespace() 149 | .nth(5) 150 | .expect("unexpected stat format"); 151 | 152 | assert_ne!(before, after); 153 | 154 | // ...and it should actually be the PID of our child! We take the child 155 | // PID here to avoid differences in handling by different shells or 156 | // distributions. 157 | let pid = after.split_whitespace().next().unwrap(); 158 | assert_eq!(after, pid); 159 | } 160 | } 161 | 162 | #[cfg(not(target_family = "unix"))] 163 | mod non_unix { 164 | use crate::common::util::TestScenario; 165 | 166 | #[test] 167 | fn unsupported_platforms() { 168 | new_ucmd!() 169 | .arg("/usr/bin/true") 170 | .fails() 171 | .code_is(1) 172 | .stderr_is("setsid: `setsid` is unavailable on non-UNIX-like platforms.\n"); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /tests/by-util/test_uuidgen.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils util-linux 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 uuid::Uuid; 7 | 8 | use crate::common::util::{TestScenario, UCommand}; 9 | 10 | fn assert_ver_eq(cmd: &mut UCommand, ver: uuid::Version) { 11 | let uuid = Uuid::parse_str( 12 | cmd.succeeds() 13 | .stdout_str() 14 | .strip_suffix('\n') 15 | .expect("newline"), 16 | ) 17 | .expect("valid UUID"); 18 | assert_eq!(uuid.get_variant(), uuid::Variant::RFC4122); 19 | assert_eq!(uuid.get_version(), Some(ver)); 20 | } 21 | 22 | #[test] 23 | fn test_random() { 24 | assert_ver_eq(&mut new_ucmd!(), uuid::Version::Random); 25 | assert_ver_eq(new_ucmd!().arg("-r"), uuid::Version::Random); 26 | assert_ver_eq(new_ucmd!().arg("--random"), uuid::Version::Random); 27 | } 28 | 29 | #[test] 30 | fn test_time() { 31 | assert_ver_eq(new_ucmd!().arg("-t"), uuid::Version::Mac); 32 | assert_ver_eq(new_ucmd!().arg("--time"), uuid::Version::Mac); 33 | } 34 | 35 | #[test] 36 | fn test_arg_conflict() { 37 | new_ucmd!().args(&["-r", "-t"]).fails().code_is(1); 38 | new_ucmd!().args(&["--time", "--random"]).fails().code_is(1); 39 | } 40 | 41 | #[test] 42 | fn test_md5_sha1() { 43 | new_ucmd!() 44 | .args(&["--namespace", "@dns", "--name", "example.com", "-m"]) 45 | .succeeds() 46 | .stdout_only("9073926b-929f-31c2-abc9-fad77ae3e8eb\n"); 47 | new_ucmd!() 48 | .args(&["-s", "--namespace", "@dns", "--name", "foobar"]) 49 | .succeeds() 50 | .stdout_only("a050b517-6677-5119-9a77-2d26bbf30507\n"); 51 | new_ucmd!() 52 | .args(&["-s", "--namespace", "@url", "--name", "foobar"]) 53 | .succeeds() 54 | .stdout_only("8304efdd-bd6e-5b7c-a27f-83f3f05c64e0\n"); 55 | new_ucmd!() 56 | .args(&["--sha1", "--namespace", "@oid", "--name", "foobar"]) 57 | .succeeds() 58 | .stdout_only("364c03e1-bcdc-58bb-94ed-43e9a92f5f08\n"); 59 | new_ucmd!() 60 | .args(&["--sha1", "--namespace", "@x500", "--name", "foobar"]) 61 | .succeeds() 62 | .stdout_only("34da942e-f4a3-5169-9c65-267d2b22cf11\n"); 63 | } 64 | 65 | #[test] 66 | fn test_invalid_arg() { 67 | new_ucmd!().arg("-Z").fails().code_is(1); 68 | new_ucmd!().args(&["-r", "-Z"]).fails().code_is(1); 69 | } 70 | 71 | #[test] 72 | fn test_name_namespace_on_non_hash() { 73 | new_ucmd!() 74 | .args(&["--namespace", "@dns", "-r"]) 75 | .fails() 76 | .code_is(1); 77 | new_ucmd!() 78 | .args(&["--name", "example.com", "-r"]) 79 | .fails() 80 | .code_is(1); 81 | new_ucmd!() 82 | .args(&["--namespace", "@dns", "--name", "example.com", "-r"]) 83 | .fails() 84 | .code_is(1); 85 | } 86 | 87 | #[test] 88 | fn test_missing_name_namespace() { 89 | new_ucmd!().arg("--sha1").fails().code_is(1); 90 | new_ucmd!() 91 | .args(&["--sha1", "--namespace", "@dns"]) 92 | .fails() 93 | .code_is(1); 94 | new_ucmd!() 95 | .args(&["--sha1", "--name", "example.com"]) 96 | .fails() 97 | .code_is(1); 98 | } 99 | -------------------------------------------------------------------------------- /tests/common/macros.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils coreutils 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 | /// Platform-independent helper for constructing a `PathBuf` from individual elements 7 | #[macro_export] 8 | macro_rules! path_concat { 9 | ($e:expr, ..$n:expr) => {{ 10 | use std::path::PathBuf; 11 | let n = $n; 12 | let mut pb = PathBuf::new(); 13 | for _ in 0..n { 14 | pb.push($e); 15 | } 16 | pb.to_str().unwrap().to_owned() 17 | }}; 18 | ($($e:expr),*) => {{ 19 | use std::path::PathBuf; 20 | let mut pb = PathBuf::new(); 21 | $( 22 | pb.push($e); 23 | )* 24 | pb.to_str().unwrap().to_owned() 25 | }}; 26 | } 27 | 28 | /// Deduce the name of the test binary from the test filename. 29 | /// 30 | /// e.g.: `tests/by-util/test_cat.rs` -> `cat` 31 | #[macro_export] 32 | macro_rules! util_name { 33 | () => { 34 | module_path!() 35 | .split("_") 36 | .nth(1) 37 | .and_then(|s| s.split("::").next()) 38 | .expect("no test name") 39 | }; 40 | } 41 | 42 | /// Convenience macro for acquiring a [`UCommand`] builder. 43 | /// 44 | /// Returns the following: 45 | /// - a [`UCommand`] builder for invoking the binary to be tested 46 | /// 47 | /// This macro is intended for quick, single-call tests. For more complex tests 48 | /// that require multiple invocations of the tested binary, see [`TestScenario`] 49 | /// 50 | /// [`UCommand`]: crate::tests::common::util::UCommand 51 | /// [`TestScenario]: crate::tests::common::util::TestScenario 52 | #[macro_export] 53 | macro_rules! new_ucmd { 54 | () => { 55 | TestScenario::new(util_name!()).ucmd() 56 | }; 57 | } 58 | 59 | /// Convenience macro for acquiring a [`UCommand`] builder and a test path. 60 | /// 61 | /// Returns a tuple containing the following: 62 | /// - an [`AtPath`] that points to a unique temporary test directory 63 | /// - a [`UCommand`] builder for invoking the binary to be tested 64 | /// 65 | /// This macro is intended for quick, single-call tests. For more complex tests 66 | /// that require multiple invocations of the tested binary, see [`TestScenario`] 67 | /// 68 | /// [`UCommand`]: crate::tests::common::util::UCommand 69 | /// [`AtPath`]: crate::tests::common::util::AtPath 70 | /// [`TestScenario]: crate::tests::common::util::TestScenario 71 | #[macro_export] 72 | macro_rules! at_and_ucmd { 73 | () => {{ 74 | let ts = TestScenario::new(util_name!()); 75 | (ts.fixtures.clone(), ts.ucmd()) 76 | }}; 77 | } 78 | 79 | /// If `common::util::expected_result` returns an error, i.e. the `util` in `$PATH` doesn't 80 | /// include a coreutils version string or the version is too low, 81 | /// this macro can be used to automatically skip the test and print the reason. 82 | #[macro_export] 83 | macro_rules! unwrap_or_return { 84 | ( $e:expr ) => { 85 | match $e { 86 | Ok(x) => x, 87 | Err(e) => { 88 | println!("test skipped: {}", e); 89 | return; 90 | } 91 | } 92 | }; 93 | } 94 | -------------------------------------------------------------------------------- /tests/common/mod.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils coreutils package. 2 | // 3 | // For the full copyright and license information, please view the LICENSE 4 | // file that was distributed with this source code. 5 | #[macro_use] 6 | pub mod macros; 7 | pub mod random; 8 | pub mod util; 9 | -------------------------------------------------------------------------------- /tests/fixtures/dmesg/kmsg.input: -------------------------------------------------------------------------------- 1 | 32,0,0,-;LOG_EMERG LOG_AUTH 2 | 80,1,1000000000,-;LOG_EMERG LOG_AUTHPRIV 3 | 72,2,2000000000,-;LOG_EMERG LOG_CRON 4 | 24,3,3000000000,-;LOG_EMERG LOG_DAEMON 5 | 88,4,4000000000,-;LOG_EMERG LOG_FTP 6 | 0,5,5000000000,-;LOG_EMERG LOG_KERN 7 | 128,6,6000000000,-;LOG_EMERG LOG_LOCAL0 8 | 136,7,7000000000,-;LOG_EMERG LOG_LOCAL1 9 | 144,8,8000000000,-;LOG_EMERG LOG_LOCAL2 10 | 152,9,9000000000,-;LOG_EMERG LOG_LOCAL3 11 | 160,10,10000000000,-;LOG_EMERG LOG_LOCAL4 12 | 168,11,11000000000,-;LOG_EMERG LOG_LOCAL5 13 | 176,12,12000000000,-;LOG_EMERG LOG_LOCAL6 14 | 184,13,13000000000,-;LOG_EMERG LOG_LOCAL7 15 | 48,14,14000000000,-;LOG_EMERG LOG_LPR 16 | 16,15,15000000000,-;LOG_EMERG LOG_MAIL 17 | 56,16,16000000000,-;LOG_EMERG LOG_NEWS 18 | 40,17,17000000000,-;LOG_EMERG LOG_SYSLOG 19 | 8,18,18000000000,-;LOG_EMERG LOG_USER 20 | 64,19,19000000000,-;LOG_EMERG LOG_UUCP 21 | 33,20,20000000000,-;LOG_ALERT LOG_AUTH 22 | 81,21,21000000000,-;LOG_ALERT LOG_AUTHPRIV 23 | 73,22,22000000000,-;LOG_ALERT LOG_CRON 24 | 25,23,23000000000,-;LOG_ALERT LOG_DAEMON 25 | 89,24,24000000000,-;LOG_ALERT LOG_FTP 26 | 1,25,25000000000,-;LOG_ALERT LOG_KERN 27 | 129,26,26000000000,-;LOG_ALERT LOG_LOCAL0 28 | 137,27,27000000000,-;LOG_ALERT LOG_LOCAL1 29 | 145,28,28000000000,-;LOG_ALERT LOG_LOCAL2 30 | 153,29,29000000000,-;LOG_ALERT LOG_LOCAL3 31 | 161,30,30000000000,-;LOG_ALERT LOG_LOCAL4 32 | 169,31,31000000000,-;LOG_ALERT LOG_LOCAL5 33 | 177,32,32000000000,-;LOG_ALERT LOG_LOCAL6 34 | 185,33,33000000000,-;LOG_ALERT LOG_LOCAL7 35 | 49,34,34000000000,-;LOG_ALERT LOG_LPR 36 | 17,35,35000000000,-;LOG_ALERT LOG_MAIL 37 | 57,36,36000000000,-;LOG_ALERT LOG_NEWS 38 | 41,37,37000000000,-;LOG_ALERT LOG_SYSLOG 39 | 9,38,38000000000,-;LOG_ALERT LOG_USER 40 | 65,39,39000000000,-;LOG_ALERT LOG_UUCP 41 | 34,40,40000000000,-;LOG_CRIT LOG_AUTH 42 | 82,41,41000000000,-;LOG_CRIT LOG_AUTHPRIV 43 | 74,42,42000000000,-;LOG_CRIT LOG_CRON 44 | 26,43,43000000000,-;LOG_CRIT LOG_DAEMON 45 | 90,44,44000000000,-;LOG_CRIT LOG_FTP 46 | 2,45,45000000000,-;LOG_CRIT LOG_KERN 47 | 130,46,46000000000,-;LOG_CRIT LOG_LOCAL0 48 | 138,47,47000000000,-;LOG_CRIT LOG_LOCAL1 49 | 146,48,48000000000,-;LOG_CRIT LOG_LOCAL2 50 | 154,49,49000000000,-;LOG_CRIT LOG_LOCAL3 51 | 162,50,50000000000,-;LOG_CRIT LOG_LOCAL4 52 | 170,51,51000000000,-;LOG_CRIT LOG_LOCAL5 53 | 178,52,52000000000,-;LOG_CRIT LOG_LOCAL6 54 | 186,53,53000000000,-;LOG_CRIT LOG_LOCAL7 55 | 50,54,54000000000,-;LOG_CRIT LOG_LPR 56 | 18,55,55000000000,-;LOG_CRIT LOG_MAIL 57 | 58,56,56000000000,-;LOG_CRIT LOG_NEWS 58 | 42,57,57000000000,-;LOG_CRIT LOG_SYSLOG 59 | 10,58,58000000000,-;LOG_CRIT LOG_USER 60 | 66,59,59000000000,-;LOG_CRIT LOG_UUCP 61 | 35,60,60000000000,-;LOG_ERR LOG_AUTH 62 | 83,61,61000000000,-;LOG_ERR LOG_AUTHPRIV 63 | 75,62,62000000000,-;LOG_ERR LOG_CRON 64 | 27,63,63000000000,-;LOG_ERR LOG_DAEMON 65 | 91,64,64000000000,-;LOG_ERR LOG_FTP 66 | 3,65,65000000000,-;LOG_ERR LOG_KERN 67 | 131,66,66000000000,-;LOG_ERR LOG_LOCAL0 68 | 139,67,67000000000,-;LOG_ERR LOG_LOCAL1 69 | 147,68,68000000000,-;LOG_ERR LOG_LOCAL2 70 | 155,69,69000000000,-;LOG_ERR LOG_LOCAL3 71 | 163,70,70000000000,-;LOG_ERR LOG_LOCAL4 72 | 171,71,71000000000,-;LOG_ERR LOG_LOCAL5 73 | 179,72,72000000000,-;LOG_ERR LOG_LOCAL6 74 | 187,73,73000000000,-;LOG_ERR LOG_LOCAL7 75 | 51,74,74000000000,-;LOG_ERR LOG_LPR 76 | 19,75,75000000000,-;LOG_ERR LOG_MAIL 77 | 59,76,76000000000,-;LOG_ERR LOG_NEWS 78 | 43,77,77000000000,-;LOG_ERR LOG_SYSLOG 79 | 11,78,78000000000,-;LOG_ERR LOG_USER 80 | 67,79,79000000000,-;LOG_ERR LOG_UUCP 81 | 36,80,80000000000,-;LOG_WARNING LOG_AUTH 82 | 84,81,81000000000,-;LOG_WARNING LOG_AUTHPRIV 83 | 76,82,82000000000,-;LOG_WARNING LOG_CRON 84 | 28,83,83000000000,-;LOG_WARNING LOG_DAEMON 85 | 92,84,84000000000,-;LOG_WARNING LOG_FTP 86 | 4,85,85000000000,-;LOG_WARNING LOG_KERN 87 | 132,86,86000000000,-;LOG_WARNING LOG_LOCAL0 88 | 140,87,87000000000,-;LOG_WARNING LOG_LOCAL1 89 | 148,88,88000000000,-;LOG_WARNING LOG_LOCAL2 90 | 156,89,89000000000,-;LOG_WARNING LOG_LOCAL3 91 | 164,90,90000000000,-;LOG_WARNING LOG_LOCAL4 92 | 172,91,91000000000,-;LOG_WARNING LOG_LOCAL5 93 | 180,92,92000000000,-;LOG_WARNING LOG_LOCAL6 94 | 188,93,93000000000,-;LOG_WARNING LOG_LOCAL7 95 | 52,94,94000000000,-;LOG_WARNING LOG_LPR 96 | 20,95,95000000000,-;LOG_WARNING LOG_MAIL 97 | 60,96,96000000000,-;LOG_WARNING LOG_NEWS 98 | 44,97,97000000000,-;LOG_WARNING LOG_SYSLOG 99 | 12,98,98000000000,-;LOG_WARNING LOG_USER 100 | 68,99,99000000000,-;LOG_WARNING LOG_UUCP 101 | 37,100,100000000000,-;LOG_NOTICE LOG_AUTH 102 | 85,101,101000000000,-;LOG_NOTICE LOG_AUTHPRIV 103 | 77,102,102000000000,-;LOG_NOTICE LOG_CRON 104 | 29,103,103000000000,-;LOG_NOTICE LOG_DAEMON 105 | 93,104,104000000000,-;LOG_NOTICE LOG_FTP 106 | 5,105,105000000000,-;LOG_NOTICE LOG_KERN 107 | 133,106,106000000000,-;LOG_NOTICE LOG_LOCAL0 108 | 141,107,107000000000,-;LOG_NOTICE LOG_LOCAL1 109 | 149,108,108000000000,-;LOG_NOTICE LOG_LOCAL2 110 | 157,109,109000000000,-;LOG_NOTICE LOG_LOCAL3 111 | 165,110,110000000000,-;LOG_NOTICE LOG_LOCAL4 112 | 173,111,111000000000,-;LOG_NOTICE LOG_LOCAL5 113 | 181,112,112000000000,-;LOG_NOTICE LOG_LOCAL6 114 | 189,113,113000000000,-;LOG_NOTICE LOG_LOCAL7 115 | 53,114,114000000000,-;LOG_NOTICE LOG_LPR 116 | 21,115,115000000000,-;LOG_NOTICE LOG_MAIL 117 | 61,116,116000000000,-;LOG_NOTICE LOG_NEWS 118 | 45,117,117000000000,-;LOG_NOTICE LOG_SYSLOG 119 | 13,118,118000000000,-;LOG_NOTICE LOG_USER 120 | 69,119,119000000000,-;LOG_NOTICE LOG_UUCP 121 | 38,120,120000000000,-;LOG_INFO LOG_AUTH 122 | 86,121,121000000000,-;LOG_INFO LOG_AUTHPRIV 123 | 78,122,122000000000,-;LOG_INFO LOG_CRON 124 | 30,123,123000000000,-;LOG_INFO LOG_DAEMON 125 | 94,124,124000000000,-;LOG_INFO LOG_FTP 126 | 6,125,125000000000,-;LOG_INFO LOG_KERN 127 | 134,126,126000000000,-;LOG_INFO LOG_LOCAL0 128 | 142,127,127000000000,-;LOG_INFO LOG_LOCAL1 129 | 150,128,128000000000,-;LOG_INFO LOG_LOCAL2 130 | 158,129,129000000000,-;LOG_INFO LOG_LOCAL3 131 | 166,130,130000000000,-;LOG_INFO LOG_LOCAL4 132 | 174,131,131000000000,-;LOG_INFO LOG_LOCAL5 133 | 182,132,132000000000,-;LOG_INFO LOG_LOCAL6 134 | 190,133,133000000000,-;LOG_INFO LOG_LOCAL7 135 | 54,134,134000000000,-;LOG_INFO LOG_LPR 136 | 22,135,135000000000,-;LOG_INFO LOG_MAIL 137 | 62,136,136000000000,-;LOG_INFO LOG_NEWS 138 | 46,137,137000000000,-;LOG_INFO LOG_SYSLOG 139 | 14,138,138000000000,-;LOG_INFO LOG_USER 140 | 70,139,139000000000,-;LOG_INFO LOG_UUCP 141 | 39,140,140000000000,-;LOG_DEBUG LOG_AUTH 142 | 87,141,141000000000,-;LOG_DEBUG LOG_AUTHPRIV 143 | 79,142,142000000000,-;LOG_DEBUG LOG_CRON 144 | 31,143,143000000000,-;LOG_DEBUG LOG_DAEMON 145 | 95,144,144000000000,-;LOG_DEBUG LOG_FTP 146 | 7,145,145000000000,-;LOG_DEBUG LOG_KERN 147 | 135,146,146000000000,-;LOG_DEBUG LOG_LOCAL0 148 | 143,147,147000000000,-;LOG_DEBUG LOG_LOCAL1 149 | 151,148,148000000000,-;LOG_DEBUG LOG_LOCAL2 150 | 159,149,149000000000,-;LOG_DEBUG LOG_LOCAL3 151 | 167,150,150000000000,-;LOG_DEBUG LOG_LOCAL4 152 | 175,151,151000000000,-;LOG_DEBUG LOG_LOCAL5 153 | 183,152,152000000000,-;LOG_DEBUG LOG_LOCAL6 154 | 191,153,153000000000,-;LOG_DEBUG LOG_LOCAL7 155 | 55,154,154000000000,-;LOG_DEBUG LOG_LPR 156 | 23,155,155000000000,-;LOG_DEBUG LOG_MAIL 157 | 63,156,156000000000,-;LOG_DEBUG LOG_NEWS 158 | 47,157,157000000000,-;LOG_DEBUG LOG_SYSLOG 159 | 15,158,158000000000,-;LOG_DEBUG LOG_USER 160 | 71,159,159000000000,-;LOG_DEBUG LOG_UUCP 161 | -------------------------------------------------------------------------------- /tests/fixtures/dmesg/kmsg.input.1: -------------------------------------------------------------------------------- 1 | 32,0,0,-;LOG_EMERG LOG_AUTH 2 | 80,1,500000,-;LOG_EMERG LOG_AUTHPRIV 3 | 72,2,333333,-;LOG_EMERG LOG_CRON 4 | 24,3,1000000,-;LOG_EMERG LOG_DAEMON 5 | 88,4,48000000,-;LOG_EMERG LOG_FTP 6 | -------------------------------------------------------------------------------- /tests/fixtures/dmesg/test_kmsg_time_format_ctime.expected: -------------------------------------------------------------------------------- 1 | [Mon Nov 18 19:34:12 2024] LOG_EMERG LOG_AUTH 2 | [Mon Nov 18 19:34:13 2024] LOG_EMERG LOG_AUTHPRIV 3 | [Mon Nov 18 19:34:13 2024] LOG_EMERG LOG_CRON 4 | [Mon Nov 18 19:34:13 2024] LOG_EMERG LOG_DAEMON 5 | [Mon Nov 18 19:35:00 2024] LOG_EMERG LOG_FTP 6 | -------------------------------------------------------------------------------- /tests/fixtures/dmesg/test_kmsg_time_format_delta.expected: -------------------------------------------------------------------------------- 1 | [< 0.000000>] LOG_EMERG LOG_AUTH 2 | [< 0.000000>] LOG_EMERG LOG_AUTHPRIV 3 | [< -0.166667>] LOG_EMERG LOG_CRON 4 | [< 0.666667>] LOG_EMERG LOG_DAEMON 5 | [< 47.000000>] LOG_EMERG LOG_FTP 6 | -------------------------------------------------------------------------------- /tests/fixtures/dmesg/test_kmsg_time_format_iso.expected: -------------------------------------------------------------------------------- 1 | 2024-11-18T19:34:12,866807+07:00 LOG_EMERG LOG_AUTH 2 | 2024-11-18T19:34:13,366807+07:00 LOG_EMERG LOG_AUTHPRIV 3 | 2024-11-18T19:34:13,200140+07:00 LOG_EMERG LOG_CRON 4 | 2024-11-18T19:34:13,866807+07:00 LOG_EMERG LOG_DAEMON 5 | 2024-11-18T19:35:00,866807+07:00 LOG_EMERG LOG_FTP 6 | -------------------------------------------------------------------------------- /tests/fixtures/dmesg/test_kmsg_time_format_notime.expected: -------------------------------------------------------------------------------- 1 | LOG_EMERG LOG_AUTH 2 | LOG_EMERG LOG_AUTHPRIV 3 | LOG_EMERG LOG_CRON 4 | LOG_EMERG LOG_DAEMON 5 | LOG_EMERG LOG_FTP 6 | -------------------------------------------------------------------------------- /tests/fixtures/dmesg/test_kmsg_time_format_raw.expected: -------------------------------------------------------------------------------- 1 | [ 0.000000] LOG_EMERG LOG_AUTH 2 | [ 0.500000] LOG_EMERG LOG_AUTHPRIV 3 | [ 0.333333] LOG_EMERG LOG_CRON 4 | [ 1.000000] LOG_EMERG LOG_DAEMON 5 | [ 48.000000] LOG_EMERG LOG_FTP 6 | -------------------------------------------------------------------------------- /tests/fixtures/dmesg/test_kmsg_time_format_reltime.expected: -------------------------------------------------------------------------------- 1 | [Nov18 19:34] LOG_EMERG LOG_AUTH 2 | [ +0.000000] LOG_EMERG LOG_AUTHPRIV 3 | [ -0.166667] LOG_EMERG LOG_CRON 4 | [ +0.666667] LOG_EMERG LOG_DAEMON 5 | [Nov18 19:35] LOG_EMERG LOG_FTP 6 | -------------------------------------------------------------------------------- /tests/fixtures/dmesg/test_since_until.expected: -------------------------------------------------------------------------------- 1 | [80000.000000] LOG_WARNING LOG_AUTH 2 | [81000.000000] LOG_WARNING LOG_AUTHPRIV 3 | [82000.000000] LOG_WARNING LOG_CRON 4 | [83000.000000] LOG_WARNING LOG_DAEMON 5 | [84000.000000] LOG_WARNING LOG_FTP 6 | -------------------------------------------------------------------------------- /tests/fixtures/last/last.input.1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uutils/util-linux/9d244e57fd6302f08a928b79e4ee78f61e316fd2/tests/fixtures/last/last.input.1 -------------------------------------------------------------------------------- /tests/fixtures/lsmem/test_lsmem_columns_json.expected: -------------------------------------------------------------------------------- 1 | { 2 | "memory": [ 3 | { 4 | "block": "0-6", 5 | "size": "896M" 6 | },{ 7 | "block": "32-149", 8 | "size": "14.8G" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /tests/fixtures/lsmem/test_lsmem_columns_pairs.expected: -------------------------------------------------------------------------------- 1 | BLOCK="0-6" SIZE="896M" 2 | BLOCK="32-149" SIZE="14.8G" 3 | -------------------------------------------------------------------------------- /tests/fixtures/lsmem/test_lsmem_columns_raw.expected: -------------------------------------------------------------------------------- 1 | BLOCK SIZE 2 | 0-6 896M 3 | 32-149 14.8G 4 | -------------------------------------------------------------------------------- /tests/fixtures/lsmem/test_lsmem_columns_table.expected: -------------------------------------------------------------------------------- 1 | BLOCK SIZE 2 | 0-6 896M 3 | 32-149 14.8G 4 | 5 | Memory block size: 128M 6 | Total online memory: 15.6G 7 | Total offline memory: 0B 8 | -------------------------------------------------------------------------------- /tests/fixtures/lsmem/test_lsmem_json.expected: -------------------------------------------------------------------------------- 1 | { 2 | "memory": [ 3 | { 4 | "range": "0x0000000000000000-0x0000000037ffffff", 5 | "size": "896M", 6 | "state": "online", 7 | "removable": true, 8 | "block": "0-6" 9 | },{ 10 | "range": "0x0000000100000000-0x00000004afffffff", 11 | "size": "14.8G", 12 | "state": "online", 13 | "removable": true, 14 | "block": "32-149" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /tests/fixtures/lsmem/test_lsmem_json_bytes.expected: -------------------------------------------------------------------------------- 1 | { 2 | "memory": [ 3 | { 4 | "range": "0x0000000000000000-0x0000000037ffffff", 5 | "size": 939524096, 6 | "state": "online", 7 | "removable": true, 8 | "block": "0-6" 9 | },{ 10 | "range": "0x0000000100000000-0x00000004afffffff", 11 | "size": 15837691904, 12 | "state": "online", 13 | "removable": true, 14 | "block": "32-149" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /tests/fixtures/lsmem/test_lsmem_json_noheadings.expected: -------------------------------------------------------------------------------- 1 | { 2 | "memory": [ 3 | { 4 | "range": "0x0000000000000000-0x0000000037ffffff", 5 | "size": "896M", 6 | "state": "online", 7 | "removable": true, 8 | "block": "0-6" 9 | },{ 10 | "range": "0x0000000100000000-0x00000004afffffff", 11 | "size": "14.8G", 12 | "state": "online", 13 | "removable": true, 14 | "block": "32-149" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /tests/fixtures/lsmem/test_lsmem_pairs.expected: -------------------------------------------------------------------------------- 1 | RANGE="0x0000000000000000-0x0000000037ffffff" SIZE="896M" STATE="online" REMOVABLE="yes" BLOCK="0-6" 2 | RANGE="0x0000000100000000-0x00000004afffffff" SIZE="14.8G" STATE="online" REMOVABLE="yes" BLOCK="32-149" 3 | -------------------------------------------------------------------------------- /tests/fixtures/lsmem/test_lsmem_pairs_bytes.expected: -------------------------------------------------------------------------------- 1 | RANGE="0x0000000000000000-0x0000000037ffffff" SIZE="939524096" STATE="online" REMOVABLE="yes" BLOCK="0-6" 2 | RANGE="0x0000000100000000-0x00000004afffffff" SIZE="15837691904" STATE="online" REMOVABLE="yes" BLOCK="32-149" 3 | -------------------------------------------------------------------------------- /tests/fixtures/lsmem/test_lsmem_pairs_noheadings.expected: -------------------------------------------------------------------------------- 1 | RANGE="0x0000000000000000-0x0000000037ffffff" SIZE="896M" STATE="online" REMOVABLE="yes" BLOCK="0-6" 2 | RANGE="0x0000000100000000-0x00000004afffffff" SIZE="14.8G" STATE="online" REMOVABLE="yes" BLOCK="32-149" 3 | -------------------------------------------------------------------------------- /tests/fixtures/lsmem/test_lsmem_raw.expected: -------------------------------------------------------------------------------- 1 | RANGE SIZE STATE REMOVABLE BLOCK 2 | 0x0000000000000000-0x0000000037ffffff 896M online yes 0-6 3 | 0x0000000100000000-0x00000004afffffff 14.8G online yes 32-149 4 | -------------------------------------------------------------------------------- /tests/fixtures/lsmem/test_lsmem_raw_all.expected: -------------------------------------------------------------------------------- 1 | RANGE SIZE STATE REMOVABLE BLOCK 2 | 0x0000000000000000-0x0000000007ffffff 128M online yes 0 3 | 0x0000000008000000-0x000000000fffffff 128M online yes 1 4 | 0x0000000010000000-0x0000000017ffffff 128M online yes 2 5 | 0x0000000018000000-0x000000001fffffff 128M online yes 3 6 | 0x0000000020000000-0x0000000027ffffff 128M online yes 4 7 | 0x0000000028000000-0x000000002fffffff 128M online yes 5 8 | 0x0000000030000000-0x0000000037ffffff 128M online yes 6 9 | 0x0000000100000000-0x0000000107ffffff 128M online yes 32 10 | 0x0000000108000000-0x000000010fffffff 128M online yes 33 11 | 0x0000000110000000-0x0000000117ffffff 128M online yes 34 12 | 0x0000000118000000-0x000000011fffffff 128M online yes 35 13 | 0x0000000120000000-0x0000000127ffffff 128M online yes 36 14 | 0x0000000128000000-0x000000012fffffff 128M online yes 37 15 | 0x0000000130000000-0x0000000137ffffff 128M online yes 38 16 | 0x0000000138000000-0x000000013fffffff 128M online yes 39 17 | 0x0000000140000000-0x0000000147ffffff 128M online yes 40 18 | 0x0000000148000000-0x000000014fffffff 128M online yes 41 19 | 0x0000000150000000-0x0000000157ffffff 128M online yes 42 20 | 0x0000000158000000-0x000000015fffffff 128M online yes 43 21 | 0x0000000160000000-0x0000000167ffffff 128M online yes 44 22 | 0x0000000168000000-0x000000016fffffff 128M online yes 45 23 | 0x0000000170000000-0x0000000177ffffff 128M online yes 46 24 | 0x0000000178000000-0x000000017fffffff 128M online yes 47 25 | 0x0000000180000000-0x0000000187ffffff 128M online yes 48 26 | 0x0000000188000000-0x000000018fffffff 128M online yes 49 27 | 0x0000000190000000-0x0000000197ffffff 128M online yes 50 28 | 0x0000000198000000-0x000000019fffffff 128M online yes 51 29 | 0x00000001a0000000-0x00000001a7ffffff 128M online yes 52 30 | 0x00000001a8000000-0x00000001afffffff 128M online yes 53 31 | 0x00000001b0000000-0x00000001b7ffffff 128M online yes 54 32 | 0x00000001b8000000-0x00000001bfffffff 128M online yes 55 33 | 0x00000001c0000000-0x00000001c7ffffff 128M online yes 56 34 | 0x00000001c8000000-0x00000001cfffffff 128M online yes 57 35 | 0x00000001d0000000-0x00000001d7ffffff 128M online yes 58 36 | 0x00000001d8000000-0x00000001dfffffff 128M online yes 59 37 | 0x00000001e0000000-0x00000001e7ffffff 128M online yes 60 38 | 0x00000001e8000000-0x00000001efffffff 128M online yes 61 39 | 0x00000001f0000000-0x00000001f7ffffff 128M online yes 62 40 | 0x00000001f8000000-0x00000001ffffffff 128M online yes 63 41 | 0x0000000200000000-0x0000000207ffffff 128M online yes 64 42 | 0x0000000208000000-0x000000020fffffff 128M online yes 65 43 | 0x0000000210000000-0x0000000217ffffff 128M online yes 66 44 | 0x0000000218000000-0x000000021fffffff 128M online yes 67 45 | 0x0000000220000000-0x0000000227ffffff 128M online yes 68 46 | 0x0000000228000000-0x000000022fffffff 128M online yes 69 47 | 0x0000000230000000-0x0000000237ffffff 128M online yes 70 48 | 0x0000000238000000-0x000000023fffffff 128M online yes 71 49 | 0x0000000240000000-0x0000000247ffffff 128M online yes 72 50 | 0x0000000248000000-0x000000024fffffff 128M online yes 73 51 | 0x0000000250000000-0x0000000257ffffff 128M online yes 74 52 | 0x0000000258000000-0x000000025fffffff 128M online yes 75 53 | 0x0000000260000000-0x0000000267ffffff 128M online yes 76 54 | 0x0000000268000000-0x000000026fffffff 128M online yes 77 55 | 0x0000000270000000-0x0000000277ffffff 128M online yes 78 56 | 0x0000000278000000-0x000000027fffffff 128M online yes 79 57 | 0x0000000280000000-0x0000000287ffffff 128M online yes 80 58 | 0x0000000288000000-0x000000028fffffff 128M online yes 81 59 | 0x0000000290000000-0x0000000297ffffff 128M online yes 82 60 | 0x0000000298000000-0x000000029fffffff 128M online yes 83 61 | 0x00000002a0000000-0x00000002a7ffffff 128M online yes 84 62 | 0x00000002a8000000-0x00000002afffffff 128M online yes 85 63 | 0x00000002b0000000-0x00000002b7ffffff 128M online yes 86 64 | 0x00000002b8000000-0x00000002bfffffff 128M online yes 87 65 | 0x00000002c0000000-0x00000002c7ffffff 128M online yes 88 66 | 0x00000002c8000000-0x00000002cfffffff 128M online yes 89 67 | 0x00000002d0000000-0x00000002d7ffffff 128M online yes 90 68 | 0x00000002d8000000-0x00000002dfffffff 128M online yes 91 69 | 0x00000002e0000000-0x00000002e7ffffff 128M online yes 92 70 | 0x00000002e8000000-0x00000002efffffff 128M online yes 93 71 | 0x00000002f0000000-0x00000002f7ffffff 128M online yes 94 72 | 0x00000002f8000000-0x00000002ffffffff 128M online yes 95 73 | 0x0000000300000000-0x0000000307ffffff 128M online yes 96 74 | 0x0000000308000000-0x000000030fffffff 128M online yes 97 75 | 0x0000000310000000-0x0000000317ffffff 128M online yes 98 76 | 0x0000000318000000-0x000000031fffffff 128M online yes 99 77 | 0x0000000320000000-0x0000000327ffffff 128M online yes 100 78 | 0x0000000328000000-0x000000032fffffff 128M online yes 101 79 | 0x0000000330000000-0x0000000337ffffff 128M online yes 102 80 | 0x0000000338000000-0x000000033fffffff 128M online yes 103 81 | 0x0000000340000000-0x0000000347ffffff 128M online yes 104 82 | 0x0000000348000000-0x000000034fffffff 128M online yes 105 83 | 0x0000000350000000-0x0000000357ffffff 128M online yes 106 84 | 0x0000000358000000-0x000000035fffffff 128M online yes 107 85 | 0x0000000360000000-0x0000000367ffffff 128M online yes 108 86 | 0x0000000368000000-0x000000036fffffff 128M online yes 109 87 | 0x0000000370000000-0x0000000377ffffff 128M online yes 110 88 | 0x0000000378000000-0x000000037fffffff 128M online yes 111 89 | 0x0000000380000000-0x0000000387ffffff 128M online yes 112 90 | 0x0000000388000000-0x000000038fffffff 128M online yes 113 91 | 0x0000000390000000-0x0000000397ffffff 128M online yes 114 92 | 0x0000000398000000-0x000000039fffffff 128M online yes 115 93 | 0x00000003a0000000-0x00000003a7ffffff 128M online yes 116 94 | 0x00000003a8000000-0x00000003afffffff 128M online yes 117 95 | 0x00000003b0000000-0x00000003b7ffffff 128M online yes 118 96 | 0x00000003b8000000-0x00000003bfffffff 128M online yes 119 97 | 0x00000003c0000000-0x00000003c7ffffff 128M online yes 120 98 | 0x00000003c8000000-0x00000003cfffffff 128M online yes 121 99 | 0x00000003d0000000-0x00000003d7ffffff 128M online yes 122 100 | 0x00000003d8000000-0x00000003dfffffff 128M online yes 123 101 | 0x00000003e0000000-0x00000003e7ffffff 128M online yes 124 102 | 0x00000003e8000000-0x00000003efffffff 128M online yes 125 103 | 0x00000003f0000000-0x00000003f7ffffff 128M online yes 126 104 | 0x00000003f8000000-0x00000003ffffffff 128M online yes 127 105 | 0x0000000400000000-0x0000000407ffffff 128M online yes 128 106 | 0x0000000408000000-0x000000040fffffff 128M online yes 129 107 | 0x0000000410000000-0x0000000417ffffff 128M online yes 130 108 | 0x0000000418000000-0x000000041fffffff 128M online yes 131 109 | 0x0000000420000000-0x0000000427ffffff 128M online yes 132 110 | 0x0000000428000000-0x000000042fffffff 128M online yes 133 111 | 0x0000000430000000-0x0000000437ffffff 128M online yes 134 112 | 0x0000000438000000-0x000000043fffffff 128M online yes 135 113 | 0x0000000440000000-0x0000000447ffffff 128M online yes 136 114 | 0x0000000448000000-0x000000044fffffff 128M online yes 137 115 | 0x0000000450000000-0x0000000457ffffff 128M online yes 138 116 | 0x0000000458000000-0x000000045fffffff 128M online yes 139 117 | 0x0000000460000000-0x0000000467ffffff 128M online yes 140 118 | 0x0000000468000000-0x000000046fffffff 128M online yes 141 119 | 0x0000000470000000-0x0000000477ffffff 128M online yes 142 120 | 0x0000000478000000-0x000000047fffffff 128M online yes 143 121 | 0x0000000480000000-0x0000000487ffffff 128M online yes 144 122 | 0x0000000488000000-0x000000048fffffff 128M online yes 145 123 | 0x0000000490000000-0x0000000497ffffff 128M online yes 146 124 | 0x0000000498000000-0x000000049fffffff 128M online yes 147 125 | 0x00000004a0000000-0x00000004a7ffffff 128M online yes 148 126 | 0x00000004a8000000-0x00000004afffffff 128M online yes 149 127 | -------------------------------------------------------------------------------- /tests/fixtures/lsmem/test_lsmem_raw_bytes.expected: -------------------------------------------------------------------------------- 1 | RANGE SIZE STATE REMOVABLE BLOCK 2 | 0x0000000000000000-0x0000000037ffffff 939524096 online yes 0-6 3 | 0x0000000100000000-0x00000004afffffff 15837691904 online yes 32-149 4 | -------------------------------------------------------------------------------- /tests/fixtures/lsmem/test_lsmem_raw_noheadings.expected: -------------------------------------------------------------------------------- 1 | 0x0000000000000000-0x0000000037ffffff 896M online yes 0-6 2 | 0x0000000100000000-0x00000004afffffff 14.8G online yes 32-149 3 | -------------------------------------------------------------------------------- /tests/fixtures/lsmem/test_lsmem_split_node.expected: -------------------------------------------------------------------------------- 1 | RANGE SIZE STATE REMOVABLE BLOCK 2 | 0x0000000000000000-0x0000000037ffffff 896M online yes 0-6 3 | 0x0000000100000000-0x00000004afffffff 14.8G online yes 32-149 4 | 5 | Memory block size: 128M 6 | Total online memory: 15.6G 7 | Total offline memory: 0B 8 | -------------------------------------------------------------------------------- /tests/fixtures/lsmem/test_lsmem_split_output_default.expected: -------------------------------------------------------------------------------- 1 | BLOCK SIZE ZONES NODE 2 | 0 128M None 0 3 | 1-6 768M DMA32 0 4 | 32-149 14.8G Normal 0 5 | 6 | Memory block size: 128M 7 | Total online memory: 15.6G 8 | Total offline memory: 0B 9 | -------------------------------------------------------------------------------- /tests/fixtures/lsmem/test_lsmem_split_removable.expected: -------------------------------------------------------------------------------- 1 | RANGE SIZE STATE REMOVABLE BLOCK 2 | 0x0000000000000000-0x0000000037ffffff 896M online yes 0-6 3 | 0x0000000100000000-0x00000004afffffff 14.8G online yes 32-149 4 | 5 | Memory block size: 128M 6 | Total online memory: 15.6G 7 | Total offline memory: 0B 8 | -------------------------------------------------------------------------------- /tests/fixtures/lsmem/test_lsmem_split_state.expected: -------------------------------------------------------------------------------- 1 | RANGE SIZE STATE REMOVABLE BLOCK 2 | 0x0000000000000000-0x0000000037ffffff 896M online yes 0-6 3 | 0x0000000100000000-0x00000004afffffff 14.8G online yes 32-149 4 | 5 | Memory block size: 128M 6 | Total online memory: 15.6G 7 | Total offline memory: 0B 8 | -------------------------------------------------------------------------------- /tests/fixtures/lsmem/test_lsmem_split_zones.expected: -------------------------------------------------------------------------------- 1 | RANGE SIZE STATE REMOVABLE BLOCK 2 | 0x0000000000000000-0x0000000007ffffff 128M online yes 0 3 | 0x0000000008000000-0x0000000037ffffff 768M online yes 1-6 4 | 0x0000000100000000-0x00000004afffffff 14.8G online yes 32-149 5 | 6 | Memory block size: 128M 7 | Total online memory: 15.6G 8 | Total offline memory: 0B 9 | -------------------------------------------------------------------------------- /tests/fixtures/lsmem/test_lsmem_summary_always.expected: -------------------------------------------------------------------------------- 1 | RANGE SIZE STATE REMOVABLE BLOCK 2 | 0x0000000000000000-0x0000000037ffffff 896M online yes 0-6 3 | 0x0000000100000000-0x00000004afffffff 14.8G online yes 32-149 4 | 5 | Memory block size: 128M 6 | Total online memory: 15.6G 7 | Total offline memory: 0B 8 | -------------------------------------------------------------------------------- /tests/fixtures/lsmem/test_lsmem_summary_empty.expected: -------------------------------------------------------------------------------- 1 | Memory block size: 128M 2 | Total online memory: 15.6G 3 | Total offline memory: 0B 4 | -------------------------------------------------------------------------------- /tests/fixtures/lsmem/test_lsmem_summary_never.expected: -------------------------------------------------------------------------------- 1 | RANGE SIZE STATE REMOVABLE BLOCK 2 | 0x0000000000000000-0x0000000037ffffff 896M online yes 0-6 3 | 0x0000000100000000-0x00000004afffffff 14.8G online yes 32-149 4 | -------------------------------------------------------------------------------- /tests/fixtures/lsmem/test_lsmem_summary_only.expected: -------------------------------------------------------------------------------- 1 | Memory block size: 128M 2 | Total online memory: 15.6G 3 | Total offline memory: 0B 4 | -------------------------------------------------------------------------------- /tests/fixtures/lsmem/test_lsmem_table.expected: -------------------------------------------------------------------------------- 1 | RANGE SIZE STATE REMOVABLE BLOCK 2 | 0x0000000000000000-0x0000000037ffffff 896M online yes 0-6 3 | 0x0000000100000000-0x00000004afffffff 14.8G online yes 32-149 4 | 5 | Memory block size: 128M 6 | Total online memory: 15.6G 7 | Total offline memory: 0B 8 | -------------------------------------------------------------------------------- /tests/fixtures/lsmem/test_lsmem_table_all.expected: -------------------------------------------------------------------------------- 1 | RANGE SIZE STATE REMOVABLE BLOCK 2 | 0x0000000000000000-0x0000000007ffffff 128M online yes 0 3 | 0x0000000008000000-0x000000000fffffff 128M online yes 1 4 | 0x0000000010000000-0x0000000017ffffff 128M online yes 2 5 | 0x0000000018000000-0x000000001fffffff 128M online yes 3 6 | 0x0000000020000000-0x0000000027ffffff 128M online yes 4 7 | 0x0000000028000000-0x000000002fffffff 128M online yes 5 8 | 0x0000000030000000-0x0000000037ffffff 128M online yes 6 9 | 0x0000000100000000-0x0000000107ffffff 128M online yes 32 10 | 0x0000000108000000-0x000000010fffffff 128M online yes 33 11 | 0x0000000110000000-0x0000000117ffffff 128M online yes 34 12 | 0x0000000118000000-0x000000011fffffff 128M online yes 35 13 | 0x0000000120000000-0x0000000127ffffff 128M online yes 36 14 | 0x0000000128000000-0x000000012fffffff 128M online yes 37 15 | 0x0000000130000000-0x0000000137ffffff 128M online yes 38 16 | 0x0000000138000000-0x000000013fffffff 128M online yes 39 17 | 0x0000000140000000-0x0000000147ffffff 128M online yes 40 18 | 0x0000000148000000-0x000000014fffffff 128M online yes 41 19 | 0x0000000150000000-0x0000000157ffffff 128M online yes 42 20 | 0x0000000158000000-0x000000015fffffff 128M online yes 43 21 | 0x0000000160000000-0x0000000167ffffff 128M online yes 44 22 | 0x0000000168000000-0x000000016fffffff 128M online yes 45 23 | 0x0000000170000000-0x0000000177ffffff 128M online yes 46 24 | 0x0000000178000000-0x000000017fffffff 128M online yes 47 25 | 0x0000000180000000-0x0000000187ffffff 128M online yes 48 26 | 0x0000000188000000-0x000000018fffffff 128M online yes 49 27 | 0x0000000190000000-0x0000000197ffffff 128M online yes 50 28 | 0x0000000198000000-0x000000019fffffff 128M online yes 51 29 | 0x00000001a0000000-0x00000001a7ffffff 128M online yes 52 30 | 0x00000001a8000000-0x00000001afffffff 128M online yes 53 31 | 0x00000001b0000000-0x00000001b7ffffff 128M online yes 54 32 | 0x00000001b8000000-0x00000001bfffffff 128M online yes 55 33 | 0x00000001c0000000-0x00000001c7ffffff 128M online yes 56 34 | 0x00000001c8000000-0x00000001cfffffff 128M online yes 57 35 | 0x00000001d0000000-0x00000001d7ffffff 128M online yes 58 36 | 0x00000001d8000000-0x00000001dfffffff 128M online yes 59 37 | 0x00000001e0000000-0x00000001e7ffffff 128M online yes 60 38 | 0x00000001e8000000-0x00000001efffffff 128M online yes 61 39 | 0x00000001f0000000-0x00000001f7ffffff 128M online yes 62 40 | 0x00000001f8000000-0x00000001ffffffff 128M online yes 63 41 | 0x0000000200000000-0x0000000207ffffff 128M online yes 64 42 | 0x0000000208000000-0x000000020fffffff 128M online yes 65 43 | 0x0000000210000000-0x0000000217ffffff 128M online yes 66 44 | 0x0000000218000000-0x000000021fffffff 128M online yes 67 45 | 0x0000000220000000-0x0000000227ffffff 128M online yes 68 46 | 0x0000000228000000-0x000000022fffffff 128M online yes 69 47 | 0x0000000230000000-0x0000000237ffffff 128M online yes 70 48 | 0x0000000238000000-0x000000023fffffff 128M online yes 71 49 | 0x0000000240000000-0x0000000247ffffff 128M online yes 72 50 | 0x0000000248000000-0x000000024fffffff 128M online yes 73 51 | 0x0000000250000000-0x0000000257ffffff 128M online yes 74 52 | 0x0000000258000000-0x000000025fffffff 128M online yes 75 53 | 0x0000000260000000-0x0000000267ffffff 128M online yes 76 54 | 0x0000000268000000-0x000000026fffffff 128M online yes 77 55 | 0x0000000270000000-0x0000000277ffffff 128M online yes 78 56 | 0x0000000278000000-0x000000027fffffff 128M online yes 79 57 | 0x0000000280000000-0x0000000287ffffff 128M online yes 80 58 | 0x0000000288000000-0x000000028fffffff 128M online yes 81 59 | 0x0000000290000000-0x0000000297ffffff 128M online yes 82 60 | 0x0000000298000000-0x000000029fffffff 128M online yes 83 61 | 0x00000002a0000000-0x00000002a7ffffff 128M online yes 84 62 | 0x00000002a8000000-0x00000002afffffff 128M online yes 85 63 | 0x00000002b0000000-0x00000002b7ffffff 128M online yes 86 64 | 0x00000002b8000000-0x00000002bfffffff 128M online yes 87 65 | 0x00000002c0000000-0x00000002c7ffffff 128M online yes 88 66 | 0x00000002c8000000-0x00000002cfffffff 128M online yes 89 67 | 0x00000002d0000000-0x00000002d7ffffff 128M online yes 90 68 | 0x00000002d8000000-0x00000002dfffffff 128M online yes 91 69 | 0x00000002e0000000-0x00000002e7ffffff 128M online yes 92 70 | 0x00000002e8000000-0x00000002efffffff 128M online yes 93 71 | 0x00000002f0000000-0x00000002f7ffffff 128M online yes 94 72 | 0x00000002f8000000-0x00000002ffffffff 128M online yes 95 73 | 0x0000000300000000-0x0000000307ffffff 128M online yes 96 74 | 0x0000000308000000-0x000000030fffffff 128M online yes 97 75 | 0x0000000310000000-0x0000000317ffffff 128M online yes 98 76 | 0x0000000318000000-0x000000031fffffff 128M online yes 99 77 | 0x0000000320000000-0x0000000327ffffff 128M online yes 100 78 | 0x0000000328000000-0x000000032fffffff 128M online yes 101 79 | 0x0000000330000000-0x0000000337ffffff 128M online yes 102 80 | 0x0000000338000000-0x000000033fffffff 128M online yes 103 81 | 0x0000000340000000-0x0000000347ffffff 128M online yes 104 82 | 0x0000000348000000-0x000000034fffffff 128M online yes 105 83 | 0x0000000350000000-0x0000000357ffffff 128M online yes 106 84 | 0x0000000358000000-0x000000035fffffff 128M online yes 107 85 | 0x0000000360000000-0x0000000367ffffff 128M online yes 108 86 | 0x0000000368000000-0x000000036fffffff 128M online yes 109 87 | 0x0000000370000000-0x0000000377ffffff 128M online yes 110 88 | 0x0000000378000000-0x000000037fffffff 128M online yes 111 89 | 0x0000000380000000-0x0000000387ffffff 128M online yes 112 90 | 0x0000000388000000-0x000000038fffffff 128M online yes 113 91 | 0x0000000390000000-0x0000000397ffffff 128M online yes 114 92 | 0x0000000398000000-0x000000039fffffff 128M online yes 115 93 | 0x00000003a0000000-0x00000003a7ffffff 128M online yes 116 94 | 0x00000003a8000000-0x00000003afffffff 128M online yes 117 95 | 0x00000003b0000000-0x00000003b7ffffff 128M online yes 118 96 | 0x00000003b8000000-0x00000003bfffffff 128M online yes 119 97 | 0x00000003c0000000-0x00000003c7ffffff 128M online yes 120 98 | 0x00000003c8000000-0x00000003cfffffff 128M online yes 121 99 | 0x00000003d0000000-0x00000003d7ffffff 128M online yes 122 100 | 0x00000003d8000000-0x00000003dfffffff 128M online yes 123 101 | 0x00000003e0000000-0x00000003e7ffffff 128M online yes 124 102 | 0x00000003e8000000-0x00000003efffffff 128M online yes 125 103 | 0x00000003f0000000-0x00000003f7ffffff 128M online yes 126 104 | 0x00000003f8000000-0x00000003ffffffff 128M online yes 127 105 | 0x0000000400000000-0x0000000407ffffff 128M online yes 128 106 | 0x0000000408000000-0x000000040fffffff 128M online yes 129 107 | 0x0000000410000000-0x0000000417ffffff 128M online yes 130 108 | 0x0000000418000000-0x000000041fffffff 128M online yes 131 109 | 0x0000000420000000-0x0000000427ffffff 128M online yes 132 110 | 0x0000000428000000-0x000000042fffffff 128M online yes 133 111 | 0x0000000430000000-0x0000000437ffffff 128M online yes 134 112 | 0x0000000438000000-0x000000043fffffff 128M online yes 135 113 | 0x0000000440000000-0x0000000447ffffff 128M online yes 136 114 | 0x0000000448000000-0x000000044fffffff 128M online yes 137 115 | 0x0000000450000000-0x0000000457ffffff 128M online yes 138 116 | 0x0000000458000000-0x000000045fffffff 128M online yes 139 117 | 0x0000000460000000-0x0000000467ffffff 128M online yes 140 118 | 0x0000000468000000-0x000000046fffffff 128M online yes 141 119 | 0x0000000470000000-0x0000000477ffffff 128M online yes 142 120 | 0x0000000478000000-0x000000047fffffff 128M online yes 143 121 | 0x0000000480000000-0x0000000487ffffff 128M online yes 144 122 | 0x0000000488000000-0x000000048fffffff 128M online yes 145 123 | 0x0000000490000000-0x0000000497ffffff 128M online yes 146 124 | 0x0000000498000000-0x000000049fffffff 128M online yes 147 125 | 0x00000004a0000000-0x00000004a7ffffff 128M online yes 148 126 | 0x00000004a8000000-0x00000004afffffff 128M online yes 149 127 | 128 | Memory block size: 128M 129 | Total online memory: 15.6G 130 | Total offline memory: 0B 131 | -------------------------------------------------------------------------------- /tests/fixtures/lsmem/test_lsmem_table_bytes.expected: -------------------------------------------------------------------------------- 1 | RANGE SIZE STATE REMOVABLE BLOCK 2 | 0x0000000000000000-0x0000000037ffffff 939524096 online yes 0-6 3 | 0x0000000100000000-0x00000004afffffff 15837691904 online yes 32-149 4 | 5 | Memory block size: 134217728 6 | Total online memory: 16777216000 7 | Total offline memory: 0 8 | -------------------------------------------------------------------------------- /tests/fixtures/lsmem/test_lsmem_table_noheadings.expected: -------------------------------------------------------------------------------- 1 | 0x0000000000000000-0x0000000037ffffff 896M online yes 0-6 2 | 0x0000000100000000-0x00000004afffffff 14.8G online yes 32-149 3 | 4 | Memory block size: 128M 5 | Total online memory: 15.6G 6 | Total offline memory: 0B 7 | -------------------------------------------------------------------------------- /tests/fixtures/util/is_atty.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -t 0 ] ; then 4 | echo "stdin is atty" 5 | else 6 | echo "stdin is not atty" 7 | fi 8 | 9 | if [ -t 1 ] ; then 10 | echo "stdout is atty" 11 | else 12 | echo "stdout is not atty" 13 | fi 14 | 15 | if [ -t 2 ] ; then 16 | echo "stderr is atty" 17 | echo "terminal size: $(stty size)" 18 | else 19 | echo "stderr is not atty" 20 | fi 21 | 22 | >&2 echo "This is an error message." 23 | 24 | true 25 | -------------------------------------------------------------------------------- /tests/tests.rs: -------------------------------------------------------------------------------- 1 | // This file is part of the uutils coreutils package. 2 | // 3 | // For the full copyright and license information, please view the LICENSE 4 | // file that was distributed with this source code. 5 | #[macro_use] 6 | mod common; 7 | 8 | #[cfg(feature = "lscpu")] 9 | #[path = "by-util/test_lscpu.rs"] 10 | mod test_lscpu; 11 | 12 | #[cfg(feature = "lsmem")] 13 | #[path = "by-util/test_lsmem.rs"] 14 | mod test_lsmem; 15 | 16 | #[cfg(feature = "lslocks")] 17 | #[path = "by-util/test_lslocks.rs"] 18 | mod test_lslocks; 19 | 20 | #[cfg(feature = "mesg")] 21 | #[path = "by-util/test_mesg.rs"] 22 | mod test_mesg; 23 | 24 | #[cfg(feature = "mountpoint")] 25 | #[path = "by-util/test_mountpoint.rs"] 26 | mod test_mountpoint; 27 | 28 | #[cfg(feature = "blockdev")] 29 | #[path = "by-util/test_blockdev.rs"] 30 | mod test_blockdev; 31 | 32 | #[cfg(feature = "ctrlaltdel")] 33 | #[path = "by-util/test_ctrlaltdel.rs"] 34 | mod test_ctrlaltdel; 35 | 36 | #[cfg(feature = "renice")] 37 | #[path = "by-util/test_renice.rs"] 38 | mod test_renice; 39 | 40 | #[cfg(feature = "rev")] 41 | #[path = "by-util/test_rev.rs"] 42 | mod test_rev; 43 | 44 | #[cfg(feature = "setsid")] 45 | #[path = "by-util/test_setsid.rs"] 46 | mod test_setsid; 47 | 48 | #[cfg(feature = "last")] 49 | #[path = "by-util/test_last.rs"] 50 | mod test_last; 51 | 52 | #[cfg(feature = "dmesg")] 53 | #[path = "by-util/test_dmesg.rs"] 54 | mod test_dmesg; 55 | 56 | #[cfg(feature = "fsfreeze")] 57 | #[path = "by-util/test_fsfreeze.rs"] 58 | mod test_fsfreeze; 59 | 60 | #[cfg(feature = "mcookie")] 61 | #[path = "by-util/test_mcookie.rs"] 62 | mod test_mcookie; 63 | 64 | #[cfg(feature = "uuidgen")] 65 | #[path = "by-util/test_uuidgen.rs"] 66 | mod test_uuidgen; 67 | --------------------------------------------------------------------------------