diff --git a/docs/markdown/Installing.md b/docs/markdown/Installing.md index a692afe7d..0bc9a4744 100644 --- a/docs/markdown/Installing.md +++ b/docs/markdown/Installing.md @@ -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 diff --git a/docs/markdown/snippets/meson_install_elevate.md b/docs/markdown/snippets/meson_install_elevate.md new file mode 100644 index 000000000..2ba92e631 --- /dev/null +++ b/docs/markdown/snippets/meson_install_elevate.md @@ -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. diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py index 9ba36cb7d..ab797b5ee 100644 --- a/mesonbuild/minstall.py +++ b/mesonbuild/minstall.py @@ -556,13 +556,23 @@ class Installer: 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}.')