0

I have a program in Python. I would like when the program starts for it to ask me to enter root password, in a GUI dialog. I run the program then it shows the below error:

couldn't connect to display ": 0.0"

If I delete all lines with pkexec stuff inside upgrade.py then it works perfectly. Also if I run in gnome-terminal with the command sudo python3 /home/user/Python/upgrade.py then it works too. Maybe it's a problem with sudo? Exactly the same happens with GTK for Python.

Python code:

from tkinter import *
#!/usr/bin/python3
import os
import subprocess
import sys

euid = os.geteuid()
if euid != 0:
    print ("Script not started as root. Running sudo..")
    args = ['pkexec', sys.executable] + sys.argv + [os.environ]
    # the next line replaces the currently-running process with the sudo
    os.execlpe('pkexec', *args)

print ('Running. Your euid is', euid)


root = Tk()
root.title('Update system')
#root.geometry("290x100")

def button_add1():
    callProcess = subprocess.Popen(['ls', '-la'], shell=True)
    subprocess.run(['pkexec', 'ls', '-la'], check=True)

def button_add4():
    root.destroy()

# Define Buttons

button_1 = Button(root, text="Upgrade system",  padx=40, pady=20, command=button_add1)
button_4 = Button(root, text="Quit", padx=40, pady=20, command=button_add4)

# Put the buttons on the screen

button_1.grid(row=0, column=0)
button_4.grid(row=0, column=4)

root.mainloop()

Please I don't want like something this... Simplest method of asking user for password using graphical dialog in Python?

I would like authentication like programs such as Synaptic, Gparted, Bleachbit

I have already asked on Stackoverflow but they said, It is related on system policy configuration and nothing related to Python or tkinter. This question is now migrated back to Stack Overflow!

I tried my code on Debian and Linux Mint.

  • You absolutely should not be running your GUI as `root`. The way Synaptic et al. do this is by spawning *a nongraphical component* by way of `pkexec`. – tripleee Dec 07 '20 at 19:35
  • @tripleee Did you underastand my quesion? I have posted in stacoverflow. They said that "It is related on system policy configuration and nothing related to Python or tkinter" So I don't understand your answear. How do other programs in python ask for root password ? I want the same. –  Dec 07 '20 at 20:44
  • Did *you* not understand what *I* wrote? They use `pkexec` all right, but you can't run your whole GUI script under `pkexec` because it won't get access to the invoking user's `$DISPLAY`. Its manual page even mentions this specifically. – tripleee Dec 08 '20 at 05:09
  • Maybe read this for background: https://wiki.archlinux.org/index.php/Running_GUI_applications_as_root – tripleee Dec 08 '20 at 05:34
  • @tripleee Ok, I understand. Synaptic manager how to do it, or other programs? Would you like to help me how to do it like synaptic manager? My program just run 2 line code. –  Dec 08 '20 at 07:38
  • I don't know `tkinter` well enough to present a full answer, but basically only use `pkexec` when you run the subprocesses. Replace `os.system("what ever")` with `subprocess.run(['pkexec', 'what', 'ever'], check=True)` (the switch away from `os.system()` is not crucial but [recommended](https://stackoverflow.com/questions/3172470/actual-meaning-of-shell-true-in-subprocess)). – tripleee Dec 08 '20 at 07:45
  • @tripleee It doesn't work. I did ALL that you said. Look at my python code, I changed it. It show the same error. `line 17, in root = Tk() self.tk = _tkinter.create(screenName, baseName, className, interactive, wantobjects, useTk sync, use) _tkinter.TclError: no display name and no $DISPLAY environment variable` –  Dec 08 '20 at 09:03
  • You are still trying to run the GUI as `root`. You need to take out all the `euid` garbage. (Also, why do you run `subprocess` twice? You want to prefer `subprocess.run()` over `subprocess.Popen()` whenever you can. Also, `shell=True` is wrong when you pass in a list as the first argument. Maybe see also https://stackoverflow.com/questions/4256107/running-bash-commands-in-python/51950538#51950538 which has a section about why.) – tripleee Dec 08 '20 at 10:51
  • @tripleee You didn't understand me. As I said I want `the same other programs' . I want the program starts and press root password ONCE in the beggining. How did it make other programs????? I want the same. Thank you for helping but you below example I have to press the button every time I. –  Dec 08 '20 at 12:47
  • Your updated example only runs `pkexec` once. (Repeatedly changing your code is somewhat problematic, as you are basically moving the goal posts.) If you want to run multiple commands behind one `pkexec`, you will need a shell after all; `subprocess.run(['pkexec', 'sh', '-c', 'apt-get update && apt-get upgrade -y'], check=True)` – tripleee Dec 08 '20 at 13:01
  • @tripleee I would like to change this `os.system("gnome-terminal -- /bin/bash -c \"sudo iptables -vnL; echo ''; read -p 'Press any key to exit'; exit; exec bash\"") ` with new `subprocess.run(['pkexec', 'sh', '-c',........blah` I would like to show gnome-terminal, without os.system. –  Dec 23 '20 at 14:26
  • So something like `subprocess.run(['gnome-terminal', '--', 'pkexec', 'sh', '-c', 'iptables -vnL; echo; read -p "Press any key"'])` would do that without attempting to run the terminal as root. Even better if you can push the `pkexec` further down into the command, like `... 'sh', '-c', 'pkexec iptables -vnL; ...'` – tripleee Dec 23 '20 at 15:49
  • @tripleee I used your example but It doesn't work. There is no show any error.....Actually I want change os.system with new subprocess.run!! –  Dec 23 '20 at 20:46
  • Probably (accept the answer to your original question here, and) post a new question with clear requirements. I'm not in a place where I can access an Ubuntu GUI until maybe next week. – tripleee Dec 23 '20 at 20:59

1 Answers1

1

You mustn't try to run the GUI as root. You should isolate the smallest possible component and only run that as privileged. See https://wiki.archlinux.org/index.php/Running_GUI_applications_as_root and the note in the pkexec manual page which specifically explains that it will not allow you to take over the invoking user's $DISPLAY.

Here's a rough sketch, but it's untested right now.

#!/usr/bin/python3
# ^ this needs to be the absolutely first line of the file

from tkinter import *   # FIXME: probably avoid import *
# import os   # no longer used
import subprocess
# import sys  # no longer used

print ('Running. Your euid is whatever it is.')


root = Tk()
root.title('Update system')
#root.geometry("290x100")

def button_add1():
    subprocess.run(['ls', '-la'], check=True)
    subprocess.run(['pkexec', 'ls', '-la'], check=True)

def button_add4():
    root.destroy()

# Define Buttons

button_1 = Button(root, text="Upgrade system",  padx=40, pady=20, command=button_add1)
button_4 = Button(root, text="Quit", padx=40, pady=20, command=button_add4)

# Put the buttons on the screen

button_1.grid(row=0, column=0)
button_4.grid(row=0, column=4)

root.mainloop()

If (as in your code before your edit) you want to run multiple commands behind one pkexec, you can do that with something like

    subprocess.run(
        ['pkexec', 'sh', '-c', 'apt-get update && apt-get upgrade -y'],
        check=True)

As an aside, if your example code is representative, you can replace from tkinter import * with from tkinter import Tk to avoid the import * antipattern.

tripleee
  • 175,061
  • 34
  • 275
  • 318
  • I read your post. I don't care for security hole. I want run my program like the other. How to do that? I don't care for security hole. I was clear with that. Thank you! –  Dec 08 '20 at 12:50
  • You said you want to do it like Synaptic, `gparted`, etc. This is how they do it. As outlined on the Arch page, there is no simple way in Wayland to give `root` access to your `DISPLAY` session, though you could do it with an `xhost` hack to punch a hole large enough to sail the Starship Enterprise through. – tripleee Dec 08 '20 at 13:05
  • It is not the same. 1. I want before start the program ask me for password. 2. Your example It is not the same like I said. It start the program then push button ,then write password then run bash command and finish. `If push button second time` you have to enter password again. I want enter a password ONCE in the beggining. May I have your e-mail or personal mesage? Thank you very much. –  Dec 08 '20 at 13:42
  • Update your question to clearly state your requirements. No, I don't engage in off-site interactions, but you can find me in chat if you like (though you need 30 rep for that to work). – tripleee Dec 09 '20 at 05:54
  • It doesn't work with `from tkinter import Tk` only with `from tkinter import *` –  Dec 09 '20 at 08:45
  • I guess maybe you need to import `Button` too. – tripleee Dec 09 '20 at 09:24
  • Please Can you tell why is better `from tkinter import Tk` from `from tkinter import * ` –  Dec 09 '20 at 21:28
  • @user14770539: the preferred way to import tkinter is `import tkinter as tk`, and then prepend `tk.` to all tkinter classes (eg: `root = tk.Tk()`). Wildcard imports are discouraged by [PEP8](https://www.python.org/dev/peps/pep-0008/). In short, wildcard imports pollute the namespace. – Bryan Oakley Dec 13 '20 at 18:55