actions: Improve log message about action execution

- Use '#' vs '$' to indicate root vs. user message.

- Remove '-n' argument to sudo as it is always present.

- Remove env arguments to sudo as they are only present during debug.

- Remove full path to the action as they are already thoroughly checked.

- Print the message as a shell command with escapes instead of as python list.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
Sunil Mohan Adapa 2019-09-06 17:16:07 -07:00 committed by James Valleroy
parent c4ab81252c
commit 3bf4b217fe
No known key found for this signature in database
GPG Key ID: 77C0C75E7B650808
2 changed files with 65 additions and 3 deletions

View File

@ -92,12 +92,14 @@ Actions run commands with this contract (version 1.1):
import logging import logging
import os import os
import re
import shlex
import subprocess import subprocess
from plinth import cfg from plinth import cfg
from plinth.errors import ActionError from plinth.errors import ActionError
LOGGER = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def run(action, options=None, input=None, run_in_background=False): def run(action, options=None, input=None, run_in_background=False):
@ -182,7 +184,7 @@ def _run(action, options=None, input=None, run_in_background=False,
if sudo_call: if sudo_call:
cmd = sudo_call + cmd cmd = sudo_call + cmd
LOGGER.info('Executing command - %s', cmd) _log_command(cmd)
# Contract 3C: don't interpret shell escape sequences. # Contract 3C: don't interpret shell escape sequences.
# Contract 5 (and 6-ish). # Contract 5 (and 6-ish).
@ -203,10 +205,44 @@ def _run(action, options=None, input=None, run_in_background=False,
output, error = output.decode(), error.decode() output, error = output.decode(), error.decode()
if proc.returncode != 0: if proc.returncode != 0:
if log_error: if log_error:
LOGGER.error('Error executing command - %s, %s, %s', cmd, logger.error('Error executing command - %s, %s, %s', cmd,
output, error) output, error)
raise ActionError(action, output, error) raise ActionError(action, output, error)
return output return output
return proc return proc
def _log_command(cmd):
"""Log a command with special pretty formatting to catch the eye."""
cmd = list(cmd) # Make a copy of the command not to affect the original
prompt = '$'
user = ''
if cmd and cmd[0] == 'sudo':
cmd = cmd[1:]
prompt = '#'
# Drop -n argument to sudo
if cmd and cmd[0] == '-n':
cmd = cmd[1:]
# Capture username separately
if len(cmd) > 1 and cmd[0] == '-u':
prompt = '$'
user = cmd[1]
cmd = cmd[2:]
# Drop environmental variables set via sudo
while cmd and re.match(r'.*=.*', cmd[0]):
cmd = cmd[1:]
# Strip the command's prefix
if cmd:
cmd[0] = cmd[0].split('/')[-1]
# Shell escape and join command arguments
cmd = ' '.join([shlex.quote(part) for part in cmd])
logger.info('%s%s %s', user, prompt, cmd)

View File

@ -26,11 +26,13 @@ import os
import pathlib import pathlib
import shutil import shutil
import tempfile import tempfile
from unittest.mock import patch
import pytest import pytest
import apt_pkg import apt_pkg
from plinth import cfg from plinth import cfg
from plinth.actions import _log_command as log_command
from plinth.actions import run, superuser_run from plinth.actions import run, superuser_run
from plinth.errors import ActionError from plinth.errors import ActionError
@ -182,3 +184,27 @@ def test_action_path(monkeypatch):
su_plinth_path = superuser_run('test_path').strip() su_plinth_path = superuser_run('test_path').strip()
assert plinth_path.startswith(cfg.root) assert plinth_path.startswith(cfg.root)
assert plinth_path == su_plinth_path assert plinth_path == su_plinth_path
@patch('plinth.actions.logger.info')
@pytest.mark.parametrize('cmd,message', [
[['ls'], '$ ls'],
[['/bin/ls'], '$ ls'],
[['ls', 'a', 'b', 'c'], '$ ls a b c'],
[['ls', 'a b c'], "$ ls 'a b c'"],
[['ls', 'a;'], "$ ls 'a;'"],
[['sudo', 'ls'], '# ls'],
[['sudo', '-n', 'ls'], '# ls'],
[['sudo', '-u', 'tester', 'ls'], 'tester$ ls'],
[['sudo', 'key1=value1', 'key2=value2', 'ls'], '# ls'],
[[
'sudo', '-n', 'PYTHONPATH=/vagrant', '/vagrant/actions/ejabberd',
'add-domain', '--domainname', 'freedombox.local'
], '# ejabberd add-domain --domainname freedombox.local'],
])
def test_log_command(logger, cmd, message):
"""Test log messages for various action calls."""
log_command(cmd)
logger.assert_called_once()
args = logger.call_args[0]
assert message == args[0] % args[1:]