Search code examples
pythonlinuxsudorenice

sudo renice in python


Generically, this is a well answered question. Linux doesn't allow non-privileged users to lower a PID's niceness, and running things as root is its own can of worms.

That said, here are my specifics: I've got a user account that manages a few processes which has passwordless sudo privileges for renice and a few other commands it uses. I also have a script that is the common entry point for all users on this system. This script can both run regular user programs as well as the processes managed by the special account. So the script, when run with a specific option, should renice if it can, but fail silently at the if it cannot.

The code I've got for this looks like:

subprocess.Popen(["sudo", "renice", "-20", str(process.pid)],
#                shell = True,
                 stdout = subprocess.DEVNULL,
                 stderr = subprocess.STDOUT)

If I have shell = True commented out, the process gets its new niceness, but if I'm running as an unprivileged user, sudo kicks out its password prompt and wrecks my terminal output. Keystrokes become invisible and everything gets stupid looking. If I uncomment shell = True, I get no terminal output. However, the process doesn't get its niceness changed, even if I run it as root.

The corrupted terminal output may well be down to the terminal emulator I'm using (haven't tried it with another one) but I want to merge these behaviors. Silence from sudo no matter what, but a niceness change if the user can sudo successfully.

Any pointers?


Solution

  • I think it's because sudo requires a TTY, even when password is not necessary.

    Try providing one:

    import os
    import pty
    import subprocess
    
    master, slave = pty.openpty()
    p = subprocess.Popen(
        ["sudo", "id", "-a"],
        stdin=slave, stdout=slave, stderr=slave
    )
    os.close(slave)
    
    output = os.read(master, 1026)
    os.close(master)
    print(output)
    

    The code above should print something like uid=0(root) gid=0(root) groups=0(root). If it does, then replace id with renice, remove unnecessary os.read and you should be good.

    Update: in OP's case, it had failed for another reason. Adding start_new_session=True to Popen had it fail silently for unprivileged users and succeed as root.