1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3# Copyright (c) 2023 Huawei Device Co., Ltd. 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 16import optparse 17import os 18import sys 19import json 20import stat 21 22sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, 23 os.pardir, os.pardir, os.pardir, "build")) 24from scripts.util import build_utils # noqa: E402 25 26#default json 27 28APP_SANDBOX_DEFAULT = ''' 29{ 30 "common" : [{ 31 "top-sandbox-switch": "ON", 32 "app-base" : [{ 33 "sandbox-root" : "/mnt/sandbox/<currentUserId>/<PackageName>", 34 "sandbox-ns-flags" : [], 35 "mount-paths" : [], 36 "symbol-links": [], 37 "flags-point" : [] 38 }], 39 "app-resources" : [{ 40 "sandbox-root" : "/mnt/sandbox/<currentUserId>/<PackageName>", 41 "mount-paths" : [], 42 "flags-point" : [], 43 "symbol-links" : [] 44 }] 45 }], 46 "individual" : [{}], 47 "permission" :[{}] 48} 49''' 50#only string in list 51 52 53def _merge_list(origin, new): 54 if origin is None or new is None: 55 return 56 for data1 in new: 57 if data1 not in origin: 58 origin.append(data1) 59 60 61def _is_same_data(data1, data2, keys): 62 for key in keys: 63 if data1.get(key) != data2.get(key): 64 return False 65 return True 66 67#for object in list 68 69 70def _handle_same_array(data1, data2): 71 for field in ["sandbox-root", "sandbox-path", "check-action-status", "fs-type", "link-name"]: 72 if data1.get(field) is not None: 73 data2[field] = data1[field] 74 75 for field in ["sandbox-flags"]: # by list merger 76 item = data1.get(field) 77 if item is not None and len(item) > 0: 78 _merge_list(data2[field], item) 79 80 81def _merge_scope_array(origin, new, keys): 82 for data1 in new: 83 found = False 84 for data2 in origin: 85 if _is_same_data(data1, data2, keys): 86 found = True 87 _handle_same_array(data1, data2) 88 break 89 if not found: 90 origin.append(data1) 91 92 93def _handle_same_data(data1, data2, field_infos): 94 for field in ["sandbox-root"]: 95 if data1.get(field) is not None: 96 data2[field] = data1[field] 97 98 # for array 99 for name, keys in field_infos.items(): 100 item = data1.get(name) 101 if item is not None and len(item) > 0: 102 _merge_scope_array(data2[field], item, keys) 103 104 105def _merge_scope_flags_point(origin, new): 106 field_infos = { 107 "mount-paths": ["src-path"] 108 } 109 for data1 in new: 110 found = False 111 for data2 in origin: 112 if _is_same_data(data1, data2, ["flags"]): 113 found = True 114 _handle_same_data(data1, data2, field_infos) 115 break 116 117 if not found: 118 origin.append(data1) 119 120 121def _merge_scope_app(origin, new): 122 field_infos = { 123 "mount-paths": ["src-path"], 124 "symbol-links": ["target-name"] 125 } 126 # normal filed 127 for k in ["sandbox-root", "sandbox-switch", "gids", "sandbox-ns-flags"]: 128 if new[0].get(k) is not None: 129 origin[0][k] = new[0].get(k) 130 131 # for flags-point 132 flags_points = new[0].get("flags-point") 133 if flags_points: 134 _merge_scope_flags_point(origin[0]["flags-point"], flags_points) 135 136 # by list merger 137 for field in ["sandbox-ns-flags"]: 138 item = origin[0].get(field) 139 if item is not None and len(item) > 0: 140 _merge_list(new[0][field], item) 141 142 # for array 143 for name, keys in field_infos.items(): 144 item = new[0].get(name) 145 if item is not None and len(item) > 0: 146 _merge_scope_array(origin[0].get(name), item, keys) 147 148 149def _merge_scope_individual(origin, new): 150 for k, v in new.items(): 151 if k not in origin: 152 origin[k] = v 153 else: 154 _merge_scope_app(origin[k], v) 155 156 157def _merge_scope_permission(origin, new): 158 for k, v in new.items(): 159 if k not in origin: 160 origin[k] = v 161 else: 162 _merge_scope_app(origin[k], v) 163 164 165def _merge_scope_common(origin, new): 166 # 处理 top-sandbox-switch 167 for name in ["top-sandbox-switch"]: 168 if new.get(name) : 169 origin[name] = new.get(name) 170 171 #处理 app-base 172 app = new.get("app-base") 173 if app is not None and len(app) > 0: 174 _merge_scope_app(origin.get("app-base"), app) 175 pass 176 177 #处理 app-resources 178 app = new.get("app-resources") 179 if app is not None and len(app) > 0: 180 _merge_scope_app(origin.get("app-resources"), app) 181 pass 182 183 184def parse_args(args): 185 args = build_utils.expand_file_args(args) 186 parser = optparse.OptionParser() 187 build_utils.add_depfile_option(parser) 188 parser.add_option('--output', help='fixed sandbox configure file') 189 parser.add_option('--source-file', help='source para file') 190 parser.add_option('--patterns', action="append", 191 type="string", dest="patterns", help='replace string patterns like libpath:lib64') 192 parser.add_option('--extra_sandbox_cfg', action="append", 193 type="string", dest="extra_sandbox_cfgs", help='extra sandbox') 194 195 options, _ = parser.parse_args(args) 196 return options 197 198 199def __substitude_contents(options, source_file): 200 with open(source_file, "r") as f: 201 contents = f.read() 202 if not options.patterns: 203 return json.loads(contents) 204 for pattern in options.patterns: 205 parts = pattern.split(":") 206 contents = contents.replace("{%s}" % parts[0], parts[1]) 207 return json.loads(contents) 208 209 210def _get_json_list(options): 211 data_list = [] 212 #decode source file 213 contents = __substitude_contents(options, options.source_file) 214 if contents : 215 data_list.append(contents) 216 217 if options.extra_sandbox_cfgs is None: 218 return data_list 219 220 #decode extra file 221 for sandbox_cfg in options.extra_sandbox_cfgs: 222 contents = __substitude_contents(options, sandbox_cfg) 223 if contents : 224 data_list.append(contents) 225 return data_list 226 227 228def fix_sandbox_config_file(options): 229 data_list = _get_json_list(options) 230 #decode template 231 origin_json = json.loads(APP_SANDBOX_DEFAULT) 232 233 for data in data_list: 234 # 处理common 235 common = data.get("common") 236 if common is not None and len(common) > 0: 237 _merge_scope_common(origin_json.get("common")[0], common[0]) 238 239 #处理individual 240 individuals = data.get("individual") 241 if individuals is not None and len(individuals) > 0: 242 _merge_scope_individual(origin_json.get("individual")[0], individuals[0]) 243 pass 244 245 # 处理permission 246 permission = data.get("permission") 247 if permission is not None and len(permission) > 0: 248 _merge_scope_permission(origin_json.get("permission")[0], permission[0]) 249 250 # dump json to output 251 flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC 252 modes = stat.S_IWUSR | stat.S_IRUSR | stat.S_IWGRP | stat.S_IRGRP 253 with os.fdopen(os.open(options.output, flags, modes), 'w') as f: 254 f.write(json.dumps(origin_json, ensure_ascii=False, indent=2)) 255 256 257def main(args): 258 options = parse_args(args) 259 depfile_deps = ([options.source_file]) 260 fix_sandbox_config_file(options) 261 build_utils.write_depfile(options.depfile, options.output, depfile_deps, add_pydeps=False) 262 263if __name__ == '__main__': 264 sys.exit(main(sys.argv[1:])) 265