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