• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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