amoffat / sh

Python process launching
https://sh.readthedocs.io/en/latest/
MIT License
6.98k stars 506 forks source link

Info pushed to stdout instead of variable when invoked with contirb.sudo #722

Open stephen-at-code opened 6 months ago

stephen-at-code commented 6 months ago

Hi there,

I have come across a bug when I have been trying to make a call to blkid using sudo with sh. Instead of pushing the output of stdout in the sh call to a variable it instead sends the output to the user's stdout, and the variable is empty. I have attached a minimum viable test file that has shown to reproduce the issue in my environment.

Environment details: OS - Ubuntu 22.04 Kernel - 6.5.0-27-generic blkid version - 2.37.2 Python version - 3.10.12 Sh version - 2.0.6

import sh

def main():
    password = input("Enter password: ")
    with sh.contrib.sudo(password=password, _with=True):
        output = sh.blkid()

if __name__ == "__main__":
    main()
ecederstrand commented 6 months ago

Thanks for the report.

I cannot reproduce this, on Ubuntu 20.04. What is the output you see? Also, are you sure the tool is not printing to stderr? You can try redirecting stderr to stdout.

stephen-at-code commented 6 months ago

Hi ecederstrand, I just tried altering the line output = sh.blkid() to output =sh.blkid(_out=sys.stderr) and I have confirmed that the issue still occurs. I also tested the mini-viable on my own 20.04 and you are correct, I can't repoduce it there, if that's because of the python version 3.8 or the Ubuntu OS difference I am not sure of at the moment.

ecederstrand commented 6 months ago

That redirects stdout to stderr, which is the opposite of what you want. Try sh.blkid(_err_to_out=True) instead.

Also, does this only happen under sudo context? What happens if you run this without sudo, i.e. just

import sh

output = sh.blkid(_err_to_out=True)

If your normal user doesn't have permissions to run blkid, then switch to root before running the above snippet.

stephen-at-code commented 6 months ago

Sure thing, here are the various alterations I tried and how they output to bash when run:

Ubuntu 22.04

import sh

output = sh.blkid(_err_to_out=True)
print("Done.")

Output:

Done.

Ubuntu 22.04

import sh

password = input("Enter password: ")
with sh.contrib.sudo(password=password, _with=True):
    output = sh.blkid(_err_to_out=True)
    print("Done.")

Output:

Enter password: <password here>
/dev/sda2: UUID="65ae0099-1a10-4431-8055-254b59f74a3a" TYPE="ext4" PARTUUID="2b727728-cc3a-4cb6-a589-0f3cac53865c"
/dev/sda1: UUID="1142-B06A" TYPE="vfat" PARTLABEL="EFI System Partition" PARTUUID="9a7d6a45-cdb1-4206-81ab-8ec37b751bb8"
/dev/sdb1: SEC_TYPE="msdos" LABEL_FATBOOT="NOT_EFI" LABEL="NOT_EFI" UUID="9A6E-6241" TYPE="vfat"
/dev/sdb2: LABEL_FATBOOT="NOT_DATA" LABEL="NOT_DATA" UUID="9A75-0169" TYPE="vfat"
Done.

Ubuntu 20.04

import sh

output = sh.blkid(_err_to_out=True)
print("Done.")

Output:

Done.

Ubuntu 20.04

import sh

password = input("Enter password: ")
with sh.contrib.sudo(password=password, _with=True):
    output = sh.blkid(_err_to_out=True)
    print("Done.")

Output:

Enter password: <password here>
Done.
ecederstrand commented 6 months ago

I tried this on Ubuntu 22.04, python 3.10.12, sh 2.0.6, and still cannot reproduce. In your non-working case, does output contain the info you expected? Or is it empty?

stephen-at-code commented 6 months ago

It is empty, the contents are output to bash instead of to the output variable. I'm not sure if this helps because I haven't used pdb much in large projects using threads before, but I found that when I tried to trace the issue to a specific line in sh if I tried to step past (not into) line 2061 pdb would freeze or crash and I wouldn't be able to continue the debug. If I continued past it handled it fine and then would produce the erroneous behavior shown in example 2. I'll also try to reproduce the bug on different hardware to try and rule that out, this might some how be specific to atom processors or something strange like that.