1#!/usr/bin/python
2# Copyright 2016 Google Inc. All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16"""Execute user provided metadata scripts."""
17
18import os
19import stat
20import subprocess
21
22
23class ScriptExecutor(object):
24  """A class for executing user provided metadata scripts."""
25
26  def __init__(self, logger, script_type, default_shell=None):
27    """Constructor.
28
29    Args:
30      logger: logger object, used to write to SysLog and serial port.
31      script_type: string, the type of the script we are running.
32      default_shell: string, the default shell to execute the script.
33    """
34    self.logger = logger
35    self.script_type = script_type
36    self.default_shell = default_shell or '/bin/bash'
37
38  def _MakeExecutable(self, metadata_script):
39    """Add executable permissions to a file.
40
41    Args:
42      metadata_script: string, the path to the executable file.
43    """
44    mode = os.stat(metadata_script).st_mode
45    os.chmod(metadata_script, mode | stat.S_IEXEC)
46
47  def _RunScript(self, metadata_key, metadata_script):
48    """Run a script and log the streamed script output.
49
50    Args:
51      metadata_key: string, the key specifing the metadata script.
52      metadata_script: string, the file location of an executable script.
53    """
54    process = subprocess.Popen(
55        metadata_script, shell=True,
56        executable=self.default_shell,
57        stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
58    while True:
59      for line in iter(process.stdout.readline, b''):
60        message = line.decode('utf-8', 'replace').rstrip('\n')
61        if message:
62          self.logger.info('%s: %s', metadata_key, message)
63      if process.poll() is not None:
64        break
65    self.logger.info('%s: Return code %s.', metadata_key, process.returncode)
66
67  def RunScripts(self, script_dict):
68    """Run the metadata scripts; execute a URL script first if one is provided.
69
70    Args:
71      script_dict: a dictionary mapping metadata keys to script files.
72    """
73    metadata_types = ['%s-script-url', '%s-script']
74    metadata_keys = [key % self.script_type for key in metadata_types]
75    metadata_keys = [key for key in metadata_keys if script_dict.get(key)]
76    if not metadata_keys:
77      self.logger.info('No %s scripts found in metadata.', self.script_type)
78    for metadata_key in metadata_keys:
79      metadata_script = script_dict.get(metadata_key)
80      self._MakeExecutable(metadata_script)
81      self._RunScript(metadata_key, metadata_script)
82