Improve Linux support to match macOS and Windows

Preserve current working directory.
Support explicit options.env environment variables.
Support multiple commands separated by semicolons.
Distinguish between elevation errors and command errors.

Fixes: #39
Fixes: #88
Fixes: #91
This commit is contained in:
Joran Dirk Greef 2019-06-03 16:22:41 +02:00
parent ab3a0bd981
commit 7e2db9e19e

View file

@ -130,6 +130,13 @@ function Linux(instance, end) {
function(error, binary) { function(error, binary) {
if (error) return end(error); if (error) return end(error);
var command = []; var command = [];
// Preserve current working directory:
command.push('cd "' + EscapeDoubleQuotes(Node.process.cwd()) + '";');
// Export environment variables:
for (var key in instance.options.env) {
var value = instance.options.env[key];
command.push('export ' + key + '="' + EscapeDoubleQuotes(value) + '";');
}
command.push('"' + EscapeDoubleQuotes(binary) + '"'); command.push('"' + EscapeDoubleQuotes(binary) + '"');
if (/kdesudo/i.test(binary)) { if (/kdesudo/i.test(binary)) {
command.push( command.push(
@ -142,15 +149,43 @@ function Linux(instance, end) {
} else if (/pkexec/i.test(binary)) { } else if (/pkexec/i.test(binary)) {
command.push('--disable-internal-agent'); command.push('--disable-internal-agent');
} }
command.push(instance.command); var magic = 'SUDOPROMPT\n';
command.push(
'/bin/bash -c "echo ' + EscapeDoubleQuotes(magic.trim()) + '; ' +
EscapeDoubleQuotes(instance.command) +
'"'
);
command = command.join(' '); command = command.join(' ');
Node.child.exec(command, { maxBuffer: MAX_BUFFER }, Node.child.exec(command, { maxBuffer: MAX_BUFFER },
function(error, stdout, stderr) { function(error, stdout, stderr) {
if (error) { // ISSUE 88:
// We must distinguish between elevation errors and command errors.
//
// KDESUDO:
// kdesudo provides no way to do this. We add a magic marker to know
// if elevation succeeded. Any error thereafter is a command error.
//
// PKEXEC:
// "Upon successful completion, the return value is the return value of
// PROGRAM. If the calling process is not authorized or an
// authorization could not be obtained through authentication or an
// error occured, pkexec exits with a return value of 127. If the
// authorization could not be obtained because the user dismissed the
// authentication dialog, pkexec exits with a return value of 126."
//
// However, we do not rely on pkexec's return of 127 since our magic
// marker is more reliable, and we already use it for kdesudo.
var elevated = stdout ? stdout.toString('utf8', 0, magic.length) : '';
if (elevated) stdout = stdout.slice(magic.length);
// Only normalize the error if it is definitely not a command error:
// In other words, if we know that the command was never elevated.
// We do not inspect error messages beyond NO_POLKIT_AGENT.
// We cannot rely on English errors because of internationalization.
if (error && !elevated) {
if (/No authentication agent found/.test(stderr)) { if (/No authentication agent found/.test(stderr)) {
error = new Error(NO_POLKIT_AGENT); error.message = NO_POLKIT_AGENT;
} else if (/Request dismissed|Command failed/i.test(error)) { } else {
error = new Error(PERMISSION_DENIED); error.message = PERMISSION_DENIED;
} }
} }
end(error, stdout, stderr); end(error, stdout, stderr);