hass-core/homeassistant/components/shell_command.py
Nick Touran 2882f05f2c Added template rendering to shell_command component (#2268)
* Added template rendering to `shell_command` component

* Security upgrades to template rendering in shell_command.

* Added new unit tests for shell_command templates.
Better failure when template is invalid in shell_command
2016-06-18 09:57:18 -07:00

69 lines
2 KiB
Python

"""
Exposes regular shell commands as services.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/shell_command/
"""
import logging
import subprocess
import voluptuous as vol
from homeassistant.helpers import template
from homeassistant.exceptions import TemplateError
import homeassistant.helpers.config_validation as cv
DOMAIN = 'shell_command'
_LOGGER = logging.getLogger(__name__)
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
cv.slug: cv.string,
}),
}, extra=vol.ALLOW_EXTRA)
SHELL_COMMAND_SCHEMA = vol.Schema({})
def setup(hass, config):
"""Setup the shell_command component."""
conf = config.get(DOMAIN, {})
def service_handler(call):
"""Execute a shell command service."""
cmd = conf[call.service]
cmd, shell = _parse_command(hass, cmd, call.data)
if cmd is None:
return
try:
subprocess.call(cmd, shell=shell,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
except subprocess.SubprocessError:
_LOGGER.exception('Error running command: %s', cmd)
for name in conf.keys():
hass.services.register(DOMAIN, name, service_handler,
schema=SHELL_COMMAND_SCHEMA)
return True
def _parse_command(hass, cmd, variables):
"""Parse command and fill in any template arguments if necessary."""
cmds = cmd.split()
prog = cmds[0]
args = ' '.join(cmds[1:])
try:
rendered_args = template.render(hass, args, variables=variables)
except TemplateError as ex:
_LOGGER.exception('Error rendering command template: %s', ex)
return None, None
if rendered_args == args:
# no template used. default behavior
shell = True
else:
# template used. Must break into list and use shell=False for security
cmd = [prog] + rendered_args.split()
shell = False
return cmd, shell