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