1#!/usr/bin/env python 2#coding=utf-8 3 4# 5# Copyright (c) 2023-2024 Huawei Device Co., Ltd. 6# Licensed under the Apache License, Version 2.0 (the "License"); 7# you may not use this file except in compliance with the License. 8# You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, 14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15# See the License for the specific language governing permissions and 16# limitations under the License. 17# 18 19import os 20import json 21import pprint 22 23 24def _create_arg_parser(): 25 import argparse 26 27 parser = argparse.ArgumentParser(description='Collect init config information from xxxx/etc/init dir.') 28 parser.add_argument('-i', '--input', 29 help='input init config files base directory example "out/rk3568/packages/phone/" ', 30 action='append', required=True) 31 32 parser.add_argument('-o', '--output', 33 help='output init config information database directory', required=False) 34 parser.add_argument('-b', '--bootevent', 35 help='input bootevent file from system ', required=False) 36 return parser 37 38 39class ItemParser(dict): 40 def __init__(self, config): 41 self._config_parser = config 42 self["name"] = "" 43 44 def create(self, json_node, parent=None, file_id=None): 45 return 46 47 def update(self, json_node, parent=None, file_id=None): 48 return 49 50 def get_name(self): 51 return self["name"] 52 53 def get(self, key): 54 if self.__contains__(key): 55 return self[key] 56 return None 57 58 # get value form json array 59 def get_strings_value(self, json_str_array): 60 if json_str_array == None or len(json_str_array) == 0: 61 return "" 62 63 string = json_str_array[0] 64 for i in range(1, len(json_str_array)): 65 string = "{}@{}".format(string, json_str_array[i]) 66 return string 67 68 69class CmdParser(ItemParser): 70 def __init__(self, config): 71 ItemParser.__init__(self, config) 72 self["content"] = "" 73 self["fileId"] = -1 74 75 def __str__(self): 76 return "cmd \"%s\" content \"%s\" " % (self["name"], self["content"]) 77 78 def create(self, json_node, parent=None, fileId=None): 79 if not isinstance(json_node, str): 80 raise Exception("json_node type error") 81 if parent == None: 82 raise Exception("parent ") 83 info = json_node.partition(" ") # 取第一个空格分割 84 self["name"] = info[0] 85 self["jobId"] = parent.get("jobId") 86 if fileId: 87 self["fileId"] = fileId 88 if len(info) > 2: 89 self["content"] = info[2] 90 return 91 92 93class JobParser(ItemParser): 94 def __init__(self, config): 95 ItemParser.__init__(self, config) 96 self["condition"] = "" 97 self["serviceId"] = -1 98 self["fileId"] = -1 99 self["jobPriority"] = -1 100 self["jobPriority"] = -1 101 self["executionTime"] = 0 102 103 def __str__(self): 104 return "jobs '%s' condition '%s' " % (self["name"], self["condition"]) 105 106 def create(self, json_node, parent=None, fileId=None): 107 if not isinstance(json_node, dict): 108 raise Exception("json_node type error") 109 self["name"] = json_node["name"] 110 self["jobId"] = self._config_parser.get_job_id() 111 self["jobPriority"] = self._config_parser.get_job_priority(json_node["name"]) 112 113 if fileId and self["fileId"] is None: 114 self["fileId"] = fileId 115 if parent != None: 116 self["serviceId"] = parent.get("serviceId") 117 118 if json_node.__contains__("condition"): 119 self["condition"] = json_node.get("condition") 120 if json_node.__contains__("cmds"): 121 self._add_cmds(json_node.get("cmds"), fileId) 122 123 return 124 125 def update(self, json_node, parent=None, fileId=None): 126 if not isinstance(json_node, dict): 127 raise Exception("json_node type error") 128 if parent != None: 129 self["serviceId"] = parent.get("serviceId") 130 if fileId and self["fileId"] is None: 131 self["fileId"] = fileId 132 if json_node.__contains__("cmds"): 133 self._add_cmds(json_node.get("cmds"), fileId) 134 return 135 136 def _add_cmds(self, cmd_list, file_id): 137 for cmd in cmd_list: 138 self._config_parser.add_cmd(cmd, self, file_id) 139 140 141class ServiceParser(ItemParser): 142 def __init__(self, config): 143 ItemParser.__init__(self, config) 144 self["critical_enable"] = False 145 self["limit_time"] = 20 146 self["limit_count"] = 4 147 self["importance"] = 0 148 self["once"] = False 149 self["console"] = False 150 self["notify_state"] = True 151 self["on_demand"] = False 152 self["sandbox"] = False 153 self["disabled"] = False 154 self["start_mode"] = "normal" 155 self["secon"] = "" 156 self["boot_job"] = "" 157 self["start_job"] = "" 158 self["stop_job"] = "" 159 self["restart_job"] = "" 160 self["path"] = "" 161 self["apl"] = "" 162 self["d_caps"] = "" 163 self["permission"] = "" 164 self["permission_acls"] = "" 165 self["fileId"] = -1 166 167 def create(self, json_node, parent=None, fileId=None): 168 if not isinstance(json_node, dict): 169 raise Exception("json_node type error") 170 self["name"] = json_node["name"] 171 if not self.get("serviceId") : 172 self["serviceId"] = self._config_parser.get_service_id() 173 if fileId : 174 self["fileId"] = fileId 175 self._handle_string_filed(json_node) 176 self._handle_bool_filed(json_node) 177 self._handle_array_filed(json_node) 178 self._handle_integer_filed(json_node) 179 180 #for file 181 if json_node.__contains__("file"): 182 for item in json_node.get("file"): 183 self._config_parser.add_service_file(item, self) 184 185 #for socket 186 if json_node.__contains__("socket"): 187 for item in json_node.get("socket"): 188 self._config_parser.add_service_socket(item, self) 189 #for jobs 190 if json_node.__contains__("jobs"): 191 self._handle_scope_jobs(json_node.get("jobs")) 192 193 #for critical 194 if json_node.__contains__("critical"): 195 critical = json_node.get("critical") 196 if isinstance(critical, list): 197 self["critical_enable"] = int(critical[0]) != 0 198 self["limit_time"] = int(critical[0]) 199 self["limit_count"] = int(critical[0]) 200 else: 201 self["critical_enable"] = int(critical) != 0 202 return 203 204 def update(self, json_node, parent=None, fileId=None): 205 self.create(json_node, parent, fileId) 206 return 207 208 def _handle_string_filed(self, json_node): 209 str_field_map = { 210 "uid" : "uid", "caps":"caps", "start_mode":"start-mode", "secon":"secon", "apl":"apl" 211 } 212 for key, name in str_field_map.items(): 213 if json_node.__contains__(name): 214 self[key] = json_node.get(name) 215 216 def _handle_integer_filed(self, json_node): 217 str_field_map = { 218 "importance" : "importance" 219 } 220 for key, name in str_field_map.items(): 221 if json_node.__contains__(name): 222 self[key] = json_node.get(name) 223 224 def _handle_bool_filed(self, json_node): 225 bool_field_map = { 226 "once" : "once", "console" : "console", "notify_state" : "notify_state", 227 "on_demand" : "ondemand", "sandbox" : "sandbox", "disabled" : "disabled", 228 "critical_enable" : "critical_enable" 229 } 230 for key, name in bool_field_map.items(): 231 if json_node.__contains__(name): 232 value = json_node.get(name) 233 if isinstance(value, bool): 234 self[key] = value 235 elif isinstance(value, int): 236 self[key] = value != 0 237 238 def _handle_array_filed(self, json_node): 239 array_field_map = { 240 "path" : "path", "gid" : "gid", "cpu_core" : "cpucore", "caps":"caps", "write_pid":"writepid", 241 "d_caps":"d-caps", "permission":"permission", "permission_acls":"permission_acls", 242 } 243 for key, name in array_field_map.items(): 244 if json_node.__contains__(name) : 245 self[key] = self.get_strings_value(json_node.get(name)) 246 247 def _handle_scope_jobs(self, json_node): 248 job_field_map = { 249 "boot_job" : "on_boot", "start_job" : "on-start", "stop_job":"on-stop", "restart_job":"on-restart" 250 } 251 for key, name in job_field_map.items(): 252 if json_node.__contains__(name): 253 self[key] = json_node.get(name) 254 self._config_parser.add_job({"name" : json_node.get(name)}, self, self["fileId"]) 255 256 257class ServiceSocketParser(ItemParser): 258 def __init__(self, config): 259 ItemParser.__init__(self, config) 260 self["family"] = "" 261 self["type"] = "" 262 self["protocol"] = "" 263 self["permissions"] = "" 264 self["uid"] = "" 265 self["gid"] = "" 266 self["serviceId"] = -1 267 268 def __repr__(self): 269 return self.__str__() 270 271 def __str__(self): 272 return "socket '%s' serviceid = %d family %s" % (self["name"], self["serviceId"], self["family"]) 273 274 def create(self, json_node, parent=None, file_id=None): 275 if not isinstance(json_node, dict): 276 raise Exception("json_node type error") 277 self["name"] = json_node["name"] 278 if parent != None: 279 self["serviceId"] = parent.get("serviceId") 280 fields = ["family", "type", "protocol", "permissions", "uid", "gid"] 281 for field in fields: 282 if json_node.get(field) : 283 self[field] = json_node.get(field) 284 if json_node.get("option") : 285 self["option"] = self.get_strings_value(json_node.get("option")) 286 287 288class ServiceFileParser(ItemParser): 289 def __init__(self, config): 290 ItemParser.__init__(self, config) 291 self["name"] = "" 292 self["content"] = "" 293 self["serviceId"] = -1 294 295 def __repr__(self): 296 return self.__str__() 297 298 def __str__(self): 299 return "file '%s' serviceid = %d content '%s'" % (self["name"], self["serviceId"], self["content"]) 300 301 def create(self, json_node, parent=None, file_id=None): 302 if not isinstance(json_node, str): 303 raise Exception("json_node type error") 304 if parent != None: 305 self["serviceId"] = parent.get("serviceId") 306 info = json_node.partition(" ") 307 self["name"] = info[0] 308 if len(info) > 2: 309 self["content"] = info[2] 310 return 311 312 313class ConfigParser(): 314 def __init__(self, path): 315 self._path = path 316 self._jobs = {} 317 self._files = {} 318 self._cmds = [] 319 self._services = {} 320 self._service_sockets = {} 321 self._service_files = {} 322 self._job_id = 0 323 self._file_id = 0 324 self._service_id = 0 325 self._selinux = "" 326 self._plug_in = [] 327 328 def load_config(self, file_name): 329 path = self._path + file_name 330 if not os.path.exists(path): 331 print("Error, invalid config file %s" % path) 332 return 333 with open(path, encoding='utf-8') as content: 334 try: 335 root = json.load(content) 336 file_id = self.add_file(file_name) 337 if not isinstance(root, dict): 338 raise Exception("root type error") 339 if (root.__contains__("services")): 340 self._load_services(root["services"], file_id) 341 if (root.__contains__("jobs")): 342 self._load_jobs(root["jobs"], file_id) 343 if (root.__contains__("import")): 344 self._load_import(root["import"]) 345 pass 346 except: 347 pass 348 349 def add_file(self, file_name): 350 if self._files.get(file_name): 351 return self._files.get(file_name).get("fileId") 352 self._file_id = self._file_id + 1 353 self._files[file_name] = { 354 "fileId" : self._file_id, 355 "file_name" : file_name 356 } 357 return self._files[file_name].get("fileId") 358 359 def add_job(self, item, service, file_id): 360 if self._jobs.get(item.get("name")): 361 self._jobs.get(item.get("name")).update(item, service, file_id) 362 return 363 parser = JobParser(self) 364 parser.create(item, service, file_id) 365 self._jobs[parser.get_name()] = parser 366 367 def add_cmd(self, item, job, file_id): 368 parser = CmdParser(self) 369 parser.create(item, job, file_id) 370 self._cmds.append(parser) 371 372 def add_service(self, item, file_id): 373 if self._services.get(item.get("name")): 374 self._services.get(item.get("name")).update(item) 375 return 376 parser = ServiceParser(self) 377 parser.create(item, None, file_id) 378 self._services[parser.get("name")] = parser 379 380 def add_service_socket(self, item, service): 381 parser = ServiceSocketParser(self) 382 parser.create(item, service) 383 self._service_sockets[parser.get_name()] = parser 384 385 def add_service_file(self, item, service): 386 parser = ServiceFileParser(self) 387 parser.create(item, service) 388 self._service_files[parser.get_name()] = parser 389 390 def get_job_id(self): 391 self._job_id = self._job_id + 1 392 return self._job_id 393 394 def get_service_id(self): 395 self._service_id = self._service_id + 1 396 return self._service_id 397 398 def dump_config(self): 399 pp = pprint.PrettyPrinter(indent=0, compact=True) 400 pp.pprint(self._jobs) 401 pass 402 403 def scan_library(self, target_cpu): 404 if target_cpu == "arm64": 405 config_paths = [ 406 "system/lib64/init", 407 "chip_prod/lib64/init", 408 "sys_prod/lib64/init", 409 "vendor/lib64/init", 410 ] 411 else: 412 config_paths = [ 413 "system/lib/init", 414 "chip_prod/lib/init", 415 "sys_prod/lib/init", 416 "vendor/lib/init", 417 ] 418 for file_name in config_paths: 419 dir_config_file = os.path.join(self._path, file_name) 420 self._scan_share_library_file(dir_config_file) 421 422 def scan_config(self): 423 config_paths = [ 424 "system/etc/init", 425 "chip_prod/etc/init", 426 "sys_prod/etc/init", 427 "vendor/etc/init", 428 ] 429 for file_name in config_paths: 430 self._scan_config_file(file_name) 431 432 def get_job_priority(self, job_name): 433 job_priority = { 434 "pre-init" : 0, 435 "init" : 1, 436 "post-init" : 2, 437 "early-fs" : 3, 438 "fs" : 4, 439 "post-fs" : 5, 440 "late-fs" : 6, 441 "post-fs-data" : 7, 442 "firmware_mounts_complete" : 8, 443 "early-boot" : 9, 444 "boot" : 10 445 } 446 447 if (job_priority.__contains__(job_name)): 448 return job_priority.get(job_name) 449 return 100 450 451 def load_boot_event_file(self, boot_event_file): 452 if not os.path.exists(boot_event_file): 453 print("Error, invalid config file %s" % boot_event_file) 454 return 455 with open(boot_event_file, encoding='utf-8') as content: 456 try: 457 root = json.load(content) 458 for item in root: 459 self._load_boot_event(item) 460 except: 461 pass 462 pass 463 464 def load_selinux_config(self, file_name): 465 path = os.path.join(self._path, file_name) 466 if not os.path.exists(path): 467 print("Error, invalid selinux config path %s" % path) 468 return 469 try: 470 with open(path, encoding='utf-8') as fp: 471 line = fp.readline() 472 while line : 473 if line.startswith("#") or len(line) < 3: 474 line = fp.readline() 475 continue 476 param_info = line.partition("=") 477 if len(param_info) != 3: 478 line = fp.readline() 479 continue 480 if param_info[0].strip() == "SELINUX": 481 self._selinux = param_info[2].strip() 482 line = fp.readline() 483 except: 484 print("Error, invalid parameter file ", file_name) 485 pass 486 487 def _load_services(self, json_node, file_id): 488 if not isinstance(json_node, list): 489 raise Exception("json_node type error") 490 for item in json_node: 491 self.add_service(item, file_id) 492 return 493 494 def _load_jobs(self, json_node, file_id): 495 if not isinstance(json_node, list): 496 raise Exception("json_node type error") 497 for item in json_node: 498 self.add_job(item, None, file_id) 499 return 500 501 def _load_import(self, import_node): 502 if not isinstance(import_node, list): 503 raise Exception("import_node type error") 504 start_with = ["/system", "/chip_prod", "/sys_prod", "/vendor"] 505 for file in import_node: 506 found = False 507 for start in start_with: 508 if file.startswith(start): 509 found = True 510 break 511 if found : 512 self.load_config(self._path + file) 513 else: 514 for start in start_with: 515 self.load_config(self._path + start + file, file) 516 517 def _is_valid_file(self, file, valid_file_ext): 518 if not file.is_file(): 519 return False 520 for ext in valid_file_ext: 521 if file.name.endswith(ext): 522 return True 523 return False 524 525 def _scan_config_file(self, file_name): 526 dir_config_file = os.path.join(self._path, file_name) 527 if not os.path.exists(dir_config_file): 528 return 529 try: 530 with os.scandir(dir_config_file) as files: 531 for file in files: 532 if self._is_valid_file(file, ".cfg"): 533 name = file.path[len(self._path) :] 534 self.load_config(name) 535 except: 536 pass 537 538 def _scan_share_library_file(self, file_name): 539 if not os.path.exists(file_name): 540 return 541 try: 542 for item in os.scandir(file_name): 543 file_path = os.path.join(file_name, item.name) 544 if item.is_file(): 545 if self._is_valid_file(item, ".so"): 546 self._plug_in.append(file_path) 547 continue 548 else: 549 dir_config_file = os.path.join(file_name, item.name) 550 self._scan_share_library_file(item) 551 except: 552 pass 553 554 def _load_boot_event(self, event): 555 if self._jobs.__contains__(event.get("name")): 556 print("loadBootEvent_ %s %f" % (event.get("name"), event.get("dur"))) 557 self._jobs.get(event.get("name"))["executionTime"] = event.get("dur") 558 559 560def startup_config_collect(base_path, target_cpu): 561 parser = ConfigParser(os.path.join(base_path, "packages/phone")) 562 parser.load_config("/system/etc/init.cfg") 563 parser.scan_library(target_cpu) 564 parser.scan_config() 565 parser.load_selinux_config("system/etc/selinux/config") 566 return parser 567 568if __name__ == '__main__': 569 args_parser = _create_arg_parser() 570 options = args_parser.parse_args() 571 startup_config_collect(options.input) 572