• 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 = 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