169570cc8Sopenharmony_ci#!/usr/bin/env python
269570cc8Sopenharmony_ci# -*- coding: utf-8 -*-
369570cc8Sopenharmony_ci# Copyright (c) 2023 Huawei Device Co., Ltd.
469570cc8Sopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License");
569570cc8Sopenharmony_ci# you may not use this file except in compliance with the License.
669570cc8Sopenharmony_ci# You may obtain a copy of the License at
769570cc8Sopenharmony_ci#
869570cc8Sopenharmony_ci#     http://www.apache.org/licenses/LICENSE-2.0
969570cc8Sopenharmony_ci#
1069570cc8Sopenharmony_ci# Unless required by applicable law or agreed to in writing, software
1169570cc8Sopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS,
1269570cc8Sopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1369570cc8Sopenharmony_ci# See the License for the specific language governing permissions and
1469570cc8Sopenharmony_ci# limitations under the License.
1569570cc8Sopenharmony_ci
1669570cc8Sopenharmony_ciimport optparse
1769570cc8Sopenharmony_ciimport os
1869570cc8Sopenharmony_ciimport sys
1969570cc8Sopenharmony_ciimport json
2069570cc8Sopenharmony_ciimport stat
2169570cc8Sopenharmony_ci
2269570cc8Sopenharmony_cisys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir,
2369570cc8Sopenharmony_ci    os.pardir, os.pardir, os.pardir, "build"))
2469570cc8Sopenharmony_cifrom scripts.util import build_utils  # noqa: E402
2569570cc8Sopenharmony_ci
2669570cc8Sopenharmony_ci#default json
2769570cc8Sopenharmony_ci
2869570cc8Sopenharmony_ciAPP_SANDBOX_DEFAULT = '''
2969570cc8Sopenharmony_ci{
3069570cc8Sopenharmony_ci    "common" : [{
3169570cc8Sopenharmony_ci        "top-sandbox-switch": "ON",
3269570cc8Sopenharmony_ci        "app-base" : [{
3369570cc8Sopenharmony_ci            "sandbox-root" : "/mnt/sandbox/<currentUserId>/<PackageName>",
3469570cc8Sopenharmony_ci            "sandbox-ns-flags" : [],
3569570cc8Sopenharmony_ci            "mount-paths" : [],
3669570cc8Sopenharmony_ci            "symbol-links": [],
3769570cc8Sopenharmony_ci            "flags-point" : []
3869570cc8Sopenharmony_ci        }],
3969570cc8Sopenharmony_ci        "app-resources" : [{
4069570cc8Sopenharmony_ci            "sandbox-root" : "/mnt/sandbox/<currentUserId>/<PackageName>",
4169570cc8Sopenharmony_ci            "mount-paths" : [],
4269570cc8Sopenharmony_ci            "flags-point" : [],
4369570cc8Sopenharmony_ci            "symbol-links" : []
4469570cc8Sopenharmony_ci        }]
4569570cc8Sopenharmony_ci    }],
4669570cc8Sopenharmony_ci    "individual" : [{}],
4769570cc8Sopenharmony_ci    "permission" :[{}]
4869570cc8Sopenharmony_ci}
4969570cc8Sopenharmony_ci'''
5069570cc8Sopenharmony_ci#only string in list
5169570cc8Sopenharmony_ci
5269570cc8Sopenharmony_ci
5369570cc8Sopenharmony_cidef _merge_list(origin, new):
5469570cc8Sopenharmony_ci    if origin is None or new is None:
5569570cc8Sopenharmony_ci        return
5669570cc8Sopenharmony_ci    for data1 in new:
5769570cc8Sopenharmony_ci        if data1 not in origin:
5869570cc8Sopenharmony_ci            origin.append(data1)
5969570cc8Sopenharmony_ci
6069570cc8Sopenharmony_ci
6169570cc8Sopenharmony_cidef _is_same_data(data1, data2, keys):
6269570cc8Sopenharmony_ci    for key in keys:
6369570cc8Sopenharmony_ci        if data1.get(key) != data2.get(key):
6469570cc8Sopenharmony_ci            return False
6569570cc8Sopenharmony_ci    return True
6669570cc8Sopenharmony_ci
6769570cc8Sopenharmony_ci#for object in list
6869570cc8Sopenharmony_ci
6969570cc8Sopenharmony_ci
7069570cc8Sopenharmony_cidef _handle_same_array(data1, data2):
7169570cc8Sopenharmony_ci    for field in ["sandbox-root", "sandbox-path", "check-action-status", "fs-type", "link-name"]:
7269570cc8Sopenharmony_ci        if data1.get(field) is not None:
7369570cc8Sopenharmony_ci            data2[field] = data1[field]
7469570cc8Sopenharmony_ci
7569570cc8Sopenharmony_ci    for field in ["sandbox-flags"]: # by list merger
7669570cc8Sopenharmony_ci        item = data1.get(field)
7769570cc8Sopenharmony_ci        if item is not None and len(item) > 0:
7869570cc8Sopenharmony_ci            _merge_list(data2[field], item)
7969570cc8Sopenharmony_ci
8069570cc8Sopenharmony_ci
8169570cc8Sopenharmony_cidef _merge_scope_array(origin, new, keys):
8269570cc8Sopenharmony_ci    for data1 in new:
8369570cc8Sopenharmony_ci        found = False
8469570cc8Sopenharmony_ci        for data2 in origin:
8569570cc8Sopenharmony_ci            if _is_same_data(data1, data2, keys):
8669570cc8Sopenharmony_ci                found = True
8769570cc8Sopenharmony_ci                _handle_same_array(data1, data2)
8869570cc8Sopenharmony_ci                break
8969570cc8Sopenharmony_ci        if not found:
9069570cc8Sopenharmony_ci            origin.append(data1)
9169570cc8Sopenharmony_ci
9269570cc8Sopenharmony_ci
9369570cc8Sopenharmony_cidef _handle_same_data(data1, data2, field_infos):
9469570cc8Sopenharmony_ci    for field in ["sandbox-root"]:
9569570cc8Sopenharmony_ci        if data1.get(field) is not None:
9669570cc8Sopenharmony_ci            data2[field] = data1[field]
9769570cc8Sopenharmony_ci
9869570cc8Sopenharmony_ci    # for array
9969570cc8Sopenharmony_ci    for name, keys in field_infos.items():
10069570cc8Sopenharmony_ci        item = data1.get(name)
10169570cc8Sopenharmony_ci        if item is not None and len(item) > 0:
10269570cc8Sopenharmony_ci            _merge_scope_array(data2[field], item, keys)
10369570cc8Sopenharmony_ci
10469570cc8Sopenharmony_ci
10569570cc8Sopenharmony_cidef _merge_scope_flags_point(origin, new):
10669570cc8Sopenharmony_ci    field_infos = {
10769570cc8Sopenharmony_ci        "mount-paths": ["src-path"]
10869570cc8Sopenharmony_ci    }
10969570cc8Sopenharmony_ci    for data1 in new:
11069570cc8Sopenharmony_ci        found = False
11169570cc8Sopenharmony_ci        for data2 in origin:
11269570cc8Sopenharmony_ci            if _is_same_data(data1, data2, ["flags"]):
11369570cc8Sopenharmony_ci                found = True
11469570cc8Sopenharmony_ci                _handle_same_data(data1, data2, field_infos)
11569570cc8Sopenharmony_ci                break
11669570cc8Sopenharmony_ci
11769570cc8Sopenharmony_ci        if not found:
11869570cc8Sopenharmony_ci            origin.append(data1)
11969570cc8Sopenharmony_ci
12069570cc8Sopenharmony_ci
12169570cc8Sopenharmony_cidef _merge_scope_app(origin, new):
12269570cc8Sopenharmony_ci    field_infos = {
12369570cc8Sopenharmony_ci        "mount-paths": ["src-path"],
12469570cc8Sopenharmony_ci        "symbol-links": ["target-name"]
12569570cc8Sopenharmony_ci    }
12669570cc8Sopenharmony_ci    # normal filed
12769570cc8Sopenharmony_ci    for k in ["sandbox-root", "sandbox-switch", "gids", "sandbox-ns-flags"]:
12869570cc8Sopenharmony_ci        if new[0].get(k) is not None:
12969570cc8Sopenharmony_ci            origin[0][k] = new[0].get(k)
13069570cc8Sopenharmony_ci
13169570cc8Sopenharmony_ci    # for flags-point
13269570cc8Sopenharmony_ci    flags_points = new[0].get("flags-point")
13369570cc8Sopenharmony_ci    if flags_points:
13469570cc8Sopenharmony_ci        _merge_scope_flags_point(origin[0]["flags-point"], flags_points)
13569570cc8Sopenharmony_ci
13669570cc8Sopenharmony_ci    # by list merger
13769570cc8Sopenharmony_ci    for field in ["sandbox-ns-flags"]:
13869570cc8Sopenharmony_ci        item = origin[0].get(field)
13969570cc8Sopenharmony_ci        if item is not None and len(item) > 0:
14069570cc8Sopenharmony_ci            _merge_list(new[0][field], item)
14169570cc8Sopenharmony_ci
14269570cc8Sopenharmony_ci    # for array
14369570cc8Sopenharmony_ci    for name, keys in field_infos.items():
14469570cc8Sopenharmony_ci        item = new[0].get(name)
14569570cc8Sopenharmony_ci        if item is not None and len(item) > 0:
14669570cc8Sopenharmony_ci            _merge_scope_array(origin[0].get(name), item, keys)
14769570cc8Sopenharmony_ci
14869570cc8Sopenharmony_ci
14969570cc8Sopenharmony_cidef _merge_scope_individual(origin, new):
15069570cc8Sopenharmony_ci    for k, v in new.items():
15169570cc8Sopenharmony_ci        if k not in origin:
15269570cc8Sopenharmony_ci            origin[k] = v
15369570cc8Sopenharmony_ci        else:
15469570cc8Sopenharmony_ci            _merge_scope_app(origin[k], v)
15569570cc8Sopenharmony_ci
15669570cc8Sopenharmony_ci
15769570cc8Sopenharmony_cidef _merge_scope_permission(origin, new):
15869570cc8Sopenharmony_ci    for k, v in new.items():
15969570cc8Sopenharmony_ci        if k not in origin:
16069570cc8Sopenharmony_ci            origin[k] = v
16169570cc8Sopenharmony_ci        else:
16269570cc8Sopenharmony_ci            _merge_scope_app(origin[k], v)
16369570cc8Sopenharmony_ci
16469570cc8Sopenharmony_ci
16569570cc8Sopenharmony_cidef _merge_scope_common(origin, new):
16669570cc8Sopenharmony_ci    # 处理 top-sandbox-switch
16769570cc8Sopenharmony_ci    for name in ["top-sandbox-switch"]:
16869570cc8Sopenharmony_ci        if new.get(name) :
16969570cc8Sopenharmony_ci            origin[name] = new.get(name)
17069570cc8Sopenharmony_ci
17169570cc8Sopenharmony_ci    #处理 app-base
17269570cc8Sopenharmony_ci    app = new.get("app-base")
17369570cc8Sopenharmony_ci    if app is not None and len(app) > 0:
17469570cc8Sopenharmony_ci        _merge_scope_app(origin.get("app-base"), app)
17569570cc8Sopenharmony_ci        pass
17669570cc8Sopenharmony_ci
17769570cc8Sopenharmony_ci    #处理 app-resources
17869570cc8Sopenharmony_ci    app = new.get("app-resources")
17969570cc8Sopenharmony_ci    if app is not None and len(app) > 0:
18069570cc8Sopenharmony_ci        _merge_scope_app(origin.get("app-resources"), app)
18169570cc8Sopenharmony_ci        pass
18269570cc8Sopenharmony_ci
18369570cc8Sopenharmony_ci
18469570cc8Sopenharmony_cidef parse_args(args):
18569570cc8Sopenharmony_ci    args = build_utils.expand_file_args(args)
18669570cc8Sopenharmony_ci    parser = optparse.OptionParser()
18769570cc8Sopenharmony_ci    build_utils.add_depfile_option(parser)
18869570cc8Sopenharmony_ci    parser.add_option('--output', help='fixed sandbox configure file')
18969570cc8Sopenharmony_ci    parser.add_option('--source-file', help='source para file')
19069570cc8Sopenharmony_ci    parser.add_option('--patterns', action="append",
19169570cc8Sopenharmony_ci        type="string", dest="patterns", help='replace string patterns like libpath:lib64')
19269570cc8Sopenharmony_ci    parser.add_option('--extra_sandbox_cfg', action="append",
19369570cc8Sopenharmony_ci        type="string", dest="extra_sandbox_cfgs", help='extra sandbox')
19469570cc8Sopenharmony_ci
19569570cc8Sopenharmony_ci    options, _ = parser.parse_args(args)
19669570cc8Sopenharmony_ci    return options
19769570cc8Sopenharmony_ci
19869570cc8Sopenharmony_ci
19969570cc8Sopenharmony_cidef __substitude_contents(options, source_file):
20069570cc8Sopenharmony_ci    with open(source_file, "r") as f:
20169570cc8Sopenharmony_ci        contents = f.read()
20269570cc8Sopenharmony_ci        if not options.patterns:
20369570cc8Sopenharmony_ci            return json.loads(contents)
20469570cc8Sopenharmony_ci        for pattern in options.patterns:
20569570cc8Sopenharmony_ci            parts = pattern.split(":")
20669570cc8Sopenharmony_ci            contents = contents.replace("{%s}" % parts[0], parts[1])
20769570cc8Sopenharmony_ci        return json.loads(contents)
20869570cc8Sopenharmony_ci
20969570cc8Sopenharmony_ci
21069570cc8Sopenharmony_cidef _get_json_list(options):
21169570cc8Sopenharmony_ci    data_list = []
21269570cc8Sopenharmony_ci    #decode source file
21369570cc8Sopenharmony_ci    contents = __substitude_contents(options, options.source_file)
21469570cc8Sopenharmony_ci    if contents :
21569570cc8Sopenharmony_ci        data_list.append(contents)
21669570cc8Sopenharmony_ci
21769570cc8Sopenharmony_ci    if options.extra_sandbox_cfgs is None:
21869570cc8Sopenharmony_ci        return data_list
21969570cc8Sopenharmony_ci
22069570cc8Sopenharmony_ci    #decode extra file
22169570cc8Sopenharmony_ci    for sandbox_cfg in options.extra_sandbox_cfgs:
22269570cc8Sopenharmony_ci        contents = __substitude_contents(options, sandbox_cfg)
22369570cc8Sopenharmony_ci        if contents :
22469570cc8Sopenharmony_ci            data_list.append(contents)
22569570cc8Sopenharmony_ci    return data_list
22669570cc8Sopenharmony_ci
22769570cc8Sopenharmony_ci
22869570cc8Sopenharmony_cidef fix_sandbox_config_file(options):
22969570cc8Sopenharmony_ci    data_list = _get_json_list(options)
23069570cc8Sopenharmony_ci    #decode template
23169570cc8Sopenharmony_ci    origin_json = json.loads(APP_SANDBOX_DEFAULT)
23269570cc8Sopenharmony_ci
23369570cc8Sopenharmony_ci    for data in data_list:
23469570cc8Sopenharmony_ci        # 处理common
23569570cc8Sopenharmony_ci        common = data.get("common")
23669570cc8Sopenharmony_ci        if common is not None and len(common) > 0:
23769570cc8Sopenharmony_ci            _merge_scope_common(origin_json.get("common")[0], common[0])
23869570cc8Sopenharmony_ci
23969570cc8Sopenharmony_ci        #处理individual
24069570cc8Sopenharmony_ci        individuals = data.get("individual")
24169570cc8Sopenharmony_ci        if individuals is not None and len(individuals) > 0:
24269570cc8Sopenharmony_ci            _merge_scope_individual(origin_json.get("individual")[0], individuals[0])
24369570cc8Sopenharmony_ci            pass
24469570cc8Sopenharmony_ci
24569570cc8Sopenharmony_ci        # 处理permission
24669570cc8Sopenharmony_ci        permission = data.get("permission")
24769570cc8Sopenharmony_ci        if permission is not None and len(permission) > 0:
24869570cc8Sopenharmony_ci            _merge_scope_permission(origin_json.get("permission")[0], permission[0])
24969570cc8Sopenharmony_ci
25069570cc8Sopenharmony_ci    # dump json to output
25169570cc8Sopenharmony_ci    flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
25269570cc8Sopenharmony_ci    modes = stat.S_IWUSR | stat.S_IRUSR | stat.S_IWGRP | stat.S_IRGRP
25369570cc8Sopenharmony_ci    with os.fdopen(os.open(options.output, flags, modes), 'w') as f:
25469570cc8Sopenharmony_ci        f.write(json.dumps(origin_json, ensure_ascii=False, indent=2))
25569570cc8Sopenharmony_ci
25669570cc8Sopenharmony_ci
25769570cc8Sopenharmony_cidef main(args):
25869570cc8Sopenharmony_ci    options = parse_args(args)
25969570cc8Sopenharmony_ci    depfile_deps = ([options.source_file])
26069570cc8Sopenharmony_ci    fix_sandbox_config_file(options)
26169570cc8Sopenharmony_ci    build_utils.write_depfile(options.depfile, options.output, depfile_deps, add_pydeps=False)
26269570cc8Sopenharmony_ci
26369570cc8Sopenharmony_ciif __name__ == '__main__':
26469570cc8Sopenharmony_ci    sys.exit(main(sys.argv[1:]))
265