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