xref: /aosp_15_r20/system/update_engine/scripts/paycheck.py (revision 5a9231315b4521097b8dc3750bc806fcafe0c72f)
1*5a923131SAndroid Build Coastguard Worker#!/usr/bin/env python
2*5a923131SAndroid Build Coastguard Worker#
3*5a923131SAndroid Build Coastguard Worker# Copyright (C) 2013 The Android Open Source Project
4*5a923131SAndroid Build Coastguard Worker#
5*5a923131SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*5a923131SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*5a923131SAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*5a923131SAndroid Build Coastguard Worker#
9*5a923131SAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
10*5a923131SAndroid Build Coastguard Worker#
11*5a923131SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*5a923131SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*5a923131SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*5a923131SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*5a923131SAndroid Build Coastguard Worker# limitations under the License.
16*5a923131SAndroid Build Coastguard Worker#
17*5a923131SAndroid Build Coastguard Worker
18*5a923131SAndroid Build Coastguard Worker"""Command-line tool for checking and applying Chrome OS update payloads."""
19*5a923131SAndroid Build Coastguard Worker
20*5a923131SAndroid Build Coastguard Workerfrom __future__ import absolute_import
21*5a923131SAndroid Build Coastguard Workerfrom __future__ import print_function
22*5a923131SAndroid Build Coastguard Worker
23*5a923131SAndroid Build Coastguard Worker# pylint: disable=import-error
24*5a923131SAndroid Build Coastguard Workerimport argparse
25*5a923131SAndroid Build Coastguard Workerimport filecmp
26*5a923131SAndroid Build Coastguard Workerimport os
27*5a923131SAndroid Build Coastguard Workerimport sys
28*5a923131SAndroid Build Coastguard Workerimport tempfile
29*5a923131SAndroid Build Coastguard Worker
30*5a923131SAndroid Build Coastguard Worker# pylint: disable=redefined-builtin
31*5a923131SAndroid Build Coastguard Workerfrom six.moves import zip
32*5a923131SAndroid Build Coastguard Workerfrom update_payload import error
33*5a923131SAndroid Build Coastguard Worker
34*5a923131SAndroid Build Coastguard Worker
35*5a923131SAndroid Build Coastguard Workerlib_dir = os.path.join(os.path.dirname(__file__), 'lib')
36*5a923131SAndroid Build Coastguard Workerif os.path.exists(lib_dir) and os.path.isdir(lib_dir):
37*5a923131SAndroid Build Coastguard Worker  sys.path.insert(1, lib_dir)
38*5a923131SAndroid Build Coastguard Workerimport update_payload  # pylint: disable=wrong-import-position
39*5a923131SAndroid Build Coastguard Worker
40*5a923131SAndroid Build Coastguard Worker
41*5a923131SAndroid Build Coastguard Worker_TYPE_FULL = 'full'
42*5a923131SAndroid Build Coastguard Worker_TYPE_DELTA = 'delta'
43*5a923131SAndroid Build Coastguard Worker
44*5a923131SAndroid Build Coastguard Workerdef CheckApplyPayload(args):
45*5a923131SAndroid Build Coastguard Worker  """Whether to check the result after applying the payload.
46*5a923131SAndroid Build Coastguard Worker
47*5a923131SAndroid Build Coastguard Worker  Args:
48*5a923131SAndroid Build Coastguard Worker    args: Parsed command arguments (the return value of
49*5a923131SAndroid Build Coastguard Worker          ArgumentParser.parse_args).
50*5a923131SAndroid Build Coastguard Worker
51*5a923131SAndroid Build Coastguard Worker  Returns:
52*5a923131SAndroid Build Coastguard Worker    Boolean value whether to check.
53*5a923131SAndroid Build Coastguard Worker  """
54*5a923131SAndroid Build Coastguard Worker  return args.dst_part_paths is not None
55*5a923131SAndroid Build Coastguard Worker
56*5a923131SAndroid Build Coastguard Workerdef ApplyPayload(args):
57*5a923131SAndroid Build Coastguard Worker  """Whether to apply the payload.
58*5a923131SAndroid Build Coastguard Worker
59*5a923131SAndroid Build Coastguard Worker  Args:
60*5a923131SAndroid Build Coastguard Worker    args: Parsed command arguments (the return value of
61*5a923131SAndroid Build Coastguard Worker          ArgumentParser.parse_args).
62*5a923131SAndroid Build Coastguard Worker
63*5a923131SAndroid Build Coastguard Worker  Returns:
64*5a923131SAndroid Build Coastguard Worker    Boolean value whether to apply the payload.
65*5a923131SAndroid Build Coastguard Worker  """
66*5a923131SAndroid Build Coastguard Worker  return CheckApplyPayload(args) or args.out_dst_part_paths is not None
67*5a923131SAndroid Build Coastguard Worker
68*5a923131SAndroid Build Coastguard Workerdef ParseArguments(argv):
69*5a923131SAndroid Build Coastguard Worker  """Parse and validate command-line arguments.
70*5a923131SAndroid Build Coastguard Worker
71*5a923131SAndroid Build Coastguard Worker  Args:
72*5a923131SAndroid Build Coastguard Worker    argv: command-line arguments to parse (excluding the program name)
73*5a923131SAndroid Build Coastguard Worker
74*5a923131SAndroid Build Coastguard Worker  Returns:
75*5a923131SAndroid Build Coastguard Worker    Returns the arguments returned by the argument parser.
76*5a923131SAndroid Build Coastguard Worker  """
77*5a923131SAndroid Build Coastguard Worker  parser = argparse.ArgumentParser(
78*5a923131SAndroid Build Coastguard Worker      description=('Applies a Chrome OS update PAYLOAD to src_part_paths'
79*5a923131SAndroid Build Coastguard Worker                   'emitting dst_part_paths, respectively. '
80*5a923131SAndroid Build Coastguard Worker                   'src_part_paths are only needed for delta payloads. '
81*5a923131SAndroid Build Coastguard Worker                   'When no partitions are provided, verifies the payload '
82*5a923131SAndroid Build Coastguard Worker                   'integrity.'),
83*5a923131SAndroid Build Coastguard Worker      epilog=('Note: a payload may verify correctly but fail to apply, and '
84*5a923131SAndroid Build Coastguard Worker              'vice versa; this is by design and can be thought of as static '
85*5a923131SAndroid Build Coastguard Worker              'vs dynamic correctness. A payload that both verifies and '
86*5a923131SAndroid Build Coastguard Worker              'applies correctly should be safe for use by the Chrome OS '
87*5a923131SAndroid Build Coastguard Worker              'Update Engine. Use --check to verify a payload prior to '
88*5a923131SAndroid Build Coastguard Worker              'applying it.'),
89*5a923131SAndroid Build Coastguard Worker      formatter_class=argparse.RawDescriptionHelpFormatter
90*5a923131SAndroid Build Coastguard Worker  )
91*5a923131SAndroid Build Coastguard Worker
92*5a923131SAndroid Build Coastguard Worker  check_args = parser.add_argument_group('Checking payload integrity')
93*5a923131SAndroid Build Coastguard Worker  check_args.add_argument('-c', '--check', action='store_true', default=False,
94*5a923131SAndroid Build Coastguard Worker                          help=('force payload integrity check (e.g. before '
95*5a923131SAndroid Build Coastguard Worker                                'applying)'))
96*5a923131SAndroid Build Coastguard Worker  check_args.add_argument('-r', '--report', metavar='FILE',
97*5a923131SAndroid Build Coastguard Worker                          help="dump payload report (`-' for stdout)")
98*5a923131SAndroid Build Coastguard Worker  check_args.add_argument('-t', '--type', dest='assert_type',
99*5a923131SAndroid Build Coastguard Worker                          help='assert the payload type',
100*5a923131SAndroid Build Coastguard Worker                          choices=[_TYPE_FULL, _TYPE_DELTA])
101*5a923131SAndroid Build Coastguard Worker  check_args.add_argument('-z', '--block-size', metavar='NUM', default=0,
102*5a923131SAndroid Build Coastguard Worker                          type=int,
103*5a923131SAndroid Build Coastguard Worker                          help='assert a non-default (4096) payload block size')
104*5a923131SAndroid Build Coastguard Worker  check_args.add_argument('-u', '--allow-unhashed', action='store_true',
105*5a923131SAndroid Build Coastguard Worker                          default=False, help='allow unhashed operations')
106*5a923131SAndroid Build Coastguard Worker  check_args.add_argument('-d', '--disabled_tests', default=(), metavar='',
107*5a923131SAndroid Build Coastguard Worker                          help=('space separated list of tests to disable. '
108*5a923131SAndroid Build Coastguard Worker                                'allowed options include: ' +
109*5a923131SAndroid Build Coastguard Worker                                ', '.join(update_payload.CHECKS_TO_DISABLE)),
110*5a923131SAndroid Build Coastguard Worker                          choices=update_payload.CHECKS_TO_DISABLE)
111*5a923131SAndroid Build Coastguard Worker  check_args.add_argument('-k', '--key', metavar='FILE',
112*5a923131SAndroid Build Coastguard Worker                          help=('override standard key used for signature '
113*5a923131SAndroid Build Coastguard Worker                                'validation'))
114*5a923131SAndroid Build Coastguard Worker  check_args.add_argument('-m', '--meta-sig', metavar='FILE',
115*5a923131SAndroid Build Coastguard Worker                          help='verify metadata against its signature')
116*5a923131SAndroid Build Coastguard Worker  check_args.add_argument('-s', '--metadata-size', metavar='NUM', default=0,
117*5a923131SAndroid Build Coastguard Worker                          help='the metadata size to verify with the one in'
118*5a923131SAndroid Build Coastguard Worker                          ' payload')
119*5a923131SAndroid Build Coastguard Worker  check_args.add_argument('--part_sizes', metavar='NUM', nargs='+', type=int,
120*5a923131SAndroid Build Coastguard Worker                          help='override partition size auto-inference')
121*5a923131SAndroid Build Coastguard Worker
122*5a923131SAndroid Build Coastguard Worker  apply_args = parser.add_argument_group('Applying payload')
123*5a923131SAndroid Build Coastguard Worker  # TODO(ahassani): Extent extract-bsdiff to puffdiff too.
124*5a923131SAndroid Build Coastguard Worker  apply_args.add_argument('-x', '--extract-bsdiff', action='store_true',
125*5a923131SAndroid Build Coastguard Worker                          default=False,
126*5a923131SAndroid Build Coastguard Worker                          help=('use temp input/output files with BSDIFF '
127*5a923131SAndroid Build Coastguard Worker                                'operations (not in-place)'))
128*5a923131SAndroid Build Coastguard Worker  apply_args.add_argument('--bspatch-path', metavar='FILE',
129*5a923131SAndroid Build Coastguard Worker                          help='use the specified bspatch binary')
130*5a923131SAndroid Build Coastguard Worker  apply_args.add_argument('--puffpatch-path', metavar='FILE',
131*5a923131SAndroid Build Coastguard Worker                          help='use the specified puffpatch binary')
132*5a923131SAndroid Build Coastguard Worker
133*5a923131SAndroid Build Coastguard Worker  apply_args.add_argument('--src_part_paths', metavar='FILE', nargs='+',
134*5a923131SAndroid Build Coastguard Worker                          help='source partitition files')
135*5a923131SAndroid Build Coastguard Worker  apply_args.add_argument('--dst_part_paths', metavar='FILE', nargs='+',
136*5a923131SAndroid Build Coastguard Worker                          help='destination partition files')
137*5a923131SAndroid Build Coastguard Worker  apply_args.add_argument('--out_dst_part_paths', metavar='FILE', nargs='+',
138*5a923131SAndroid Build Coastguard Worker                          help='created destination partition files')
139*5a923131SAndroid Build Coastguard Worker
140*5a923131SAndroid Build Coastguard Worker  parser.add_argument('payload', metavar='PAYLOAD', help='the payload file')
141*5a923131SAndroid Build Coastguard Worker  parser.add_argument('--part_names', metavar='NAME', nargs='+',
142*5a923131SAndroid Build Coastguard Worker                      help='names of partitions')
143*5a923131SAndroid Build Coastguard Worker
144*5a923131SAndroid Build Coastguard Worker  # Parse command-line arguments.
145*5a923131SAndroid Build Coastguard Worker  args = parser.parse_args(argv)
146*5a923131SAndroid Build Coastguard Worker
147*5a923131SAndroid Build Coastguard Worker  # There are several options that imply --check.
148*5a923131SAndroid Build Coastguard Worker  args.check = (args.check or args.report or args.assert_type or
149*5a923131SAndroid Build Coastguard Worker                args.block_size or args.allow_unhashed or
150*5a923131SAndroid Build Coastguard Worker                args.disabled_tests or args.meta_sig or args.key or
151*5a923131SAndroid Build Coastguard Worker                args.part_sizes is not None or args.metadata_size)
152*5a923131SAndroid Build Coastguard Worker
153*5a923131SAndroid Build Coastguard Worker  # Makes sure the following arguments have the same length as |part_names| if
154*5a923131SAndroid Build Coastguard Worker  # set.
155*5a923131SAndroid Build Coastguard Worker  for arg in ['part_sizes', 'src_part_paths', 'dst_part_paths',
156*5a923131SAndroid Build Coastguard Worker              'out_dst_part_paths']:
157*5a923131SAndroid Build Coastguard Worker    if getattr(args, arg) is None:
158*5a923131SAndroid Build Coastguard Worker      # Parameter is not set.
159*5a923131SAndroid Build Coastguard Worker      continue
160*5a923131SAndroid Build Coastguard Worker    if len(args.part_names) != len(getattr(args, arg, [])):
161*5a923131SAndroid Build Coastguard Worker      parser.error('partitions in --%s do not match --part_names' % arg)
162*5a923131SAndroid Build Coastguard Worker
163*5a923131SAndroid Build Coastguard Worker  def _IsSrcPartPathsProvided(args):
164*5a923131SAndroid Build Coastguard Worker    return args.src_part_paths is not None
165*5a923131SAndroid Build Coastguard Worker
166*5a923131SAndroid Build Coastguard Worker  # Makes sure parameters are coherent with payload type.
167*5a923131SAndroid Build Coastguard Worker  if ApplyPayload(args):
168*5a923131SAndroid Build Coastguard Worker    if _IsSrcPartPathsProvided(args):
169*5a923131SAndroid Build Coastguard Worker      if args.assert_type == _TYPE_FULL:
170*5a923131SAndroid Build Coastguard Worker        parser.error('%s payload does not accept source partition arguments'
171*5a923131SAndroid Build Coastguard Worker                     % _TYPE_FULL)
172*5a923131SAndroid Build Coastguard Worker      else:
173*5a923131SAndroid Build Coastguard Worker        args.assert_type = _TYPE_DELTA
174*5a923131SAndroid Build Coastguard Worker    else:
175*5a923131SAndroid Build Coastguard Worker      if args.assert_type == _TYPE_DELTA:
176*5a923131SAndroid Build Coastguard Worker        parser.error('%s payload requires source partitions arguments'
177*5a923131SAndroid Build Coastguard Worker                     % _TYPE_DELTA)
178*5a923131SAndroid Build Coastguard Worker      else:
179*5a923131SAndroid Build Coastguard Worker        args.assert_type = _TYPE_FULL
180*5a923131SAndroid Build Coastguard Worker  else:
181*5a923131SAndroid Build Coastguard Worker    # Not applying payload.
182*5a923131SAndroid Build Coastguard Worker    if args.extract_bsdiff:
183*5a923131SAndroid Build Coastguard Worker      parser.error('--extract-bsdiff can only be used when applying payloads')
184*5a923131SAndroid Build Coastguard Worker    if args.bspatch_path:
185*5a923131SAndroid Build Coastguard Worker      parser.error('--bspatch-path can only be used when applying payloads')
186*5a923131SAndroid Build Coastguard Worker    if args.puffpatch_path:
187*5a923131SAndroid Build Coastguard Worker      parser.error('--puffpatch-path can only be used when applying payloads')
188*5a923131SAndroid Build Coastguard Worker
189*5a923131SAndroid Build Coastguard Worker  # By default, look for a metadata-signature file with a name based on the name
190*5a923131SAndroid Build Coastguard Worker  # of the payload we are checking. We only do it if check was triggered.
191*5a923131SAndroid Build Coastguard Worker  if args.check and not args.meta_sig:
192*5a923131SAndroid Build Coastguard Worker    default_meta_sig = args.payload + '.metadata-signature'
193*5a923131SAndroid Build Coastguard Worker    if os.path.isfile(default_meta_sig):
194*5a923131SAndroid Build Coastguard Worker      args.meta_sig = default_meta_sig
195*5a923131SAndroid Build Coastguard Worker      print('Using default metadata signature', args.meta_sig, file=sys.stderr)
196*5a923131SAndroid Build Coastguard Worker
197*5a923131SAndroid Build Coastguard Worker  return args
198*5a923131SAndroid Build Coastguard Worker
199*5a923131SAndroid Build Coastguard Worker
200*5a923131SAndroid Build Coastguard Workerdef main(argv):
201*5a923131SAndroid Build Coastguard Worker  # Parse and validate arguments.
202*5a923131SAndroid Build Coastguard Worker  args = ParseArguments(argv[1:])
203*5a923131SAndroid Build Coastguard Worker
204*5a923131SAndroid Build Coastguard Worker  with open(args.payload, 'rb') as payload_file:
205*5a923131SAndroid Build Coastguard Worker    payload = update_payload.Payload(payload_file)
206*5a923131SAndroid Build Coastguard Worker    try:
207*5a923131SAndroid Build Coastguard Worker      # Initialize payload.
208*5a923131SAndroid Build Coastguard Worker      payload.Init()
209*5a923131SAndroid Build Coastguard Worker
210*5a923131SAndroid Build Coastguard Worker      # Perform payload integrity checks.
211*5a923131SAndroid Build Coastguard Worker      if args.check:
212*5a923131SAndroid Build Coastguard Worker        report_file = None
213*5a923131SAndroid Build Coastguard Worker        do_close_report_file = False
214*5a923131SAndroid Build Coastguard Worker        metadata_sig_file = None
215*5a923131SAndroid Build Coastguard Worker        try:
216*5a923131SAndroid Build Coastguard Worker          if args.report:
217*5a923131SAndroid Build Coastguard Worker            if args.report == '-':
218*5a923131SAndroid Build Coastguard Worker              report_file = sys.stdout
219*5a923131SAndroid Build Coastguard Worker            else:
220*5a923131SAndroid Build Coastguard Worker              report_file = open(args.report, 'w')
221*5a923131SAndroid Build Coastguard Worker              do_close_report_file = True
222*5a923131SAndroid Build Coastguard Worker
223*5a923131SAndroid Build Coastguard Worker          part_sizes = (args.part_sizes and
224*5a923131SAndroid Build Coastguard Worker                        dict(zip(args.part_names, args.part_sizes)))
225*5a923131SAndroid Build Coastguard Worker          metadata_sig_file = args.meta_sig and open(args.meta_sig, 'rb')
226*5a923131SAndroid Build Coastguard Worker          payload.Check(
227*5a923131SAndroid Build Coastguard Worker              pubkey_file_name=args.key,
228*5a923131SAndroid Build Coastguard Worker              metadata_sig_file=metadata_sig_file,
229*5a923131SAndroid Build Coastguard Worker              metadata_size=int(args.metadata_size),
230*5a923131SAndroid Build Coastguard Worker              report_out_file=report_file,
231*5a923131SAndroid Build Coastguard Worker              assert_type=args.assert_type,
232*5a923131SAndroid Build Coastguard Worker              block_size=int(args.block_size),
233*5a923131SAndroid Build Coastguard Worker              part_sizes=part_sizes,
234*5a923131SAndroid Build Coastguard Worker              allow_unhashed=args.allow_unhashed,
235*5a923131SAndroid Build Coastguard Worker              disabled_tests=args.disabled_tests)
236*5a923131SAndroid Build Coastguard Worker        finally:
237*5a923131SAndroid Build Coastguard Worker          if metadata_sig_file:
238*5a923131SAndroid Build Coastguard Worker            metadata_sig_file.close()
239*5a923131SAndroid Build Coastguard Worker          if do_close_report_file:
240*5a923131SAndroid Build Coastguard Worker            report_file.close()
241*5a923131SAndroid Build Coastguard Worker
242*5a923131SAndroid Build Coastguard Worker      # Apply payload.
243*5a923131SAndroid Build Coastguard Worker      if ApplyPayload(args):
244*5a923131SAndroid Build Coastguard Worker        dargs = {'bsdiff_in_place': not args.extract_bsdiff}
245*5a923131SAndroid Build Coastguard Worker        if args.bspatch_path:
246*5a923131SAndroid Build Coastguard Worker          dargs['bspatch_path'] = args.bspatch_path
247*5a923131SAndroid Build Coastguard Worker        if args.puffpatch_path:
248*5a923131SAndroid Build Coastguard Worker          dargs['puffpatch_path'] = args.puffpatch_path
249*5a923131SAndroid Build Coastguard Worker        if args.assert_type == _TYPE_DELTA:
250*5a923131SAndroid Build Coastguard Worker          dargs['old_parts'] = dict(zip(args.part_names, args.src_part_paths))
251*5a923131SAndroid Build Coastguard Worker
252*5a923131SAndroid Build Coastguard Worker        out_dst_parts = {}
253*5a923131SAndroid Build Coastguard Worker        file_handles = []
254*5a923131SAndroid Build Coastguard Worker        if args.out_dst_part_paths is not None:
255*5a923131SAndroid Build Coastguard Worker          for name, path in zip(args.part_names, args.out_dst_part_paths):
256*5a923131SAndroid Build Coastguard Worker            handle = open(path, 'wb+')
257*5a923131SAndroid Build Coastguard Worker            file_handles.append(handle)
258*5a923131SAndroid Build Coastguard Worker            out_dst_parts[name] = handle.name
259*5a923131SAndroid Build Coastguard Worker        else:
260*5a923131SAndroid Build Coastguard Worker          for name in args.part_names:
261*5a923131SAndroid Build Coastguard Worker            handle = tempfile.NamedTemporaryFile()
262*5a923131SAndroid Build Coastguard Worker            file_handles.append(handle)
263*5a923131SAndroid Build Coastguard Worker            out_dst_parts[name] = handle.name
264*5a923131SAndroid Build Coastguard Worker
265*5a923131SAndroid Build Coastguard Worker        payload.Apply(out_dst_parts, **dargs)
266*5a923131SAndroid Build Coastguard Worker
267*5a923131SAndroid Build Coastguard Worker        # If destination kernel and rootfs partitions are not given, then this
268*5a923131SAndroid Build Coastguard Worker        # just becomes an apply operation with no check.
269*5a923131SAndroid Build Coastguard Worker        if CheckApplyPayload(args):
270*5a923131SAndroid Build Coastguard Worker          # Prior to comparing, add the unused space past the filesystem
271*5a923131SAndroid Build Coastguard Worker          # boundary in the new target partitions to become the same size as
272*5a923131SAndroid Build Coastguard Worker          # the given partitions. This will truncate to larger size.
273*5a923131SAndroid Build Coastguard Worker          for part_name, out_dst_part, dst_part in zip(args.part_names,
274*5a923131SAndroid Build Coastguard Worker                                                       file_handles,
275*5a923131SAndroid Build Coastguard Worker                                                       args.dst_part_paths):
276*5a923131SAndroid Build Coastguard Worker            out_dst_part.truncate(os.path.getsize(dst_part))
277*5a923131SAndroid Build Coastguard Worker
278*5a923131SAndroid Build Coastguard Worker            # Compare resulting partitions with the ones from the target image.
279*5a923131SAndroid Build Coastguard Worker            if not filecmp.cmp(out_dst_part.name, dst_part):
280*5a923131SAndroid Build Coastguard Worker              raise error.PayloadError(
281*5a923131SAndroid Build Coastguard Worker                  'Resulting %s partition corrupted.' % part_name)
282*5a923131SAndroid Build Coastguard Worker
283*5a923131SAndroid Build Coastguard Worker        # Close the output files. If args.out_dst_* was not given, then these
284*5a923131SAndroid Build Coastguard Worker        # files are created as temp files and will be deleted upon close().
285*5a923131SAndroid Build Coastguard Worker        for handle in file_handles:
286*5a923131SAndroid Build Coastguard Worker          handle.close()
287*5a923131SAndroid Build Coastguard Worker    except error.PayloadError as e:
288*5a923131SAndroid Build Coastguard Worker      sys.stderr.write('Error: %s\n' % e)
289*5a923131SAndroid Build Coastguard Worker      return 1
290*5a923131SAndroid Build Coastguard Worker
291*5a923131SAndroid Build Coastguard Worker  return 0
292*5a923131SAndroid Build Coastguard Worker
293*5a923131SAndroid Build Coastguard Worker
294*5a923131SAndroid Build Coastguard Workerif __name__ == '__main__':
295*5a923131SAndroid Build Coastguard Worker  sys.exit(main(sys.argv))
296