Several incompatible changes to the experimental proxy API to make it simpler
[rox-lib.git] / python / rox / su.py
blob54bcdb7e84d859510b5c1fbdf40fa6592d82e1f0
1 """The su (switch user) module allows you to execute a command as some
2 other user (normally 'root'). It supports a variety of methods to perform
3 the switch (using su, xsu, sudo, etc) so you don't have to worry about
4 which ones are available on the current platform."""
6 import os, sys, pwd
7 import rox
8 from rox import g, _, master_proxy
9 import traceback
10 from select import select
11 import fcntl
12 try:
13 import pty
14 except ImportError:
15 pty = None
17 _my_dir = os.path.abspath(os.path.dirname(__file__))
18 _child_script = os.path.join(_my_dir, 'suchild.sh')
20 def create_su_proxy(message, uid = 0, confirm = True):
21 """Creates a new master_proxy.MasterObject and starts the child
22 process. If necessary, the user is prompted for a password. If no
23 password is required, the user is simply asked to confirm,
24 unless 'confirm' is False.
25 Raises UserAbort if the user clicks Cancel."""
26 method = default_method(message, uid, confirm)
27 return method.get_master().root
29 class Method:
30 need_interaction = True
32 def __init__(self, uid):
33 self.uid = uid
35 def get_master(self):
36 raise NotImplemented() # Abstract
38 class Pipe:
39 """Contains Python file objects for two pipe ends.
40 Wrapping the FDs in this way ensures that they will be freed on error."""
41 readable = None
42 writeable = None
44 def __init__(self):
45 r, w = os.pipe()
46 try:
47 self.readable = os.fdopen(r, 'r')
48 except:
49 os.close(r)
50 raise
51 try:
52 self.writeable = os.fdopen(w, 'w')
53 except:
54 os.close(w)
55 raise
57 class XtermMethod(Method):
58 _master = None
60 def __init__(self, message, uid, confirm):
61 Method.__init__(self, uid)
62 self.message = message
64 def get_master(self):
65 assert self._master is None
67 to_child = Pipe()
68 from_child = Pipe()
70 if os.fork() == 0:
71 try:
72 try:
73 to_child.writeable.close()
74 from_child.readable.close()
75 self.exec_child(from_child.writeable,
76 to_child.readable)
77 except:
78 traceback.print_exc()
79 finally:
80 os._exit(1)
81 from_child.writeable.close()
82 to_child.readable.close()
84 assert self._master is None
85 self._master = master_proxy.MasterProxy(to_child.writeable,
86 from_child.readable)
87 return self._master
89 def exec_child(self, to_parent, from_parent):
90 fcntl.fcntl(to_parent, fcntl.F_SETFD, 0)
91 fcntl.fcntl(from_parent, fcntl.F_SETFD, 0)
92 os.execlp('xterm', 'xterm',
93 '-geometry', '40x10',
94 '-title', 'Enter password',
95 '-e',
96 _child_script,
97 self.message,
98 sys.executable,
99 str(to_parent.fileno()),
100 str(from_parent.fileno()))
102 default_method = XtermMethod