diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml index 44985d9a162..9eeaba04951 100644 --- a/.github/workflows/cibuild.yml +++ b/.github/workflows/cibuild.yml @@ -34,7 +34,7 @@ jobs: env: ${{ matrix.env }} steps: - name: Repository checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Ubuntu setup run: sudo -E .github/workflows/cibuild-setup-ubuntu.sh - name: Configure @@ -64,7 +64,7 @@ jobs: COVERAGE: yes steps: - name: Repository checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Ubuntu setup run: sudo -E .github/workflows/cibuild-setup-ubuntu.sh - name: Configure & Make @@ -89,7 +89,7 @@ jobs: SANITIZE: no steps: - name: Repository checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Ubuntu setup run: sudo -E .github/workflows/cibuild-setup-ubuntu.sh - name: Meson configure @@ -110,7 +110,7 @@ jobs: TRANSLATE_MANPAGES: yes steps: - name: Repository checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Ubuntu setup run: sudo -E .github/workflows/cibuild-setup-ubuntu.sh - name: Configure @@ -162,7 +162,7 @@ jobs: - arch: armv7 steps: - name: Repository checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - uses: uraimo/run-on-arch-action@v3 with: arch: ${{ matrix.arch }} @@ -217,7 +217,7 @@ jobs: COMPILER: none steps: - name: Repository checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Ubuntu setup run: sudo -E .github/workflows/cibuild-setup-ubuntu.sh - name: OpenWrt environment diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml index e4777eb182c..d2736cd0b28 100644 --- a/.github/workflows/cifuzz.yml +++ b/.github/workflows/cifuzz.yml @@ -52,7 +52,7 @@ jobs: dry-run: false sanitizer: ${{ matrix.sanitizer }} - name: Upload Crash - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 if: failure() && steps.build.outcome == 'success' with: name: ${{ matrix.sanitizer }}-${{ matrix.architecture }}-artifacts diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 223111611cc..3a43aeca409 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -32,10 +32,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Initialize CodeQL - uses: github/codeql-action/init@v3 + uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} queries: +security-extended,security-and-quality @@ -54,7 +54,7 @@ jobs: COMPILER: gcc - name: Autobuild - uses: github/codeql-action/autobuild@v3 + uses: github/codeql-action/autobuild@v4 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + uses: github/codeql-action/analyze@v4 diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml index c4593ad061a..702697a32a8 100644 --- a/.github/workflows/coverity.yml +++ b/.github/workflows/coverity.yml @@ -18,7 +18,7 @@ jobs: COVERITY_SCAN_TOKEN: "${{ secrets.COVERITY_SCAN_TOKEN }}" steps: - name: Repository checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Ubuntu setup run: sudo -E .github/workflows/cibuild-setup-ubuntu.sh - name: Configure diff --git a/.github/workflows/differential-shellcheck.yml b/.github/workflows/differential-shellcheck.yml index e524a11734b..cd6b7ebb18c 100644 --- a/.github/workflows/differential-shellcheck.yml +++ b/.github/workflows/differential-shellcheck.yml @@ -16,7 +16,7 @@ jobs: steps: - name: Repository checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 0 diff --git a/Documentation/howto-contribute.txt b/Documentation/howto-contribute.txt index 97f3ce1e160..80c2e9f0a1b 100644 --- a/Documentation/howto-contribute.txt +++ b/Documentation/howto-contribute.txt @@ -188,6 +188,9 @@ Coding Style multiple lines. In case the shorthand does not look good on one line use the normal "if () else" syntax. + * To avoid whitespace errors, consider installing an EditorConfig plugin + (https://editorconfig.org/) into your favorite editor or IDE. + Options * The rule of thumb for options is that once they exist, you may not diff --git a/bash-completion/bits b/bash-completion/bits index 786be4d92f5..c0e9fe63bd6 100644 --- a/bash-completion/bits +++ b/bash-completion/bits @@ -11,7 +11,7 @@ _bits_module() esac case $cur in -*) - OPTS="--version --help --width --mask --grouped-mask --bit --list" + OPTS="--version --help --width --mask --grouped-mask --bit --binary --list" COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) return 0 ;; diff --git a/bash-completion/blkid b/bash-completion/blkid index a7e4ef5da22..de1c2c22a8d 100644 --- a/bash-completion/blkid +++ b/bash-completion/blkid @@ -86,6 +86,7 @@ _blkid_module() --usages --match-types --no-part-details + --hint --help --version " diff --git a/bash-completion/blockdev b/bash-completion/blockdev index f37cf034d8b..6244e8f2be8 100644 --- a/bash-completion/blockdev +++ b/bash-completion/blockdev @@ -29,7 +29,9 @@ _blockdev_module() --getfra --flushbufs --rereadpt - $DEVS" + $DEVS + --help + --version" case $prev in '--setbsz') COMPREPLY=( $(compgen -W "bytes" -- $cur) ) diff --git a/bash-completion/cal b/bash-completion/cal index 8791d123a8d..272f6f6c829 100644 --- a/bash-completion/cal +++ b/bash-completion/cal @@ -6,6 +6,9 @@ _cal_module() case $cur in -*) OPTS=" --one + --columns + --iso + --reform --three --months --sunday diff --git a/bash-completion/cfdisk b/bash-completion/cfdisk index 0a57d68a644..683986d8ed4 100644 --- a/bash-completion/cfdisk +++ b/bash-completion/cfdisk @@ -20,6 +20,7 @@ _cfdisk_module() --lock --help --read-only + --sector-size --version" COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) return 0 diff --git a/bash-completion/chmem b/bash-completion/chmem index 3e3af87acaa..f10646677e1 100644 --- a/bash-completion/chmem +++ b/bash-completion/chmem @@ -14,6 +14,9 @@ _chmem_module() OPTS=" --enable --disable + --configure + --deconfigure + --memmap-on-memory --blocks --verbose --zone diff --git a/bash-completion/column b/bash-completion/column index fb209204d58..eea03f35d4a 100644 --- a/bash-completion/column +++ b/bash-completion/column @@ -13,7 +13,7 @@ _column_module() COMPREPLY=( $(compgen -W "auto never always" -- $cur) ) return 0 ;; - '-s'|'--separator'|'-o'|'--output-separator'|'-n'|'--table-name'|'-O') + '-s'|'--separator'|'--input-separator'|'-o'|'--output-separator'|'-n'|'--table-name'|'-O') COMPREPLY=( $(compgen -W "string" -- $cur) ) return 0 ;; @@ -36,6 +36,7 @@ _column_module() --table-colorscheme --table-name --table-order + --table-column --table-columns --table-columns-limit --table-noextreme @@ -53,7 +54,9 @@ _column_module() --tree-parent --output-width --separator + --input-separator --output-separator + --wrap-separator --fillrows --use-spaces --color diff --git a/bash-completion/fadvise b/bash-completion/fadvise index 9d75ded6a3e..44a87f2fbbe 100644 --- a/bash-completion/fadvise +++ b/bash-completion/fadvise @@ -26,6 +26,7 @@ _fadvise_module() case $cur in -*) OPTS='--advice + --fd --length --offset --help diff --git a/bash-completion/fallocate b/bash-completion/fallocate index 670874c9602..dd0d07d07f5 100644 --- a/bash-completion/fallocate +++ b/bash-completion/fallocate @@ -24,6 +24,7 @@ _fallocate_module() --offset --punch-hole --zero-range + --write-zeroes --posix --verbose --help diff --git a/bash-completion/fincore b/bash-completion/fincore index 34007e04d8f..c2341f6dfc3 100644 --- a/bash-completion/fincore +++ b/bash-completion/fincore @@ -31,6 +31,7 @@ _fincore_module() --noheadings --output --output-all + --total --raw --recursive --cachestat diff --git a/bash-completion/findmnt b/bash-completion/findmnt index fe04cba5c75..0990eb7e983 100644 --- a/bash-completion/findmnt +++ b/bash-completion/findmnt @@ -136,6 +136,15 @@ _findmnt_module() --real --pseudo --list-columns + --bytes + --filter + --nocanonicalize + --shadowed + --shell + --uniq + --verbose + --verify + --vfs-all --version" COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) return 0 diff --git a/bash-completion/flock b/bash-completion/flock index a4de186f388..ce5049b7bca 100644 --- a/bash-completion/flock +++ b/bash-completion/flock @@ -27,7 +27,8 @@ _flock_module() OPTS="--shared --exclusive --unlock - --nonblock + --nb + --nonblocking --timeout --conflict-exit-code --close @@ -36,6 +37,8 @@ _flock_module() --fcntl --start --length + --wait + --verbose --help --version" COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) diff --git a/bash-completion/fsck b/bash-completion/fsck index 8e6942c672e..cf8756c5b74 100644 --- a/bash-completion/fsck +++ b/bash-completion/fsck @@ -27,7 +27,7 @@ _fsck_module() esac case $cur in -*) - OPTS="-p -n -y -c -f -v -b -B -j -l -L" + OPTS="-p -n -y -c -f -v -b -B -j -l -L --help --version" COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) return 0 ;; diff --git a/bash-completion/getopt b/bash-completion/getopt index 5e3fca61cbc..9cd9b7319a6 100644 --- a/bash-completion/getopt +++ b/bash-completion/getopt @@ -27,7 +27,18 @@ _getopt_module() esac case $cur in -*) - OPTS="--alternative --help --longoptions --name --options --quiet --quiet-output --shell --test --unquoted --version" + OPTS="--alternative + --help + --longoptions + --name + --options + --quiet + --quiet-output + --shell + --test + --unknown + --unquoted + --version" COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) return 0 ;; diff --git a/bash-completion/hardlink b/bash-completion/hardlink index 3b0200640cd..4af9e5c56f7 100644 --- a/bash-completion/hardlink +++ b/bash-completion/hardlink @@ -49,18 +49,27 @@ _hardlink_module() -*) OPTS=" --content + --cache-size + --exclude + --exclude-subtree --respect-dir --respect-name --maximize --minimize + --minimum-size + --maximum-size --mount --dry-run + --io-size + --include --ignore-owner --keep-oldest --list-duplicates --ignore-mode --quiet + --prioritize-trees --ignore-time + --reflink --verbose --respect-xattrs --skip-reflinks diff --git a/bash-completion/hwclock b/bash-completion/hwclock index 6240d7d4be6..39232041624 100644 --- a/bash-completion/hwclock +++ b/bash-completion/hwclock @@ -54,9 +54,12 @@ _hwclock_module() --epoch --param-get --param-set + --param-index + --verbose --vl-read --vl-clear --update-drift + --ul-debug --noadjfile --adjfile --test diff --git a/bash-completion/lastlog2 b/bash-completion/lastlog2 index 09aa4ed11e4..40d867789bc 100644 --- a/bash-completion/lastlog2 +++ b/bash-completion/lastlog2 @@ -35,7 +35,7 @@ _lastlog2_module() esac case $cur in -*) - OPTS=" + OPTS=" --active --before --clear --database diff --git a/bash-completion/losetup b/bash-completion/losetup index 783d966c79c..2d314c111ed 100644 --- a/bash-completion/losetup +++ b/bash-completion/losetup @@ -60,6 +60,9 @@ _losetup_module() --remove --show --verbose + --direct-io + --loop-ref + --sector-size --json --list --noheadings diff --git a/bash-completion/lsblk b/bash-completion/lsblk index ff826858137..577dcce13cf 100644 --- a/bash-completion/lsblk +++ b/bash-completion/lsblk @@ -96,6 +96,11 @@ _lsblk_module() --virtio --sort --width + --noempty + --shell + --sysroot + --tree + --zoned --list-columns --help --version" diff --git a/bash-completion/lsclocks b/bash-completion/lsclocks index 7158701ea7c..175b3f05bf7 100644 --- a/bash-completion/lsclocks +++ b/bash-completion/lsclocks @@ -53,6 +53,8 @@ _lsclocks_module() --raw --time --dynamic-clock + --no-discover-dynamic + --no-discover-rtc --rtc --cpu-clock --help diff --git a/bash-completion/lscpu b/bash-completion/lscpu index b26086afb17..950be9bfaa0 100644 --- a/bash-completion/lscpu +++ b/bash-completion/lscpu @@ -38,6 +38,7 @@ _lscpu_module() --parse= --sysroot --hex + --hierarchic --physical --output-all --raw diff --git a/bash-completion/lslogins b/bash-completion/lslogins index daf614d313a..dd1770855d5 100644 --- a/bash-completion/lslogins +++ b/bash-completion/lslogins @@ -71,7 +71,9 @@ _lslogins_module() --print0 --wtmp-file --btmp-file - --lastlog + --shell + --lastlog-file + --lastlog2-file --help --version" -- $cur) ) return 0 diff --git a/bash-completion/lsmem b/bash-completion/lsmem index 7d6e8424785..27793e743e7 100644 --- a/bash-completion/lsmem +++ b/bash-completion/lsmem @@ -9,7 +9,7 @@ _lsmem_module() local prefix realcur OUTPUT_ALL OUTPUT realcur="${cur##*,}" prefix="${cur%$realcur}" - OUTPUT_ALL='RANGE SIZE STATE REMOVABLE BLOCK NODE ZONES' + OUTPUT_ALL='RANGE SIZE STATE REMOVABLE BLOCK NODE ZONES CONFIGURED MEMMAP-ON-MEMORY' for WORD in $OUTPUT_ALL; do if ! [[ $prefix == *"$WORD"* ]]; then OUTPUT="$WORD ${OUTPUT:-""}" @@ -43,6 +43,7 @@ _lsmem_module() --output-all --raw --sysroot + --split --summary --help --version diff --git a/bash-completion/mkfs.bfs b/bash-completion/mkfs.bfs index b7efc767f4a..3d4b492392b 100644 --- a/bash-completion/mkfs.bfs +++ b/bash-completion/mkfs.bfs @@ -19,7 +19,7 @@ _mkfs.bfs_module() esac case $cur in -*) - OPTS='--inodes --vname --fname --verbose --help --version' + OPTS='--inodes --vname --fname --lock --verbose --help --version' COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) return 0 ;; diff --git a/bash-completion/mkfs.cramfs b/bash-completion/mkfs.cramfs index b7e92fce74c..61a65b37f5f 100644 --- a/bash-completion/mkfs.cramfs +++ b/bash-completion/mkfs.cramfs @@ -31,7 +31,7 @@ _mkfs.cramfs_module() esac case $cur in -*) - OPTS="-h -v -E -b -e -N -i -n -p -s -z" + OPTS="-h -v -E -b -e -N -i -n -p -s -z --help --version" COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) return 0 ;; diff --git a/bash-completion/mkfs.minix b/bash-completion/mkfs.minix index e7a26cd0a7a..c43fef82ddb 100644 --- a/bash-completion/mkfs.minix +++ b/bash-completion/mkfs.minix @@ -21,7 +21,7 @@ _mkfs.minix_module() esac case $cur in -*) - OPTS="--namelength --inodes --check --badblocks --help --version -1 -2 -3" + OPTS="--namelength --inodes --check --badblocks --lock --help --version -1 -2 -3" COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) return 0 ;; diff --git a/bash-completion/mkswap b/bash-completion/mkswap index 46939c0102c..693347516d3 100644 --- a/bash-completion/mkswap +++ b/bash-completion/mkswap @@ -35,7 +35,21 @@ _mkswap_module() esac case $cur in -*) - OPTS="--check --force --pagesize --lock --label --swapversion --uuid --offset --verbose --version --help --size --file" + OPTS="--check + --endianness + --quiet + --force + --pagesize + --lock + --label + --swapversion + --uuid + --offset + --verbose + --version + --help + --size + --file" COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) return 0 ;; diff --git a/bash-completion/more b/bash-completion/more index d93200a454b..e372f0de1b6 100644 --- a/bash-completion/more +++ b/bash-completion/more @@ -16,6 +16,7 @@ _more_module() case $cur in -*) OPTS=" + --exit-on-eof --silent --logical --no-pause diff --git a/bash-completion/mount b/bash-completion/mount index 3e208282847..616fb0b50dd 100644 --- a/bash-completion/mount +++ b/bash-completion/mount @@ -72,6 +72,7 @@ _mount_module() --options-source-force --test-opts --read-only + --ro --types --source --target diff --git a/bash-completion/mountpoint b/bash-completion/mountpoint index 15c6d2314aa..cca764517fe 100644 --- a/bash-completion/mountpoint +++ b/bash-completion/mountpoint @@ -11,7 +11,7 @@ _mountpoint_module() esac case $cur in -*) - OPTS="--quiet --nofollow --fs-devno --devno --help --version" + OPTS="--quiet --nofollow --fs-devno --devno --show --help --version" COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) return 0 ;; diff --git a/bash-completion/namei b/bash-completion/namei index 640273500a1..85a3d74c821 100644 --- a/bash-completion/namei +++ b/bash-completion/namei @@ -11,7 +11,15 @@ _namei_module() esac case $cur in -*) - OPTS="--help --version --mountpoints --modes --owners --long --nosymlinks --vertical" + OPTS=" --context + --help + --version + --mountpoints + --modes + --owners + --long + --nosymlinks + --vertical" COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) return 0 ;; diff --git a/bash-completion/nsenter b/bash-completion/nsenter index 1acc764ad09..165300a2a8f 100644 --- a/bash-completion/nsenter +++ b/bash-completion/nsenter @@ -34,6 +34,7 @@ _nsenter_module() -*) OPTS=" --all + --follow-context --target --mount= --uts= diff --git a/bash-completion/pg b/bash-completion/pg index 8fce1302abe..04b47f340b0 100644 --- a/bash-completion/pg +++ b/bash-completion/pg @@ -15,7 +15,7 @@ _pg_module() esac case $cur in -*) - OPTS="-number -c -e -f -n -p -r -s -h -V" + OPTS="-number -c -e -f -n -p -r -s -h -V --help --version" COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) return 0 ;; diff --git a/bash-completion/renice b/bash-completion/renice index 20ddb9d90d9..8af037d2bc9 100644 --- a/bash-completion/renice +++ b/bash-completion/renice @@ -32,6 +32,7 @@ _renice_module() OPTS="--pgrp --priority --pid + --relative --user --help --version" diff --git a/bash-completion/scriptlive b/bash-completion/scriptlive index 56a296f5b57..69f0a3633da 100644 --- a/bash-completion/scriptlive +++ b/bash-completion/scriptlive @@ -14,13 +14,19 @@ _scriptlive_module() COMPREPLY=( $(compgen -W "digit" -- $cur) ) return 0 ;; + '-E'|'--echo') + COMPREPLY=( $(compgen -W "auto always never" -- $cur) ) + return 0 + ;; '-h'|'--help'|'-V'|'--version') return 0 ;; esac case $cur in -*) - OPTS="--timing + OPTS=" + --echo + --timing --log-in --log-io --log-timing diff --git a/bash-completion/setpriv b/bash-completion/setpriv index d19b275573a..a472943fa51 100644 --- a/bash-completion/setpriv +++ b/bash-completion/setpriv @@ -124,6 +124,9 @@ _setpriv_module() --securebits --pdeathsig --reset-env + --init-groups + --nnp + --ptracer --selinux-label --apparmor-profile --landlock-access diff --git a/bash-completion/setsid b/bash-completion/setsid index 955ec27c0ee..bf22477cf44 100644 --- a/bash-completion/setsid +++ b/bash-completion/setsid @@ -11,7 +11,7 @@ _setsid_module() esac case $cur in -*) - OPTS="--ctty --wait --help --version" + OPTS="--ctty --wait --fork --help --version" COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) return 0 ;; diff --git a/bash-completion/sfdisk b/bash-completion/sfdisk index 27f64c6021a..290d234fdfb 100644 --- a/bash-completion/sfdisk +++ b/bash-completion/sfdisk @@ -89,6 +89,11 @@ _sfdisk_module() --wipe-partitions --label --label-nested + --Linux + --move-use-fsync + --sector-size + --show-pt-geometry + --unit --help --version " diff --git a/bash-completion/swapon b/bash-completion/swapon index 5906ec8445f..0b09ff88ac4 100644 --- a/bash-completion/swapon +++ b/bash-completion/swapon @@ -65,6 +65,7 @@ _swapon_module() --summary --show --output-all + --options --noheadings --raw --bytes diff --git a/bash-completion/unshare b/bash-completion/unshare index d421eb1a3b4..19eeb8f08f2 100644 --- a/bash-completion/unshare +++ b/bash-completion/unshare @@ -33,12 +33,21 @@ _unshare_module() --mount-proc --map-current-user --map-root-user + --owner --propagation --setgroups --help --version --root --wd + --load-interp + --map-auto + --map-group + --map-groups + --map-user + --map-users + --map-subids + --mount-binfmt --monotonic --boottime --setuid diff --git a/bash-completion/uuidd b/bash-completion/uuidd index c8f6697c9b8..d827454a022 100644 --- a/bash-completion/uuidd +++ b/bash-completion/uuidd @@ -29,7 +29,21 @@ _uuidd_module() esac case $cur in -*) - OPTS="--pid --socket --timeout --kill --random --time --uuids --no-pid --no-fork --socket-activation --debug --quiet --version --help" + OPTS="--cont-clock + --pid + --socket + --timeout + --kill + --random + --time + --uuids + --no-pid + --no-fork + --socket-activation + --debug + --quiet + --version + --help" COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) return 0 ;; diff --git a/bash-completion/wdctl b/bash-completion/wdctl index f1a870ef695..2a4e62eaf88 100644 --- a/bash-completion/wdctl +++ b/bash-completion/wdctl @@ -56,6 +56,8 @@ _wdctl_module() --notimeouts --settimeout --flags-only + --setpregovernor + --setpretimeout --help --version" COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) diff --git a/bash-completion/whereis b/bash-completion/whereis index 7db67a398c7..55c6d2dfdb2 100644 --- a/bash-completion/whereis +++ b/bash-completion/whereis @@ -17,7 +17,7 @@ _whereis_module() esac case $cur in -*) - OPTS="-b -B -m -M -s -S -f -u -l -g" + OPTS="-b -B -m -M -s -S -f -u -l -g --help --version" COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) return 0 ;; diff --git a/configure.ac b/configure.ac index 6836f0564aa..a424e09476b 100644 --- a/configure.ac +++ b/configure.ac @@ -685,12 +685,14 @@ AC_CHECK_FUNCS([ \ ]) AC_FUNC_FSEEKO +AC_CHECK_FUNCS([fallocate], [have_fallocate=yes], [have_fallocate=no]) AC_CHECK_FUNCS([futimens], [have_futimens=yes]) AC_CHECK_FUNCS([getusershell], [have_getusershell=yes],[have_getusershell=no]) AC_CHECK_FUNCS([inotify_init1], [have_inotify_init1=yes]) AC_CHECK_FUNCS([ioperm iopl], [have_io=yes]) AC_CHECK_FUNCS([openat fstatat unlinkat], [have_openat=yes], [have_openat=no]) AC_CHECK_FUNCS([open_memstream], [have_open_memstream=yes],[have_open_memstream=no]) +AC_CHECK_FUNCS([posix_fallocate], [have_posix_fallocate=yes], [have_posix_fallocate=no]) AC_CHECK_FUNCS([reboot], [have_reboot=yes],[have_reboot=no]) AC_CHECK_FUNCS([updwtmpx updwtmpx], [have_gnu_utmpx=yes], [have_gnu_utmpx=no]) @@ -1388,6 +1390,10 @@ AC_ARG_ENABLE([libmount-mountfd-support], AS_HELP_STRING([--disable-libmount-mountfd-support], [do not use new mount API based on FDs]), [], [enable_libmount_mountfd_support=check] ) +AC_ARG_ENABLE([libmount-udev-support], + AS_HELP_STRING([--disable-libmount-udev-support], [do not read from udev in libmount]), + [], [enable_libmount_udev_support=check] +) UL_BUILD_INIT([libmount_mountfd_support]) UL_REQUIRES_BUILD([libmount_mountfd_support], [libmount]) UL_REQUIRES_LINUX([libmount_mountfd_support]) @@ -1587,70 +1593,10 @@ AC_ARG_ENABLE([fallocate], ) UL_BUILD_INIT([fallocate]) UL_REQUIRES_LINUX([fallocate]) -UL_REQUIRES_SYSCALL_CHECK([fallocate], [UL_CHECK_SYSCALL([fallocate])]) +UL_REQUIRES_HAVE([fallocate], [fallocate], [fallocate functions]) +UL_REQUIRES_HAVE([fallocate], [posix_fallocate], [posix_fallocate functions]) AM_CONDITIONAL([BUILD_FALLOCATE], [test "x$build_fallocate" = xyes]) -AS_IF([test "x$build_fallocate" = xyes], [ - dnl check for valid fallocate() function - dnl with 32 bits glibc 2.10, fallocate() exists but not fallocate64() - dnl when _FILE_OFFSET_BITS==64, fallocate() is redirect to fallocate64() - dnl and program can't be linked. - dnl AC_CHECK_FUNC can't catch such errors since it's redefining - dnl function prototype. - AC_MSG_CHECKING([for valid fallocate() function]) - AC_LINK_IFELSE([AC_LANG_PROGRAM([[ -#ifdef HAVE_UNISTD_H -# include -#endif -#ifdef HAVE_SYS_TYPES_H -# include -#endif -#ifdef HAVE_LINUX_FALLOC_H -# include -#endif -#ifdef HAVE_FCNTL_H -# include -#endif -]],[[ - long ret; - ret = fallocate(0, FALLOC_FL_KEEP_SIZE, 0xfffffffful, 0xfffffffful); - if (ret != 0) { - return 1; - } - ]])],[ - AC_MSG_RESULT([yes]) - AC_DEFINE([HAVE_FALLOCATE], [1], [Have valid fallocate() function])],[ - AC_MSG_RESULT([no])]) -]) - -AS_IF([test "x$build_fallocate" = xyes], [ - dnl check for valid posix_fallocate() function - AC_MSG_CHECKING([for valid posix_fallocate() function]) - AC_LINK_IFELSE([AC_LANG_PROGRAM([[ -#ifdef HAVE_UNISTD_H -# include -#endif -#ifdef HAVE_SYS_TYPES_H -# include -#endif -#ifdef HAVE_LINUX_FALLOC_H -# include -#endif -#ifdef HAVE_FCNTL_H -# include -#endif -]],[[ - long ret; - ret = posix_fallocate(0, 0xfffffffful, 0xfffffffful); - if (ret != 0) { - return 1; - } - ]])],[ - AC_MSG_RESULT([yes]) - AC_DEFINE([HAVE_POSIX_FALLOCATE], [1], [Have valid posix_fallocate() function])],[ - AC_MSG_RESULT([no])]) -]) - AC_ARG_ENABLE([unshare], AS_HELP_STRING([--disable-unshare], [do not build unshare]), @@ -2759,6 +2705,20 @@ AS_IF([test "x$with_systemd" != xno], [ ]) AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$have_systemd" = xyes]) +libmount_udev_support=no +AS_IF([test "x$enable_libmount_udev_support" != xno && test "x$build_libmount" = xyes], [ + AS_CASE([$enable_libmount_udev_support:$have_systemd], + [yes:no], + [AC_MSG_ERROR([libmount udev support expected but libsystemd not found])], + [*:yes], [ + AC_CHECK_DECLS([sd_device_new_from_devname], [ + AC_DEFINE([USE_LIBMOUNT_UDEV_SUPPORT], [1], [Define if use udev support in libmount]) + libmount_udev_support=yes], + [], [#include ]) + ] + ) +]) +AM_CONDITIONAL([USE_LIBMOUNT_UDEV_SUPPORT], [test "x$libmount_udev_support" = xyes]) AC_ARG_WITH([systemdsystemunitdir], AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [directory for systemd unit files (requires systemd support)]), @@ -2800,6 +2760,16 @@ AS_IF([test "x$with_sysusersdir" != "xno"], [ AC_SUBST([sysusersdir], [$with_sysusersdir]) ]) + +AC_ARG_WITH([ntfs-mounttype], + AS_HELP_STRING([--with-ntfs-mounttype=TYPE], [overwrite default ntfs3 mount type for NTFS]), + [], [with_ntfs_mounttype=no] +) +AS_IF([test "x$with_ntfs_mounttype" != "xno"], [ + AC_DEFINE_UNQUOTED([CONFIG_UL_NTFS_MOUNTTYPE], ["$with_ntfs_mounttype"], [NTFS mounttype]) +]) + + AC_ARG_WITH([smack], AS_HELP_STRING([--with-smack], [build with SMACK support]), [], [with_smack=no] diff --git a/disk-utils/fsck.c b/disk-utils/fsck.c index beccb17508f..02ddaedae94 100644 --- a/disk-utils/fsck.c +++ b/disk-utils/fsck.c @@ -442,7 +442,7 @@ static struct libmnt_fs *add_dummy_fs(const char *device) } mnt_unref_fs(fs); - err(FSCK_EX_ERROR, _("failed to setup description for %s"), device); + err(FSCK_EX_ERROR, _("failed to set up description for %s"), device); } static void fs_interpret_type(struct libmnt_fs *fs) diff --git a/disk-utils/partx.c b/disk-utils/partx.c index 68d29f62f41..bb1bdc26895 100644 --- a/disk-utils/partx.c +++ b/disk-utils/partx.c @@ -219,7 +219,8 @@ static int get_partno_from_device(char *partition, dev_t devno) static int get_max_partno(const char *disk, dev_t devno) { - char path[PATH_MAX], *parent, *dirname = NULL; + char path[PATH_MAX], *dirname = NULL; + const char *parent; struct stat st; DIR *dir; struct dirent *d; @@ -813,7 +814,7 @@ int main(int argc, char **argv) { "bytes", no_argument, NULL, 'b' }, { "noheadings", no_argument, NULL, 'g' }, { "raw", no_argument, NULL, 'r' }, - { "list", no_argument, NULL, 'l' }, + { "list", no_argument, NULL, 'l' }, /* deprecated */ { "show", no_argument, NULL, 's' }, { "add", no_argument, NULL, 'a' }, { "delete", no_argument, NULL, 'd' }, diff --git a/include/c.h b/include/c.h index f7cd08fd8bb..e74a4486bf9 100644 --- a/include/c.h +++ b/include/c.h @@ -133,6 +133,10 @@ C23 : https://en.cppreference.com/w/c/language/attributes/fallthrough #define __has_feature(x) 0 #endif +#ifndef __has_extension + #define __has_extension __has_feature +#endif + /* * Function attributes */ @@ -173,6 +177,15 @@ C23 : https://en.cppreference.com/w/c/language/attributes/fallthrough #define UL_BUILD_BUG_ON_ZERO(e) __extension__ (sizeof(struct { int:-!!(e); })) #define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); })) +#if __has_include() +#include +#define ARRAY_SIZE(arr) countof(arr) +#endif + +#if !defined(ARRAY_SIZE) && __has_extension(c_countof) +#define ARRAY_SIZE(arr) _Countof(arr) +#endif + #ifndef ARRAY_SIZE # define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr)) #endif @@ -639,6 +652,7 @@ static inline int fputsln(const char *s, FILE *stream) { #endif #define SINT_MAX(t) (((t)1 << (sizeof(t) * 8 - 2)) - (t)1 + ((t)1 << (sizeof(t) * 8 - 2))) +#define MAX_OF_UINT_TYPE(t) ~((t)0) #ifndef HAVE_REALLOCARRAY static inline void *reallocarray(void *ptr, size_t nmemb, size_t size) diff --git a/include/optutils.h b/include/optutils.h index 6fe4aeb7a51..87d20d0d533 100644 --- a/include/optutils.h +++ b/include/optutils.h @@ -11,7 +11,11 @@ #include "nls.h" #include "cctype.h" -static inline const char *option_to_longopt(int c, const struct option *opts) +/* + * Converts the short option @c to the corresponding long option from @opts, or + * returns NULL. + */ +static inline const char *ul_get_longopt(const struct option *opts, int c) { const struct option *o; @@ -22,6 +26,19 @@ static inline const char *option_to_longopt(int c, const struct option *opts) return NULL; } +/* + * Converts the short options @c to "%c" or "0x" if not printable. + */ +static inline const char *ul_get_shortopt(char *buf, size_t bufsz, int c) +{ + if (c_isgraph(c)) + snprintf(buf, bufsz, "%c", c); + else + snprintf(buf, bufsz, "<0x%02x>", c); /* should not happen */ + + return buf; +} + #ifndef OPTUTILS_EXIT_CODE # define OPTUTILS_EXIT_CODE EXIT_FAILURE #endif @@ -84,23 +101,16 @@ static inline void err_exclusive_options( if (status[e] == 0) status[e] = c; else if (status[e] != c) { - size_t ct = 0; + const char *a = ul_get_longopt(opts, status[e]); + const char *b = ul_get_longopt(opts, c); + char buf[16]; /* short option in hex */ - fprintf(stderr, _("%s: mutually exclusive " - "arguments:"), - program_invocation_short_name); - - for (op = excl[e]; - ct + 1 < ARRAY_SIZE(excl[0]) && *op; - op++, ct++) { - const char *n = option_to_longopt(*op, opts); - if (n) - fprintf(stderr, " --%s", n); - else if (c_isgraph(*op)) - fprintf(stderr, " -%c", *op); - } - fputc('\n', stderr); - exit(OPTUTILS_EXIT_CODE); + errx(OPTUTILS_EXIT_CODE, + _("options %s%s and %s%s cannot be combined"), + a ? "--" : "-", + a ? a : ul_get_shortopt(buf, sizeof(buf), status[e]), + b ? "--" : "-", + b ? b : ul_get_shortopt(buf, sizeof(buf), c)); } break; } @@ -108,4 +118,3 @@ static inline void err_exclusive_options( } #endif - diff --git a/include/pidfd-utils.h b/include/pidfd-utils.h index 08627ca8d28..af67b865b0a 100644 --- a/include/pidfd-utils.h +++ b/include/pidfd-utils.h @@ -31,6 +31,10 @@ # define PIDFD_GET_UTS_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 10) #endif +#if (defined(HAVE_PIDFD_OPEN) || defined(SYS_pidfd_open)) && defined(HAVE_STATX) \ + && defined(HAVE_STRUCT_STATX) +#define USE_PIDFD_INO_SUPPORT 1 +#endif #ifdef HAVE_SYS_SYSCALL_H # include @@ -94,4 +98,8 @@ static inline int pidfd_getfd(int pidfd __attribute__((unused)), } #endif +int pfd_is_pidfs(int pidfd); +ino_t pidfd_get_inode(int pidfd); +int ul_get_valid_pidfd_or_err(pid_t pid, uint64_t pidfd_ino __attribute__((__unused__))); + #endif /* UTIL_LINUX_PIDFD_UTILS */ diff --git a/include/pidutils.h b/include/pidutils.h index 9d3257dfdf3..b2c575c190b 100644 --- a/include/pidutils.h +++ b/include/pidutils.h @@ -9,6 +9,6 @@ #include -extern int ul_parse_pid_str(char *pidstr, pid_t *pid_num, ino_t *pfd_ino); +extern int ul_parse_pid_str(char *pidstr, pid_t *pid_num, uint64_t *pfd_ino); -#endif /* UTIL_LINUX_PIDUTILS_H */ \ No newline at end of file +#endif /* UTIL_LINUX_PIDUTILS_H */ diff --git a/include/pt-gpt-partnames.h b/include/pt-gpt-partnames.h index b17a636299f..cf12d2baa46 100644 --- a/include/pt-gpt-partnames.h +++ b/include/pt-gpt-partnames.h @@ -202,8 +202,8 @@ DEF_GUID("52637672-7900-11AA-AA11-00306543ECAC", N_("Apple Silicon recovery")), /* Solaris */ DEF_GUID("6A82CB45-1DD2-11B2-99A6-080020736631", N_("Solaris boot")), DEF_GUID("6A85CF4D-1DD2-11B2-99A6-080020736631", N_("Solaris root")), -/* same as Apple ZFS */ -DEF_GUID("6A898CC3-1DD2-11B2-99A6-080020736631", N_("Solaris /usr & Apple ZFS")), +/* ZFS pool member; originally Solaris /usr and briefly Apple ZFS */ +DEF_GUID("6A898CC3-1DD2-11B2-99A6-080020736631", N_("ZFS pool member")), DEF_GUID("6A87C46F-1DD2-11B2-99A6-080020736631", N_("Solaris swap")), DEF_GUID("6A8B642B-1DD2-11B2-99A6-080020736631", N_("Solaris backup")), DEF_GUID("6A8EF2E9-1DD2-11B2-99A6-080020736631", N_("Solaris /var")), diff --git a/include/pwdutils.h b/include/pwdutils.h index 1d44242746e..321b2ac5966 100644 --- a/include/pwdutils.h +++ b/include/pwdutils.h @@ -9,10 +9,12 @@ #include #include -extern struct passwd *xgetpwnam(const char *username, char **pwdbuf); -extern struct group *xgetgrnam(const char *groupname, char **grpbuf); extern struct passwd *xgetpwuid(uid_t uid, char **pwdbuf); +extern struct passwd *xgetuserpw(const char *str, char **pwdbuf); +extern struct group *xgetgroup(const char *str, char **pwdbuf); extern char *xgetlogin(void); +extern struct group *ul_getgrp_str(const char *str); +extern struct passwd *ul_getuserpw_str(const char *str); #endif /* UTIL_LINUX_PWDUTILS_H */ diff --git a/include/shells.h b/include/shells.h index c770a13ba98..7f2d2469c27 100644 --- a/include/shells.h +++ b/include/shells.h @@ -4,7 +4,14 @@ #ifndef UTIL_LINUX_SHELLS_H #define UTIL_LINUX_SHELLS_H +#include + +#define UL_SHELL_NOENV (1 << 0) +#define UL_SHELL_NOPWD (1 << 1) + extern void print_shells(FILE *out, const char *format); extern int is_known_shell(const char *shell_name); +const char *ul_default_shell(int flags, const struct passwd *pw); + #endif /* UTIL_LINUX_SHELLS_H */ diff --git a/include/statfs_magic.h b/include/statfs_magic.h index 857058a7314..6921abaa4dc 100644 --- a/include/statfs_magic.h +++ b/include/statfs_magic.h @@ -77,6 +77,7 @@ #define STATFS_OCFS2_MAGIC 0x7461636f #define STATFS_OMFS_MAGIC 0xC2993D87 #define STATFS_OPENPROMFS_MAGIC 0x9fa1 +#define STATFS_PIDFS_MAGIC 0x50494446 #define STATFS_PIPEFS_MAGIC 0x50495045 #define STATFS_PROC_MAGIC 0x9fa0 #define STATFS_PSTOREFS_MAGIC 0x6165676C diff --git a/lib/Makemodule.am b/lib/Makemodule.am index a9da577348b..9464bd9d2ef 100644 --- a/lib/Makemodule.am +++ b/lib/Makemodule.am @@ -34,6 +34,7 @@ libcommon_la_SOURCES = \ lib/netaddrq.c \ lib/netlink.c \ lib/pidutils.c \ + lib/pidfd-utils.c \ lib/pwdutils.c \ lib/randutils.c \ lib/sha1.c \ @@ -49,6 +50,7 @@ libcommon_la_SOURCES = \ if LINUX libcommon_la_SOURCES += \ lib/linux_version.c \ + lib/shells.c \ lib/loopdev.c endif @@ -231,6 +233,7 @@ test_timeutils_LDADD = $(LDADD) libcommon.la test_pwdutils_SOURCES = lib/pwdutils.c test_pwdutils_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM +test_pwdutils_LDADD = $(LDADD) libcommon.la test_remove_env_SOURCES = lib/env.c test_remove_env_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM_ENV diff --git a/lib/color-names.c b/lib/color-names.c index 88c4ef95774..daebcedaef5 100644 --- a/lib/color-names.c +++ b/lib/color-names.c @@ -54,7 +54,8 @@ const char *color_sequence_from_colorname(const char *str) { "white", UL_COLOR_WHITE }, { "yellow", UL_COLOR_BOLD_YELLOW } }; - struct ul_color_name key = { .name = str }, *res; + struct ul_color_name key = { .name = str }; + const struct ul_color_name *res; if (!str) return NULL; diff --git a/lib/exec_shell.c b/lib/exec_shell.c index ffe65f006e8..8f10ea4df49 100644 --- a/lib/exec_shell.c +++ b/lib/exec_shell.c @@ -26,19 +26,15 @@ #include "xalloc.h" #include "exec_shell.h" - -#define DEFAULT_SHELL "/bin/sh" +#include "shells.h" void __attribute__((__noreturn__)) exec_shell(void) { - const char *shell = getenv("SHELL"); + const char *shell = ul_default_shell(0, NULL); char *shellc; const char *shell_basename; char *arg0; - if (!shell) - shell = DEFAULT_SHELL; - shellc = xstrdup(shell); shell_basename = basename(shellc); xasprintf(&arg0, "-%s", shell_basename); diff --git a/lib/meson.build b/lib/meson.build index 0f94a9b99b3..db871913d29 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -20,11 +20,13 @@ lib_common_sources = ''' netaddrq.c netlink.c pidutils.c + pidfd-utils.c procfs.c pwdutils.c randutils.c sha1.c sha256.c + shells.c signames.c strutils.c strv.c diff --git a/lib/pidfd-utils.c b/lib/pidfd-utils.c new file mode 100644 index 00000000000..32b67a3b197 --- /dev/null +++ b/lib/pidfd-utils.c @@ -0,0 +1,97 @@ +/* + * No copyright is claimed. This code is in the public domain; do with + * it what you wish. + * + * Authors: Christian Goeschel Ndjomouo [2025] + */ +#include +#include +#include +#include +#include +#include + +#include "c.h" +#include "nls.h" +#include "strutils.h" +#include "pidfd-utils.h" +#include "statfs_magic.h" + +/* + * Returns 1, if the pidfd has the pidfs file system type, otherwise 0. + */ +int pfd_is_pidfs(int pidfd) +{ + struct statfs stfs; + int rc; + + rc = fstatfs(pidfd, &stfs); + if (rc < 0) + return 0; + + return F_TYPE_EQUAL(stfs.f_type, STATFS_PIDFS_MAGIC); +} + +#ifdef USE_PIDFD_INO_SUPPORT +uint64_t pidfd_get_inode(int pidfd) +{ + struct statx stx; + int rc; + + rc = statx(pidfd, "", AT_EMPTY_PATH, STATX_INO, &stx); + if (rc < 0) { + close(pidfd); + err(EXIT_FAILURE, N_("failed to statx() pidfd")); + } + return stx.stx_ino; +} +#endif + +/* + * ul_get_valid_pidfd_or_err() - Return a valid file descriptor for a PID + * or exit the process with an error message. + * + * @pid: PID number for which to get a file descriptor + * @pfd_ino: A pidfd inode number that is expected to be the + * same as for the new file descriptor. + * + * Pass @pfd_ino as NULL, if the pidfd should not be validated. + * + * Return: On success, a file descriptor is returned. + * On failure, err() or errx() is called to + * print an error message and kill the program. + * + */ +#ifdef USE_PIDFD_INO_SUPPORT +int ul_get_valid_pidfd_or_err(pid_t pid, uint64_t pidfd_ino) +#else +int ul_get_valid_pidfd_or_err(pid_t pid, uint64_t pidfd_ino __attribute__((__unused__))) +#endif +{ + int pfd; + + pfd = pidfd_open(pid, 0); + if (pfd < 0) + err(EXIT_FAILURE, N_("pidfd_open() failed")); + + /* the file descriptor has to have the pidfs file system type + * otherwise the inode assigned to it will not be useful. + */ + if (!pfd_is_pidfs(pfd)) { + close(pfd); + errx(EXIT_FAILURE, N_("pidfd needs to have the pidfs file system type")); + } + +#ifdef USE_PIDFD_INO_SUPPORT + uint64_t real_pidfd_ino; + if (pidfd_ino) { + real_pidfd_ino = pidfd_get_inode(pfd); + if (real_pidfd_ino != pidfd_ino) { + close(pfd); + errx(EXIT_FAILURE, N_("pidfd inode %"PRIu64" not found for pid %d"), + pidfd_ino, pid); + } + } +#endif + return pfd; +} diff --git a/lib/pidutils.c b/lib/pidutils.c index 56f3b857889..a7567eb3fd4 100644 --- a/lib/pidutils.c +++ b/lib/pidutils.c @@ -23,7 +23,7 @@ * Return: On success, 0 is returned. * On failure, a negative errno number will be returned. */ -int ul_parse_pid_str(char *pidstr, pid_t *pid_num, ino_t *pfd_ino) +int ul_parse_pid_str(char *pidstr, pid_t *pid_num, uint64_t *pfd_ino) { int rc; char *end = NULL; @@ -40,7 +40,7 @@ int ul_parse_pid_str(char *pidstr, pid_t *pid_num, ino_t *pfd_ino) if (*end == ':' && pfd_ino) { rc = ul_strtou64(++end, pfd_ino, 10); if (rc != 0) - return -ERANGE; + return -ERANGE; *end = '\0'; } if (errno != 0 || ((end && *end != '\0') || pidstr >= end)) diff --git a/lib/procfs.c b/lib/procfs.c index 47cc8eea2c7..ca34e54b066 100644 --- a/lib/procfs.c +++ b/lib/procfs.c @@ -414,7 +414,6 @@ int fd_is_procfs(int fd) } while (ret != 0); return F_TYPE_EQUAL(st.f_type, STATFS_PROC_MAGIC); - return 0; } #else int fd_is_procfs(int fd __attribute__((__unused__))) diff --git a/lib/pwdutils.c b/lib/pwdutils.c index 1c1f13e9293..35f1f7866cc 100644 --- a/lib/pwdutils.c +++ b/lib/pwdutils.c @@ -1,31 +1,28 @@ /* - * No copyright is claimed. This code is in the public domain; do with + * No copyright is claimed. This code is in the public domain; do with * it what you wish. */ #include #include +#include #include "c.h" #include "pwdutils.h" #include "xalloc.h" +#include "strutils.h" -/* Returns allocated passwd and allocated pwdbuf to store passwd strings - * fields. In case of error returns NULL and set errno, for unknown user set - * errno to EINVAL - */ -struct passwd *xgetpwnam(const char *username, char **pwdbuf) +struct passwd *xgetpwuid(uid_t uid, char **pwdbuf) { struct passwd *pwd = NULL, *res = NULL; int rc; assert(pwdbuf); - assert(username); *pwdbuf = xmalloc(UL_GETPW_BUFSIZ); pwd = xcalloc(1, sizeof(struct passwd)); errno = 0; - rc = getpwnam_r(username, pwd, *pwdbuf, UL_GETPW_BUFSIZ, &res); + rc = getpwuid_r(uid, pwd, *pwdbuf, UL_GETPW_BUFSIZ, &res); if (rc != 0) { errno = rc; goto failed; @@ -41,23 +38,39 @@ struct passwd *xgetpwnam(const char *username, char **pwdbuf) return NULL; } -/* Returns allocated group and allocated grpbuf to store group strings - * fields. In case of error returns NULL and set errno, for unknown group set - * errno to EINVAL +/* Returns allocated passwd and allocated pwdbuf for the username or UID passed + * as @str. In case of error returns NULL and set errno, for unknown user it + * sets errno to EINVAL. */ -struct group *xgetgrnam(const char *groupname, char **grpbuf) +struct passwd *xgetuserpw(const char *str ,char **pwdbuf) { - struct group *grp = NULL, *res = NULL; + struct passwd *pwd = NULL, *res = NULL; int rc; + uint64_t uid; - assert(grpbuf); - assert(groupname); + assert(pwdbuf); + assert(str); - *grpbuf = xmalloc(UL_GETPW_BUFSIZ); - grp = xcalloc(1, sizeof(struct group)); + *pwdbuf = xmalloc(UL_GETPW_BUFSIZ); + pwd = xcalloc(1, sizeof(struct passwd)); + + /* is @str a UID ? */ + rc = ul_strtou64(str, &uid, 10); + if (rc == -ERANGE) { + errno = ERANGE; + goto failed; + } + /* @str is an invalid number, let's assume it is the username */ + if (rc == -EINVAL) { + rc = getpwnam_r(str, pwd, *pwdbuf, UL_GETPW_BUFSIZ, &res); + } else { + if (uid > MAX_OF_UINT_TYPE(uid_t)) { + errno = ERANGE; + goto failed; + } + rc = getpwuid_r((uid_t)uid, pwd, *pwdbuf, UL_GETPW_BUFSIZ, &res); + } - errno = 0; - rc = getgrnam_r(groupname, grp, *grpbuf, UL_GETPW_BUFSIZ, &res); if (rc != 0) { errno = rc; goto failed; @@ -66,25 +79,46 @@ struct group *xgetgrnam(const char *groupname, char **grpbuf) errno = EINVAL; goto failed; } - return grp; + return pwd; failed: - free(grp); - free(*grpbuf); + free(pwd); + free(*pwdbuf); return NULL; } -struct passwd *xgetpwuid(uid_t uid, char **pwdbuf) +/* Returns allocated group and allocated grpbuf for the group name or GID passed + * as @str. In case of error returns NULL and set errno, for unknown group it + * sets errno to EINVAL. + */ +struct group *xgetgroup(const char *str, char **grpbuf) { - struct passwd *pwd = NULL, *res = NULL; + struct group *grp = NULL, *res = NULL; int rc; + uint64_t gid; - assert(pwdbuf); + assert(grpbuf); + assert(str); - *pwdbuf = xmalloc(UL_GETPW_BUFSIZ); - pwd = xcalloc(1, sizeof(struct passwd)); + *grpbuf = xmalloc(UL_GETPW_BUFSIZ); + grp = xcalloc(1, sizeof(struct group)); + + /* is @str a GID ? */ + rc = ul_strtou64(str, &gid, 10); + if (rc == -ERANGE) { + errno = ERANGE; + goto failed; + } + /* @str is an invalid number, let's assume it is the group name */ + if (rc == -EINVAL) { + rc = getgrnam_r(str, grp, *grpbuf, UL_GETPW_BUFSIZ, &res); + } else { + if (gid > MAX_OF_UINT_TYPE(gid_t)) { + errno = ERANGE; + goto failed; + } + rc = getgrgid_r((gid_t)gid, grp, *grpbuf, UL_GETPW_BUFSIZ, &res); + } - errno = 0; - rc = getpwuid_r(uid, pwd, *pwdbuf, UL_GETPW_BUFSIZ, &res); if (rc != 0) { errno = rc; goto failed; @@ -93,10 +127,10 @@ struct passwd *xgetpwuid(uid_t uid, char **pwdbuf) errno = EINVAL; goto failed; } - return pwd; + return grp; failed: - free(pwd); - free(*pwdbuf); + free(grp); + free(*grpbuf); return NULL; } @@ -127,6 +161,44 @@ char *xgetlogin(void) return NULL; } +/* + * Return a pointer to a `struct group` for a matching group name or GID. + */ +struct group *ul_getgrp_str(const char *str) +{ + int rc; + uint64_t gid; + + rc = ul_strtou64(str, &gid, 10); + if (rc == -ERANGE) + return NULL; + if (rc == -EINVAL) + return getgrnam(str); + if (gid > MAX_OF_UINT_TYPE(gid_t)) + return NULL; + + return getgrgid((gid_t)gid); +} + +/* + * Return a pointer to a `struct passwd` for a matching username or UID. + */ +struct passwd *ul_getuserpw_str(const char *str) +{ + int rc; + uint64_t uid; + + rc = ul_strtou64(str, &uid, 10); + if (rc == -ERANGE) + return NULL; + if (rc == -EINVAL) + return getpwnam(str); + if (uid > MAX_OF_UINT_TYPE(uid_t)) + return NULL; + + return getpwuid((uid_t)uid); +} + #ifdef TEST_PROGRAM int main(int argc, char *argv[]) { @@ -138,7 +210,7 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - pwd = xgetpwnam(argv[1], &buf); + pwd = xgetuserpw(argv[1], &buf); if (!pwd) err(EXIT_FAILURE, "failed to get %s pwd entry", argv[1]); diff --git a/lib/shells.c b/lib/shells.c index 13f293c5e0c..ef2aecd0f5e 100644 --- a/lib/shells.c +++ b/lib/shells.c @@ -1,6 +1,11 @@ /* * SPDX-License-Identifier: GPL-2.0-or-later */ +#include +#include +#include +#include +#include #include #if defined (HAVE_LIBECONF) && defined (USE_VENDORDIR) #include @@ -116,3 +121,24 @@ extern int is_known_shell(const char *shell_name) #endif return ret; } + +const char *ul_default_shell(int flags, const struct passwd *pw) +{ + const char *shell = NULL; + + if (!(flags & UL_SHELL_NOENV)) { + shell = getenv("SHELL"); + if (shell && *shell) + return shell; + } + if (!(flags & UL_SHELL_NOPWD)) { + if (!pw) + pw = getpwuid(getuid()); + if (pw) + shell = pw->pw_shell; + if (shell && *shell) + return shell; + } + + return _PATH_BSHELL; +} diff --git a/libblkid/src/blkid.h.in b/libblkid/src/blkid.h.in index db70d9e5ad2..a0b381ccbaf 100644 --- a/libblkid/src/blkid.h.in +++ b/libblkid/src/blkid.h.in @@ -339,7 +339,7 @@ extern uint64_t blkid_topology_get_diskseq(blkid_topology tp) * partitions probing */ extern int blkid_known_pttype(const char *pttype); -extern int blkid_partitions_get_name(const size_t idx, const char **name); +extern int blkid_partitions_get_name(size_t idx, const char **name); extern int blkid_probe_enable_partitions(blkid_probe pr, int enable) __ul_attribute__((nonnull)); diff --git a/libblkid/src/partitions/partitions.c b/libblkid/src/partitions/partitions.c index 9df813c9214..e838b3db1f7 100644 --- a/libblkid/src/partitions/partitions.c +++ b/libblkid/src/partitions/partitions.c @@ -907,7 +907,7 @@ int blkid_known_pttype(const char *pttype) * * Returns: -1 if @idx is out of range, or 0 on success. */ -int blkid_partitions_get_name(const size_t idx, const char **name) +int blkid_partitions_get_name(size_t idx, const char **name) { if (idx < ARRAY_SIZE(idinfos)) { *name = idinfos[idx]->name; diff --git a/libblkid/src/superblocks/ntfs.c b/libblkid/src/superblocks/ntfs.c index 42c8565df4e..8ce557a113c 100644 --- a/libblkid/src/superblocks/ntfs.c +++ b/libblkid/src/superblocks/ntfs.c @@ -248,7 +248,7 @@ int blkid_probe_is_ntfs(blkid_probe pr) const struct blkid_idinfo ntfs_idinfo = { - .name = "ntfs3", + .name = "ntfs", .usage = BLKID_USAGE_FILESYSTEM, .probefunc = probe_ntfs, .magics = diff --git a/libblkid/src/tag.c b/libblkid/src/tag.c index 178336505fd..f195c0625cf 100644 --- a/libblkid/src/tag.c +++ b/libblkid/src/tag.c @@ -202,21 +202,23 @@ int blkid_set_tag(blkid_dev dev, const char *name, */ int blkid_parse_tag_string(const char *token, char **ret_type, char **ret_val) { - char *name, *value, *cp; + char *name, *value; + const char *eq; DBG(TAG, ul_debug("trying to parse '%s' as a tag", token)); - if (!token || !(cp = strchr(token, '='))) + if (!token || !(eq = strchr(token, '='))) return -1; name = strdup(token); if (!name) return -1; - value = name + (cp - token); + value = name + (eq - token); *value++ = '\0'; if (*value == '"' || *value == '\'') { char c = *value++; - if (!(cp = strrchr(value, c))) + char *cp = strrchr(value, c); + if (!cp) goto errout; /* missing closing quote */ *cp = '\0'; } diff --git a/libfdisk/src/dos.c b/libfdisk/src/dos.c index db7e257164b..c88d2a4f213 100644 --- a/libfdisk/src/dos.c +++ b/libfdisk/src/dos.c @@ -1241,8 +1241,8 @@ static int get_disk_ranges(struct fdisk_context *cxt, int logical, else *last = cxt->total_sectors - 1; - if (*last > UINT_MAX) - *last = UINT_MAX; + if (*last >= UINT_MAX) + *last = UINT_MAX - 1; *first = cxt->first_lba; } diff --git a/libmount/meson.build b/libmount/meson.build index 28feadf4845..558f30468aa 100644 --- a/libmount/meson.build +++ b/libmount/meson.build @@ -98,6 +98,11 @@ lib__mount_deps = [ cryptsetup_dlopen ? lib_dl : lib_cryptsetup, realtime_libs ] + +if conf.get('USE_LIBMOUNT_UDEV_SUPPORT').to_string() == '1' + lib__mount_deps += [lib_systemd] +endif + lib_mount = library( 'mount', link_whole : lib__mount, diff --git a/libmount/src/Makemodule.am b/libmount/src/Makemodule.am index 28dbc581130..bbd83b8bcd4 100644 --- a/libmount/src/Makemodule.am +++ b/libmount/src/Makemodule.am @@ -69,6 +69,10 @@ libmount_la_LIBADD += $(CRYPTSETUP_LIBS) endif endif +if USE_LIBMOUNT_UDEV_SUPPORT +libmount_la_LIBADD += $(SYSTEMD_LIBS) +endif + libmount_la_CFLAGS = \ $(AM_CFLAGS) \ $(SOLIB_CFLAGS) \ diff --git a/libmount/src/cache.c b/libmount/src/cache.c index 29426544915..4d2a20f235f 100644 --- a/libmount/src/cache.c +++ b/libmount/src/cache.c @@ -30,10 +30,16 @@ #include #include +/* sd-device is a replacement for libudev */ +#ifdef USE_LIBMOUNT_UDEV_SUPPORT +# include +#endif + #include "canonicalize.h" #include "mountP.h" #include "loopdev.h" #include "strutils.h" +#include "mangle.h" /* * Canonicalized (resolved) paths & tags cache @@ -72,6 +78,25 @@ struct libmnt_cache { struct libmnt_table *mountinfo; }; + +struct libmnt_cachetag { + const char *mnt_name; /* tag name used by libmount */ + const char *blk_name; /* tag name used by libblkid */ + const char *udev_name; /* tag name used by udev db */ +}; + +static const struct libmnt_cachetag mnttags[] = +{ + /* mount blkid udev */ + { "LABEL", "LABEL", "ID_FS_LABEL_ENC" }, + { "UUID", "UUID", "ID_FS_UUID_ENC" }, + { "TYPE", "TYPE", "ID_FS_TYPE" }, + { "PARTUUID", "PART_ENTRY_UUID", "ID_PART_ENTRY_UUID" }, + { "PARTLABEL", "PART_ENTRY_NAME", "ID_PART_ENTRY_NAME" }, + + { NULL, NULL } +}; + /** * mnt_new_cache: * @@ -324,42 +349,41 @@ static char *cache_find_tag_value(struct libmnt_cache *cache, return NULL; } -/** - * mnt_cache_read_tags - * @cache: pointer to struct libmnt_cache instance - * @devname: path device - * - * Reads @devname LABEL and UUID to the @cache. - * - * Returns: 0 if at least one tag was added, 1 if no tag was added or - * negative number in case of error. - */ -int mnt_cache_read_tags(struct libmnt_cache *cache, const char *devname) +static bool is_device_cached(struct libmnt_cache *cache, const char *devname) { - blkid_probe pr; - size_t i, ntags = 0; - int rc; - const char *tags[] = { "LABEL", "UUID", "TYPE", "PARTUUID", "PARTLABEL" }; - const char *blktags[] = { "LABEL", "UUID", "TYPE", "PART_ENTRY_UUID", "PART_ENTRY_NAME" }; - - if (!cache || !devname) - return -EINVAL; - - DBG(CACHE, ul_debugobj(cache, "tags for %s requested", devname)); + size_t i; - /* check if device is already cached */ for (i = 0; i < cache->nents; i++) { struct mnt_cache_entry *e = &cache->ents[i]; if (!(e->flag & MNT_CACHE_TAGREAD)) continue; if (strcmp(e->value, devname) == 0) - /* tags have already been read */ - return 0; + return 1; /* already in cache */ } + return 0; +} + +/* + * read data from libblkid into local cache + * returns: <0 on error; 0 success; 1 nothing +*/ +static int read_from_blkid(struct libmnt_cache *cache, const char *devname) +{ + blkid_probe pr; + const struct libmnt_cachetag *t; + size_t ntags = 0; + int rc; + char *cacheval = NULL; + + assert(cache); + assert(devname); + + DBG(CACHE, ul_debugobj(cache, "%s: reading from blkid", devname)); + pr = blkid_new_probe_from_filename(devname); if (!pr) - return -1; + return -EINVAL; blkid_probe_enable_superblocks(pr, 1); blkid_probe_set_superblocks_flags(pr, @@ -371,38 +395,114 @@ int mnt_cache_read_tags(struct libmnt_cache *cache, const char *devname) rc = blkid_do_safeprobe(pr); if (rc) - goto error; - - DBG(CACHE, ul_debugobj(cache, "reading tags for: %s", devname)); + goto done; - for (i = 0; i < ARRAY_SIZE(tags); i++) { + for (t = mnttags; t && t->mnt_name; t++) { const char *data; - char *dev; - if (cache_find_tag_value(cache, devname, tags[i])) { - DBG(CACHE, ul_debugobj(cache, - "\ntag %s already cached", tags[i])); + if (cache_find_tag_value(cache, devname, t->mnt_name)) continue; - } - if (blkid_probe_lookup_value(pr, blktags[i], &data, NULL)) + if (blkid_probe_lookup_value(pr, t->blk_name, &data, NULL)) continue; - dev = strdup(devname); - if (!dev) - goto error; - if (cache_add_tag(cache, tags[i], data, dev, - MNT_CACHE_TAGREAD)) { - free(dev); - goto error; - } + + cacheval = strdup(devname); + rc = !cacheval ? -ENOMEM : + cache_add_tag(cache, t->mnt_name, data, cacheval, MNT_CACHE_TAGREAD); + if (rc) + break; ntags++; + cacheval = NULL; /* stored into cache */ } - DBG(CACHE, ul_debugobj(cache, "\tread %zd tags", ntags)); - blkid_free_probe(pr); - return ntags ? 0 : 1; -error: +done: + DBG(CACHE, ul_debugobj(cache, "\tread %zd tags [rc=%d]", ntags, rc)); blkid_free_probe(pr); - return rc < 0 ? rc : -1; + free(cacheval); + + return rc ? rc : ntags ? 0 : 1; +} + +#ifdef USE_LIBMOUNT_UDEV_SUPPORT +/* + * read data from udev into local cache + * returns: <0 on error; 0 success; 1 nothing + */ +static int read_from_udev(struct libmnt_cache *cache, const char *devname) +{ + sd_device *sd = NULL; + const struct libmnt_cachetag *t; + size_t ntags = 0; + char *tagval = NULL, *cacheval = NULL; + int rc; + + assert(cache); + assert(devname); + + rc = sd_device_new_from_devname(&sd, devname); + if (rc < 0) + return rc; + + DBG(CACHE, ul_debugobj(cache, "%s: reading from udev", devname)); + + for (t = mnttags; t && t->mnt_name; t++) { + const char *data; + + if (cache_find_tag_value(cache, devname, t->mnt_name)) + continue; + if (sd_device_get_property_value(sd, t->udev_name, &data) < 0) + continue; + + tagval = strdup(data); /* temporary for unhexmangle() */ + cacheval = strdup(devname); + + if (tagval && cacheval) { + unhexmangle_string(tagval); + rc = cache_add_tag(cache, t->mnt_name, + tagval, cacheval, MNT_CACHE_TAGREAD); + } else + rc = -ENOMEM; + if (rc) + break; + ntags++; + cacheval = NULL; /* stored into cache */ + free(tagval), tagval = NULL; + } + + DBG(CACHE, ul_debugobj(cache, "\tread %zd tags [rc=%d]", ntags, rc)); + sd_device_unref(sd); + free(cacheval); + free(tagval); + + return rc ? rc : ntags ? 0 : 1; +} +#endif /* USE_LIBMOUNT_UDEV_SUPPORT */ + + +/** + * mnt_cache_read_tags + * @cache: pointer to struct libmnt_cache instance + * @devname: path device + * + * Reads @devname information into the @cache. + * + * Returns: 0 if at least one tag was added, 1 if no tag was added or + * negative number in case of error. + */ +int mnt_cache_read_tags(struct libmnt_cache *cache, const char *devname) +{ + if (!cache || !devname) + return -EINVAL; + + DBG(CACHE, ul_debugobj(cache, "tags for %s requested", devname)); + + if (is_device_cached(cache, devname)) + return 0; + +#ifdef USE_LIBMOUNT_UDEV_SUPPORT + if (read_from_udev(cache, devname) == 0) + return 0; +#endif + return read_from_blkid(cache, devname); } /** @@ -446,7 +546,7 @@ static int __mnt_cache_find_tag_value(struct libmnt_cache *cache, * mnt_cache_find_tag_value: * @cache: cache for results * @devname: device name - * @token: tag name ("LABEL" or "UUID") + * @token: tag name ("LABEL", "UUID", ...) * * Returns: LABEL or UUID for the @devname or NULL in case of error. */ @@ -460,35 +560,24 @@ char *mnt_cache_find_tag_value(struct libmnt_cache *cache, return NULL; } -/** - * mnt_get_fstype: - * @devname: device name - * @ambi: returns TRUE if probing result is ambivalent (optional argument) - * @cache: cache for results or NULL - * - * Returns: filesystem type or NULL in case of error. The result has to be - * deallocated by free() if @cache is NULL. - */ -char *mnt_get_fstype(const char *devname, int *ambi, struct libmnt_cache *cache) +static char *fstype_from_cache(const char *devname, struct libmnt_cache *cache) +{ + char *val = NULL; + + assert(cache); + + if (__mnt_cache_find_tag_value(cache, devname, "TYPE", &val) != 0) + return NULL; + return val; +} + +static char *fstype_from_blkid(const char *devname, int *ambi) { blkid_probe pr; const char *data; char *type = NULL; int rc; - DBG(CACHE, ul_debugobj(cache, "get %s FS type", devname)); - - if (cache) { - char *val = NULL; - rc = __mnt_cache_find_tag_value(cache, devname, "TYPE", &val); - if (ambi) - *ambi = rc == -2 ? TRUE : FALSE; - return rc ? NULL : val; - } - - /* - * no cache, probe directly - */ pr = blkid_new_probe_from_filename(devname); if (!pr) return NULL; @@ -498,11 +587,8 @@ char *mnt_get_fstype(const char *devname, int *ambi, struct libmnt_cache *cache) rc = blkid_do_safeprobe(pr); - DBG(CACHE, ul_debugobj(cache, "libblkid rc=%d", rc)); - if (!rc && !blkid_probe_lookup_value(pr, "TYPE", &data, NULL)) type = strdup(data); - if (ambi) *ambi = rc == -2 ? TRUE : FALSE; @@ -510,6 +596,28 @@ char *mnt_get_fstype(const char *devname, int *ambi, struct libmnt_cache *cache) return type; } +/** + * mnt_get_fstype: + * @devname: device name + * @ambi: returns TRUE if probing result is ambivalent (optional argument) + * @cache: cache for results or NULL + * + * Note: If the cache is not specified, it reads the file system type from the + * device, and in this case, there is no optimization like udev db, etc. * + * + * Returns: filesystem type or NULL in case of error. The result has to be + * deallocated by free() if @cache is NULL. + */ +char *mnt_get_fstype(const char *devname, int *ambi, struct libmnt_cache *cache) +{ + DBG(CACHE, ul_debugobj(cache, "get %s FS type", devname)); + + if (cache) + return fstype_from_cache(devname, cache); + + return fstype_from_blkid(devname, ambi); +} + static char *canonicalize_path_and_cache(const char *path, struct libmnt_cache *cache) { diff --git a/libmount/src/context.c b/libmount/src/context.c index 66b5c2d8f1b..6dfc5913dfd 100644 --- a/libmount/src/context.c +++ b/libmount/src/context.c @@ -2090,6 +2090,17 @@ int mnt_context_guess_srcpath_fstype(struct libmnt_context *cxt, char **type) } } + if (rc == 0 && *type) { + const char *x = mnt_fstype_to_mounttype(*type); + + if (x) { + free(*type); + *type = strdup(x); + if (!*type) + rc = -ENOMEM; + } + } + return rc; } diff --git a/libmount/src/context_mount.c b/libmount/src/context_mount.c index c6d854f7a63..2de6766cda5 100644 --- a/libmount/src/context_mount.c +++ b/libmount/src/context_mount.c @@ -1584,7 +1584,7 @@ int mnt_context_get_mount_excode( return MNT_EX_USAGE; case -MNT_ERR_LOOPDEV: if (buf) - snprintf(buf, bufsz, _("failed to setup loop device for %s"), src); + snprintf(buf, bufsz, _("failed to set up loop device for %s"), src); return MNT_EX_FAIL; case -MNT_ERR_LOOPOVERLAP: if (buf) diff --git a/libmount/src/context_umount.c b/libmount/src/context_umount.c index 8fbd662ab48..854b704b05a 100644 --- a/libmount/src/context_umount.c +++ b/libmount/src/context_umount.c @@ -298,7 +298,9 @@ static int lookup_umount_fs_by_statfs(struct libmnt_context *cxt, const char *tg close(fd); } if (type) { - int rc = mnt_fs_set_fstype(cxt->fs, type); + const char *x = mnt_fstype_to_mounttype(type); + int rc = mnt_fs_set_fstype(cxt->fs, x ? x : type); + if (rc) return rc; } diff --git a/libmount/src/hook_idmap.c b/libmount/src/hook_idmap.c index 5095de1f6ac..821269f1e91 100644 --- a/libmount/src/hook_idmap.c +++ b/libmount/src/hook_idmap.c @@ -492,7 +492,7 @@ static int hook_prepare_options( return 0; err: - DBG(HOOK, ul_debugobj(hs, " failed to setup idmap")); + DBG(HOOK, ul_debugobj(hs, " failed to set up idmap")); free_hook_data(hd); free(buf); return -MNT_ERR_MOUNTOPT; diff --git a/libmount/src/hook_loopdev.c b/libmount/src/hook_loopdev.c index b72164535c5..0bb97e59b4e 100644 --- a/libmount/src/hook_loopdev.c +++ b/libmount/src/hook_loopdev.c @@ -138,7 +138,7 @@ static int setup_loopdev(struct libmnt_context *cxt, if (!backing_file) return -EINVAL; - DBG(LOOP, ul_debugobj(cxt, "trying to setup device for %s", backing_file)); + DBG(LOOP, ul_debugobj(cxt, "trying to set up device for %s", backing_file)); if (mnt_optlist_is_rdonly(ol)) { DBG(LOOP, ul_debugobj(cxt, "enabling READ-ONLY flag")); @@ -332,7 +332,7 @@ static int setup_loopdev(struct libmnt_context *cxt, break; /* success */ if (loopdev || rc != -EBUSY) { - DBG(LOOP, ul_debugobj(cxt, "failed to setup device")); + DBG(LOOP, ul_debugobj(cxt, "failed to set up device")); rc = -MNT_ERR_LOOPDEV; goto done; } diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h index fb01041f6b1..02b86585cd6 100644 --- a/libmount/src/mountP.h +++ b/libmount/src/mountP.h @@ -109,6 +109,7 @@ struct libmnt_listmnt; extern int mnt_valid_tagname(const char *tagname); extern const char *mnt_statfs_get_fstype(struct statfs *vfs); +extern const char *mnt_fstype_to_mounttype(const char *fstype); extern int is_file_empty(const char *name); extern int mnt_is_readonly(const char *path) diff --git a/libmount/src/optlist.c b/libmount/src/optlist.c index 256c592fdec..1d32c2dd6ea 100644 --- a/libmount/src/optlist.c +++ b/libmount/src/optlist.c @@ -601,7 +601,7 @@ static int optlist_add_flags(struct libmnt_optlist *ls, unsigned long flags, for (ent = map; ent && ent->name; ent++) { - char *p; + const char *p; size_t sz; struct libmnt_opt *opt; @@ -621,7 +621,7 @@ static int optlist_add_flags(struct libmnt_optlist *ls, unsigned long flags, sz = p - ent->name; p -= sz; } else { - p = (char *) ent->name; + p = ent->name; sz = strlen(ent->name); /* alone "name" */ } @@ -1162,8 +1162,7 @@ const char *mnt_opt_get_value(struct libmnt_opt *opt) /* check if option value is @str or comma separated @str */ int mnt_opt_value_with(struct libmnt_opt *opt, const char *str) { - char *p; - const char *start = opt->value; + const char *p, *start = opt->value; size_t len; if (!str || !opt->value || !*opt->value) diff --git a/libmount/src/optstr.c b/libmount/src/optstr.c index ac20d875571..51526973dfa 100644 --- a/libmount/src/optstr.c +++ b/libmount/src/optstr.c @@ -807,7 +807,7 @@ int mnt_optstr_apply_flags(char **optstr, unsigned long flags, const struct libmnt_optmap *ent; struct ul_buffer buf = UL_INIT_BUFFER; size_t sz; - char *p; + const char *p; ul_buffer_refer_string(&buf, *optstr); diff --git a/libmount/src/tab_parse.c b/libmount/src/tab_parse.c index bd15ad90985..7ec4c401635 100644 --- a/libmount/src/tab_parse.c +++ b/libmount/src/tab_parse.c @@ -186,6 +186,7 @@ static int mnt_parse_mountinfo_line(struct libmnt_fs *fs, const char *s) int rc = 0; unsigned int maj, min; char *p; + const char *sep; fs->flags |= MNT_FS_KERNEL; mnt_fs_mark_attached(fs); @@ -243,15 +244,15 @@ static int mnt_parse_mountinfo_line(struct libmnt_fs *fs, const char *s) } /* (7) optional fields, terminated by " - " */ - p = strstr(s, " - "); - if (!p) { + sep = strstr(s, " - "); + if (!sep) { DBG(TAB, ul_debug("mountinfo parse error: separator not found")); return -EINVAL; } - if (p > s + 1) - fs->opt_fields = strndup(s + 1, p - s - 1); + if (sep > s + 1) + fs->opt_fields = strndup(s + 1, sep - s - 1); - s = skip_separator(p + 3); + s = skip_separator(sep + 3); /* (8) FS type */ p = unmangle(s, &s); diff --git a/libmount/src/utils.c b/libmount/src/utils.c index 7626b6f91ee..49166f96fd7 100644 --- a/libmount/src/utils.c +++ b/libmount/src/utils.c @@ -506,10 +506,11 @@ const char *mnt_statfs_get_fstype(struct statfs *vfs) case STATFS_NCP_MAGIC: return "ncp"; case STATFS_NFS_MAGIC: return "nfs"; case STATFS_NILFS_MAGIC: return "nilfs2"; - case STATFS_NTFS_MAGIC: return "ntfs3"; + case STATFS_NTFS_MAGIC: return "ntfs"; case STATFS_OCFS2_MAGIC: return "ocfs2"; case STATFS_OMFS_MAGIC: return "omfs"; case STATFS_OPENPROMFS_MAGIC: return "openpromfs"; + case STATFS_PIDFS_MAGIC: return "pidfs"; case STATFS_PIPEFS_MAGIC: return "pipefs"; case STATFS_PROC_MAGIC: return "proc"; case STATFS_PSTOREFS_MAGIC: return "pstore"; @@ -541,6 +542,31 @@ const char *mnt_statfs_get_fstype(struct statfs *vfs) return NULL; } +/* + * Default NTFS mount type (used by libmount and libblkid) + */ +#ifndef CONFIG_UL_NTFS_MOUNTTYPE +# define CONFIG_UL_NTFS_MOUNTTYPE "ntfs3" +#endif + +/* + * Convert FS-type (as provided by libblkid or udev) to the preferred + * kernel FS driver (type used to mount the FS). + * + * This is a temporary solution; the final solution should be + * based on config files like /etc/mount/fs.d/ (from lib/configs.c). + */ +const char *mnt_fstype_to_mounttype(const char *fstype) +{ + if (!fstype) + return NULL; + + if (strcmp(fstype, "ntfs") == 0) + return CONFIG_UL_NTFS_MOUNTTYPE; + + return NULL; +} + /** * mnt_match_fstype: * @type: filesystem type diff --git a/libmount/src/version.c b/libmount/src/version.c index 19326d280cb..5ec0d490cde 100644 --- a/libmount/src/version.c +++ b/libmount/src/version.c @@ -52,6 +52,9 @@ static const char *lib_features[] = { #ifdef HAVE_STRUCT_FANOTIFY_EVENT_INFO_HEADER "fanotify", #endif +#ifdef USE_LIBMOUNT_UDEV_SUPPORT + "udev", +#endif #if !defined(NDEBUG) "assert", /* libc assert.h stuff */ #endif diff --git a/libuuid/meson.build b/libuuid/meson.build index 5115944adad..17b759a0c05 100644 --- a/libuuid/meson.build +++ b/libuuid/meson.build @@ -31,8 +31,6 @@ if cc.has_link_argument('-Wl,--version-script=@0@'.format(libuuid_sym_path)) libuuid_link_args += ['-Wl,--version-script=@0@'.format(libuuid_sym_path)] endif -thread_dep = dependency('threads') - lib_uuid = library( 'uuid', list_h, @@ -47,7 +45,7 @@ lib_uuid = library( link_depends : libuuid_link_depends, version : libuuid_version, link_args : libuuid_link_args, - dependencies : [socket_libs, thread_dep, + dependencies : [socket_libs, thread_libs, build_libuuid ? [] : disabler()], install : build_libuuid) uuid_dep = declare_dependency(link_with: lib_uuid, include_directories: dir_libuuid) diff --git a/login-utils/chfn.c b/login-utils/chfn.c index 059432b3d28..8577916a286 100644 --- a/login-utils/chfn.c +++ b/login-utils/chfn.c @@ -44,6 +44,7 @@ #include "logindefs.h" #include "ch-common.h" +#include "pwdutils.h" #ifdef HAVE_LIBSELINUX # include @@ -88,7 +89,7 @@ static void __attribute__((__noreturn__)) usage(void) { FILE *fp = stdout; fputs(USAGE_HEADER, fp); - fprintf(fp, _(" %s [options] []\n"), program_invocation_short_name); + fprintf(fp, _(" %s [options] [|]\n"), program_invocation_short_name); fputs(USAGE_SEPARATOR, fp); fputs(_("Change your finger information.\n"), fp); @@ -186,7 +187,7 @@ static void parse_argv(struct chfn_control *ctl, int argc, char **argv) /* done parsing arguments. check for a username. */ if (optind < argc) { if (optind + 1 < argc) { - warnx(_("cannot handle multiple usernames")); + warnx(_("cannot handle multiple usernames or UIDs")); errtryhelp(EXIT_FAILURE); } ctl->username = argv[optind]; @@ -417,13 +418,13 @@ int main(int argc, char **argv) if (!ctl.pw) errx(EXIT_FAILURE, _("you (user %d) don't exist."), uid); - ctl.username = ctl.pw->pw_name; } else { - ctl.pw = getpwnam(ctl.username); + ctl.pw = ul_getuserpw_str(ctl.username); if (!ctl.pw) errx(EXIT_FAILURE, _("user \"%s\" does not exist."), ctl.username); } + ctl.username = ctl.pw->pw_name; parse_passwd(&ctl); #ifndef HAVE_LIBUSER if (!(is_local(ctl.username))) diff --git a/login-utils/chsh.c b/login-utils/chsh.c index 3850bb843ad..0723367389c 100644 --- a/login-utils/chsh.c +++ b/login-utils/chsh.c @@ -207,7 +207,7 @@ int main(int argc, char **argv) errx(EXIT_FAILURE, _("you (user %d) don't exist."), uid); } else { - pw = xgetpwnam(info.username, &pwbuf); + pw = xgetuserpw(info.username, &pwbuf); if (!pw) errx(EXIT_FAILURE, _("user \"%s\" does not exist."), info.username); diff --git a/login-utils/login.1.adoc b/login-utils/login.1.adoc index 0b380942ac7..de89b03c6ff 100644 --- a/login-utils/login.1.adoc +++ b/login-utils/login.1.adoc @@ -18,7 +18,7 @@ login - begin session on the system == SYNOPSIS -*login* [*-p*] [*-h* _host_] [*-H*] [*-f* _username_|_username_] +*login* [*-p*] [*-s* _shell_] [*-h* _host_] [*-H*] [*-f* _username_|_username_] == DESCRIPTION @@ -26,7 +26,7 @@ login - begin session on the system The user is then prompted for a password, where appropriate. Echoing is disabled to prevent revealing the password. Only a number of password failures are permitted before *login* exits and the communications link is severed. See *LOGIN_RETRIES* in the *CONFIG FILE ITEMS* section. -If password aging has been enabled for the account, the user may be prompted for a new password before proceeding. In such case old password must be provided and the new password entered before continuing. Please refer to *passwd*(1) for more information. +If password aging has been enabled for the account, the user may be prompted for a new password before proceeding. In such a case, the old password must be provided and the new password entered before continuing. Please refer to *passwd*(1) for more information. The user and group ID will be set according to their values in the _/etc/passwd_ file. There is one exception if the user ID is zero. In this case, only the primary group ID of the account is set. This should allow the system administrator to login even in case of network problems. The environment variable values for *$HOME*, *$USER*, *$SHELL*, *$PATH*, *$LOGNAME*, and *$MAIL* are set according to the appropriate fields in the password entry. *$PATH* defaults to _/usr/local/bin:/bin:/usr/bin_ for normal users, and to _/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin_ for root, if not otherwise configured. @@ -36,7 +36,7 @@ Other environment variables are preserved if the *-p* option is given or if *LOG The environment variables defined by PAM are always preserved. -Then the user's shell is started. If no shell is specified for the user in _/etc/passwd_, then _/bin/sh_ is used. If the specified shell contains a space, it is treated as a shell script. If there is no home directory specified in _/etc/passwd_, then _/_ is used, followed by _.hushlogin_ check as described below. +Then the user's shell is started. If no shell is specified for the user with *-s* or in _/etc/passwd_, then _/bin/sh_ is used. If the specified shell contains a space, it is treated as a shell script. If there is no home directory specified in _/etc/passwd_, then _/_ is used, followed by _.hushlogin_ check as described below. If the file _.hushlogin_ exists, then a "quiet" login is performed. This disables the checking of mail and the printing of the last login time and message of the day. Otherwise, if _/var/log/lastlog_ exists, the last login time is printed, and the current login is recorded. @@ -56,6 +56,9 @@ Note that the *-h* option has an impact on the *PAM service* *name*. The standar *-H*:: Used by other servers (for example, *telnetd*(8)) to tell *login* that printing the hostname should be suppressed in the login: prompt. See also *LOGIN_PLAIN_PROMPT* below. +*-s*, *--shell* _shell_:: +Specify a _shell_, other than the one defined in _/etc/passwd_, to log in to. + include::man-common/help-version.adoc[] == CONFIG FILE ITEMS diff --git a/login-utils/login.c b/login-utils/login.c index ee42d081cbf..abf7c920815 100644 --- a/login-utils/login.c +++ b/login-utils/login.c @@ -111,6 +111,8 @@ struct login_context { const char *tty_number; /* end of the tty_path */ mode_t tty_mode; /* chmod() mode */ + char *shell_arg; /* command line argument defining the login shell */ + const char *username; /* points to PAM, pwd or cmd_username */ char *cmd_username; /* username specified on command line */ @@ -478,11 +480,9 @@ static void chown_tty(struct login_context *cxt) grname = getlogindefs_str("TTYGROUP", TTYGRPNAME); if (grname && *grname) { - struct group *gr = getgrnam(grname); - if (gr) /* group by name */ + struct group *gr = ul_getgrp_str(grname); + if (gr) gid = gr->gr_gid; - else /* group by ID */ - gid = (gid_t) getlogindefs_num("TTYGROUP", gid); } if (fchown(0, uid, gid)) /* tty */ chown_err(cxt->tty_name, uid, gid); @@ -643,13 +643,13 @@ static void log_audit(struct login_context *cxt, int status) if (audit_fd == -1) return; if (!pwd && cxt->username) - pwd = getpwnam(cxt->username); + pwd = ul_getuserpw_str(cxt->username); ignore_result( audit_log_acct_message(audit_fd, AUDIT_USER_LOGIN, NULL, "login", - cxt->username ? cxt->username : "(unknown)", + pwd ? pwd->pw_name : "(unknown)", pwd ? pwd->pw_uid : (unsigned int)-1, cxt->hostname, NULL, @@ -1282,17 +1282,18 @@ static void init_remote_info(struct login_context *cxt, char *remotehost) static void __attribute__((__noreturn__)) usage(void) { fputs(USAGE_HEADER, stdout); - printf(_(" %s [-p] [-h ] [-H] [[-f] ]\n"), program_invocation_short_name); + printf(_(" %s [-p] [-s ] [-h ] [-H] [[-f] ]\n"), program_invocation_short_name); fputs(USAGE_SEPARATOR, stdout); fputs(_("Begin a session on the system.\n"), stdout); fputs(USAGE_OPTIONS, stdout); - puts(_(" -p do not destroy the environment")); - puts(_(" -f skip a login authentication")); - puts(_(" -h hostname to be used for utmp logging")); - puts(_(" -H suppress hostname in the login prompt")); - printf(" --help %s\n", USAGE_OPTSTR_HELP); - printf(" -V, --version %s\n", USAGE_OPTSTR_VERSION); + puts(_(" -p do not destroy the environment")); + puts(_(" -f skip a login authentication")); + puts(_(" -h hostname to be used for utmp logging")); + puts(_(" -H suppress hostname in the login prompt")); + puts(_(" -s, --shell define the shell to log in to")); + printf(" --help %s\n", USAGE_OPTSTR_HELP); + printf(" -V, --version %s\n", USAGE_OPTSTR_VERSION); printf(USAGE_MAN_TAIL("login(1)")); exit(EXIT_SUCCESS); } @@ -1328,6 +1329,7 @@ static void initialize(int argc, char **argv, struct login_context *cxt) /* the only two longopts to satisfy UL standards */ enum { HELP_OPTION = CHAR_MAX + 1 }; const struct option longopts[] = { + {"shell", required_argument, NULL, 's'}, {"help", no_argument, NULL, HELP_OPTION}, {"version", no_argument, NULL, 'V'}, {NULL, 0, NULL, 0} @@ -1340,7 +1342,7 @@ static void initialize(int argc, char **argv, struct login_context *cxt) timeout = (unsigned int)getlogindefs_num("LOGIN_TIMEOUT", LOGIN_TIMEOUT); /* TRANSLATORS: The standard value for %u is 60. */ - xasprintf(&timeout_msg, _("%s: timed out after %u seconds"), + xasprintf(&timeout_msg, _("%s: timed out after %u seconds\n"), program_invocation_short_name, timeout); signal(SIGALRM, timedout); @@ -1356,7 +1358,7 @@ static void initialize(int argc, char **argv, struct login_context *cxt) load_credentials(cxt); - while ((c = getopt_long(argc, argv, "fHh:pV", longopts, NULL)) != -1) + while ((c = getopt_long(argc, argv, "fHh:ps:V", longopts, NULL)) != -1) switch (c) { case 'f': cxt->noauth = 1; @@ -1379,6 +1381,10 @@ static void initialize(int argc, char **argv, struct login_context *cxt) cxt->keep_env = 1; break; + case 's': + cxt->shell_arg = xstrdup(optarg); + break; + case 'V': print_version(EXIT_SUCCESS); case HELP_OPTION: @@ -1521,7 +1527,7 @@ int main(int argc, char **argv) */ loginpam_acct(&cxt); - cxt.pwd = xgetpwnam(cxt.username, &cxt.pwdbuf); + cxt.pwd = xgetuserpw(cxt.username, &cxt.pwdbuf); if (!cxt.pwd) { warnx(_("\nSession setup problem, abort.")); syslog(LOG_ERR, _("Invalid user name \"%s\". Abort."), @@ -1580,8 +1586,11 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } - if (pwd->pw_shell == NULL || *pwd->pw_shell == '\0') + if (cxt.shell_arg && *cxt.shell_arg != '\0') { + pwd->pw_shell = cxt.shell_arg; + } else if (pwd->pw_shell == NULL || *pwd->pw_shell == '\0') { pwd->pw_shell = _PATH_BSHELL; + } init_environ(&cxt); /* init $HOME, $TERM ... */ diff --git a/login-utils/lslogins.c b/login-utils/lslogins.c index 90db60cbc89..96d0299c638 100644 --- a/login-utils/lslogins.c +++ b/login-utils/lslogins.c @@ -800,7 +800,6 @@ static struct lslogins_user *get_user_info(struct lslogins_control *ctl, const c size_t n = 0; time_t time; uid_t uid; - errno = 0; errno = 0; pwd = username ? getpwnam(username) : getpwent(); @@ -1183,7 +1182,7 @@ static int create_usertree(struct lslogins_control *ctl) int rc = get_user(ctl, &user, ctl->ulist[n]); if (ctl->fail_on_unknown && !user) { - warnx(_("cannot found '%s'"), ctl->ulist[n]); + warnx(_("cannot find '%s'"), ctl->ulist[n]); return -1; } if (rc || !user) @@ -1582,9 +1581,11 @@ static void __attribute__((__noreturn__)) usage(void) fputs(_(" -z, --print0 delimit user entries with a nul character\n"), out); fputs(_(" --wtmp-file set an alternate path for wtmp\n"), out); fputs(_(" --btmp-file set an alternate path for btmp\n"), out); - fputs(_(" --lastlog set an alternate path for lastlog\n"), out); + fputs(_(" --lastlog-file \n" + " set an alternate path for lastlog\n"), out); #ifdef HAVE_LIBLASTLOG2 - fputs(_(" --lastlog2 set an alternate path for lastlog2\n"), out); + fputs(_(" --lastlog2-file \n" + " set an alternate path for lastlog2\n"), out); #endif fputs(USAGE_SEPARATOR, out); /* FIXME: Replace with USAGE_LIST_COLUMNS_OPTION() macro from include/c.h */ diff --git a/login-utils/setpwnam.c b/login-utils/setpwnam.c index 7778e98f7cc..e55fbb2346d 100644 --- a/login-utils/setpwnam.c +++ b/login-utils/setpwnam.c @@ -168,6 +168,7 @@ int setpwnam(struct passwd *pwd, const char *prefix) /* finally: success */ ulckpwdf(); free(linebuf); + free(tmpname); return 0; fail: diff --git a/login-utils/su-common.c b/login-utils/su-common.c index 38d476a1a29..c75b7027591 100644 --- a/login-utils/su-common.c +++ b/login-utils/su-common.c @@ -105,9 +105,6 @@ UL_DEBUG_DEFINE_MASKNAMES(su) = UL_DEBUG_EMPTY_MASKNAMES; #define is_pam_failure(_rc) ((_rc) != PAM_SUCCESS) -/* The shell to run if none is given in the user's passwd entry. */ -#define DEFAULT_SHELL "/bin/sh" - /* The user to become if none is specified. */ #define DEFAULT_USER "root" @@ -267,7 +264,7 @@ static void chownmod_pty(struct su_context *su) const char *grname = getlogindefs_str("TTYGROUP", TTYGRPNAME); if (grname && *grname) { - struct group *gr = getgrnam(grname); + struct group *gr = ul_getgrp_str(grname); if (gr) /* group by name */ gid = gr->gr_gid; else /* group by ID */ @@ -970,7 +967,7 @@ static gid_t add_supp_group(const char *name, gid_t **groups, size_t *ngroups) "specifying more than %d supplemental groups is not possible", NGROUPS_MAX - 1), NGROUPS_MAX - 1); - gr = getgrnam(name); + gr = ul_getgrp_str(name); if (!gr) errx(EXIT_FAILURE, _("group %s does not exist"), name); @@ -1036,7 +1033,7 @@ int su_main(int argc, char **argv, int mode) su->conv.appdata_ptr = (void *) su; while ((optc = - getopt_long(argc, argv, "c:fg:G:lmpPTs:u:hVw:", longopts, + getopt_long(argc, argv, "+c:fg:G:lmpPTs:u:hVw:", longopts, NULL)) != -1) { err_exclusive_options(optc, longopts, excl, excl_st); @@ -1142,7 +1139,7 @@ int su_main(int argc, char **argv, int mode) } FALLTHROUGH; case SU_MODE: - if (optind < argc) + if (optind < argc && *argv[optind] != '-') su->new_user = argv[optind++]; break; } @@ -1154,7 +1151,7 @@ int su_main(int argc, char **argv, int mode) logindefs_set_loader(load_config, (void *) su); init_tty(su); - su->pwd = xgetpwnam(su->new_user, &su->pwdbuf); + su->pwd = xgetuserpw(su->new_user, &su->pwdbuf); if (!su->pwd || !su->pwd->pw_passwd || !su->pwd->pw_name || !*su->pwd->pw_name @@ -1167,7 +1164,7 @@ int su_main(int argc, char **argv, int mode) su->old_user = xgetlogin(); if (!su->pwd->pw_shell || !*su->pwd->pw_shell) - su->pwd->pw_shell = DEFAULT_SHELL; + su->pwd->pw_shell = _PATH_BSHELL; if (use_supp && !use_gid) su->pwd->pw_gid = groups[0]; diff --git a/login-utils/sulogin.c b/login-utils/sulogin.c index eb4609db656..c546cc7c163 100644 --- a/login-utils/sulogin.c +++ b/login-utils/sulogin.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -892,7 +893,7 @@ static void sushell(struct passwd *pwd, struct console *con) if (pwd->pw_shell[0]) su_shell = pwd->pw_shell; else - su_shell = "/bin/sh"; + su_shell = _PATH_BSHELL; } if ((p = strrchr(su_shell, '/')) == NULL) p = su_shell; @@ -941,9 +942,9 @@ static void sushell(struct passwd *pwd, struct console *con) execl(su_shell, shell, (char *)NULL); warn(_("failed to execute %s"), su_shell); - xsetenv("SHELL", "/bin/sh", 1); - execl("/bin/sh", profile ? "-sh" : "sh", (char *)NULL); - warn(_("failed to execute %s"), "/bin/sh"); + xsetenv("SHELL", _PATH_BSHELL, 1); + execl(_PATH_BSHELL, profile ? "-sh" : "sh", (char *)NULL); + warn(_("failed to execute %s"), _PATH_BSHELL); } #ifdef HAVE_LIBSELINUX diff --git a/lsfd-cmd/cdev.c b/lsfd-cmd/cdev.c index 1405f4c65cd..f047ef1b47c 100644 --- a/lsfd-cmd/cdev.c +++ b/lsfd-cmd/cdev.c @@ -20,6 +20,10 @@ */ #include "lsfd.h" +#include "pidfd-utils.h" + +#include /* ioctl */ +#include /* TUNGETDEVNETNS */ static struct list_head miscdevs; static struct list_head ttydrvs; @@ -369,6 +373,11 @@ static struct cdev_ops cdev_misc_ops = { /* * tun device driver */ +struct tundata { + const char *iff; + ino_t devnetns; /* 0 implies no value given. */ +}; + static bool cdev_tun_probe(struct cdev *cdev) { const char *miscdev; @@ -377,25 +386,37 @@ static bool cdev_tun_probe(struct cdev *cdev) return false; miscdev = get_miscdev(minor(cdev->file.stat.st_rdev)); - if (miscdev && strcmp(miscdev, "tun") == 0) + if (miscdev && strcmp(miscdev, "tun") == 0) { + struct tundata *tundata = xcalloc(1, sizeof *tundata); + cdev->cdev_data = tundata; return true; + } return false; } static void cdev_tun_free(const struct cdev *cdev) { - if (cdev->cdev_data) - free(cdev->cdev_data); + if (cdev->cdev_data) { + struct tundata *tundata = cdev->cdev_data; + free((void *)tundata->iff); + free(tundata); + } } static char * cdev_tun_get_name(struct cdev *cdev) { char *str = NULL; + struct tundata *tundata = cdev->cdev_data; - if (cdev->cdev_data == NULL) + if (tundata == NULL || tundata->iff == NULL) return NULL; - xasprintf(&str, "iface=%s", (const char *)cdev->cdev_data); + if (tundata->devnetns) + xasprintf(&str, "iface=%s devnetns=%llu", + tundata->iff, (unsigned long long)tundata->devnetns); + else + xasprintf(&str, "iface=%s", tundata->iff); + return str; } @@ -406,6 +427,8 @@ static bool cdev_tun_fill_column(struct proc *proc __attribute__((__unused__)), size_t column_index __attribute__((__unused__)), char **str) { + struct tundata *tundata = cdev->cdev_data; + switch(column_id) { case COL_MISCDEV: *str = xstrdup("tun"); @@ -413,22 +436,89 @@ static bool cdev_tun_fill_column(struct proc *proc __attribute__((__unused__)), case COL_SOURCE: *str = xstrdup("misc:tun"); return true; + case COL_TUN_DEVNETNS: + if (tundata && tundata->devnetns) { + xasprintf(str, "%llu", (unsigned long long)tundata->devnetns); + return true; + } + break; case COL_TUN_IFACE: - if (cdev->cdev_data) { - *str = xstrdup(cdev->cdev_data); + if (tundata && tundata->iff) { + *str = xstrdup(tundata->iff); return true; } + break; } return false; } static int cdev_tun_handle_fdinfo(struct cdev *cdev, const char *key, const char *val) { - if (strcmp(key, "iff") == 0 && cdev->cdev_data == NULL) { - cdev->cdev_data = xstrdup(val); + struct tundata *tundata = cdev->cdev_data; + + if (strcmp(key, "iff") == 0 && tundata->iff == NULL) { + tundata->iff = xstrdup(val); return 1; } - return false; + return 0; +} + +#ifdef TUNGETDEVNETNS +static int call_with_foreign_fd(pid_t target_pid, int target_fd, + int (*fn)(int, void*), void *data) +{ + int pidfd, tfd, r; + + pidfd = pidfd_open(target_pid, 0); + if (pidfd < 0) + return pidfd; + + tfd = pidfd_getfd(pidfd, target_fd, 0); + if (tfd < 0) { + close(pidfd); + return tfd; + } + + r = fn(tfd, data); + + close(tfd); + close(pidfd); + return r; +} + +static int cdev_tun_get_devnetns(int tfd, void *data) +{ + + struct tundata *tundata = data; + int nsfd = ioctl(tfd, TUNGETDEVNETNS); + struct stat sb; + + if (nsfd < 0) + return -1; + + if (fstat(nsfd, &sb) == 0) + tundata->devnetns = sb.st_ino; + + close(nsfd); + + return 0; +} +#endif + +static void cdev_tun_attach_xinfo(struct cdev *cdev) +{ + struct tundata *tundata = cdev->cdev_data; + + if (tundata->iff == NULL) + return; + +#ifdef TUNGETDEVNETNS + { + struct file *file = &cdev->file; + call_with_foreign_fd(file->proc->pid, file->association, + cdev_tun_get_devnetns, tundata); + } +#endif } static struct cdev_ops cdev_tun_ops = { @@ -438,6 +528,7 @@ static struct cdev_ops cdev_tun_ops = { .get_name = cdev_tun_get_name, .fill_column = cdev_tun_fill_column, .handle_fdinfo = cdev_tun_handle_fdinfo, + .attach_xinfo = cdev_tun_attach_xinfo, }; /* diff --git a/lsfd-cmd/file.c b/lsfd-cmd/file.c index 2857c941039..d52f3a31946 100644 --- a/lsfd-cmd/file.c +++ b/lsfd-cmd/file.c @@ -319,10 +319,16 @@ const struct file_class readlink_error_class = { .fill_column = readlink_error_fill_column, }; +static void stat_error_file_free_content(struct file *file) +{ + free(file->name); +} + const struct file_class stat_error_class = { .super = &error_class, .size = sizeof(struct file), .initialize_content = init_error_content, + .free_content = stat_error_file_free_content, }; /* @@ -384,7 +390,7 @@ void decode_source(char *buf, size_t bufsize, dev_minor); } -static char *strnrstr(const char *haystack, const char *needle, size_t needle_len) +static char *strnrstr(char *haystack, const char *needle, size_t needle_len) { char *last = strstr(haystack, needle); if (last == NULL) diff --git a/lsfd-cmd/lsfd.1.adoc b/lsfd-cmd/lsfd.1.adoc index ab30b231d73..195e54d7eb9 100644 --- a/lsfd-cmd/lsfd.1.adoc +++ b/lsfd-cmd/lsfd.1.adoc @@ -307,7 +307,7 @@ inotify::: inodes=_INOTIFY.INODES_ + misc:tun::: -iface=_TUN.IFACE_ +iface=_TUN.IFACE_[ devnetns=_TUN.DEVNETNS_] + NETLINK::: protocol=_NETLINK.PROTOCOL_[ lport=_NETLINK.LPORT_[ group=_NETLINK.GROUPS_]] @@ -457,7 +457,7 @@ SOCK.LISTENING <``boolean``>:: Listening socket. SOCK.NETS <``number``>:: -Inode identifying network namespace where the socket belongs to. +Inode identifying network namespace where the socket belongs. SOCK.PROTONAME <``string``>:: Protocol name. @@ -525,6 +525,9 @@ Remaining time. PTMX.TTY-INDEX <``number``>:: TTY index of the counterpart. +TUN.DEVNETNS <``number``>:: +Inode identifying network namespace where the device belongs. + TUN.IFACE <``string``>:: Network interface behind the tun device. diff --git a/lsfd-cmd/lsfd.c b/lsfd-cmd/lsfd.c index 335594144dc..26d0bd54a61 100644 --- a/lsfd-cmd/lsfd.c +++ b/lsfd-cmd/lsfd.c @@ -363,7 +363,7 @@ static const struct colinfo infos[] = { N_("listening socket") }, [COL_SOCK_NETNS] = { "SOCK.NETNS", 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER, - N_("inode identifying network namespace where the socket belongs to") }, + N_("inode identifying network namespace where the socket belongs") }, [COL_SOCK_PROTONAME] = { "SOCK.PROTONAME", 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING, N_("protocol name") }, @@ -406,6 +406,9 @@ static const struct colinfo infos[] = { [COL_TIMERFD_REMAINING]= { "TIMERFD.REMAINING", 0, SCOLS_FL_RIGHT, SCOLS_JSON_FLOAT, N_("remaining time") }, + [COL_TUN_DEVNETNS] = { "TUN.DEVNETNS", + 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER, + N_("inode identifying network namespace where the device belongs") }, [COL_TUN_IFACE] = { "TUN.IFACE", 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING, N_("network interface behind the tun device") }, @@ -1875,7 +1878,7 @@ static void mark_poll_fds_as_multiplexed(char *buf, struct file *file = list_entry(f, struct file, files); if (is_opened_file(file) && !file->multiplexed) { int fd = file->association; - if (bsearch(&(struct pollfd){.fd = fd,}, local.iov_base, + if (bsearch((&(struct pollfd){.fd = fd,}), local.iov_base, nfds, sizeof(struct pollfd), pollfdcmp)) file->multiplexed = 1; } @@ -2317,7 +2320,7 @@ static struct libscols_filter *new_filter(const char *expr, bool debug, struct l return f; } -static struct counter_spec *new_counter_spec(const char *spec_str) +static struct counter_spec *new_counter_spec(char *spec_str) { char *sep; struct counter_spec *spec; diff --git a/lsfd-cmd/lsfd.h b/lsfd-cmd/lsfd.h index 916ad2d7652..ea9bf5e70fc 100644 --- a/lsfd-cmd/lsfd.h +++ b/lsfd-cmd/lsfd.h @@ -131,6 +131,7 @@ enum { COL_TIMERFD_CLOCKID, COL_TIMERFD_INTERVAL, COL_TIMERFD_REMAINING, + COL_TUN_DEVNETNS, COL_TUN_IFACE, COL_TYPE, COL_UDP_LADDR, diff --git a/lsfd-cmd/sock-xinfo.c b/lsfd-cmd/sock-xinfo.c index 6803300cb88..78986eb4679 100644 --- a/lsfd-cmd/sock-xinfo.c +++ b/lsfd-cmd/sock-xinfo.c @@ -2127,11 +2127,11 @@ static char *netlink_get_name(struct sock_xinfo *sock_xinfo, const char *protocol = netlink_decode_protocol(nl->protocol); if (nl->groups) - xasprintf(&str, "protocol=%s lport=%"PRIu16 " groups=%"PRIu32, + xasprintf(&str, "protocol=%s lport=%"PRIu32 " groups=%"PRIu32, protocol, nl->lportid, nl->groups); else - xasprintf(&str, "protocol=%s lport=%"PRIu16, + xasprintf(&str, "protocol=%s lport=%"PRIu32, protocol, nl->lportid); return str; diff --git a/meson.build b/meson.build index 66fcbef543d..8b7880e465d 100644 --- a/meson.build +++ b/meson.build @@ -396,6 +396,12 @@ have = cc.has_function( dependencies : lib_systemd) conf.set('HAVE_DECL_SD_SESSION_GET_USERNAME', have ? 1 : false) +have = cc.has_function( + 'sd_device_new_from_devname', + dependencies : lib_systemd) +conf.set('USE_LIBMOUNT_UDEV_SUPPORT', + not get_option('build-libmount-udev-support').disabled() and have ? 1 : false) + lib_udev = dependency( 'libudev', required : get_option('systemd')) @@ -606,6 +612,7 @@ funcs = ''' err errx explicit_bzero + fallocate fnmatch fseeko fsconfig @@ -644,6 +651,7 @@ funcs = ''' pidfd_open pidfd_send_signal posix_fadvise + posix_fallocate prctl qsort_r reallocarray @@ -804,7 +812,7 @@ if not have endif conf.set('HAVE_CLOCK_GETTIME', have ? 1 : false) -thread_libs = dependency('threads') +thread_libs = dependency('threads', required: false) conf.set('HAVE_LIBPTHREAD', thread_libs.found() ? 1 : false) have = cc.has_function('timer_create') @@ -880,7 +888,7 @@ have = cc.has_header_symbol('linux/vm_sockets.h', 'VMADDR_CID_LOCAL', prefix : '#include ') conf.set('HAVE_DECL_VMADDR_CID_LOCAL', have ? 1 : false) -build_plymouth_support = (not build_plymouth_support.disabled() and +build_plymouth_support = (not build_plymouth_support.disabled() and have_tiocglcktrmios and have_sock_cloexec and have_sock_nonblock and @@ -890,38 +898,6 @@ summary('plymouth support', build_plymouth_support ? 'enabled' : 'disabled', section : 'components') -# check for valid fallocate() function -# with 32 bits glibc 2.10, fallocate() exists but not fallocate64() -# when _FILE_OFFSET_BITS==64, fallocate() is redirect to fallocate64() -# and program can't be linked. -code = ''' -#define _GNU_SOURCE -#include -#include - -int main(void) { - long ret; - ret = fallocate(0, FALLOC_FL_KEEP_SIZE, 0xfffffffful, 0xfffffffful); - return ret == 0 ? 0 : 1; -} -''' -have = cc.links(code, name : 'fallocate() function') -conf.set('HAVE_FALLOCATE', have ? 1 : false) - -code = ''' -#define _POSIX_C_SOURCE 200112L -#include -#include - -int main(void) { - long ret; - ret = posix_fallocate(0, 0xfffffffful, 0xfffffffful); - return ret == 0 ? 0 : 1; -} -''' -have = cc.links(code, name : 'posix_fallocate() function') -conf.set('HAVE_POSIX_FALLOCATE', have ? 1 : false) - use_hwclock_cmos = host_machine.cpu_family() in ['x86', 'x86_64'] message('Use CMOS clock: @0@'.format(use_hwclock_cmos)) conf.set('USE_HWCLOCK_CMOS', use_hwclock_cmos ? 1 : false) @@ -942,6 +918,11 @@ if fs_search_path_extra != '' endif conf.set_quoted('FS_SEARCH_PATH', fs_search_path) +ntfs_mounttype = get_option('ntfs-mounttype') +if ntfs_mounttype != '' + conf.set_quoted('CONFIG_UL_NTFS_MOUNTTYPE', ntfs_mounttype) +endif + systemdsystemunitdir = '' if systemd.found() systemdsystemunitdir = systemd.get_variable(pkgconfig : 'systemdsystemunitdir') @@ -2071,7 +2052,11 @@ if opt and not is_disabler(exe) bashcompletions += ['mountpoint'] endif -opt = not get_option('build-fallocate').disabled() +opt = get_option('build-fallocate') \ + .require(LINUX) \ + .require(conf.get('HAVE_FALLOCATE').to_string() == '1') \ + .require(conf.get('HAVE_POSIX_FALLOCATE').to_string() == '1') \ + .allowed() exe = executable( 'fallocate', fallocate_sources, diff --git a/meson_options.txt b/meson_options.txt index 1ec904668be..b764b56fbb2 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -303,6 +303,15 @@ option('fs-search-path', option('fs-search-path-extra', type : 'string', description : 'additional search path for fs helpers') + +option('build-libmount-udev-support', + type : 'feature', + description : 'support reading from udev in libmount') + +option('ntfs-mounttype', + type : 'string', + description : 'overwrite default ntfs3 mount type for NTFS') + option('vendordir', type: 'string', description : 'directory for distribution provided econf files') diff --git a/misc-utils/cal.1.adoc b/misc-utils/cal.1.adoc index b4096ed15fb..a464d56b4df 100644 --- a/misc-utils/cal.1.adoc +++ b/misc-utils/cal.1.adoc @@ -120,6 +120,9 @@ If a _number_ is specified, the requested week in the desired or current year will be printed and its number highlighted. The _number_ may be ignored if _month_ is also specified. + +If the _number_ is specified but the current day is unspecified on the command +line, then the current day is not highlighted. ++ See the *NOTES* section for more details. *--color*[**=**_when_]:: diff --git a/misc-utils/cal.c b/misc-utils/cal.c index 09622165a4d..c264610671f 100644 --- a/misc-utils/cal.c +++ b/misc-utils/cal.c @@ -778,27 +778,35 @@ static void cal_output_header(struct cal_month *month, const struct cal_control char out[FMT_ST_CHARS]; struct cal_month *i; - cal_enable_color(CAL_COLOR_HEADER); if (ctl->header_hint || ctl->header_year) { + cal_enable_color(CAL_COLOR_HEADER); for (i = month; i; i = i->next) { snprintf(out, sizeof(out), "%s", ctl->full_month[i->month - 1]); center(out, ctl->week_width, i->next == NULL ? 0 : ctl->gutter_width); } + cal_disable_color(CAL_COLOR_HEADER); + if (!ctl->header_year) { fputc('\n', stdout); + cal_enable_color(CAL_COLOR_HEADER); for (i = month; i; i = i->next) { snprintf(out, sizeof(out), "%04d", i->year); center(out, ctl->week_width, i->next == NULL ? 0 : ctl->gutter_width); } + cal_disable_color(CAL_COLOR_HEADER); } } else { + cal_enable_color(CAL_COLOR_HEADER); for (i = month; i; i = i->next) { snprintf(out, sizeof(out), "%s %04d", ctl->full_month[i->month - 1], i->year); center(out, ctl->week_width, i->next == NULL ? 0 : ctl->gutter_width); } + cal_disable_color(CAL_COLOR_HEADER); } fputc('\n', stdout); + + cal_enable_color(CAL_COLOR_HEADER); for (i = month; i; i = i->next) { if (ctl->weektype) { if (ctl->julian) @@ -821,20 +829,25 @@ static void cal_vert_output_header(struct cal_month *month, struct cal_month *m; int month_width; - cal_enable_color(CAL_COLOR_HEADER); month_width = ctl->day_width * (MAXDAYS / DAYS_IN_WEEK); /* Padding for the weekdays */ + cal_enable_color(CAL_COLOR_HEADER); printf("%*s", (int)ctl->day_width + 1, ""); + cal_disable_color(CAL_COLOR_HEADER); if (ctl->header_hint || ctl->header_year) { + cal_enable_color(CAL_COLOR_HEADER); for (m = month; m; m = m->next) { snprintf(out, sizeof(out), "%s", ctl->full_month[m->month - 1]); left(out, month_width, ctl->gutter_width); } + cal_disable_color(CAL_COLOR_HEADER); + if (!ctl->header_year) { fputc('\n', stdout); + cal_enable_color(CAL_COLOR_HEADER); /* Padding for the weekdays */ printf("%*s", (int)ctl->day_width + 1, ""); @@ -842,15 +855,17 @@ static void cal_vert_output_header(struct cal_month *month, snprintf(out, sizeof(out), "%04d", m->year); left(out, month_width, ctl->gutter_width); } + cal_disable_color(CAL_COLOR_HEADER); } } else { + cal_enable_color(CAL_COLOR_HEADER); for (m = month; m; m = m->next) { snprintf(out, sizeof(out), "%s %04d", ctl->full_month[m->month - 1], m->year); left(out, month_width, ctl->gutter_width); } + cal_disable_color(CAL_COLOR_HEADER); } - cal_disable_color(CAL_COLOR_HEADER); fputc('\n', stdout); } diff --git a/misc-utils/enosys.c b/misc-utils/enosys.c index 68e426c9ddc..f1438b8e83e 100644 --- a/misc-utils/enosys.c +++ b/misc-utils/enosys.c @@ -108,7 +108,7 @@ static struct blocked_number *parse_block(const char *s, int ret, const struct s struct blocked_number *blocked; const char *name, *error_name; long blocked_number; - char *colon; + const char *colon; bool found; size_t i; diff --git a/misc-utils/fadvise.c b/misc-utils/fadvise.c index da7fc691a0f..6e9ced0083f 100644 --- a/misc-utils/fadvise.c +++ b/misc-utils/fadvise.c @@ -50,6 +50,7 @@ static void __attribute__((__noreturn__)) usage(void) fputs(USAGE_OPTIONS, out); fputs(_(" -a, --advice applying advice to the file (default: \"dontneed\")\n"), out); + fputs(_(" -d, --fd applying advice to the file descriptor\n"), out); fputs(_(" -l, --length length for range operations, in bytes\n"), out); fputs(_(" -o, --offset offset for range operations, in bytes\n"), out); diff --git a/misc-utils/fincore.c b/misc-utils/fincore.c index c297c104573..eba23445543 100644 --- a/misc-utils/fincore.c +++ b/misc-utils/fincore.c @@ -333,10 +333,10 @@ static int mincore_fd (struct fincore_control *ctl, } rc = do_mincore(ctl, window, len, st); + munmap (window, len); if (rc) break; - munmap (window, len); } return rc; @@ -370,7 +370,7 @@ static int fincore_fd (struct fincore_control *ctl, if (errno != ENOSYS || ctl->cachestat) warn(_("failed to do cachestat: %s"), st->name); - if (ctl->cachestat) + if (errno == EPERM || ctl->cachestat) return -errno; return mincore_fd(ctl, fd, st); @@ -610,6 +610,7 @@ int main(int argc, char ** argv) rc |= fincore_name(&ctl, ent->fts_accpath, ent->fts_path, ent->fts_statp); } } + fts_close(fts); } #endif } else { diff --git a/misc-utils/kill.c b/misc-utils/kill.c index b522bcb2088..4fe11d02bdf 100644 --- a/misc-utils/kill.c +++ b/misc-utils/kill.c @@ -72,7 +72,6 @@ #if defined(HAVE_PIDFD_OPEN) && defined(HAVE_PIDFD_SEND_SIGNAL) # define USE_KILL_WITH_TIMEOUT 1 -# define USE_KILL_WITH_PIDFDINO 1 #endif enum { @@ -91,10 +90,10 @@ struct timeouts { #endif struct kill_control { - char *arg; - pid_t pid; - ino_t pidfd_ino; - int numsig; + char *arg; + pid_t pid; + uint64_t pidfd_ino; + int numsig; #ifdef HAVE_SIGQUEUE union sigval sigdata; #endif @@ -330,7 +329,7 @@ static void __attribute__((__noreturn__)) print_kill_version(void) #ifdef USE_KILL_WITH_TIMEOUT "pidfd", #endif -#ifdef USE_KILL_WITH_PIDFDINO +#ifdef USE_PIDFD_INO_SUPPORT "pidfdino", #endif }; @@ -534,8 +533,7 @@ static int kill_with_timeout(const struct kill_control *ctl) info.si_value.sival_int = ctl->use_sigval != 0 ? ctl->use_sigval : ctl->numsig; - if ((pfd = pidfd_open(ctl->pid, 0)) < 0) - err(EXIT_FAILURE, _("pidfd_open() failed: %d"), ctl->pid); + pfd = ul_get_valid_pidfd_or_err(ctl->pid, ctl->pidfd_ino); p.fd = pfd; p.events = POLLIN; @@ -557,22 +555,9 @@ static int kill_with_timeout(const struct kill_control *ctl) err(EXIT_FAILURE, _("pidfd_send_signal() failed")); } } - return 0; -} -#endif -#ifdef USE_KILL_WITH_PIDFDINO -static int validate_pfd_ino(int pfd, ino_t pfd_ino) -{ - int rc; - struct stat f; + close(pfd); - rc = fstat(pfd, &f); - if (rc != 0) - return -EINVAL; - - if (f.st_ino != pfd_ino) - return -EINVAL; return 0; } #endif @@ -597,21 +582,15 @@ static int kill_verbose(const struct kill_control *ctl) rc = sigqueue(ctl->pid, ctl->numsig, ctl->sigdata); else #endif -#ifdef USE_KILL_WITH_PIDFDINO +#ifdef USE_PIDFD_INO_SUPPORT if ((ctl->pidfd_ino > 0)) { int pfd; - pfd = pidfd_open(ctl->pid, 0); - if (pfd < 0) - err(EXIT_FAILURE, _("pidfd_open() failed: %d"), ctl->pid); - - rc = validate_pfd_ino(pfd, ctl->pidfd_ino); - if (rc < 0) - errx(EXIT_FAILURE, _("pidfd inode %"PRIu64" not found for pid %d: %s"), - ctl->pidfd_ino, ctl->pid, strerror(-rc)); - + pfd = ul_get_valid_pidfd_or_err(ctl->pid, ctl->pidfd_ino); rc = pidfd_send_signal(pfd, ctl->numsig, 0, 0); if (rc < 0) err(EXIT_FAILURE, _("pidfd_send_signal() failed")); + + close(pfd); } else #endif rc = kill(ctl->pid, ctl->numsig); diff --git a/misc-utils/logger.c b/misc-utils/logger.c index d27c8064f32..89fa4e8a314 100644 --- a/misc-utils/logger.c +++ b/misc-utils/logger.c @@ -699,10 +699,10 @@ static char *get_structured_data_string(struct logger_ctl *ctl) static int valid_structured_data_param(const char *str) { - char *s; - char *eq = strchr(str, '='), - *qm1 = strchr(str, '"'), - *qm2 = qm1 ? ul_strchr_escaped(qm1 + 1, '"') : NULL; + const char *s; + const char *eq = strchr(str, '='), + *qm1 = strchr(str, '"'), + *qm2 = qm1 ? ul_strchr_escaped(qm1 + 1, '"') : NULL; /* something is missing */ if (!eq || !qm1 || !qm2) @@ -710,7 +710,7 @@ static int valid_structured_data_param(const char *str) /* ']' need to be escaped */ for (s = qm1 + 1; s && *s; ) { - char *p = strchr(s, ']'); + const char *p = strchr(s, ']'); if (!p) break; if (p > qm2 || p == ul_strchr_escaped(s, ']')) @@ -720,7 +720,7 @@ static int valid_structured_data_param(const char *str) /* '\' is allowed only before '[]"\' chars */ for (s = qm1 + 1; s && *s; ) { - char *p = strchr(s, '\\'); + const char *p = strchr(s, '\\'); if (!p) break; if (!strchr("[]\"\\", *(p + 1))) @@ -739,7 +739,7 @@ static int valid_structured_data_param(const char *str) */ static int valid_structured_data_id(const char *str) { - char *at = strchr(str, '@'); + const char *at = strchr(str, '@'); const char *p; /* standardized IDs without @ */ diff --git a/misc-utils/lsclocks.c b/misc-utils/lsclocks.c index 1d4f567bb33..5d22285b527 100644 --- a/misc-utils/lsclocks.c +++ b/misc-utils/lsclocks.c @@ -225,6 +225,7 @@ static void __attribute__((__noreturn__)) usage(void) fputs(_(" -r, --raw use raw output format\n"), out); fputs(_(" -t, --time show current time of single clock\n"), out); fputs(_(" --no-discover-dynamic do not try to discover dynamic clocks\n"), out); + fputs(_(" --no-discover-rtc do not try to discover RTCs"), out); fputs(_(" -d, --dynamic-clock also display specified dynamic clock\n"), out); fputs(_(" -c, --cpu-clock also display CPU clock of specified process\n"), out); diff --git a/misc-utils/namei.c b/misc-utils/namei.c index 0a9ed8df80e..768ae00d7ab 100644 --- a/misc-utils/namei.c +++ b/misc-utils/namei.c @@ -100,7 +100,7 @@ readlink_to_namei(struct namei *nm, const char *path) if (sz < 1) err(EXIT_FAILURE, _("failed to read symlink: %s"), path); if (*sym != '/') { - char *p = strrchr(path, '/'); + const char *p = strrchr(path, '/'); if (p) { isrel = 1; @@ -387,7 +387,7 @@ static const struct option longopts[] = { "modes", no_argument, NULL, 'm' }, { "owners", no_argument, NULL, 'o' }, { "long", no_argument, NULL, 'l' }, - { "nolinks", no_argument, NULL, 'n' }, + { "nosymlinks", no_argument, NULL, 'n' }, { "vertical", no_argument, NULL, 'v' }, #ifdef HAVE_LIBSELINUX { "context", no_argument, NULL, 'Z' }, diff --git a/misc-utils/whereis.c b/misc-utils/whereis.c index f705ac278ef..2e2ecda9e98 100644 --- a/misc-utils/whereis.c +++ b/misc-utils/whereis.c @@ -270,7 +270,7 @@ static void dirlist_add_subdir(struct wh_dirlist **ls, int type, const char *dir char buf[PATH_MAX], *d; DIR *dirp; struct dirent *dp; - char *postfix; + const char *postfix; size_t len; postfix = strchr(dir, '*'); @@ -474,11 +474,12 @@ static void lookup(const char *pattern, struct wh_dirlist *ls, int want) { char patbuf[PATH_MAX] = { 0 }; int count = 0; - char *wait = NULL, *p; + char *wait = NULL; + const char *p; /* canonicalize pattern -- remove path suffix etc. */ p = strrchr(pattern, '/'); - p = p ? p + 1 : (char *) pattern; + p = p ? p + 1 : pattern; xstrncpy(patbuf, p, PATH_MAX); DBG(SEARCH, ul_debug("lookup dirs for '%s' (%s), want: %s %s %s", diff --git a/sys-utils/chmem.8.adoc b/sys-utils/chmem.8.adoc index 5067a98b87d..537beec63a2 100644 --- a/sys-utils/chmem.8.adoc +++ b/sys-utils/chmem.8.adoc @@ -12,7 +12,7 @@ chmem - configure memory == SYNOPSIS -*chmem* [*-h*] [*-V*] [*-v*] [*-e*|*-d*] [_SIZE_|_RANGE_|*-b* _BLOCKRANGE_] [*-z* _ZONE_] +*chmem* [*-h*] [*-V*] [*-v*] [*-c*|*-e*|*-d*|*-g*] [_SIZE_|_RANGE_|*-b* _BLOCKRANGE_] [*-z* _ZONE_] [*-m* _MEMMAP-ON-MEMORY_] == DESCRIPTION @@ -26,23 +26,48 @@ The *chmem* command sets a particular size or range of memory online or offline. * Specify _ZONE_ as the name of a memory zone, as shown in the output of the *lsmem -o +ZONES* command. The output shows one or more valid memory zones for each memory range. If multiple zones are shown, then the memory range currently belongs to the first zone. By default, *chmem* will set memory online to the zone Movable, if this is among the valid zones. This default can be changed by specifying the *--zone* option with another valid zone. For memory ballooning, it is recommended to select the zone Movable for memory online and offline, if possible. Memory in this zone is much more likely to be able to be offlined again, but it cannot be used for arbitrary kernel allocations, only for migratable pages (e.g., anonymous and page cache pages). Use the *--help* option to see all available zones. +* Specify _MEMMAP-ON-MEMORY_ as 1 or 0. A value of 1 allocates hotplug metadata (such as the struct pages array) from the hotplug memory itself, enabling hot-add operations even under memory pressure and without requiring additional system memory to do so. A value of 0 stores hotplugged memory metadata in regular system memory, which helps avoid issues related to fragmentation of continuous physical memory across memory blocks. The value can only be set when the memory block is in a deconfigured state, and *--memmap-on-memory* is valid only with *--configure*. If not specified, and if supported, *chmem* uses the default value shown in *lsmem* output. + +The *--enable* option configures the memory, if this is supported by the architecture. If configuring memory is not supported by the architecture, *--enable* still brings the memory online. + +The *--disable* option brings the memory offline and performs an optional deconfigure step if this is supported by the architecture. + +The *--configure* option requests memory from the hypervisor without bringing it online, when supported by the architecture, allowing explicit control and use of *--memmap-on-memory*. + +The *--deconfigure* option returns memory resources to the hypervisor if supported by the architecture. + _SIZE_ and _RANGE_ must be aligned to the Linux memory block size, as shown in the output of the *lsmem*(1) command. Setting memory online can fail for various reasons. On virtualized systems it can fail if the hypervisor does not have enough memory left, for example because memory was overcommitted. Setting memory offline can fail if Linux cannot free the memory. If only part of the requested memory can be set online or offline, a message tells you how much memory was set online or offline instead of the requested amount. -When setting memory online *chmem* starts with the lowest memory block numbers. When setting memory offline *chmem* starts with the highest memory block numbers. +When setting memory online or when configuring memory, *chmem* starts with the lowest memory block numbers. When setting memory offline or deconfiguring memory, *chmem* starts with the highest memory block numbers. + +== ARCHITECTURE + +* s390 architecture: + +_MEMMAP-ON-MEMORY_: For memory blocks configured online at boot, the default value is 0 because they are added without memmap-on-memory support. Memory added dynamically at runtime uses the default value displayed in *lsmem* output. == OPTIONS *-b*, *--blocks*:: Use a _BLOCKRANGE_ parameter instead of _RANGE_ or _SIZE_ for the *--enable* and *--disable* options. +*-c*, *--configure*:: +Set the specified _RANGE_, _SIZE_, or _BLOCKRANGE_ of memory to be configured. + *-d*, *--disable*:: Set the specified _RANGE_, _SIZE_, or _BLOCKRANGE_ of memory offline. *-e*, *--enable*:: Set the specified _RANGE_, _SIZE_, or _BLOCKRANGE_ of memory online. +*-g*, *--deconfigure*:: +Set the specified _RANGE_, _SIZE_, or _BLOCKRANGE_ of memory to be deconfigured. + +*-m*, *--memmap-on-memory*:: +Select memmap-on-memory for the specified _RANGE_, _SIZE_, or _BLOCKRANGE_ of memory. This option is valid only with *--configure*. + *-z*, *--zone*:: Select the memory _ZONE_ where to set the specified _RANGE_, _SIZE_, or _BLOCKRANGE_ of memory online or offline. By default, memory will be set online to the zone Movable, if possible. @@ -70,13 +95,25 @@ partial success This command requests 1024 MiB of memory to be set online. *chmem -e 2g*:: -This command requests 2 GiB of memory to be set online. +This command requests 2 GB of memory to be brought online and, if supported by the architecture, configures the memory beforehand. *chmem --disable 0x00000000e4000000-0x00000000f3ffffff*:: -This command requests the memory range starting with 0x00000000e4000000 and ending with 0x00000000f3ffffff to be set offline. +This command takes the memory range from 0x00000000e4000000 to 0x00000000f3ffffff offline and deconfigures it if supported by the architecture. *chmem -b -d 10*:: -This command requests the memory block number 10 to be set offline. +This command takes memory block number 10 offline. + +*chmem -b -c 10 -m 1*:: +This command configures memory block 10 with  _MEMMAP-ON-MEMORY_ set. The block must be in a deconfigured state. + +*chmem -b -c 10*:: +This command configures memory block 10 with the default _MEMMAP-ON-MEMORY_ setting. The default value is displayed in *lsmem --output-all*. The block must be in a deconfigured state. + +*chmem -b -g 10*:: +This command deconfigures memory block 10. The block must be offline. + +*chmem -d 5g*:: +This command takes 5 GB of memory offline and deconfigures it if supported by the architecture. Blocks that are already offline but still configured are skipped and must be explicitly deconfigured with *--deconfigure*. == SEE ALSO diff --git a/sys-utils/chmem.c b/sys-utils/chmem.c index bee2a90f8cb..6ec66bb6b34 100644 --- a/sys-utils/chmem.c +++ b/sys-utils/chmem.c @@ -37,11 +37,16 @@ #define CHMEM_EXIT_SOMEOK 64 #define _PATH_SYS_MEMORY "/sys/devices/system/memory" +#define _PATH_SYS_MEMCONFIG "/sys/firmware/memory" struct chmem_desc { struct path_cxt *sysmem; /* _PATH_SYS_MEMORY handler */ + struct path_cxt *sysmemconfig; /* _PATH_SYS_MEMCONFIG directory handler */ struct dirent **dirs; + struct dirent **memconfig_dirs; int ndirs; + int memconfig_ndirs; + int memmap_on_memory; uint64_t block_size; uint64_t start; uint64_t end; @@ -50,11 +55,14 @@ struct chmem_desc { unsigned int is_size : 1; unsigned int verbose : 1; unsigned int have_zones : 1; + unsigned int have_memconfig : 1; }; enum { CMD_MEMORY_ENABLE = 0, CMD_MEMORY_DISABLE, + CMD_MEMORY_CONFIGURE, + CMD_MEMORY_DECONFIGURE, CMD_NONE }; @@ -101,16 +109,129 @@ static void idxtostr(struct chmem_desc *desc, uint64_t idx, char *buf, size_t bu idx, start, end); } -static int chmem_size(struct chmem_desc *desc, int enable, int zone_id) +static bool chmem_memmap_enabled(struct chmem_desc *desc) +{ + if (desc->memmap_on_memory == 0 || desc->memmap_on_memory == 1) + return true; + else + return false; +} + +static int chmem_set_memmap_on_memory(struct chmem_desc *desc, char *name) +{ + int rc, index; + + index = strtou64_or_err(name + 6, _("Failed to parse index")); + rc = ul_path_writef_u64(desc->sysmemconfig, desc->memmap_on_memory, + "%s/memmap_on_memory", name); + if (rc) { + char str[64]; + idxtostr(desc, index, str, sizeof(str)); + warn(_("%s memmap-on-memory failed"), str); + } + return rc; +} + +static int chmem_config(struct chmem_desc *desc, char *name, int configure) +{ + int mblock_configured, memmap, rc, index; + char str[BUFSIZ], state[BUFSIZ]; + + index = strtou64_or_err(name + 6, _("Failed to parse index")); + idxtostr(desc, index, str, sizeof(str)); + rc = ul_path_readf_s32(desc->sysmemconfig, &mblock_configured, "%s/config", name); + if (rc) + goto out; + rc = ul_path_readf_s32(desc->sysmemconfig, &memmap, "%s/memmap_on_memory", name); + if (rc) + goto out; + if (mblock_configured) { + if (configure) { + if (chmem_memmap_enabled(desc) && + memmap != desc->memmap_on_memory) { + if (!desc->is_size || desc->verbose) + fprintf(stdout, + _("%s must be deconfigured before using -m option\n"), str); + rc = -1; + } else if (desc->is_size) { + /* + * Allow chmem_onoff_size() to proceed with + * configuring different memory blocks when the + * current block is already configured. + */ + rc = -1; + } else if (desc->verbose) { + fprintf(stdout, _("%s already configured\n"), str); + } + goto out; + } else if (ul_path_readf_buffer(desc->sysmem, state, + sizeof(state), "%s/state", name) > 0 && + strncmp("online", state, 6) == 0) { + if (!desc->is_size || desc->verbose) + fprintf(stdout, _("%s must be offline before deconfiguration\n"), str); + rc = -1; + goto out; + } + } else { + /* + * If memory block is currently in deconfigured state, set + * memmap-on-memory if -m option is enabled. + */ + if (chmem_memmap_enabled(desc)) { + rc = chmem_set_memmap_on_memory(desc, name); + if (rc) + goto out; + } else if (!configure) { + /* + * Allow chmem_onoff_size() to proceed with + * deconfiguring different memory blocks when the + * current block is already deconfigured. + */ + if (desc->is_size) + rc = -1; + else if (desc->verbose) + fprintf(stdout, _("%s already deconfigured\n"), str); + goto out; + } + } + rc = ul_path_writef_u64(desc->sysmemconfig, configure ? 1 : 0, "%s/config", name); + if (rc) { + if (!desc->is_size) { + warn(configure ? _("%s configure failed") : + _("%s deconfigure failed"), str); + } else if (desc->verbose) { + if (configure) + fprintf(stdout, _("%s configure failed\n"), str); + else + fprintf(stdout, _("%s deconfigure failed\n"), str); + } + } else if (desc->verbose) { + if (configure) + fprintf(stdout, _("%s configured\n"), str); + else + fprintf(stdout, _("%s deconfigured\n"), str); + } +out: + return rc; +} + +static int chmem_configured(struct chmem_desc *desc, char *name) +{ + int mblock_configured = 0; + + ul_path_readf_s32(desc->sysmemconfig, &mblock_configured, "%s/config", name); + return mblock_configured; +} + +static int chmem_onoff_size(struct chmem_desc *desc, int enable, int zone_id) { char *name, *onoff, line[BUFSIZ], str[BUFSIZ]; uint64_t size, index; + int i, rc = 0, ndirs; const char *zn; - int i, rc; size = desc->size; onoff = enable ? "online" : "offline"; - i = enable ? 0 : desc->ndirs - 1; if (enable && zone_id >= 0) { if (zone_id == ZONE_MOVABLE) @@ -118,15 +239,30 @@ static int chmem_size(struct chmem_desc *desc, int enable, int zone_id) else onoff = "online_kernel"; } - - for (; i >= 0 && i < desc->ndirs && size; i += enable ? 1 : -1) { - name = desc->dirs[i]->d_name; + ndirs = desc->have_memconfig ? desc->memconfig_ndirs : desc->ndirs; + i = enable ? 0 : ndirs - 1; + for (; i >= 0 && i < ndirs && size; i += enable ? 1 : -1) { + if (desc->have_memconfig) + name = desc->memconfig_dirs[i]->d_name; + else + name = desc->dirs[i]->d_name; index = strtou64_or_err(name + 6, _("Failed to parse index")); - - if (ul_path_readf_buffer(desc->sysmem, line, sizeof(line), "%s/state", name) > 0 - && strncmp(onoff, line, 6) == 0) + if (enable && desc->have_memconfig && !chmem_configured(desc, name)) { + /* Configure memory block */ + rc = chmem_config(desc, name, enable); + if (rc) + continue; + } else if (ul_path_readf_buffer(desc->sysmem, line, sizeof(line), "%s/state", name) > 0) { + if (strncmp(onoff, line, 6) == 0) + continue; + } else if (!enable) { + /* + * If /sys/devices/system/memory/memoryX is + * unavailable, memory block is offline and + * deconfigured. + */ continue; - + } if (desc->have_zones) { ul_path_readf_buffer(desc->sysmem, line, sizeof(line), "%s/valid_zones", name); if (zone_id >= 0) { @@ -143,7 +279,6 @@ static int chmem_size(struct chmem_desc *desc, int enable, int zone_id) onoff = "online"; } } - idxtostr(desc, index, str, sizeof(str)); rc = ul_path_writef_string(desc->sysmem, onoff, "%s/state", name); if (rc != 0 && desc->verbose) { @@ -157,6 +292,12 @@ static int chmem_size(struct chmem_desc *desc, int enable, int zone_id) else fprintf(stdout, _("%s disabled\n"), str); } + if (!rc && !enable && desc->have_memconfig) { + /* Deconfigure memory block */ + rc = chmem_config(desc, name, enable); + if (rc) + continue; + } if (rc == 0) size--; } @@ -175,12 +316,80 @@ static int chmem_size(struct chmem_desc *desc, int enable, int zone_id) return size == 0 ? 0 : size == desc->size ? -1 : 1; } -static int chmem_range(struct chmem_desc *desc, int enable, int zone_id) +static int chmem_config_size(struct chmem_desc *desc, int configure) +{ + uint64_t size; + char *name; + int i, rc; + + if (!desc->have_memconfig) { + if (configure) + fprintf(stdout, + _("Skip configuration. Use chmem -e instead.\n")); + else + fprintf(stdout, + _("Skip deconfiguration. Use chmem -d instead\n")); + return -1; + } + size = desc->size; + i = configure ? 0 : desc->memconfig_ndirs - 1; + for (; i >= 0 && i < desc->memconfig_ndirs && size; i += configure ? 1 : -1) { + name = desc->memconfig_dirs[i]->d_name; + rc = chmem_config(desc, name, configure); + if (rc == 0) + size--; + } + if (size) { + uint64_t bytes; + char *sizestr; + + bytes = (desc->size - size) * desc->block_size; + sizestr = size_to_human_string(SIZE_SUFFIX_1LETTER, bytes); + if (configure) + fprintf(stdout, _("Could only configure %s of memory\n"), sizestr); + else + fprintf(stdout, _("Could only deconfigure %s of memory\n"), sizestr); + free(sizestr); + } + return size == 0 ? 0 : size == desc->size ? -1 : 1; +} + +static int chmem_config_range(struct chmem_desc *desc, int configure) +{ + uint64_t index, todo; + char *name; + int rc, i; + + if (!desc->have_memconfig) { + if (configure) + fprintf(stdout, + _("Skip configuration. Use chmem -e instead.\n")); + else + fprintf(stdout, + _("Skip deconfiguration. Use chmem -d instead\n")); + return -1; + } + todo = desc->end - desc->start + 1; + for (i = 0; i < desc->memconfig_ndirs; i++) { + name = desc->memconfig_dirs[i]->d_name; + index = strtou64_or_err(name + 6, _("Failed to parse index")); + if (index < desc->start) + continue; + if (index > desc->end) + break; + rc = chmem_config(desc, name, configure); + if (rc == 0) + todo--; + } + return todo == 0 ? 0 : todo == desc->end - desc->start + 1 ? -1 : 1; +} + +static int chmem_onoff_range(struct chmem_desc *desc, int enable, int zone_id) { char *name, *onoff, line[BUFSIZ], str[BUFSIZ]; uint64_t index, todo; + int i, rc, ndirs; const char *zn; - int i, rc; todo = desc->end - desc->start + 1; onoff = enable ? "online" : "offline"; @@ -192,22 +401,43 @@ static int chmem_range(struct chmem_desc *desc, int enable, int zone_id) onoff = "online_kernel"; } - for (i = 0; i < desc->ndirs; i++) { - name = desc->dirs[i]->d_name; + ndirs = desc->have_memconfig ? desc->memconfig_ndirs : desc->ndirs; + for (i = 0; i < ndirs; i++) { + name = desc->have_memconfig ? desc->memconfig_dirs[i]->d_name : + desc->dirs[i]->d_name; index = strtou64_or_err(name + 6, _("Failed to parse index")); if (index < desc->start) continue; if (index > desc->end) break; + if (enable && desc->have_memconfig && !chmem_configured(desc, name)) { + /* Configure memory block */ + rc = chmem_config(desc, name, enable); + if (rc) + continue; + } idxtostr(desc, index, str, sizeof(str)); - if (ul_path_readf_buffer(desc->sysmem, line, sizeof(line), "%s/state", name) > 0 - && strncmp(onoff, line, 6) == 0) { - if (desc->verbose && enable) - fprintf(stdout, _("%s already enabled\n"), str); - else if (desc->verbose && !enable) - fprintf(stdout, _("%s already disabled\n"), str); - todo--; - continue; + if (ul_path_readf_buffer(desc->sysmem, line, sizeof(line), "%s/state", name) > 0) { + if (strncmp(onoff, line, 6) == 0) { + if (desc->verbose && enable) + fprintf(stdout, _("%s already enabled\n"), str); + else if (desc->verbose && !enable) + fprintf(stdout, _("%s already disabled\n"), str); + todo--; + continue; + } + } else { + /* + * If /sys/devices/system/memory/memoryX is + * unavailable, memory block is offline and + * deconfigured. + */ + if (!enable) { + if (desc->verbose) + fprintf(stdout, _("%s already disabled\n"), str); + todo--; + continue; + } } if (desc->have_zones) { @@ -243,6 +473,12 @@ static int chmem_range(struct chmem_desc *desc, int enable, int zone_id) else fprintf(stdout, _("%s disabled\n"), str); } + if (!rc && !enable && desc->have_memconfig) { + /* Deconfigure memory block */ + rc = chmem_config(desc, name, enable); + if (rc) + continue; + } if (rc == 0) todo--; } @@ -256,6 +492,16 @@ static int filter(const struct dirent *de) return isdigit_string(de->d_name + 6); } +static void read_conf(struct chmem_desc *desc) +{ + if (!desc->have_memconfig) + return; + desc->memconfig_ndirs = scandir(_PATH_SYS_MEMCONFIG, &desc->memconfig_dirs, + filter, versionsort); + if (desc->memconfig_ndirs <= 0) + err(EXIT_FAILURE, _("Failed to read %s"), _PATH_SYS_MEMCONFIG); +} + static void read_info(struct chmem_desc *desc) { char line[128]; @@ -269,6 +515,7 @@ static void read_info(struct chmem_desc *desc) desc->block_size = strtoumax(line, NULL, 16); if (errno) goto fail; + read_conf(desc); return; fail: err(EXIT_FAILURE, _("Failed to read %s"), _PATH_SYS_MEMORY); @@ -347,6 +594,9 @@ static void __attribute__((__noreturn__)) usage(void) fputs(_(" -d, --disable disable memory\n"), out); fputs(_(" -b, --blocks use memory blocks\n"), out); fputs(_(" -z, --zone select memory zone (see below)\n"), out); + fputs(_(" -c, --configure configure range\n"), out); + fputs(_(" -g, --deconfigure deconfigure range\n"), out); + fputs(_(" -m, --memmap-on-memory select memmap-on-memory\n"), out); fputs(_(" -v, --verbose verbose output\n"), out); fprintf(out, USAGE_HELP_OPTIONS(20)); @@ -359,6 +609,52 @@ static void __attribute__((__noreturn__)) usage(void) exit(EXIT_SUCCESS); } +static int chmem_range(struct chmem_desc *desc, int cmd, int zone_id) +{ + int rc = -1; + + switch (cmd) { + case CMD_MEMORY_ENABLE: + rc = chmem_onoff_range(desc, 1, zone_id); + break; + case CMD_MEMORY_DISABLE: + rc = chmem_onoff_range(desc, 0, zone_id); + break; + case CMD_MEMORY_CONFIGURE: + rc = chmem_config_range(desc, 1); + break; + case CMD_MEMORY_DECONFIGURE: + rc = chmem_config_range(desc, 0); + break; + default: + break; + } + return rc; +} + +static int chmem_size(struct chmem_desc *desc, int cmd, int zone_id) +{ + int rc = -1; + + switch (cmd) { + case CMD_MEMORY_ENABLE: + rc = chmem_onoff_size(desc, 1, zone_id); + break; + case CMD_MEMORY_DISABLE: + rc = chmem_onoff_size(desc, 0, zone_id); + break; + case CMD_MEMORY_CONFIGURE: + rc = chmem_config_size(desc, 1); + break; + case CMD_MEMORY_DECONFIGURE: + rc = chmem_config_size(desc, 0); + break; + default: + break; + } + return rc; +} + int main(int argc, char **argv) { struct chmem_desc _desc = { 0 }, *desc = &_desc; @@ -374,11 +670,15 @@ int main(int argc, char **argv) {"verbose", no_argument, NULL, 'v'}, {"version", no_argument, NULL, 'V'}, {"zone", required_argument, NULL, 'z'}, + {"configure", no_argument, NULL, 'c'}, + {"deconfigure", no_argument, NULL, 'g'}, + {"memmap-on-memory", required_argument, NULL, 'm'}, {NULL, 0, NULL, 0} }; static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ - { 'd','e' }, + { 'd', 'e', 'g', 'm' }, + { 'c', 'd', 'e', 'g' }, { 0 } }; int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; @@ -389,13 +689,18 @@ int main(int argc, char **argv) close_stdout_atexit(); ul_path_init_debug(); + desc->memmap_on_memory = -1; desc->sysmem = ul_new_path(_PATH_SYS_MEMORY); if (!desc->sysmem) err(EXIT_FAILURE, _("failed to initialize %s handler"), _PATH_SYS_MEMORY); - + desc->sysmemconfig = ul_new_path(_PATH_SYS_MEMCONFIG); + if (!desc->sysmemconfig) + err(EXIT_FAILURE, _("failed to initialize %s handler"), _PATH_SYS_MEMCONFIG); + if (ul_path_access(desc->sysmemconfig, F_OK, "memory0") == 0) + desc->have_memconfig = 1; read_info(desc); - while ((c = getopt_long(argc, argv, "bdehvVz:", longopts, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "bcdeghm:vVz:", longopts, NULL)) != -1) { err_exclusive_options(c, longopts, excl, excl_st); @@ -415,7 +720,15 @@ int main(int argc, char **argv) case 'z': zone = xstrdup(optarg); break; - + case 'c': + cmd = CMD_MEMORY_CONFIGURE; + break; + case 'g': + cmd = CMD_MEMORY_DECONFIGURE; + break; + case 'm': + desc->memmap_on_memory = atoi(optarg); + break; case 'h': usage(); case 'V': @@ -448,9 +761,9 @@ int main(int argc, char **argv) } if (desc->is_size) - rc = chmem_size(desc, cmd == CMD_MEMORY_ENABLE ? 1 : 0, zone_id); + rc = chmem_size(desc, cmd, zone_id); else - rc = chmem_range(desc, cmd == CMD_MEMORY_ENABLE ? 1 : 0, zone_id); + rc = chmem_range(desc, cmd, zone_id); ul_unref_path(desc->sysmem); diff --git a/sys-utils/dmesg.c b/sys-utils/dmesg.c index f4b04840025..402323247d6 100644 --- a/sys-utils/dmesg.c +++ b/sys-utils/dmesg.c @@ -844,8 +844,7 @@ static const char *parse_callerid(const char *p_str, const char *end, const char *p_after; const char *p_next; size_t cid_size; - char *p_scn; - char *p_cid; + const char *p_scn, *p_cid; /* Check for PRINTK_CALLER prefix, must be before msg text */ p_cid = strstr(p_str, DMESG_CALLER_PREFIX); diff --git a/sys-utils/eject.c b/sys-utils/eject.c index f5d48badb38..0debd0df334 100644 --- a/sys-utils/eject.c +++ b/sys-utils/eject.c @@ -492,7 +492,7 @@ static void select_speed(const struct eject_control *ctl) static int read_speed(const char *devname) { int drive_number = -1; - char *name; + const char *name; FILE *f; f = fopen(_PATH_PROC_CDROMINFO, "r"); diff --git a/sys-utils/fallocate.c b/sys-utils/fallocate.c index 686e4925e24..aedb091cfbd 100644 --- a/sys-utils/fallocate.c +++ b/sys-utils/fallocate.c @@ -33,10 +33,6 @@ #include #include -#ifndef HAVE_FALLOCATE -# include -#endif - #if defined(HAVE_LINUX_FALLOC_H) && \ (!defined(FALLOC_FL_KEEP_SIZE) || !defined(FALLOC_FL_PUNCH_HOLE) || \ !defined(FALLOC_FL_COLLAPSE_RANGE) || !defined(FALLOC_FL_ZERO_RANGE) || \ @@ -99,9 +95,7 @@ static void __attribute__((__noreturn__)) usage(void) fputs(_(" -p, --punch-hole replace a range with a hole (implies -n)\n"), out); fputs(_(" -v, --verbose verbose mode\n"), out); fputs(_(" -w, --write-zeroes write zeroes and ensure allocation of a range\n"), out); -#ifdef HAVE_POSIX_FALLOCATE fputs(_(" -x, --posix use posix_fallocate(3) instead of fallocate(2)\n"), out); -#endif fputs(_(" -z, --zero-range zero and ensure allocation of a range\n"), out); fputs(USAGE_SEPARATOR, out); @@ -129,11 +123,8 @@ static void xfallocate(int fd, int mode, off_t offset, off_t length) { int error; -#ifdef HAVE_FALLOCATE error = fallocate(fd, mode, offset, length); -#else - error = syscall(SYS_fallocate, fd, mode, offset, length); -#endif + /* * EOPNOTSUPP: The FALLOC_FL_KEEP_SIZE is unsupported * ENOSYS: The filesystem does not support sys_fallocate @@ -145,7 +136,6 @@ static void xfallocate(int fd, int mode, off_t offset, off_t length) } } -#ifdef HAVE_POSIX_FALLOCATE static void xposix_fallocate(int fd, off_t offset, off_t length) { errno = posix_fallocate(fd, offset, length); @@ -153,7 +143,6 @@ static void xposix_fallocate(int fd, off_t offset, off_t length) err(EXIT_FAILURE, _("fallocate failed")); } } -#endif /* The real buffer size has to be bufsize + sizeof(uintptr_t) */ static int is_nul(void *buf, size_t bufsize) @@ -294,9 +283,7 @@ int main(int argc, char **argv) int fd; int mode = 0; int dig = 0; -#ifdef HAVE_POSIX_FALLOCATE int posix = 0; -#endif loff_t length = -2LL; loff_t offset = 0; @@ -363,12 +350,8 @@ int main(int argc, char **argv) mode |= FALLOC_FL_WRITE_ZEROES; break; case 'x': -#ifdef HAVE_POSIX_FALLOCATE posix = 1; break; -#else - errx(EXIT_FAILURE, _("posix_fallocate support is not compiled")); -#endif case 'v': verbose++; break; @@ -416,11 +399,9 @@ int main(int argc, char **argv) if (dig) dig_holes(fd, offset, length); else { -#ifdef HAVE_POSIX_FALLOCATE if (posix) xposix_fallocate(fd, offset, length); else -#endif xfallocate(fd, mode, offset, length); if (verbose) { diff --git a/sys-utils/flock.c b/sys-utils/flock.c index fe1a71f7282..642e026c855 100644 --- a/sys-utils/flock.c +++ b/sys-utils/flock.c @@ -47,6 +47,7 @@ #include "closestream.h" #include "monotonic.h" #include "timer.h" +#include "shells.h" #ifndef F_OFD_GETLK #define F_OFD_GETLK 36 @@ -75,7 +76,7 @@ static void __attribute__((__noreturn__)) usage(void) fputs(_( " -s, --shared get a shared lock\n"), stdout); fputs(_( " -x, --exclusive get an exclusive lock (default)\n"), stdout); fputs(_( " -u, --unlock remove a lock\n"), stdout); - fputs(_( " -n, --nonblock fail rather than wait\n"), stdout); + fputs(_( " -n, --nb, --nonblocking fail rather than wait\n"), stdout); fputs(_( " -w, --timeout wait for a limited amount of time\n"), stdout); fputs(_( " -E, --conflict-exit-code exit code after conflict or timeout\n"), stdout); fputs(_( " -o, --close close file descriptor before running command\n"), stdout); @@ -207,6 +208,7 @@ int main(int argc, char *argv[]) int conflict_exit_code = 1; char **cmd_argv = NULL, *sh_c_argv[4]; const char *filename = NULL; + enum { OPT_VERBOSE = CHAR_MAX + 1, OPT_FCNTL, @@ -327,9 +329,7 @@ int main(int argc, char *argv[]) _("%s requires exactly one command argument"), argv[optind + 1]); cmd_argv = sh_c_argv; - cmd_argv[0] = getenv("SHELL"); - if (!cmd_argv[0] || !*cmd_argv[0]) - cmd_argv[0] = _PATH_BSHELL; + cmd_argv[0] = (char *)ul_default_shell(0, NULL); cmd_argv[1] = "-c"; cmd_argv[2] = argv[optind + 2]; cmd_argv[3] = NULL; diff --git a/sys-utils/hwclock-rtc.c b/sys-utils/hwclock-rtc.c index f8af5545dc5..a210eac941a 100644 --- a/sys-utils/hwclock-rtc.c +++ b/sys-utils/hwclock-rtc.c @@ -498,6 +498,7 @@ int get_param_rtc(const struct hwclock_control *ctl, int set_param_rtc(const struct hwclock_control *ctl, const char *opt0) { int rtc_fd, rc = 1; + struct rtc_param current_param = { .index = ctl->param_idx }; struct rtc_param param = { .index = ctl->param_idx }; char *tok, *opt = xstrdup(opt0); @@ -520,13 +521,25 @@ int set_param_rtc(const struct hwclock_control *ctl, const char *opt0) goto done; } - /* set parameter */ rtc_fd = open_rtc(ctl); if (rtc_fd < 0) { warnx(_("cannot open %s"), rtc_dev_name); goto done; } + /* get parameter and compare with value */ + current_param.param = param.param; + if (!ioctl(rtc_fd, RTC_PARAM_GET, ¤t_param) + && current_param.uvalue == param.uvalue) { + /* value to be written matches current value, skip write */ + if (ctl->verbose) + printf(_("skipping ioctl(%d, RTC_PARAM_SET, param) to %s: value unchanged\n"), + rtc_fd, rtc_dev_name); + rc = 0; + goto done; + } + + /* set parameter */ if (ioctl(rtc_fd, RTC_PARAM_SET, ¶m) == -1) { warn(_("ioctl(%d, RTC_PARAM_SET, param) to %s failed"), rtc_fd, rtc_dev_name); diff --git a/sys-utils/losetup.8.adoc b/sys-utils/losetup.8.adoc index f51710af87b..54a825c63ea 100644 --- a/sys-utils/losetup.8.adoc +++ b/sys-utils/losetup.8.adoc @@ -12,31 +12,27 @@ losetup - set up and control loop devices == SYNOPSIS -Get info: - -*losetup* [_loopdev_] +Set up a loop device: -*losetup* *-l* [*-a*] +*losetup* [options] *-f*|_loopdev file_ -*losetup* *-j* _file_ [*-o* _offset_] +Get info: -Detach a loop device: +*losetup* [*-l*] [*-a*|_loopdev_] -*losetup* *-d* _loopdev_ ... +*losetup* *-j* _file_ [*-o* _offset_] -Detach all associated loop devices: +Recalibrate the size of a loop device: -*losetup* *-D* - -Set up a loop device: +*losetup* *-c* _loopdev_ -*losetup* [*-o* _offset_] [*--sizelimit* _size_] [*--sector-size* _size_] [*--loop-ref* _name_] [*-Pr*] [*--show*] *-f*|_loopdev file_ +Detach loop devices: -Resize a loop device: +*losetup* *-d* _loopdev_ ... -*losetup* *-c* _loopdev_ +*losetup* *-D* -Remove a loop device: +Prevent loop devices from getting listed: *losetup* *--remove* _loopdev_ ... @@ -106,9 +102,6 @@ Set up a read-only loop device. *--direct-io*[**=on**|*off*]:: Enable or disable direct I/O for the backing file. The default is *off*. Specifying either *--direct-io* or *--direct-io=on* will enable it. But, *--direct-io=off* can be provided to explicitly turn it off. -*-v*, *--verbose*:: -Verbose mode. - *-l*, *--list*:: If a loop device or the *-a* option is specified, print the default columns for either the specified loop device or all loop devices; the default is to print info about all devices. See also *--output*, *--noheadings*, *--raw*, and *--json*. @@ -155,7 +148,7 @@ The *--remove* option completely removes the loop device node from the system us == NOTES -Since version 2.37 *losetup* uses *LOOP_CONFIGURE* ioctl to setup a new loop device by one ioctl call. The old versions use *LOOP_SET_FD* and *LOOP_SET_STATUS64* ioctls to do the same. +Since version 2.37 *losetup* uses *LOOP_CONFIGURE* ioctl to set up a new loop device by one ioctl call. The old versions use *LOOP_SET_FD* and *LOOP_SET_STATUS64* ioctls to do the same. == ENVIRONMENT diff --git a/sys-utils/losetup.c b/sys-utils/losetup.c index c2eec5600ba..7da0ebbd00a 100644 --- a/sys-utils/losetup.c +++ b/sys-utils/losetup.c @@ -501,7 +501,6 @@ static void __attribute__((__noreturn__)) usage(void) fputs(_(" --direct-io[=] open backing file with O_DIRECT\n"), out); fputs(_(" --loop-ref loop device reference\n"), out); fputs(_(" --show print device name after setup (with -f)\n"), out); - fputs(_(" -v, --verbose verbose mode\n"), out); /* output options */ fputs(USAGE_SEPARATOR, out); @@ -730,7 +729,6 @@ int main(int argc, char **argv) { "raw", no_argument, NULL, OPT_RAW }, { "loop-ref", required_argument, NULL, OPT_REF, }, { "show", no_argument, NULL, OPT_SHOW }, - { "verbose", no_argument, NULL, 'v' }, { "version", no_argument, NULL, 'V' }, { "remove", no_argument, NULL, OPT_REMOVE }, { NULL, 0, NULL, 0 } @@ -739,7 +737,7 @@ int main(int argc, char **argv) static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ { 'D','a','c','d','f','j',OPT_REMOVE }, { 'D','c','d','f','l',OPT_REMOVE }, - { 'D','c','d','f','O',OPT_REMOVE }, + { 'D','O','c','d','f',OPT_REMOVE }, { 'J',OPT_RAW }, { 0 } }; @@ -829,8 +827,6 @@ int main(int argc, char **argv) if (use_dio) lo_flags |= LO_FLAGS_DIRECT_IO; break; - case 'v': - break; case OPT_SIZELIMIT: /* --sizelimit */ sizelimit = strtosize_or_err(optarg, _("failed to parse size")); flags |= LOOPDEV_FL_SIZELIMIT; diff --git a/sys-utils/lscpu-arm.c b/sys-utils/lscpu-arm.c index b5cdd69a28a..563ececfd83 100644 --- a/sys-utils/lscpu-arm.c +++ b/sys-utils/lscpu-arm.c @@ -23,6 +23,9 @@ struct id_part { }; static const struct id_part arm_part[] = { + { 0xb36, "ARM1136j-s" }, + { 0xb56, "ARM1156t2-s" }, + { 0xb76, "ARM1176jz-s" }, { 0x810, "ARM810" }, { 0x920, "ARM920" }, { 0x922, "ARM922" }, @@ -69,10 +72,12 @@ static const struct id_part arm_part[] = { { 0xd0d, "Cortex-A77" }, { 0xd0e, "Cortex-A76AE" }, { 0xd13, "Cortex-R52" }, + { 0xd14, "Cortex-R82AE" }, { 0xd15, "Cortex-R82" }, { 0xd16, "Cortex-R52+" }, { 0xd20, "Cortex-M23" }, { 0xd21, "Cortex-M33" }, + { 0xd24, "Cortex-M52" }, { 0xd22, "Cortex-M55" }, { 0xd23, "Cortex-M85" }, { 0xd40, "Neoverse-V1" }, diff --git a/sys-utils/lscpu-cputype.c b/sys-utils/lscpu-cputype.c index eda6bd1004b..958cbe7b67c 100644 --- a/sys-utils/lscpu-cputype.c +++ b/sys-utils/lscpu-cputype.c @@ -450,7 +450,8 @@ static char *key_cleanup(char *str, int *keynum) static const struct cpuinfo_pattern *cpuinfo_parse_line(char *str, char **value, int *keynum) { - struct cpuinfo_pattern key = { .id = 0 }, *pat; + struct cpuinfo_pattern key = { .id = 0 }; + const struct cpuinfo_pattern *pat; char *p, *v; char buf[CPUTYPE_PATTERN_BUFSZ] = { 0 }; diff --git a/sys-utils/lsmem.1.adoc b/sys-utils/lsmem.1.adoc index d588051a816..e7226725a3b 100644 --- a/sys-utils/lsmem.1.adoc +++ b/sys-utils/lsmem.1.adoc @@ -24,10 +24,50 @@ The *lsmem* command lists a new memory range always when the current memory bloc Note that some output columns may provide inaccurate information if a split policy forces *lsmem* to ignore differences in some attributes. For example if you merge removable and non-removable memory blocks to the one range than all the range will be marked as non-removable on *lsmem* output. -Not all columns are supported on all systems. If an unsupported column is specified, *lsmem* prints the column but does not provide any data for it. +The supported columns are RANGE, SIZE, STATE, REMOVABLE, BLOCK, NODE, ZONES, CONFIGURED, MEMMAP-ON-MEMORY. +RANGE +The start and end physical address of the memory range. + +SIZE +The size of the memory range, representing the total amount of memory in that range. + +STATE +The current online status of the memory range. Common states include online, offline or transitional states. + +BLOCK +The specific memory block number. + +NODE +The NUMA (Non-Uniform Memory Access) node to which the memory block belongs. + +ZONES +The memory zones to which the blocks belongs, such as DMA, Normal, Movable. + +CONFIGURED +The configuration state of a memory block. Refer to *chmem* for details on configuring or deconfiguring memory blocks. + +MEMMAP-ON-MEMORY +The memmap-on-memory state of the memory block at configuration time. This setting indicates where memory hotplug stores its internal metadata (the struct pages array or memmap). If MEMMAP-ON-MEMORY is set to 1, the metadata is allocated directly from the newly added hotplugged memory, enabling hot-add operations even when the system is under high memory pressure. If set to 0, the memmap metadata is allocated from existing system memory. + +Possible BLOCK, CONFIGURED, STATE, MEMMAP-ON-MEMORY states:: + +[cols="10,10,10,15,60", options="header"] +|=== +| BLOCK | STATE | CONFIGURED | MEMMAP-ON-MEMORY | Description + +| 0 | online | yes | yes/no | The memory is configured with memmap-on-memory set to (1 or 0) and memory is currently online. + +| 1 | offline | yes | yes/no | The memory is configured, but memory is offline. + +| 2 | offline | no | yes/no | The memory is offline and deconfigured. +|=== + +Not all columns are supported on all systems. If an unsupported column is specified, *lsmem* prints the column but does not provide any data for it. Additionally, *lsmem* may skip columns like CONFIGURED or MEMMAP-ON-MEMORY if these states are not relevant to the system's architecture. Use the *--help* option to see the columns description. +Memmap on memory parameter output displays the globally enabled memmap-on-memory setting for memory_hotplug. This is typically set on the kernel command line via memory_hotplug.memmap_on_memory. + == OPTIONS *-a*, *--all*:: @@ -43,7 +83,7 @@ Use JSON output format. Do not print a header line. *-o*, *--output* _list_:: -Specify which output columns to print. Use *--help* to get a list of all supported columns. The default list of columns may be extended if _list_ is specified in the format **+**__list__ (e.g., *lsmem -o +NODE*). +Specify which output columns to print. Use *--help* to obtain a list of all supported columns. To extend the default list of columns specify _list_ in the format **+**__list__. For example, *lsmem -o +NODE*. *--output-all*:: Output all available columns. @@ -55,7 +95,7 @@ Produce output in the form of key="value" pairs. All potentially unsafe value ch Produce output in raw format. All potentially unsafe characters are hex-escaped (\x). *-S*, *--split* _list_:: -Specify which columns (attributes) use to split memory blocks to ranges. The supported columns are STATE, REMOVABLE, NODE and ZONES, or "none". The other columns are silently ignored. For more details see *DESCRIPTION* above. +Specify which columns are used to split memory blocks to ranges. The supported columns are STATE, REMOVABLE, NODE, ZONES, CONFIGURED, MEMMAP-ON-MEMORY or "none". The other columns are silently ignored. For more details see *DESCRIPTION* above. *-s*, *--sysroot* _directory_:: Gather memory data for a Linux instance other than the instance from which the *lsmem* command is issued. The specified _directory_ is the system root of the Linux instance to be inspected. diff --git a/sys-utils/lsmem.c b/sys-utils/lsmem.c index 39967bfc962..79d90ff5572 100644 --- a/sys-utils/lsmem.c +++ b/sys-utils/lsmem.c @@ -32,6 +32,8 @@ #include "optutils.h" #define _PATH_SYS_MEMORY "/sys/devices/system/memory" +#define _PATH_SYS_MEMMAP_PARM "/sys/module/memory_hotplug/parameters/memmap_on_memory" +#define _PATH_SYS_MEMCONFIG "/sys/firmware/memory" #define MEMORY_STATE_ONLINE 0 #define MEMORY_STATE_OFFLINE 1 @@ -58,12 +60,17 @@ struct memory_block { int nr_zones; int zones[MAX_NR_ZONES]; bool removable; + bool config; + bool memmap_on_memory; }; struct lsmem { struct path_cxt *sysmem; /* _PATH_SYS_MEMORY directory handler */ + struct path_cxt *sysmemconfig; /* _PATH_SYS_MEMCONFIG directory handler */ struct dirent **dirs; + struct dirent **memconfig_dirs; int ndirs; + int memconfig_ndirs; struct memory_block *blocks; int nblocks; uint64_t block_size; @@ -85,7 +92,10 @@ struct lsmem { split_by_state : 1, split_by_removable : 1, split_by_zones : 1, - have_zones : 1; + split_by_memmap_on_memory : 1, + split_by_config : 1, + have_zones : 1, + have_memconfig : 1; }; @@ -97,6 +107,8 @@ enum { COL_BLOCK, COL_NODE, COL_ZONES, + COL_CONFIG, + COL_MEMMAP, }; static const char *const zone_names[] = { @@ -127,6 +139,10 @@ static struct coldesc coldescs[] = { [COL_BLOCK] = { "BLOCK", 0, SCOLS_FL_RIGHT, N_("memory block number or blocks range")}, [COL_NODE] = { "NODE", 0, SCOLS_FL_RIGHT, N_("numa node of memory")}, [COL_ZONES] = { "ZONES", 0, SCOLS_FL_RIGHT, N_("valid zones for the memory range")}, + [COL_CONFIG] = { "CONFIGURED", 0, SCOLS_FL_RIGHT, + N_("configuration status of the memory range")}, + [COL_MEMMAP] = { "MEMMAP-ON-MEMORY", 0, SCOLS_FL_RIGHT, + N_("memmap-on-memory status of the memory range")}, }; /* columns[] array specifies all currently wanted output column. The columns @@ -196,6 +212,8 @@ static inline void reset_split_policy(struct lsmem *l, int enable) l->split_by_node = enable; l->split_by_removable = enable; l->split_by_zones = enable; + l->split_by_memmap_on_memory = enable; + l->split_by_config = enable; } static void set_split_policy(struct lsmem *l, int cols[], size_t ncols) @@ -218,12 +236,23 @@ static void set_split_policy(struct lsmem *l, int cols[], size_t ncols) case COL_ZONES: l->split_by_zones = 1; break; + case COL_MEMMAP: + l->split_by_memmap_on_memory = 1; + break; + case COL_CONFIG: + l->split_by_config = 1; + break; default: break; } } } +static bool skip_memconfig_column(struct lsmem *lsmem, int id) +{ + return (id == COL_MEMMAP || id == COL_CONFIG) && !lsmem->have_memconfig; +} + static void add_scols_line(struct lsmem *lsmem, struct memory_block *blk) { size_t i; @@ -236,6 +265,8 @@ static void add_scols_line(struct lsmem *lsmem, struct memory_block *blk) for (i = 0; i < ncolumns; i++) { char *str = NULL; + if (skip_memconfig_column(lsmem, get_column_id(i))) + continue; switch (get_column_id(i)) { case COL_RANGE: { @@ -291,6 +322,14 @@ static void add_scols_line(struct lsmem *lsmem, struct memory_block *blk) str = xstrdup(valid_zones); } break; + case COL_CONFIG: + if (lsmem->have_memconfig) + str = xstrdup(blk->config ? _("yes") : _("no")); + break; + case COL_MEMMAP: + if (lsmem->have_memconfig) + str = xstrdup(blk->memmap_on_memory ? _("yes") : _("no")); + break; } if (str && scols_line_refer_data(line, i, str) != 0) @@ -306,8 +345,24 @@ static void fill_scols_table(struct lsmem *lsmem) add_scols_line(lsmem, &lsmem->blocks[i]); } +static int get_memmap_mode(char *res, char *src, int len) +{ + if (!strncmp(src, "Y", 1)) + xstrncpy(res, N_("yes"), len); + else if (!strncmp(src, "N", 1)) + xstrncpy(res, N_("no"), len); + else if (!strncmp(src, "force", 5)) + xstrncpy(res, N_("force"), len); + else + return -1; + return 0; +} + static void print_summary(struct lsmem *lsmem) { + char buf[8], res[8]; + FILE *memmap; + if (lsmem->bytes) { printf("%-32s %15"PRId64"\n",_("Memory block size:"), lsmem->block_size); printf("%-32s %15"PRId64"\n",_("Total online memory:"), lsmem->mem_online); @@ -327,6 +382,18 @@ static void print_summary(struct lsmem *lsmem) printf("%-32s %5s\n",_("Total offline memory:"), p); free(p); } + memmap = fopen(_PATH_SYS_MEMMAP_PARM, "r"); + if (!memmap) + return; + if (fgets(buf, sizeof(buf), memmap)) { + if (!get_memmap_mode(res, buf, sizeof(res))) { + if (lsmem->bytes) + printf("%-32s %15s\n", _("Memmap on memory parameter:"), _(res)); + else + printf("%-32s %5s\n", _("Memmap on memory parameter:"), _(res)); + } + } + fclose(memmap); } static int memory_block_get_node(struct lsmem *lsmem, char *name) @@ -371,6 +438,15 @@ static int memory_block_read_attrs(struct lsmem *lsmem, char *name, if (errno) rc = -errno; + if (lsmem->have_memconfig) { + if (ul_path_readf_s32(lsmem->sysmemconfig, &x, "%s/config", name) == 0) + blk->config = x == 1; + if (ul_path_readf_s32(lsmem->sysmemconfig, &x, "%s/memmap_on_memory", name) == 0) + blk->memmap_on_memory = x == 1; + blk->state = MEMORY_STATE_OFFLINE; + if (!blk->config) + return rc; + } if (ul_path_readf_s32(lsmem->sysmem, &x, "%s/removable", name) == 0) blk->removable = x == 1; @@ -434,6 +510,10 @@ static int is_mergeable(struct lsmem *lsmem, struct memory_block *blk) return 0; } } + if (lsmem->split_by_config && curr->config != blk->config) + return 0; + if (lsmem->split_by_memmap_on_memory && curr->memmap_on_memory != blk->memmap_on_memory) + return 0; return 1; } @@ -449,11 +529,33 @@ static void free_info(struct lsmem *lsmem) free(lsmem->dirs); } +static int memory_block_filter(const struct dirent *de) +{ + if (strncmp("memory", de->d_name, 6) != 0) + return 0; + return isdigit_string(de->d_name + 6); +} + +static void read_memconfig(struct lsmem *lsmem) +{ + char dir[PATH_MAX]; + + if (!lsmem->have_memconfig) { + lsmem->memconfig_ndirs = 0; + return; + } + ul_path_get_abspath(lsmem->sysmemconfig, dir, sizeof(dir), NULL); + lsmem->memconfig_ndirs = scandir(dir, &lsmem->memconfig_dirs, + memory_block_filter, versionsort); + if (lsmem->memconfig_ndirs <= 0) + err(EXIT_FAILURE, _("Failed to read %s"), dir); +} + static void read_info(struct lsmem *lsmem) { struct memory_block blk; - char buf[128]; - int i; + char buf[128], *name; + int i, num_dirs; if (ul_path_read_buffer(lsmem->sysmem, buf, sizeof(buf), "block_size_bytes") <= 0) err(EXIT_FAILURE, _("failed to read memory block size")); @@ -463,8 +565,17 @@ static void read_info(struct lsmem *lsmem) if (errno) err(EXIT_FAILURE, _("failed to read memory block size")); - for (i = 0; i < lsmem->ndirs; i++) { - memory_block_read_attrs(lsmem, lsmem->dirs[i]->d_name, &blk); + read_memconfig(lsmem); + if (lsmem->have_memconfig) + num_dirs = lsmem->memconfig_ndirs; + else + num_dirs = lsmem->ndirs; + for (i = 0; i < num_dirs; i++) { + if (lsmem->have_memconfig) + name = lsmem->memconfig_dirs[i]->d_name; + else + name = lsmem->dirs[i]->d_name; + memory_block_read_attrs(lsmem, name, &blk); if (blk.state == MEMORY_STATE_ONLINE) lsmem->mem_online += lsmem->block_size; else @@ -479,13 +590,6 @@ static void read_info(struct lsmem *lsmem) } } -static int memory_block_filter(const struct dirent *de) -{ - if (strncmp("memory", de->d_name, 6) != 0) - return 0; - return isdigit_string(de->d_name + 6); -} - static void read_basic_info(struct lsmem *lsmem) { char dir[PATH_MAX]; @@ -667,6 +771,12 @@ int main(int argc, char **argv) lsmem->sysmem = ul_new_path(_PATH_SYS_MEMORY); if (!lsmem->sysmem) err(EXIT_FAILURE, _("failed to initialize %s handler"), _PATH_SYS_MEMORY); + lsmem->sysmemconfig = ul_new_path(_PATH_SYS_MEMCONFIG); + /* Always check for the existence of /sys/firmware/memory/memory0 first */ + if (ul_path_access(lsmem->sysmemconfig, F_OK, "memory0") == 0) + lsmem->have_memconfig = 1; + if (!lsmem->sysmemconfig) + err(EXIT_FAILURE, _("failed to initialized %s handler"), _PATH_SYS_MEMCONFIG); if (prefix && ul_path_set_prefix(lsmem->sysmem, prefix) != 0) err(EXIT_FAILURE, _("invalid argument to --sysroot")); if (!ul_path_is_accessible(lsmem->sysmem)) @@ -716,6 +826,8 @@ int main(int argc, char **argv) struct coldesc *ci = get_column_desc(i); struct libscols_column *cl; + if (skip_memconfig_column(lsmem, get_column_id(i))) + continue; cl = scols_table_new_column(lsmem->table, ci->name, ci->whint, ci->flags); if (!cl) err(EXIT_FAILURE, _("failed to initialize output column")); diff --git a/sys-utils/lsns.8.adoc b/sys-utils/lsns.8.adoc index 49f0d5b950f..d9ab587feff 100644 --- a/sys-utils/lsns.8.adoc +++ b/sys-utils/lsns.8.adoc @@ -115,7 +115,7 @@ mailto:kzak@redhat.com[Karel Zak] *clone*(2), *namespaces*(7), *ioctl_ns*(2), -*ip-netns*(8) +*ip-netns*(8), *scols-filter*(5) include::man-common/bugreports.adoc[] diff --git a/sys-utils/lsns.c b/sys-utils/lsns.c index 2e887e80253..2b73377775c 100644 --- a/sys-utils/lsns.c +++ b/sys-utils/lsns.c @@ -1030,8 +1030,8 @@ static int read_persistent_namespaces(struct lsns *ls) struct libmnt_fs *fs = NULL; while (mnt_table_next_fs(ls->tab, itr, &fs) == 0) { - const char *root; - char *p, *end = NULL; + const char *root, *p; + char *end = NULL; ino_t ino; int fd; @@ -1126,7 +1126,7 @@ static int is_path_included(const char *path_set, const char *elt, { size_t elt_len; size_t path_set_len; - char *tmp; + const char *tmp; tmp = strstr(path_set, elt); diff --git a/sys-utils/mount.8.adoc b/sys-utils/mount.8.adoc index 9a5479fe54e..28978eda6c1 100644 --- a/sys-utils/mount.8.adoc +++ b/sys-utils/mount.8.adoc @@ -437,7 +437,7 @@ Use options from _fstab_/_mtab_ even if both _device_ and _dir_ are specified. *-R*, *--rbind*:: Remount a subtree and all possible submounts somewhere else (so that its contents are available in both places). See above, the subsection *Bind mount operation*. -*-r*, *--read-only*:: +*-r*, *--ro*, *--read-only*:: Mount the filesystem read-only. A synonym is *-o ro*. + Note that, depending on the filesystem type, state and kernel behavior, the system may still write to the device. For example, ext3 and ext4 will replay the journal if the filesystem is dirty. To prevent this kind of write access, you may want to mount an ext3 or ext4 filesystem with the *ro,noload* mount options or set the block device itself to read-only mode, see the *blockdev*(8) command. diff --git a/sys-utils/mount.c b/sys-utils/mount.c index 2450228d04d..377cafcf849 100644 --- a/sys-utils/mount.c +++ b/sys-utils/mount.c @@ -583,7 +583,7 @@ static void __attribute__((__noreturn__)) usage(void) fputs(_(" --onlyonce check if filesystem is already mounted on target\n"), out); fputs(_(" -o, --options comma-separated list of mount options\n"), out); fputs(_(" -O, --test-opts limit the set of filesystems (use with -a)\n"), out); - fputs(_(" -r, --read-only mount the filesystem read-only (same as -o ro)\n"), out); + fputs(_(" -r, --ro, --read-only mount the filesystem read-only (same as -o ro)\n"), out); fputs(_(" -t, --types limit the set of filesystem types\n"), out); fputs(_(" --source explicitly specifies source (path, label, uuid)\n"), out); fputs(_(" --target explicitly specifies mountpoint\n"), out); diff --git a/sys-utils/mountpoint.1.adoc b/sys-utils/mountpoint.1.adoc index bc8a2e9ed69..250f6d1a954 100644 --- a/sys-utils/mountpoint.1.adoc +++ b/sys-utils/mountpoint.1.adoc @@ -18,7 +18,7 @@ mountpoint - see if a directory or file is a mountpoint == DESCRIPTION -*mountpoint* checks whether the given _directory_ or _file_ is mentioned in the _/proc/self/mountinfo_ file. +*mountpoint* checks whether the given _directory_ or _file_ is a mountpoint. On kernels that support the *statmount*(2) system call (Linux 6.8 and newer), it uses that interface. On older kernels, it falls back to reading _/proc/self/mountinfo_. == OPTIONS @@ -34,6 +34,9 @@ Do not follow symbolic link if it the last element of the _directory_ path. *-x*, *--devno*:: Show the major/minor numbers of the given blockdevice on standard output. +*--show*:: +Print the mountpoint path for the given path. This resolves the given directory or file to its actual mountpoint, which is useful with bind mounts, symlinks, or paths within filesystems. This option requires kernel support for the *statmount*(2) system call (Linux 6.8 and newer). On older kernels, this option will fail with an error message. + include::man-common/help-version.adoc[] == EXIT STATUS diff --git a/sys-utils/mountpoint.c b/sys-utils/mountpoint.c index 6294a4c07a8..688ee67c650 100644 --- a/sys-utils/mountpoint.c +++ b/sys-utils/mountpoint.c @@ -31,63 +31,139 @@ #include "c.h" #include "closestream.h" #include "pathnames.h" +#include "mount-api-utils.h" #define MOUNTPOINT_EXIT_NOMNT 32 struct mountpoint_control { char *path; + char *mnt_target; dev_t dev; struct stat st; + struct libmnt_cache *cache; bool dev_devno, fs_devno, nofollow, - quiet; + quiet, + show; }; +#ifdef HAVE_STATMOUNT_API +/* + * dir_to_device_statmount - check if path is a mountpoint using statmount() + * @ctl: mountpoint control structure + * + * Returns: <0 on error, 0 if mountpoint, 1 if not a mountpoint + */ +static int dir_to_device_statmount(struct mountpoint_control *ctl) +{ + struct libmnt_fs *fs = NULL; + const char *mnt_target; + char *cn = NULL; + uint64_t id = 0; + int rc; + + cn = mnt_resolve_path(ctl->path, ctl->cache); + + rc = mnt_id_from_path(cn ? cn : ctl->path, &id, NULL); + if (rc) + goto done; + + fs = mnt_new_fs(); + if (!fs) { + rc = -ENOMEM; + goto done; + } + + mnt_fs_set_uniq_id(fs, id); + + rc = mnt_fs_fetch_statmount(fs, STATMOUNT_MNT_POINT | STATMOUNT_SB_BASIC); + if (rc) + goto done; + + mnt_target = mnt_fs_get_target(fs); + if (!mnt_target) { + rc = -EINVAL; + goto done; + } + + if (ctl->show) + ctl->mnt_target = xstrdup(mnt_target); + + if (strcmp(mnt_target, cn ? cn : ctl->path) != 0) + rc = 1; /* not a mountpoint */ + else { + ctl->dev = mnt_fs_get_devno(fs); + rc = 0; /* is a mountpoint */ + } +done: + mnt_unref_fs(fs); + return rc; +} +#endif /* STATMOUNT_MNT_POINT */ + +/* + * dir_to_device - check if path is a mountpoint + * @ctl: mountpoint control structure + * + * Returns: <0 on error, 0 if mountpoint, 1 if not a mountpoint + */ static int dir_to_device(struct mountpoint_control *ctl) { - struct libmnt_table *tb = mnt_new_table_from_file(_PATH_PROC_MOUNTINFO); + struct libmnt_table *tb; struct libmnt_fs *fs; - struct libmnt_cache *cache; - int rc = -1; + int rc; +#ifdef HAVE_STATMOUNT_API + rc = dir_to_device_statmount(ctl); + if (rc >= 0) + return rc; +#endif + /* + * Fallback for older kernels without statmount() support + */ + if (ctl->show) + errx(EXIT_FAILURE, _("--show is not supported on this system")); + + tb = mnt_new_table_from_file(_PATH_PROC_MOUNTINFO); if (!tb) { /* * Fallback. Traditional way to detect mountpoints. This way * is independent on /proc, but not able to detect bind mounts. */ struct stat pst; - char buf[PATH_MAX], *cn; + char buf[PATH_MAX]; + const char *cn; int len; - cn = mnt_resolve_path(ctl->path, NULL); /* canonicalize */ + cn = mnt_resolve_path(ctl->path, ctl->cache); /* canonicalize */ len = snprintf(buf, sizeof(buf), "%s/..", cn ? cn : ctl->path); - free(cn); if (len < 0 || (size_t) len >= sizeof(buf)) - return -1; - if (stat(buf, &pst) !=0) - return -1; + return -EINVAL; + + rc = stat(buf, &pst); + if (rc) + return -errno; if (ctl->st.st_dev != pst.st_dev || ctl->st.st_ino == pst.st_ino) { ctl->dev = ctl->st.st_dev; - return 0; + return 0; /* is a mountpoint */ } - return -1; + return 1; /* not a mountpoint */ } /* to canonicalize all necessary paths */ - cache = mnt_new_cache(); - mnt_table_set_cache(tb, cache); - mnt_unref_cache(cache); + mnt_table_set_cache(tb, ctl->cache); fs = mnt_table_find_target(tb, ctl->path, MNT_ITER_BACKWARD); if (fs && mnt_fs_get_target(fs)) { ctl->dev = mnt_fs_get_devno(fs); - rc = 0; - } + rc = 0; /* is a mountpoint */ + } else + rc = 1; /* not a mountpoint */ mnt_unref_table(tb); return rc; @@ -119,7 +195,8 @@ static void __attribute__((__noreturn__)) usage(void) fputs(_(" -q, --quiet quiet mode - don't print anything\n" " --nofollow do not follow symlink\n" " -d, --fs-devno print maj:min device number of the filesystem\n" - " -x, --devno print maj:min device number of the block device\n"), out); + " -x, --devno print maj:min device number of the block device\n" + " --show print mountpoint for given path\n"), out); fputs(USAGE_SEPARATOR, out); fprintf(out, USAGE_HELP_OPTIONS(20)); fprintf(out, USAGE_MAN_TAIL("mountpoint(1)")); @@ -129,11 +206,12 @@ static void __attribute__((__noreturn__)) usage(void) int main(int argc, char **argv) { - int c; + int c, rc; struct mountpoint_control ctl = { NULL }; enum { - OPT_NOFOLLOW = CHAR_MAX + 1 + OPT_NOFOLLOW = CHAR_MAX + 1, + OPT_SHOW }; static const struct option longopts[] = { @@ -141,6 +219,7 @@ int main(int argc, char **argv) { "nofollow", no_argument, NULL, OPT_NOFOLLOW }, { "fs-devno", no_argument, NULL, 'd' }, { "devno", no_argument, NULL, 'x' }, + { "show", no_argument, NULL, OPT_SHOW }, { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, { NULL, 0, NULL, 0 } @@ -168,6 +247,9 @@ int main(int argc, char **argv) case 'x': ctl.dev_devno = 1; break; + case OPT_SHOW: + ctl.show = 1; + break; case 'h': usage(); @@ -196,15 +278,48 @@ int main(int argc, char **argv) if (ctl.dev_devno) return print_devno(&ctl) ? MOUNTPOINT_EXIT_NOMNT : EXIT_SUCCESS; - if ((ctl.nofollow && S_ISLNK(ctl.st.st_mode)) || dir_to_device(&ctl)) { + if (ctl.nofollow && S_ISLNK(ctl.st.st_mode)) { if (!ctl.quiet) printf(_("%s is not a mountpoint\n"), ctl.path); return MOUNTPOINT_EXIT_NOMNT; } + + ctl.cache = mnt_new_cache(); + if (!ctl.cache) + err(EXIT_FAILURE, _("failed to initialize libmount cache")); + + rc = dir_to_device(&ctl); + if (rc < 0) { + if (!ctl.quiet) { + errno = -rc; + warn("%s", ctl.path); + } + rc = EXIT_FAILURE; + goto done; + } + + if (ctl.show) { + printf("%s\n", ctl.mnt_target); + rc = EXIT_SUCCESS; + goto done; + } + + if (rc == 1) { + if (!ctl.quiet) + printf(_("%s is not a mountpoint\n"), ctl.path); + rc = MOUNTPOINT_EXIT_NOMNT; + goto done; + } + if (ctl.fs_devno) printf("%u:%u\n", major(ctl.dev), minor(ctl.dev)); else if (!ctl.quiet) printf(_("%s is a mountpoint\n"), ctl.path); - return EXIT_SUCCESS; + rc = EXIT_SUCCESS; + +done: + free(ctl.mnt_target); + mnt_unref_cache(ctl.cache); + return rc; } diff --git a/sys-utils/rtcwake.8.adoc b/sys-utils/rtcwake.8.adoc index b118534c836..5164a23a378 100644 --- a/sys-utils/rtcwake.8.adoc +++ b/sys-utils/rtcwake.8.adoc @@ -24,7 +24,7 @@ This is normally used like the old *apmsleep* utility, to wake from a suspend st On some systems, this can also be used like *nvram-wakeup*, waking from states like ACPI S4 (suspend to disk). Not all systems have persistent media that are appropriate for such suspend modes. -Note that alarm functionality depends on hardware; not every RTC is able to setup an alarm up to 24 hours in the future. +Note that alarm functionality depends on hardware; not every RTC is able to set up an alarm up to 24 hours in the future. The suspend setup may be interrupted by active hardware; for example wireless USB input devices that continue to send events for some fraction of a second after the return key is pressed. *rtcwake* tries to avoid this problem and it waits to the terminal to settle down before entering a system sleep. diff --git a/sys-utils/setpriv.c b/sys-utils/setpriv.c index c218be8e5e3..505d1ee5b4e 100644 --- a/sys-utils/setpriv.c +++ b/sys-utils/setpriv.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "all-io.h" #include "c.h" @@ -56,9 +57,6 @@ #define SETPRIV_EXIT_PRIVERR 127 /* how we exit when we fail to set privs */ -/* The shell to set SHELL env.variable if none is given in the user's passwd entry. */ -#define DEFAULT_SHELL "/bin/sh" - static gid_t get_group(const char *s, const char *err); enum cap_type { @@ -741,7 +739,7 @@ static void do_reset_environ(struct passwd *pw) if (pw->pw_shell && *pw->pw_shell) xsetenv("SHELL", pw->pw_shell, 1); else - xsetenv("SHELL", DEFAULT_SHELL, 1); + xsetenv("SHELL", _PATH_BSHELL, 1); xsetenv("HOME", pw->pw_dir, 1); xsetenv("USER", pw->pw_name, 1); diff --git a/sys-utils/swapon.8.adoc b/sys-utils/swapon.8.adoc index 5798925ec5b..7a6038acf61 100644 --- a/sys-utils/swapon.8.adoc +++ b/sys-utils/swapon.8.adoc @@ -116,7 +116,7 @@ Requires "swap" as the filesystem type. === The fourth field (options) -It is formatted as a comma-separated list of options. All unknown options are silently ignored. If options are unnecessary, the recommended convention is to use "default". The options specified in _fstab_ extend or overwrite settings specified on the *swapon* command line. +It is formatted as a comma-separated list of options. All unknown options are silently ignored. If options are unnecessary, the recommended convention is to use "defaults". The options specified in _fstab_ extend or overwrite settings specified on the *swapon* command line. Supported swap options: diff --git a/sys-utils/tunelp.c b/sys-utils/tunelp.c index 2201f81ce36..68b795d3b96 100644 --- a/sys-utils/tunelp.c +++ b/sys-utils/tunelp.c @@ -133,7 +133,6 @@ int main(int argc, char **argv) {"check-status", required_argument, NULL, 'o'}, {"careful", required_argument, NULL, 'C'}, {"status", no_argument, NULL, 's'}, - {"trust-irq", required_argument, NULL, 'T'}, {"reset", no_argument, NULL, 'r'}, {"print-irq", required_argument, NULL, 'q'}, {"version", no_argument, NULL, 'V'}, diff --git a/sys-utils/unshare.1.adoc b/sys-utils/unshare.1.adoc index 1f259962c0e..85d00af3c9e 100644 --- a/sys-utils/unshare.1.adoc +++ b/sys-utils/unshare.1.adoc @@ -121,6 +121,9 @@ Run the program only after the current effective user and group IDs have been ma *-c*, *--map-current-user*:: Run the program only after the current effective user and group IDs have been mapped to the same UID and GID in the newly created user namespace. This option implies *--setgroups=deny* and *--user*. This option is equivalent to *--map-user=$(id -ru) --map-group=$(id -rg)*. +*--owner* __uid__**:**__gid__:: +Set the owner user and group when creating a user namespace. These determine which user in the parent namespace has CAP_SYS_ADMIN in the new child namespace and can *setns*(2) into it. This option allows a privileged user to create a namespace on behalf of an unprivileged one, using its privileges to map ids and/or bind mount the namespace into the filesystem. It implies *--user*. + *--propagation* **private**|**shared**|**slave**|**unchanged**:: Recursively set the mount propagation flag in the new mount namespace. The default is to set the propagation to _private_. It is possible to disable this feature with the argument *unchanged*. The option is silently ignored when the mount namespace (*--mount*) is not requested. diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c index e7c8c0f26f6..6f82461b86c 100644 --- a/sys-utils/unshare.c +++ b/sys-utils/unshare.c @@ -199,18 +199,6 @@ static int bind_ns_files(pid_t pid) return 0; } -static ino_t get_mnt_ino(pid_t pid) -{ - struct stat st; - char path[PATH_MAX]; - - snprintf(path, sizeof(path), "/proc/%u/ns/mnt", (unsigned) pid); - - if (stat(path, &st) != 0) - err(EXIT_FAILURE, _("stat of %s failed"), path); - return st.st_ino; -} - static void settime(int64_t offset, clockid_t clk_id) { char buf[sizeof(stringify_value(ULONG_MAX)) * 3]; @@ -310,52 +298,33 @@ static pid_t fork_and_wait(int *fd) static pid_t bind_ns_files_from_child(int *fd) { pid_t child, ppid = getpid(); - ino_t ino = get_mnt_ino(ppid); child = fork_and_wait(fd); if (child) return child; - if (get_mnt_ino(ppid) == ino) - exit(EXIT_FAILURE); bind_ns_files(ppid); exit(EXIT_SUCCESS); } -static uid_t get_user(const char *s, const char *err) +static uid_t get_user(const char *s) { struct passwd *pw; - char *buf = NULL; - uid_t ret; - pw = xgetpwnam(s, &buf); - if (pw) { - ret = pw->pw_uid; - free(pw); - free(buf); - } else { - ret = strtoul_or_err(s, err); - } - - return ret; + pw = ul_getuserpw_str(s); + if (!pw) + errx(EXIT_FAILURE, _("failed to parse uid '%s'"), s); + return pw->pw_uid; } -static gid_t get_group(const char *s, const char *err) +static gid_t get_group(const char *s) { struct group *gr; - char *buf = NULL; - gid_t ret; - - gr = xgetgrnam(s, &buf); - if (gr) { - ret = gr->gr_gid; - free(gr); - free(buf); - } else { - ret = strtoul_or_err(s, err); - } - return ret; + gr = ul_getgrp_str(s); + if (!gr) + errx(EXIT_FAILURE, _("failed to parse gid '%s'"), s); + return gr->gr_gid; } /** @@ -469,6 +438,7 @@ static struct map_range read_subid_range(char *filename, uid_t uid, int identity fclose(idmap); free(pw); free(pwbuf); + free(line); return map; } @@ -690,8 +660,7 @@ static void map_ids_internal(const char *type, int ppid, struct map_range *chain * * Return: The pid of the child. */ -static pid_t map_ids_from_child(int *fd, uid_t mapuser, - struct map_range *usermap, gid_t mapgroup, +static pid_t map_ids_from_child(int *fd, struct map_range *usermap, struct map_range *groupmap) { pid_t child, pid = 0; @@ -701,11 +670,6 @@ static pid_t map_ids_from_child(int *fd, uid_t mapuser, if (child) return child; - if (usermap) - add_single_map_range(&usermap, geteuid(), mapuser); - if (groupmap) - add_single_map_range(&groupmap, getegid(), mapgroup); - if (geteuid() == 0) { if (usermap) map_ids_internal("uid_map", ppid, usermap); @@ -799,6 +763,7 @@ static void __attribute__((__noreturn__)) usage(void) " map count users from outeruid to inneruid (implies --user)\n"), out); fputs(_(" --map-groups ::\n" " map count groups from outergid to innergid (implies --user)\n"), out); + fputs(_(" --owner : set the user namespace owner (implies --user)\n"), out); fputs(USAGE_SEPARATOR, out); fputs(_(" -f, --fork fork before launching \n"), out); fputs(_(" --kill-child[=] when dying, kill the forked child (implies --fork)\n" @@ -834,6 +799,7 @@ int main(int argc, char *argv[]) OPT_MAPGROUPS, OPT_MAPAUTO, OPT_MAPSUBIDS, + OPT_OWNER, }; static const struct option longopts[] = { { "help", no_argument, NULL, 'h' }, @@ -860,6 +826,7 @@ int main(int argc, char *argv[]) { "map-current-user", no_argument, NULL, 'c' }, { "map-auto", no_argument, NULL, OPT_MAPAUTO }, { "map-subids", no_argument, NULL, OPT_MAPSUBIDS }, + { "owner", required_argument, NULL, OPT_OWNER }, { "propagation", required_argument, NULL, OPT_PROPAGATION }, { "setgroups", required_argument, NULL, OPT_SETGROUPS }, { "keep-caps", no_argument, NULL, OPT_KEEPCAPS }, @@ -876,8 +843,8 @@ int main(int argc, char *argv[]) int setgrpcmd = SETGROUPS_NONE; int unshare_flags = 0; int c, forkit = 0; - uid_t mapuser = -1; - gid_t mapgroup = -1; + uid_t mapuser = -1, owneruser = -1; + gid_t mapgroup = -1, ownergroup = -1; struct map_range *usermap = NULL; struct map_range *groupmap = NULL; int kill_child_signo = 0; /* 0 means --kill-child was not used */ @@ -969,11 +936,11 @@ int main(int argc, char *argv[]) break; case OPT_MAPUSER: unshare_flags |= CLONE_NEWUSER; - mapuser = get_user(optarg, _("failed to parse uid")); + mapuser = get_user(optarg); break; case OPT_MAPGROUP: unshare_flags |= CLONE_NEWUSER; - mapgroup = get_group(optarg, _("failed to parse gid")); + mapgroup = get_group(optarg); break; case 'r': unshare_flags |= CLONE_NEWUSER; @@ -1021,6 +988,12 @@ int main(int argc, char *argv[]) insert_map_range(&usermap, read_subid_range(_PATH_SUBUID, real_euid, 1)); insert_map_range(&groupmap, read_subid_range(_PATH_SUBGID, real_euid, 1)); break; + case OPT_OWNER: + unshare_flags |= CLONE_NEWUSER; + if (sscanf(optarg, "%u:%u%n", &owneruser, &ownergroup, + &c) < 2 || optarg[c]) + errx(EXIT_FAILURE, _("failed to parse owner")); + break; case OPT_SETGROUPS: setgrpcmd = setgroups_str2id(optarg); break; @@ -1089,12 +1062,28 @@ int main(int argc, char *argv[]) /* clear any inherited settings */ signal(SIGCHLD, SIG_DFL); - if (npersists && (unshare_flags & CLONE_NEWNS)) + if (npersists && (unshare_flags & (CLONE_NEWNS | CLONE_NEWUSER))) pid_bind = bind_ns_files_from_child(&fd_bind); + if (usermap || (mapuser != (uid_t) -1 && owneruser != (uid_t) -1)) { + add_single_map_range(&usermap, real_euid, mapuser); + mapuser = -1; + } + + if (groupmap || (mapgroup != (uid_t) -1 && ownergroup != (uid_t) -1)) { + add_single_map_range(&groupmap, real_egid, mapgroup); + mapgroup = -1; + } + if (usermap || groupmap) - pid_idmap = map_ids_from_child(&fd_idmap, mapuser, usermap, - mapgroup, groupmap); + pid_idmap = map_ids_from_child(&fd_idmap, usermap, groupmap); + + if (ownergroup != (gid_t) -1 && setgroups(0, NULL) != 0) + err(EXIT_FAILURE, _("setgroups failed")); + if (ownergroup != (gid_t) -1 && setgid(ownergroup) != 0) + err(EXIT_FAILURE, _("setgid() failed")); + if (owneruser != (uid_t) -1 && setuid(owneruser) != 0) + err(EXIT_FAILURE, _("setuid() failed")); if (-1 == unshare(unshare_flags)) err(EXIT_FAILURE, _("unshare failed")); @@ -1144,7 +1133,7 @@ int main(int argc, char *argv[]) if (npersists && (pid || !forkit)) { /* run in parent */ - if (pid_bind && (unshare_flags & CLONE_NEWNS)) + if (pid_bind && (unshare_flags & (CLONE_NEWNS | CLONE_NEWUSER))) sync_with_child(pid_bind, fd_bind); else /* simple way, just bind */ @@ -1205,14 +1194,14 @@ int main(int argc, char *argv[]) #endif } - if (mapuser != (uid_t) -1 && !usermap) + if (mapuser != MAX_OF_UINT_TYPE(uid_t)) map_id(_PATH_PROC_UIDMAP, mapuser, real_euid); /* Since Linux 3.19 unprivileged writing of /proc/self/gid_map * has been disabled unless /proc/self/setgroups is written * first to permanently disable the ability to call setgroups * in that user namespace. */ - if (mapgroup != (gid_t) -1 && !groupmap) { + if (mapgroup != MAX_OF_UINT_TYPE(gid_t)) { if (setgrpcmd == SETGROUPS_ALLOW) errx(EXIT_FAILURE, _("options --setgroups=allow and " "--map-group are mutually exclusive")); diff --git a/sys-utils/wdctl.c b/sys-utils/wdctl.c index f3ae0e3f8c6..0cf8ee7a436 100644 --- a/sys-utils/wdctl.c +++ b/sys-utils/wdctl.c @@ -779,7 +779,7 @@ int main(int argc, char *argv[]) close_stdout_atexit(); while ((c = getopt_long(argc, argv, - "d:f:g:hFnITp:o:s:OrVx", long_opts, NULL)) != -1) { + "f:g:hFnITp:o:s:OrVx", long_opts, NULL)) != -1) { err_exclusive_options(c, long_opts, excl, excl_st); diff --git a/sys-utils/zramctl.8.adoc b/sys-utils/zramctl.8.adoc index c08f1e00503..7bccc3909d3 100644 --- a/sys-utils/zramctl.8.adoc +++ b/sys-utils/zramctl.8.adoc @@ -34,6 +34,8 @@ Set up a zram device: :: If no option is given, all non-zero size zram devices are shown. +Use *--help* to get an overview of the supported output columns and their descriptions. + Note that _zramdev_ node specified on command line has to already exist. The command *zramctl* creates a new _/dev/zram_ nodes only when *--find* option specified. It's possible (and common) that after system boot _/dev/zram_ nodes are not created yet. == OPTIONS diff --git a/term-utils/agetty.c b/term-utils/agetty.c index 7a97167cd8c..f4d720448be 100644 --- a/term-utils/agetty.c +++ b/term-utils/agetty.c @@ -1952,7 +1952,7 @@ static void eval_issue_file(struct issue *ie, ul_configs_file_list(&file_list, NULL, _PATH_SYSCONFDIR, - _PATH_SYSCONFDIR, + _PATH_RUNSTATEDIR, _PATH_SYSCONFSTATICDIR, "issue", ISSUEDIR_EXT); diff --git a/term-utils/script.c b/term-utils/script.c index ff7f4409f61..4e302347f22 100644 --- a/term-utils/script.c +++ b/term-utils/script.c @@ -70,6 +70,7 @@ #include "signames.h" #include "pty-session.h" #include "debug.h" +#include "shells.h" static UL_DEBUG_DEFINE_MASK(script); UL_DEBUG_DEFINE_MASKNAMES(script) = UL_DEBUG_EMPTY_MASKNAMES; @@ -966,9 +967,7 @@ int main(int argc, char **argv) log_associate(&ctl, &ctl.in, timingfile, format); } - shell = getenv("SHELL"); - if (!shell) - shell = _PATH_BSHELL; + shell = ul_default_shell(0, NULL); ctl.pty = ul_new_pty(ctl.isterm); if (!ctl.pty) diff --git a/term-utils/scriptlive.c b/term-utils/scriptlive.c index e4a3434ed5b..6ac685506f2 100644 --- a/term-utils/scriptlive.c +++ b/term-utils/scriptlive.c @@ -38,6 +38,7 @@ #include "pty-session.h" #include "script-playutils.h" #include "monotonic.h" +#include "shells.h" #define SCRIPT_MIN_DELAY 0.0001 /* from original scriptreplay.pl */ @@ -281,9 +282,7 @@ main(int argc, char *argv[]) replay_set_delay_max(ss.setup, &maxdelay); replay_set_delay_min(ss.setup, &mindelay); - shell = getenv("SHELL"); - if (shell == NULL) - shell = _PATH_BSHELL; + shell = ul_default_shell(0, NULL); fprintf(stdout, _(">>> scriptlive: Starting your typescript execution by %s.\n"), command ? command : shell); diff --git a/tests/expected/blkid/low-probe-ntfs b/tests/expected/blkid/low-probe-ntfs index 3c364bca022..d18e929a892 100644 --- a/tests/expected/blkid/low-probe-ntfs +++ b/tests/expected/blkid/low-probe-ntfs @@ -3,7 +3,7 @@ ID_FS_FSBLOCKSIZE=4096 ID_FS_FSSIZE=10485248 ID_FS_LABEL=Новый_том ID_FS_LABEL_ENC=Новый\x20том -ID_FS_TYPE=ntfs3 +ID_FS_TYPE=ntfs ID_FS_USAGE=filesystem ID_FS_UUID=09CBB6DE30C87310 ID_FS_UUID_ENC=09CBB6DE30C87310 diff --git a/tests/expected/cal/color-first-day b/tests/expected/cal/color-first-day index 229681d6aab..f0a836dc1b1 100644 --- a/tests/expected/cal/color-first-day +++ b/tests/expected/cal/color-first-day @@ -1,5 +1,5 @@ - January 0001 -Su Mo Tu We Th Fr Sa + January 0001  +Su Mo Tu We Th Fr Sa  [ 1  2 3 4 5 6 7 8  9 10 11 12 13 14 15 diff --git a/tests/expected/cal/color-last-day b/tests/expected/cal/color-last-day index b7020ab3ffd..622839167d9 100644 --- a/tests/expected/cal/color-last-day +++ b/tests/expected/cal/color-last-day @@ -1,5 +1,5 @@ - November 9999 December 9999 January 10000 -Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa + November 9999 December 9999 January 10000  +Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa  1 2 3 4 5 6  1 2 3 4  1  7 8 9 10 11 12 13  5 6 7 8 9 10 11  2 3 4 5 6 7 8 14 15 16 17 18 19 20 12 13 14 15 16 17 18  9 10 11 12 13 14 15 diff --git a/tests/expected/cal/color-reformation-corner-cases-1 b/tests/expected/cal/color-reformation-corner-cases-1 index af9e2bd81a5..56448187e5f 100644 --- a/tests/expected/cal/color-reformation-corner-cases-1 +++ b/tests/expected/cal/color-reformation-corner-cases-1 @@ -1,5 +1,5 @@ - September 1752 -Su Mo Tu We Th Fr Sa + September 1752  +Su Mo Tu We Th Fr Sa  1 [ 2 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 diff --git a/tests/expected/cal/color-reformation-corner-cases-2 b/tests/expected/cal/color-reformation-corner-cases-2 index 75402e99a92..afe0f864a55 100644 --- a/tests/expected/cal/color-reformation-corner-cases-2 +++ b/tests/expected/cal/color-reformation-corner-cases-2 @@ -1,5 +1,5 @@ - September 1752 -Su Mo Tu We Th Fr Sa + September 1752  +Su Mo Tu We Th Fr Sa  1 2 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 diff --git a/tests/expected/cal/color-reformation-corner-cases-3 b/tests/expected/cal/color-reformation-corner-cases-3 index 75402e99a92..afe0f864a55 100644 --- a/tests/expected/cal/color-reformation-corner-cases-3 +++ b/tests/expected/cal/color-reformation-corner-cases-3 @@ -1,5 +1,5 @@ - September 1752 -Su Mo Tu We Th Fr Sa + September 1752  +Su Mo Tu We Th Fr Sa  1 2 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 diff --git a/tests/expected/cal/color-reformation-corner-cases-4 b/tests/expected/cal/color-reformation-corner-cases-4 index dbf47a90858..52dd9ce97ef 100644 --- a/tests/expected/cal/color-reformation-corner-cases-4 +++ b/tests/expected/cal/color-reformation-corner-cases-4 @@ -1,5 +1,5 @@ - September 1752 -Su Mo Tu We Th Fr Sa + September 1752  +Su Mo Tu We Th Fr Sa  1 2 [14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 diff --git a/tests/expected/cal/color-vertical b/tests/expected/cal/color-vertical index f4ffac6a5d4..0d7e2596b28 100644 --- a/tests/expected/cal/color-vertical +++ b/tests/expected/cal/color-vertical @@ -1,4 +1,4 @@ - February 2023  + February 2023  Su  5 12 19 26 Mo  6 13 20 27 Tu  7 14 21 28 diff --git a/tests/expected/cal/color-vertical-week b/tests/expected/cal/color-vertical-week index 70603618538..dfaad2b7710 100644 --- a/tests/expected/cal/color-vertical-week +++ b/tests/expected/cal/color-vertical-week @@ -1,4 +1,4 @@ - February 2023  + February 2023  Su  5 12 19 26 Mo  6 13 20 27 Tu  7 14 21 28 diff --git a/tests/expected/cal/colorw-first-day-week-numbers b/tests/expected/cal/colorw-first-day-week-numbers index f3117d89b26..f1fb66f9a9b 100644 --- a/tests/expected/cal/colorw-first-day-week-numbers +++ b/tests/expected/cal/colorw-first-day-week-numbers @@ -1,5 +1,5 @@ - January 0001 - Su Mo Tu We Th Fr Sa + January 0001  + Su Mo Tu We Th Fr Sa  1  [ 1  2 2 3 4 5 6 7 8  3 9 10 11 12 13 14 15 diff --git a/tests/expected/cal/colorw-last-day-week-numbers b/tests/expected/cal/colorw-last-day-week-numbers index dd081fdfc65..ddf8f54b22a 100644 --- a/tests/expected/cal/colorw-last-day-week-numbers +++ b/tests/expected/cal/colorw-last-day-week-numbers @@ -1,5 +1,5 @@ - November 9999 December 9999 January 10000 - Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa + November 9999 December 9999 January 10000  + Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa 45  1 2 3 4 5 6 49  1 2 3 4  1  1 46 7 8 9 10 11 12 13 50 5 6 7 8 9 10 11  2 2 3 4 5 6 7 8 47 14 15 16 17 18 19 20 51 12 13 14 15 16 17 18  3 9 10 11 12 13 14 15 diff --git a/tests/expected/cal/colorw-reformation-corner-cases-1-week-numbers b/tests/expected/cal/colorw-reformation-corner-cases-1-week-numbers index a94a28e549e..9144c72db97 100644 --- a/tests/expected/cal/colorw-reformation-corner-cases-1-week-numbers +++ b/tests/expected/cal/colorw-reformation-corner-cases-1-week-numbers @@ -1,5 +1,5 @@ - September 1752 - Su Mo Tu We Th Fr Sa + September 1752  + Su Mo Tu We Th Fr Sa 36  1 [ 2 14 15 16 37 17 18 19 20 21 22 23 38 24 25 26 27 28 29 30 diff --git a/tests/expected/cal/colorw-reformation-corner-cases-2-week-numbers b/tests/expected/cal/colorw-reformation-corner-cases-2-week-numbers index 701eb91fe83..88955a4935a 100644 --- a/tests/expected/cal/colorw-reformation-corner-cases-2-week-numbers +++ b/tests/expected/cal/colorw-reformation-corner-cases-2-week-numbers @@ -1,5 +1,5 @@ - September 1752 - Su Mo Tu We Th Fr Sa + September 1752  + Su Mo Tu We Th Fr Sa 36  1 2 14 15 16 37 17 18 19 20 21 22 23 38 24 25 26 27 28 29 30 diff --git a/tests/expected/cal/colorw-reformation-corner-cases-3-week-numbers b/tests/expected/cal/colorw-reformation-corner-cases-3-week-numbers index 701eb91fe83..88955a4935a 100644 --- a/tests/expected/cal/colorw-reformation-corner-cases-3-week-numbers +++ b/tests/expected/cal/colorw-reformation-corner-cases-3-week-numbers @@ -1,5 +1,5 @@ - September 1752 - Su Mo Tu We Th Fr Sa + September 1752  + Su Mo Tu We Th Fr Sa 36  1 2 14 15 16 37 17 18 19 20 21 22 23 38 24 25 26 27 28 29 30 diff --git a/tests/expected/cal/colorw-reformation-corner-cases-4-week-numbers b/tests/expected/cal/colorw-reformation-corner-cases-4-week-numbers index e35901a0f3c..fc4fb46946c 100644 --- a/tests/expected/cal/colorw-reformation-corner-cases-4-week-numbers +++ b/tests/expected/cal/colorw-reformation-corner-cases-4-week-numbers @@ -1,5 +1,5 @@ - September 1752 - Su Mo Tu We Th Fr Sa + September 1752  + Su Mo Tu We Th Fr Sa 36  1 2 [14 15 16 37 17 18 19 20 21 22 23 38 24 25 26 27 28 29 30 diff --git a/tests/expected/libmount/loop-o-loop-val-conflict b/tests/expected/libmount/loop-o-loop-val-conflict index 0eb73221454..faedb3b9bc1 100644 --- a/tests/expected/libmount/loop-o-loop-val-conflict +++ b/tests/expected/libmount/loop-o-loop-val-conflict @@ -1,2 +1,2 @@ -mount: failed to setup loop device for +mount: failed to set up loop device for Success diff --git a/tests/expected/lsfd/mkfds-cdev-tun b/tests/expected/lsfd/mkfds-cdev-tun-domestic similarity index 100% rename from tests/expected/lsfd/mkfds-cdev-tun rename to tests/expected/lsfd/mkfds-cdev-tun-domestic diff --git a/tests/expected/lsfd/mkfds-cdev-tun-domestic-devnetns b/tests/expected/lsfd/mkfds-cdev-tun-domestic-devnetns new file mode 100644 index 00000000000..291bc7d5315 --- /dev/null +++ b/tests/expected/lsfd/mkfds-cdev-tun-domestic-devnetns @@ -0,0 +1 @@ +TUN.DEVNETNS: 0 diff --git a/tests/expected/lsfd/mkfds-cdev-tun-foreign b/tests/expected/lsfd/mkfds-cdev-tun-foreign new file mode 100644 index 00000000000..5217d7939a3 --- /dev/null +++ b/tests/expected/lsfd/mkfds-cdev-tun-foreign @@ -0,0 +1,4 @@ +3 rw- CHR misc:tun +ASSOC,MODE,TYPE,SOURCE: 0 +NAME: 0 +TUN.IFACE: 0 diff --git a/tests/expected/lsfd/mkfds-cdev-tun-foreign-devnetns b/tests/expected/lsfd/mkfds-cdev-tun-foreign-devnetns new file mode 100644 index 00000000000..291bc7d5315 --- /dev/null +++ b/tests/expected/lsfd/mkfds-cdev-tun-foreign-devnetns @@ -0,0 +1 @@ +TUN.DEVNETNS: 0 diff --git a/tests/expected/lsmem/lsmem-s390-zvm-6g b/tests/expected/lsmem/lsmem-s390-zvm-6g index ac576e1ab5b..4fcdc718cb0 100644 --- a/tests/expected/lsmem/lsmem-s390-zvm-6g +++ b/tests/expected/lsmem/lsmem-s390-zvm-6g @@ -17,6 +17,7 @@ RANGE SIZE STATE REMOVABLE BLOCK Memory block size: 256M Total online memory: 4.8G Total offline memory: 1.3G +Memmap on memory parameter: no --- @@ -27,6 +28,7 @@ RANGE SIZE Memory block size: 256M Total online memory: 4.8G Total offline memory: 1.3G +Memmap on memory parameter: no --- @@ -40,6 +42,7 @@ RANGE SIZE STATE Memory block size: 256M Total online memory: 4.8G Total offline memory: 1.3G +Memmap on memory parameter: no --- @@ -73,6 +76,7 @@ RANGE SIZE STATE REMOVABLE BLOCK NODE Memory block size: 256M Total online memory: 4.8G Total offline memory: 1.3G +Memmap on memory parameter: no --- @@ -216,6 +220,7 @@ RANGE SIZE STATE REMOVABLE BLOCK ZON Memory block size: 256M Total online memory: 4.8G Total offline memory: 1.3G +Memmap on memory parameter: no --- @@ -235,3 +240,4 @@ RANGE SIZE STATE REMOVABLE BLOCK Memory block size: 256M Total online memory: 4.8G Total offline memory: 1.3G +Memmap on memory parameter: no diff --git a/tests/expected/lsmem/lsmem-x86_64-16g b/tests/expected/lsmem/lsmem-x86_64-16g index 663a8fe1dae..955ad958128 100644 --- a/tests/expected/lsmem/lsmem-x86_64-16g +++ b/tests/expected/lsmem/lsmem-x86_64-16g @@ -37,6 +37,7 @@ RANGE SIZE STATE REMOVABLE BLOCK Memory block size: 128M Total online memory: 16G Total offline memory: 0B +Memmap on memory parameter: no --- @@ -48,6 +49,7 @@ RANGE SIZE Memory block size: 128M Total online memory: 16G Total offline memory: 0B +Memmap on memory parameter: no --- @@ -59,6 +61,7 @@ RANGE SIZE STATE Memory block size: 128M Total online memory: 16G Total offline memory: 0B +Memmap on memory parameter: no --- @@ -196,6 +199,7 @@ RANGE SIZE STATE REMOVABLE BLOCK NODE ZONES Memory block size: 128M Total online memory: 16G Total offline memory: 0B +Memmap on memory parameter: no --- @@ -519,6 +523,7 @@ RANGE SIZE STATE REMOVABLE BLOCK ZONES Memory block size: 128M Total online memory: 16G Total offline memory: 0B +Memmap on memory parameter: no --- @@ -558,3 +563,4 @@ RANGE SIZE STATE REMOVABLE BLOCK Memory block size: 128M Total online memory: 16G Total offline memory: 0B +Memmap on memory parameter: no diff --git a/tests/expected/su/group-group-id b/tests/expected/su/group-group-id new file mode 100644 index 00000000000..b4b6910249b --- /dev/null +++ b/tests/expected/su/group-group-id @@ -0,0 +1 @@ +ts_grp diff --git a/tests/expected/su/group-primary-login b/tests/expected/su/group-primary-login new file mode 100644 index 00000000000..b4b6910249b --- /dev/null +++ b/tests/expected/su/group-primary-login @@ -0,0 +1 @@ +ts_grp diff --git a/tests/expected/su/group-primary-no-login b/tests/expected/su/group-primary-no-login new file mode 100644 index 00000000000..b4b6910249b --- /dev/null +++ b/tests/expected/su/group-primary-no-login @@ -0,0 +1 @@ +ts_grp diff --git a/tests/expected/su/group-supplemental-login b/tests/expected/su/group-supplemental-login new file mode 100644 index 00000000000..b4b6910249b --- /dev/null +++ b/tests/expected/su/group-supplemental-login @@ -0,0 +1 @@ +ts_grp diff --git a/tests/expected/su/group-supplemental-no-login b/tests/expected/su/group-supplemental-no-login new file mode 100644 index 00000000000..b4b6910249b --- /dev/null +++ b/tests/expected/su/group-supplemental-no-login @@ -0,0 +1 @@ +ts_grp diff --git a/tests/expected/su/shell-additional-args b/tests/expected/su/shell-additional-args new file mode 100644 index 00000000000..01dca2d74bb --- /dev/null +++ b/tests/expected/su/shell-additional-args @@ -0,0 +1 @@ +/bin/bash diff --git a/tests/expected/su/shell-command-option b/tests/expected/su/shell-command-option new file mode 100644 index 00000000000..01dca2d74bb --- /dev/null +++ b/tests/expected/su/shell-command-option @@ -0,0 +1 @@ +/bin/bash diff --git a/tests/ts/lsfd/mkfds-cdev-tun b/tests/ts/lsfd/mkfds-cdev-tun index 7354509c4dd..1079167fb14 100755 --- a/tests/ts/lsfd/mkfds-cdev-tun +++ b/tests/ts/lsfd/mkfds-cdev-tun @@ -25,41 +25,98 @@ ts_skip_nonroot ts_check_test_command "$TS_CMD_LSFD" ts_check_test_command "$TS_HELPER_MKFDS" +ts_check_test_command "$TS_CMD_UNSHARE" +ts_check_test_command "$TS_CMD_LSNS" ts_cd "$TS_OUTDIR" PID= FD=3 IFNAME= +readonly MYNETNS=$($TS_CMD_LSNS -n -t net -p $$ -oNS) +if [[ -z "$MYNETNS" ]]; then + ts_skip "the current netns is unknown" +fi + +cdev_tun_test() { - coproc MKFDS { "$TS_HELPER_MKFDS" cdev-tun $FD ; } - if read -u ${MKFDS[0]} PID IFNAME; then - EXPR='(FD == '"$FD"')' - ${TS_CMD_LSFD} -p "${PID}" -n -o ASSOC,MODE,TYPE,SOURCE -Q "${EXPR}" - echo 'ASSOC,MODE,TYPE,SOURCE': $? - - output=$(${TS_CMD_LSFD} -p "${PID}" -n --raw -o NAME -Q "${EXPR}") - if [[ "$output" == "iface=$IFNAME" ]]; then - echo 'NAME': $? - else - echo 'NAME': $? - echo "expected NAME: iface=$IFNAME" - echo "output NAME: $output" - fi + local unshare=$1 + local tname + local devnetns_available + local netns + + if [[ -z "$unshare" ]]; then + tname=domestic + netns=$MYNETNS + else + tname=foreign + fi + + ts_init_subtest "$tname" + { + coproc MKFDS { $unshare "$TS_HELPER_MKFDS" cdev-tun $FD ; } + + if read -u ${MKFDS[0]} PID IFNAME; then + EXPR='(FD == '"$FD"')' + ${TS_CMD_LSFD} -p "${PID}" -n -o ASSOC,MODE,TYPE,SOURCE -Q "${EXPR}" + echo 'ASSOC,MODE,TYPE,SOURCE': $? + + if [[ -z "$netns" ]]; then + netns=$($TS_CMD_LSNS -n -t net -p $PID -oNS) + fi - output=$(${TS_CMD_LSFD} -p "${PID}" -n --raw -o TUN.IFACE -Q "${EXPR}") - if [[ "$output" == "$IFNAME" ]]; then - echo 'TUN.IFACE': $? - else - echo 'TUN.IFAEC': $? - echo "expected TUN.IFACE: $IFNAME" - echo "output TUN.IFACE: $output" + output=$(${TS_CMD_LSFD} -p "${PID}" -n --raw -o NAME -Q "${EXPR}") + if [[ "$output" =~ "iface=$IFNAME"(\\x20devnetns=$netns)? ]]; then + echo 'NAME': $? + if [[ -n "${BASH_REMATCH[1]}" ]]; then + devnetns_available=yes + fi + else + echo 'NAME': $? + echo "expected NAME: iface=$IFNAME" + echo "output NAME: $output" + echo "netns: $netns" + fi + + output=$(${TS_CMD_LSFD} -p "${PID}" -n --raw -o TUN.IFACE -Q "${EXPR}") + if [[ "$output" == "$IFNAME" ]]; then + echo 'TUN.IFACE': $? + else + echo 'TUN.IFACE': $? + echo "expected TUN.IFACE: $IFNAME" + echo "output TUN.IFACE: $output" + fi fi + } > $TS_OUTPUT 2>&1 + ts_finalize_subtest + + ts_init_subtest "$tname"-devnetns + if [[ -z "${devnetns_available}" ]]; then + ts_skip_subtest "no method to access devnetns on this platform" + else + { + output=$(${TS_CMD_LSFD} -p "${PID}" -n --raw -o TUN.DEVNETNS -Q "${EXPR}") + if [[ "$output" == "$netns" ]]; then + echo 'TUN.DEVNETNS': $? + else + echo 'TUN.DEVNETNS': $? + echo "expected TUN.DEVNETNS: $netns" + echo "output TUN.DEVNETNS: $output" + fi + } > $TS_OUTPUT 2>&1 + fi + ts_finalize_subtest + + if [[ -n "$PID" ]]; then echo DONE >&"${MKFDS[1]}" fi + wait ${MKFDS_PID} -} > $TS_OUTPUT 2>&1 +} + +cdev_tun_test +cdev_tun_test "$TS_CMD_UNSHARE" --net ts_finalize diff --git a/tests/ts/misc/swaplabel b/tests/ts/misc/swaplabel index 9db7d2b80f2..6e777ac2a97 100755 --- a/tests/ts/misc/swaplabel +++ b/tests/ts/misc/swaplabel @@ -21,14 +21,7 @@ ts_init "$*" ts_check_test_command "$TS_CMD_MKSWAP" ts_check_test_command "$TS_CMD_SWAPLABEL" ts_check_test_command "$TS_HELPER_SYSINFO" - -# fallocate does not work on most file systems -function fallocate_or_skip() -{ - $TS_CMD_FALLOCATE -x -l $1 $2 2>/dev/null || \ - truncate -s $1 $2 || \ - ts_skip "no way to create test image" -} +ts_check_test_command "$TS_CMD_FALLOCATE" IMAGE=${TS_OUTDIR}/${TS_TESTNAME}.file @@ -38,7 +31,7 @@ MIN_SWAP_SIZE=$(( 10 * $PAGE_SIZE )) MIN_SWAP_SIZE_KB=$(( MIN_SWAP_SIZE / 1024 )) rm -f $IMAGE -fallocate_or_skip $(( $MIN_SWAP_SIZE - 1 )) $IMAGE +$TS_CMD_FALLOCATE -x -l $(( $MIN_SWAP_SIZE - 1 )) $IMAGE 2> /dev/null $TS_CMD_MKSWAP \ --label 1234567890abcdef \ --uuid 12345678-abcd-abcd-abcd-1234567890ab \ @@ -50,7 +43,7 @@ sed -i -e "s/ $MIN_SWAP_SIZE_KB KiB/ 10 pages/" \ $TS_OUTPUT $TS_ERRLOG rm -f $IMAGE -fallocate_or_skip $MIN_SWAP_SIZE $IMAGE +$TS_CMD_FALLOCATE -x -l $MIN_SWAP_SIZE $IMAGE 2> /dev/null $TS_CMD_MKSWAP \ --label 1234567890abcdef \ --uuid 12345678-abcd-abcd-abcd-1234567890ab \ diff --git a/tests/ts/su/group b/tests/ts/su/group new file mode 100755 index 00000000000..d3294fd7293 --- /dev/null +++ b/tests/ts/su/group @@ -0,0 +1,88 @@ +#!/bin/bash + +# This file is part of util-linux. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This file is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# Copyright (C) 2025 Christian Goeschel Ndjomouo + +TS_TOPDIR="${0%/*}/../.." +TS_DESC="group" + +. "$TS_TOPDIR/functions.sh" +ts_init "$*" + +ts_skip_nonroot +ts_check_test_command "$TS_CMD_SU" +ts_check_prog "groupadd" +ts_check_prog "groupdel" +ts_check_prog "getent" + +TS_CMD_ID="id" + +username="$(whoami)" +grp_name="ts_grp" + +if ! groupadd --force "$grp_name"; then + ts_skip "could not create group '$grp_name'" +fi + +# Since here '-c' comes after it'll be passed to the actual shell +# instead of being interpreted by su(1). +ts_init_subtest "primary-no-login" + +"$TS_CMD_SU" --group "$grp_name" "$username" \ + -c "$TS_CMD_ID --groups --name" 2>> "$TS_ERRLOG" \ + | grep -o "$grp_name" \ + | uniq >> "$TS_OUTPUT" + +ts_finalize_subtest + +ts_init_subtest "primary-login" +"$TS_CMD_SU" --group "$grp_name" - "$username" \ + -c "$TS_CMD_ID --groups --name" 2>> "$TS_ERRLOG" \ + | grep -o "$grp_name" \ + | uniq >> "$TS_OUTPUT" + +grep -q 'Authentication failure' "$TS_ERRLOG" +[ "$?" -eq 0 ] && ts_skip_subtest "authentication failure" +ts_finalize_subtest + +ts_init_subtest "supplemental-no-login" +"$TS_CMD_SU" --supp-group "$grp_name" "$username" \ + -c "$TS_CMD_ID --groups --name" 2>> "$TS_ERRLOG" \ + | grep -o "$grp_name" \ + | uniq >> "$TS_OUTPUT" +ts_finalize_subtest + +ts_init_subtest "supplemental-login" +"$TS_CMD_SU" --supp-group "$grp_name" - "$username" \ + -c "$TS_CMD_ID --groups --name" 2>> "$TS_ERRLOG" \ + | grep -o "$grp_name" \ + | uniq >> "$TS_OUTPUT" + +grep -q 'Authentication failure' "$TS_ERRLOG" +[ "$?" -eq 0 ] && ts_skip_subtest "authentication failure" +ts_finalize_subtest + +ts_init_subtest "group-id" + +grp_id="$(cat /etc/group | grep -E "^$grp_name" | cut -d: -f 3)" +"$TS_CMD_SU" --group "$grp_id" "$username" \ + -c "$TS_CMD_ID --groups --name" 2>> "$TS_ERRLOG" \ + | grep -o "$grp_name" \ + | uniq >> "$TS_OUTPUT" + +ts_finalize_subtest + +groupdel "$grp_name" + +ts_finalize \ No newline at end of file diff --git a/tests/ts/su/shell b/tests/ts/su/shell new file mode 100755 index 00000000000..dc77b0b4fbf --- /dev/null +++ b/tests/ts/su/shell @@ -0,0 +1,35 @@ +#!/bin/bash + +# This file is part of util-linux. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This file is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# Copyright (C) 2025 Christian Goeschel Ndjomouo + +TS_TOPDIR="${0%/*}/../.." +TS_DESC="shell" + +. "$TS_TOPDIR/functions.sh" +ts_init "$*" + +ts_skip_nonroot + +username="$(whoami)" + +ts_init_subtest "command-option" +"$TS_CMD_SU" --shell "/bin/bash" --command "echo \$SHELL" "$username" >> "$TS_OUTPUT" 2>> "$TS_ERRLOG" +ts_finalize_subtest + +ts_init_subtest "additional-args" +"$TS_CMD_SU" --shell "/bin/bash" "$username" -c "echo \$SHELL" >> "$TS_OUTPUT" 2>> "$TS_ERRLOG" +ts_finalize_subtest + +ts_finalize \ No newline at end of file diff --git a/text-utils/column.1.adoc b/text-utils/column.1.adoc index 69f2f794ffc..e1e5a1c6f44 100644 --- a/text-utils/column.1.adoc +++ b/text-utils/column.1.adoc @@ -99,7 +99,7 @@ Omit printing the header. This option allows having user-supplied column names o *-o, --output-separator* _string_:: Column delimiter for table output (default is two spaces). -*-s, --separator* _separators_:: +*-s, --input-separator, --separator* _separators_:: Possible input-item delimiters (default is whitespace). *-S, --use-spaces* _number_:: diff --git a/text-utils/column.c b/text-utils/column.c index 7a37cb886c7..5a9e9a03b65 100644 --- a/text-utils/column.c +++ b/text-utils/column.c @@ -1021,7 +1021,8 @@ static void __attribute__((__noreturn__)) usage(void) fputs(_(" -c, --output-width width of output in number of characters\n"), out); fputs(_(" -o, --output-separator columns separator for table output\n" " (default is two spaces)\n"), out); - fputs(_(" -s, --separator possible table delimiters\n"), out); + fputs(_(" -s, --input-separator, --separator \n" + " possible table delimiters\n"), out); fputs(_(" -x, --fillrows fill rows before columns\n"), out); fputs(_(" -S, --use-spaces minimal whitespaces between columns (no tabs)\n"), out); @@ -1059,6 +1060,7 @@ int main(int argc, char **argv) { "color", optional_argument, NULL, OPT_COLOR }, { "fillrows", no_argument, NULL, 'x' }, { "help", no_argument, NULL, 'h' }, + { "input-separator", required_argument, NULL, 's' }, /* alias for --separator */ { "json", no_argument, NULL, 'J' }, { "keep-empty-lines", no_argument, NULL, 'L' }, { "output-separator", required_argument, NULL, 'o' }, diff --git a/text-utils/more.c b/text-utils/more.c index 4980aef4cac..bc04064cf15 100644 --- a/text-utils/more.c +++ b/text-utils/more.c @@ -89,6 +89,7 @@ #include "widechar.h" #include "closestream.h" #include "env.h" +#include "shells.h" #ifdef TEST_PROGRAM # define NON_INTERACTIVE_MORE 1 @@ -174,7 +175,7 @@ struct more_control { int next_jump; /* number of lines to skip ahead */ char **file_names; /* The list of file names */ int num_files; /* Number of files left to process */ - char *shell; /* name of the shell to use */ + const char *shell; /* name of the shell to use */ int sigfd; /* signalfd() file descriptor */ sigset_t sigset; /* signal operations */ char *line_buf; /* line buffer */ @@ -2110,8 +2111,7 @@ static void initterm(struct more_control *ctl) if ((ctl->backspace_ch = tigetstr(TERM_BACKSPACE)) == NULL) ctl->backspace_ch = BACKSPACE; - if ((ctl->shell = getenv("SHELL")) == NULL) - ctl->shell = _PATH_BSHELL; + ctl->shell = ul_default_shell(0, NULL); } int main(int argc, char **argv) diff --git a/tools/checkcompletion.sh b/tools/checkcompletion.sh index 58e1f7a08c7..c92354a2c46 100755 --- a/tools/checkcompletion.sh +++ b/tools/checkcompletion.sh @@ -6,8 +6,10 @@ # 2. All bash-completion files are registered in bash-completion/Makemodule.am # 3. All bash-completion files are registered in meson.build # 4. All bash-completion files correspond to actual programs +# 5. All bash-completion files handle all available long options in each program # # Copyright (C) 2025 Karel Zak +# Copyright (C) 2025 Christian Goeschel Ndjomouo # set -e @@ -43,7 +45,11 @@ exclude_programs="nologin|agetty|login|sulogin|switch_root|vipw|line|kill" # These are handled via install-data-hook-bashcomp-* rules # - runuser: symlinked to su completion # - lastb: symlinked to last completion -special_handling="runuser|lastb" +special_handling="runuser|lastb" + +# Certain completions have an unusual algorithm that is distinct from the pattern used +# in the majority of completion files, we skip these for now. +unusual_completions="pipesz" top_srcdir=${1:-.} [ -d "${top_srcdir}" ] || die "directory '${top_srcdir}' not found" @@ -97,6 +103,44 @@ extract_meson_registered() { fi } +# Check for the bash-completion file integrity, i.e. all long options are completed +# Argument(s): program_name +check_completion_file_integrity() { + local prog="$1" + + prog_long_opts="$( TOP_SRCDIR="${top_srcdir}" "${top_srcdir}"/tools/get-options.sh "$prog" \ + | sed -e 's/^$//' -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" + + if [[ "$?" != "0" || -z "$prog_long_opts" ]]; then + echo "Failed to get long options for $prog" + return 1 + fi + + # tools/get-options.sh prints 'ENOTSUP' when it receives the name of an + # unsupported program. See comments for the 'unsupported_programs' variable + # in tools/get-options.sh for more details. + # + # We do not treat this case as an error, thereby we simply return 0 to the + # caller and skip the comparison. + if [ "$prog_long_opts" == "ENOTSUP" ]; then + return 0 + fi + + comp_opts="$( cat "${completion_dir}/${prog}" \ + | grep -o -P '[[:space:]]*--(?![^[:alnum:]])[A-Za-z-.0-9_]*' \ + | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' \ + | sort \ + | uniq )" + + res="$( comm -23 <(echo "${prog_long_opts}") <(echo "${comp_opts}") )" + if [ -n "$res" ]; then + printf "%s\n%s\n" "${prog}:" "$res" + return 1 + fi + + return 0 +} + # Get programs that should have completions programs=$(extract_programs | grep -v -w -E "(${exclude_programs}|${special_handling})") @@ -148,6 +192,11 @@ if [ -n "$meson_unregistered" ]; then errors=$((errors + 1)) fi +for f in $files; do + [[ "$f" =~ $unusual_completions ]] && continue + check_completion_file_integrity "$f" || errors=$((errors + 1)) +done + if [ $errors -eq 0 ]; then echo "All bash-completion files are consistent." exit 0 diff --git a/tools/get-options.sh b/tools/get-options.sh new file mode 100755 index 00000000000..49d798ce7ba --- /dev/null +++ b/tools/get-options.sh @@ -0,0 +1,96 @@ +#!/bin/bash + +# This file is part of util-linux. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This file is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# Copyright (C) 2025 Christian Goeschel Ndjomouo +TOP_SRCDIR=${TOP_SRCDIR:-../} + +# Directories that contain relevant source files for util-linux programs. +src_file_paths="$(grep -rE --include="*.c" --exclude="*test_*" \ + --exclude-dir="lib*" \ + --exclude-dir="po*" \ + --exclude-dir="tests" \ + --exclude-dir="tools" \ + --exclude-dir="bash-completion" \ + --exclude-dir=".[a-z]*" \ + --exclude-dir="man-common" \ + --exclude-dir="Documentation" \ + --exclude-dir="build*" \ + -l "getopt_long(_only)?\s*\(" \ + )" + +# We skip these programs because they do not make use of 'struct option longopts[]' +# which is passed to getopt(3) for command line argument parsing. +unsupported_programs='blockdev|fsck|mkfs\.cramfs|pg|renice|whereis' + +# In general a program's source file name will be '.c', however +# some tools have differing file names. To handle these special cases we build +# a hash table with the program name as the key and the actual source file name +# as the value, the latter will ultimately be passed to find_prog_src(). +typeset -A canonical_src_prefix +canonical_src_prefix=( \ + [su]="su-common" +) + +function find_prog_src() { + local prog + prog="$1" + + for p in ${src_file_paths}; do + if [[ "${p##*/}" =~ ^"${prog}".c$ ]]; then + echo "${TOP_SRCDIR}/${p}" + break + fi + done +} + +function extract_long_opts() { + local src_path + src_path="$1" + + awk -F ',' 'BEGIN { x = 0 }; \ + /struct[[:space:]]*option[[:space:]]*.*[[:space:]]*\[\][[:space:]]*=[[:space:]]*(\{)?/ { x = 1 } \ + x && ! /.*\/\*.*(deprecated|COMPLETION:no).*\*\/.*/ { print $1 } \ + /\};/ { x = 0 }' "${src_path}" \ + | grep -Eo '".*"' \ + | tr -d '"' \ + | sort \ + | awk '{ printf "--%s\n", $0 }' \ + | grep -v '^--_.*$' +} + +function main() { + local progname + progname="$1" + + if [[ "$progname" =~ $unsupported_programs ]]; then + echo "ENOTSUP" + return 0 + fi + + # Handle special programs that have unusual source file names + if [ -n "${canonical_src_prefix[$progname]+exists}" ]; then + progname="${canonical_src_prefix[$progname]}" + fi + + src_path="$(find_prog_src "$progname")" + if [ -z "$src_path" ]; then + return 1 + fi + + extract_long_opts "$src_path" + + return 0 +} + +main "$@"