Home | History | Annotate | Download | only in build
      1 # Copyright 2018 - The Android Open Source Project
      2 #
      3 # Licensed under the Apache License, Version 2.0 (the "License");
      4 # you may not use this file except in compliance with the License.
      5 # You may obtain a copy of the License at
      6 #
      7 #     http://www.apache.org/licenses/LICENSE-2.0
      8 #
      9 # Unless required by applicable law or agreed to in writing, software
     10 # distributed under the License is distributed on an "AS IS" BASIS,
     11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 # See the License for the specific language governing permissions and
     13 # limitations under the License.
     14 
     15 """Command-line tool to build SEPolicy files."""
     16 
     17 import argparse
     18 import os
     19 import subprocess
     20 import sys
     21 
     22 import file_utils
     23 
     24 
     25 # All supported commands in this module.
     26 # For each command, need to add two functions. Take 'build_cil' for example:
     27 #   - setup_build_cil()
     28 #     - Sets up command parsers and sets default function to do_build_cil().
     29 #   - do_build_cil()
     30 _SUPPORTED_COMMANDS = ('build_cil',)
     31 
     32 
     33 def run_host_command(args, **kwargs):
     34     """Runs a host command and prints output."""
     35     if kwargs.get('shell'):
     36         command_log = args
     37     else:
     38         command_log = ' '.join(args)  # For args as a sequence.
     39 
     40     try:
     41         subprocess.check_call(args, **kwargs)
     42     except subprocess.CalledProcessError as err:
     43         sys.stderr.write(
     44             'build_sepolicy - failed to run command: {!r} (ret:{})\n'.format(
     45                 command_log, err.returncode))
     46         sys.exit(err.returncode)
     47 
     48 
     49 def do_build_cil(args):
     50     """Builds a sepolicy CIL (Common Intermediate Language) file.
     51 
     52     This functions invokes some host utils (e.g., secilc, checkpolicy,
     53     version_sepolicy) to generate a .cil file.
     54 
     55     Args:
     56         args: the parsed command arguments.
     57     """
     58     # Determines the raw CIL file name.
     59     input_file_name = os.path.splitext(args.input_policy_conf)[0]
     60     raw_cil_file = input_file_name + '_raw.cil'
     61     # Builds the raw CIL.
     62     file_utils.make_parent_dirs(raw_cil_file)
     63     checkpolicy_cmd = [args.checkpolicy_env]
     64     checkpolicy_cmd += [os.path.join(args.android_host_path, 'checkpolicy'),
     65                         '-C', '-M', '-c', args.policy_vers,
     66                         '-o', raw_cil_file, args.input_policy_conf]
     67     # Using shell=True to setup args.checkpolicy_env variables.
     68     run_host_command(' '.join(checkpolicy_cmd), shell=True)
     69     file_utils.filter_out([args.reqd_mask], raw_cil_file)
     70 
     71     # Builds the output CIL by versioning the above raw CIL.
     72     output_file = args.output_cil
     73     if output_file is None:
     74         output_file = input_file_name + '.cil'
     75     file_utils.make_parent_dirs(output_file)
     76 
     77     run_host_command([os.path.join(args.android_host_path, 'version_policy'),
     78                       '-b', args.base_policy, '-t', raw_cil_file,
     79                       '-n', args.treble_sepolicy_vers, '-o', output_file])
     80     if args.filter_out_files:
     81         file_utils.filter_out(args.filter_out_files, output_file)
     82 
     83     # Tests that the output file can be merged with the given CILs.
     84     if args.dependent_cils:
     85         merge_cmd = [os.path.join(args.android_host_path, 'secilc'),
     86                      '-m', '-M', 'true', '-G', '-N', '-c', args.policy_vers]
     87         merge_cmd += args.dependent_cils      # the give CILs to merge
     88         merge_cmd += [output_file, '-o', '/dev/null', '-f', '/dev/null']
     89         run_host_command(merge_cmd)
     90 
     91 
     92 def setup_build_cil(subparsers):
     93     """Sets up command args for 'build_cil' command."""
     94 
     95     # Required arguments.
     96     parser = subparsers.add_parser('build_cil', help='build CIL files')
     97     parser.add_argument('-i', '--input_policy_conf', required=True,
     98                         help='source policy.conf')
     99     parser.add_argument('-m', '--reqd_mask', required=True,
    100                         help='the bare minimum policy.conf to use checkpolicy')
    101     parser.add_argument('-b', '--base_policy', required=True,
    102                         help='base policy for versioning')
    103     parser.add_argument('-t', '--treble_sepolicy_vers', required=True,
    104                         help='the version number to use for Treble-OTA')
    105     parser.add_argument('-p', '--policy_vers', required=True,
    106                         help='SELinux policy version')
    107 
    108     # Optional arguments.
    109     parser.add_argument('-c', '--checkpolicy_env',
    110                         help='environment variables passed to checkpolicy')
    111     parser.add_argument('-f', '--filter_out_files', nargs='+',
    112                         help='the pattern files to filter out the output cil')
    113     parser.add_argument('-d', '--dependent_cils', nargs='+',
    114                         help=('check the output file can be merged with '
    115                               'the dependent cil files'))
    116     parser.add_argument('-o', '--output_cil', help='the output cil file')
    117 
    118     # The function that performs the actual works.
    119     parser.set_defaults(func=do_build_cil)
    120 
    121 
    122 def run(argv):
    123     """Sets up command parser and execuates sub-command."""
    124     parser = argparse.ArgumentParser()
    125 
    126     # Adds top-level arguments.
    127     parser.add_argument('-a', '--android_host_path', default='',
    128                         help='a path to host out executables')
    129 
    130     # Adds subparsers for each COMMAND.
    131     subparsers = parser.add_subparsers(title='COMMAND')
    132     for command in _SUPPORTED_COMMANDS:
    133         globals()['setup_' + command](subparsers)
    134 
    135     args = parser.parse_args(argv[1:])
    136     args.func(args)
    137 
    138 
    139 if __name__ == '__main__':
    140     run(sys.argv)
    141