Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
201 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
import base64 | ||
import os | ||
import pickle | ||
import subprocess | ||
import sys | ||
|
||
import fbuild | ||
import fbuild.builders | ||
import fbuild.path | ||
import fbuild.subprocess.killableprocess | ||
|
||
# ------------------------------------------------------------------------------ | ||
|
||
|
||
class Commander: pass | ||
|
||
|
||
class LocalCommander: | ||
def install(self, file, target, perms=None): | ||
file.copy(target) | ||
if perms is not None: | ||
file.chmod(perms) | ||
|
||
def close(self): pass | ||
|
||
|
||
class PolkitCommander: | ||
def __init__(self, proc): | ||
self.proc = proc | ||
|
||
self._send('chdir', os.getcwd()) | ||
|
||
def _send(self, *message): | ||
encoded = self._encode(message) | ||
self.proc.stdin.write(encoded) | ||
self.proc.stdin.write('\n') | ||
self.proc.stdin.flush() | ||
|
||
@staticmethod | ||
def _encode(data): | ||
return base64.b64encode(pickle.dumps(data)).decode('ascii') | ||
|
||
@staticmethod | ||
def _decode(data): | ||
return pickle.loads(base64.b64decode(data.encode('ascii'))) | ||
|
||
def install(self, file, target, perms=None): | ||
self._send('install', file, target, perms) | ||
|
||
def close(self): | ||
self._send('close') | ||
self.proc.wait() | ||
|
||
|
||
def polkit_process(): | ||
commander = LocalCommander() | ||
|
||
sys.stdout.write('\n') | ||
sys.stdout.close() | ||
sys.stdout = sys.stderr | ||
|
||
for line in sys.stdin: | ||
message = PolkitCommander._decode(line.strip()) | ||
command, args = message[0], message[1:] | ||
|
||
if command == 'chdir': | ||
os.chdir(args[0]) | ||
elif command == 'install': | ||
commander.install(*args) | ||
elif command == 'close': | ||
sys.exit() | ||
|
||
|
||
class Installer: | ||
"""An Installer manages installation of all the files marked for installation.""" | ||
|
||
def __init__(self, ctx): | ||
self.ctx = ctx | ||
self.privileged = False | ||
|
||
def _install(self, commander): | ||
for file, subdir, rename, perms in self.ctx.to_install: | ||
# Generate the full subdirectory. | ||
target_root = fbuild.path.Path(subdir).addroot(self.ctx.install_prefix) | ||
target_root.makedirs(exist_ok=True) | ||
|
||
# Generate the target path. | ||
target = target_root / (rename or file.basename()) | ||
file = file.relpath(file.getcwd()) | ||
|
||
# Install the file. | ||
self.ctx.logger.check(' * install', '%s -> %s' % (file, target), | ||
color='yellow') | ||
commander.install(file, target, perms) | ||
|
||
def _find_pkexec(self): | ||
try: | ||
return fbuild.builders.find_program(self.ctx, ['pkexec'], quieter=1) | ||
except fbuild.builders.MissingProgram: | ||
return None | ||
|
||
def _load_polkit_process(self, pkexec): | ||
root_module = fbuild.path.Path(fbuild.__file__) | ||
root_directory = root_module.parent.parent | ||
|
||
# Make sure Fbuild can be found. | ||
env = os.environ.copy() | ||
if 'PYTHONPATH' in env: | ||
env['PYTHONPATH'] = '%s:%s' % (root_directory, env['PYTHONPATH']) | ||
else: | ||
env['PYTHONPATH'] = root_directory | ||
|
||
# Run the process. | ||
cmd = [pkexec, sys.executable, '-m', 'fbuild.install', 'polkit'] | ||
proc = fbuild.subprocess.killableprocess.Popen(cmd, stdin=subprocess.PIPE, | ||
stdout=subprocess.PIPE, | ||
universal_newlines=True) | ||
|
||
# Make sure it's ready first. | ||
proc.stdout.readline() | ||
return proc | ||
|
||
# try: | ||
# p.wait() | ||
# except KeyboardInterrupt: | ||
# os.kill(os.getpid(), signal.SIGKILL) | ||
# # p.wait() | ||
# raise | ||
|
||
# if p.returncode != 0: | ||
# self.ctx.logger.log("Failed to run 'fbuild install' via pkexec.", color='red') | ||
# sys.exit(p.returncode) | ||
|
||
def install(self): | ||
"""Install all the files that are marked for installation. If the user does | ||
not have permissions to install to the installation prefix, but Polkit's pkexec | ||
is present, then pkexec will be used to spawn a privileged Fbuild process that | ||
can install the files.""" | ||
|
||
commander = LocalCommander() | ||
|
||
if not self.ctx.install_prefix.access(os.W_OK): | ||
pkexec = None | ||
|
||
if not self.privileged: | ||
pkexec = self._find_pkexec() | ||
|
||
if pkexec is None: | ||
self.ctx.logger.log('Warning: You do not have write access for the ' \ | ||
'installation directory.', color='red') | ||
else: | ||
proc = self._load_polkit_process(pkexec) | ||
commander = PolkitCommander(proc) | ||
|
||
try: | ||
self._install(commander) | ||
finally: | ||
commander.close() | ||
|
||
|
||
if __name__ == '__main__': | ||
assert sys.argv[1] == 'polkit' | ||
polkit_process() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD polkit Policy Configuration 1.0//EN" | ||
"http://www.freedesktop.org/software/polkit/policyconfig-1.dtd"> | ||
<policyconfig> | ||
|
||
<vendor>The Fbuild Build System</vendor> | ||
<vendor_url>https://github.com/felix-lang/fbuild</vendor_url> | ||
|
||
<action id="org.github.fbuild.install.run"> | ||
<description>Install the given project via Fbuild</description> | ||
<message>Authentication is required to install the project via Fbuild</message> | ||
<icon_name>audio-x-generic</icon_name> | ||
<defaults> | ||
<allow_any>no</allow_any> | ||
<allow_inactive>no</allow_inactive> | ||
<allow_active>auth_admin_keep</allow_active> | ||
</defaults> | ||
<annotate key="org.freedesktop.policykit.exec.path">/usr/bin/python</annotate> | ||
<annotate key="org.freedesktop.policykit.exec.argv1">-m</annotate> | ||
<annotate key="org.freedesktop.policykit.exec.argv2">fbuild.install</annotate> | ||
</action> | ||
|
||
</policyconfig> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters