Skip to content

Commit

Permalink
minstall: rework root elevation prompt for extensibility and behavior
Browse files Browse the repository at this point in the history
There's a couple issues with the current approach:
- pkexec is an unusual elevation method, the standard is sudo
- it tries to elevate even in automated workflows
- the user may not want to automatically rerun as root, that might be
  badly behaved

Do some upfront checks instead, first to make sure it even makes sense
to try becoming root, and then to ask the user "do you really want
this". Also check for a couple common approaches to root elevation,
including doas.

Fixes mesonbuild#7345
Fixes mesonbuild#7809
  • Loading branch information
Eli Schwartz committed Feb 21, 2023
1 parent 7884149 commit a878c38
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 6 deletions.
14 changes: 14 additions & 0 deletions docs/markdown/Installing.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,20 @@ Telling Meson to run this script at install time is a one-liner.
The argument is the name of the script file relative to the current
subdirectory.

## Installing as the superuser

When building as a non-root user, but installing to root-owned locations via
e.g. `sudo ninja install`, ninja will attempt to rebuild any out of date
targets as root. This results in various bad behaviors due to build outputs and
ninja internal files being owned by root.

Running `meson install` is preferred for several reasons. It can rebuild out of
date targets and then re-invoke itself as root.

*(since 1.1.0)* Re-invoking as root will try to guess the user's preferred method for
re-running commands as root. The order of precedence is: sudo, doas, pkexec
(polkit). An elevation tool can be forced by setting `$MESON_ROOT_CMD`.

## DESTDIR support

Sometimes you need to install to a different directory than the
Expand Down
9 changes: 9 additions & 0 deletions docs/markdown/snippets/meson_install_elevate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## `meson install` now supports user-preferred root elevation tools

Previously, when installing a project, if any files could not be installed due
to insufficient permissions the install process was automatically re-run using
polkit. Now it prompts to ask whether that is desirable, and checks for
CLI-based tools such as sudo or opendoas or `$MESON_ROOT_CMD`, first.

Meson will no longer attempt privilege elevation at all, when not running
interactively.
22 changes: 16 additions & 6 deletions mesonbuild/minstall.py
Original file line number Diff line number Diff line change
Expand Up @@ -556,13 +556,23 @@ def do_install(self, datafilename: str) -> None:
self.log('Preserved {} unchanged files, see {} for the full list'
.format(self.preserved_file_count, os.path.normpath(self.lf.name)))
except PermissionError:
if shutil.which('pkexec') is not None and 'PKEXEC_UID' not in os.environ and destdir == '':
print('Installation failed due to insufficient permissions.')
print('Attempting to use polkit to gain elevated privileges...')
os.execlp('pkexec', 'pkexec', sys.executable, main_file, *sys.argv[1:],
'-C', os.getcwd(), '--no-rebuild')
else:
if is_windows() or destdir != '' or not os.isatty(sys.stdout.fileno()) or not os.isatty(sys.stderr.fileno()):
# can't elevate to root except in an interactive unix environment *and* when not doing a destdir install
raise
rootcmd = os.environ.get('MESON_ROOT_CMD') or shutil.which('sudo') or shutil.which('doas')
pkexec = shutil.which('pkexec')
if rootcmd is None and pkexec is not None and 'PKEXEC_UID' not in os.environ:
rootcmd = pkexec

if rootcmd is not None:
print('Installation failed due to insufficient permissions.')
ans = input(f'Attempt to use {rootcmd} to gain elevated privileges? [y/n] ')
if ans not in {'y', 'n'}:
raise MesonException('Answer not one of [y/n]')
elif ans == 'y':
os.execlp(rootcmd, rootcmd, sys.executable, main_file, *sys.argv[1:],
'-C', os.getcwd(), '--no-rebuild')
raise

def do_strip(self, strip_bin: T.List[str], fname: str, outname: str) -> None:
self.log(f'Stripping target {fname!r}.')
Expand Down

0 comments on commit a878c38

Please sign in to comment.