diff --git a/README.md b/README.md index 481fd40..9937aae 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,6 @@ It started with a revelation that bumblebee in current state offers very poor pe 1. run `nvidia-xrun [app]` 1. enjoy -Currently sudo is required as the script needs to wake up GPU, modprobe the nvidia driver and perform cleanup afterwards. The systemd service can be used to completely remove the card from the kernel device tree (so that it won't even show in `lspci` output), and this will @@ -23,6 +22,17 @@ The service can be enabled with this command: When the nvidia-xrun command is used, the device is added again to the tree so that the nvidia module can be loaded properly: nvidia-xrun will remove the device and enable PM again after the application terminates. +### Passwordless `sudo` +Whitelisting `nvidia-toggle` in your sudoer's file allows you to use `nvidia-xrun` without entering your password: + +``` +%users ALL=(root) NOPASSWD:/usr/bin/nvidia-toggle +``` + +...where `/usr/bin/nvidia-toggle` is the full path to the `nvidia-toggle` script. + +Note: it is a good practice to ensure binaries/scripts/etc. that are whitelisted for passwordless `sudo` are owned by root. + ## Structure * **nvidia-xrun** - uses following dir structure: * **/usr/bin/nvidia-xrun** - the executable script diff --git a/nvidia-toggle b/nvidia-toggle new file mode 100755 index 0000000..8c0b388 --- /dev/null +++ b/nvidia-toggle @@ -0,0 +1,90 @@ +#!/bin/bash +# nvidia-toggle [-d] (on | off | gpu-off) + +if [[ $EUID -ne 0 ]]; then + (>&2 echo "This script must be run as root") + exit 1 +fi + +DRY_RUN=0 +if [ "$1" == "-d" ]; then + DRY_RUN=1 + shift 1 +fi + +function say { echo ">>Dry run. Command: $*"; } +if [ $DRY_RUN -eq 1 ]; then + execute=say +else + execute=eval +fi + +function die { + (>&2 echo "$@") + exit 1 +} + +function turn_off_gpu { + if [[ "$ENABLE_PM" != '1' ]]; then return; fi + if [[ "$REMOVE_DEVICE" == '1' ]]; then + echo 'Removing Nvidia bus from the kernel' + $execute "tee /sys/bus/pci/devices/${DEVICE_BUS_ID}/remove <<<1" + else + echo 'Enabling powersave for the graphic card' + $execute "tee /sys/bus/pci/devices/${DEVICE_BUS_ID}/power/control <<>Dry run. Command: $*" - else - eval $* - fi -} - -function turn_off_gpu { - if [[ "$REMOVE_DEVICE" == '1' ]]; then - echo 'Removing Nvidia bus from the kernel' - execute "sudo tee /sys/bus/pci/devices/${DEVICE_BUS_ID}/remove <<<1" - else - echo 'Enabling powersave for the graphic card' - execute "sudo tee /sys/bus/pci/devices/${DEVICE_BUS_ID}/power/control <<>Dry run. Command: $*"; } +if [ $DRY_RUN -eq 1 ]; then + execute='say' +else + execute='eval' +fi + +function die { + (>&2 echo "$@") + exit 1 +} + # load config file . /etc/default/nvidia-xrun -# this is used by the systemd service to turn off the gpu at boot -if [[ "$TURN_OFF_GPU_ONLY" == '1' ]]; then - turn_off_gpu && exit 0 -fi - if [[ $EUID -eq 0 ]]; then - echo "This script must not be run as root unless TURN_OFF_GPU_ONLY=1 is set" >&2 + echo "This script must not be run as root" >&2 exit 1 fi # calculate current VT -LVT=`fgconsole` +LVT="vt$(fgconsole)" # calculate first usable display -XNUM="-1" -SOCK="something" -while [[ ! -z "$SOCK" ]] +XNUM=0 +SOCK=/tmp/.X11-unix/X$XNUM +while [[ -e "$SOCK" ]] do - XNUM=$(( $XNUM + 1 )) - SOCK=$(ls -A -1 /tmp/.X11-unix | grep "X$XNUM" ) + XNUM=$(( XNUM + 1 )) + SOCK=/tmp/.X11-unix/X$XNUM done NEWDISP=":$XNUM" -if [[ ! -z "$*" ]] # generate exec line if arguments are given +if [[ -n "$*" ]] # generate exec line if arguments are given then # test if executable exists in path - if [[ -x "$(which $1 2> /dev/null)" ]] + if [[ -x "$(command -v "$1" 2> /dev/null)" ]] then # generate exec line - EXECL="$(which $1)" + CMD="$(command -v "$1")" # test if executable exists on disk elif [[ -e "$(realpath "$1")" ]] then # generate exec line - EXECL="$(realpath "$1")" + CMD="$(realpath "$1")" else echo "$1: No such executable!" exit 1 fi shift 1 - EXECL="$EXECL $*" + ARGV=("$CMD" "$@") else # prepare to start new X sessions if no arguments passed - EXECL="" + ARGV=() fi -EXECL="/etc/X11/xinit/nvidia-xinitrc \"$EXECL\"" - -COMMAND="xinit $EXECL -- $NEWDISP vt$LVT -nolisten tcp -br -config nvidia-xorg.conf -configdir nvidia-xorg.conf.d" +CLIENT=(/etc/X11/xinit/nvidia-xinitrc "${ARGV[*]}") +SERVER=("$NEWDISP" "$LVT" -nolisten tcp -br -config nvidia-xorg.conf -configdir nvidia-xorg.conf.d) +COMMAND=(xinit "${CLIENT[@]}" -- "${SERVER[@]}") -# --------- TURNING ON GPU ----------- -if [[ "$ENABLE_PM" == '1' ]]; then - turn_on_gpu -fi - -# ---------- LOADING MODULES ---------- -load_modules +# ---------- TURNING ON GPU ----------- +echo 'Loading nvidia' +$execute "sudo nvidia-toggle on" || die "Could not load nvidia" # ---------- EXECUTING COMMAND -------- -execute ${COMMAND} - -# ---------- UNLOADING MODULES -------- -unload_modules +$execute "${COMMAND[*]}" || die "Could not execute command" # --------- TURNING OFF GPU ---------- -if [[ "$ENABLE_PM" == '1' ]]; then - turn_off_gpu -fi +echo 'Unloading nvidia' +$execute "sudo nvidia-toggle off" || die "Could not unload nvidia" diff --git a/nvidia-xrun-pm.service b/nvidia-xrun-pm.service index 038bba7..f7d4d94 100644 --- a/nvidia-xrun-pm.service +++ b/nvidia-xrun-pm.service @@ -3,8 +3,7 @@ Description="Remove Nvidia GPU from kernel devices list and enable PM" [Service] Type=oneshot -Environment="TURN_OFF_GPU_ONLY=1" -ExecStart=/usr/bin/nvidia-xrun +ExecStart=/usr/bin/nvidia-toggle gpu-off [Install] WantedBy=multi-user.target