1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# Copyright (c) 2024 Huawei Device Co., Ltd.
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16import optparse
17import os
18import sys
19import stat
20import json
21import operator
22
23
24def parse_args(args):
25    from scripts.util import build_utils  # noqa: E402
26
27    args = build_utils.expand_file_args(args)
28
29    parser = optparse.OptionParser()
30    build_utils.add_depfile_option(parser)
31    parser.add_option('--output', help='the final install file name')
32    parser.add_option('--source-file', help='source passwd files')
33    parser.add_option('--append-file', action="append", type="string", dest="files", help='appended files')
34    parser.add_option('--append-line', action="append", type="string", dest="lines", help='appended lines')
35    parser.add_option('--input-ranges', help="uid/gid value range")
36
37    options, _ = parser.parse_args(args)
38    return options
39
40
41def load_group_file_as_dict(source_f):
42    source_dict = {}
43    for line in source_f:
44        arr = line.strip().split(":")
45        if arr:
46            key = arr[0]
47            passwd_gid_value = [arr[1], arr[2]]
48            value = [':'.join(passwd_gid_value), arr[3]]
49            source_dict[key] = value
50    return source_dict
51
52
53def insert_append_user_value(key, arr, source_dict):
54    if source_dict[key][1] and arr[3]:
55        if arr[3] not in source_dict[key][1]:
56            user_value = [source_dict[key][1], arr[3]]
57            source_dict[key][1] = ','.join(user_value)
58    elif source_dict[key][1] == " " and arr[3]:
59        source_dict[key][1] = arr[3]
60
61
62def get_append_value(src, source_dict):
63    for line in src:
64        if line != "\n":
65            arr = line.strip().split(":")
66            key = arr[0]
67            passwd_gid_value = [arr[1], arr[2]]
68            if len(arr) > 3:
69                value = [':'.join(passwd_gid_value), arr[3]]
70            else:
71                value = [':'.join(passwd_gid_value), " "]
72            if key in source_dict.keys():
73                insert_append_user_value(key, arr, source_dict)
74            else:
75                source_dict[key] = value
76
77
78def append_group_by_lines(options, source_dict):
79    for line in options.lines:
80        arr = line.strip().split(":")
81        key = arr[0]
82        passwd_gid_value = [arr[1], arr[2]]
83        if len(arr) > 3:
84            value = [':'.join(passwd_gid_value), arr[3]]
85        else:
86            value = [':'.join(passwd_gid_value), " "]
87        if key in source_dict.keys():
88            insert_append_user_value(key, arr, source_dict)
89        else:
90            source_dict[key] = value
91
92
93def append_group_by_files(options, source_dict):
94    for append_f in options.files:
95        with open(append_f, 'r') as src:
96            get_append_value(src, source_dict)
97
98
99def append_group_files(target_f, options):
100    with open(options.source_file, 'r') as source_f:
101        source_dict = load_group_file_as_dict(source_f)
102    if options.files:
103        append_group_by_files(options, source_dict)
104    if options.lines:
105        append_group_by_lines(options, source_dict)
106    for item in source_dict:
107        target_f.write(f"{item}:{':'.join(source_dict[item])}\n")
108
109
110def handle_passwd_info(passwd_info, limits):
111    is_passed = True
112    name = passwd_info[0].strip()
113    gid = int(passwd_info[3], 10)
114    uid = int(passwd_info[2], 10)
115    if gid >= int(limits[0]) and gid <= int(limits[1]):
116        pass
117    else:
118        is_passed = False
119        log_str = "error: name={} gid={} is not in range {}".format(name, gid, limits)
120        print(log_str)
121
122    if uid >= int(limits[0]) and uid <= int(limits[1]):
123        pass
124    else:
125        is_passed = False
126        log_str = "error: name={} uid={} is not in range {}".format(name, gid, limits)
127        print(log_str)
128    return is_passed
129
130
131def check_passwd_file(file_name, limits):
132    is_passed = True
133    with open(file_name, encoding='utf-8') as fp:
134        line = fp.readline()
135        while line :
136            if line.startswith("#") or len(line) < 3:
137                line = fp.readline()
138                continue
139            passwd_info = line.strip("\n").split(":")
140            if len(passwd_info) < 4:
141                line = fp.readline()
142                continue
143            if not handle_passwd_info(passwd_info, limits):
144                is_passed = False
145            line = fp.readline()
146    return is_passed
147
148
149def load_file(file_name, limit):
150
151    if not os.path.exists(file_name):
152        print("error: %s is not exit", file_name)
153        return False
154    is_passed = True
155    limits = limit.split("-")
156    try:
157        is_passed = check_passwd_file(file_name, limits)
158    except:
159        raise Exception("Exception in reading passwd, file name:", file_name)
160    return is_passed
161
162
163def append_passwd_files(target_f, options):
164    # Read source file
165    file_list = options.source_file.split(":")
166    range_list = options.input_ranges.split(":")
167
168    for i, file in enumerate(file_list):
169        if i >= len(range_list):
170            print("error: %s is error", file)
171            return
172        if not load_file(file, range_list[i]):
173            # check gid/uid Exception log: raise Exception("Exception, check passwd file error, ", file)
174            print("error: heck passwd file error, file path: ", file)
175            pass
176        try:
177            with open(file, 'r') as source_f:
178                source_contents = source_f.read()
179            target_f.write(source_contents)
180        except:
181            raise Exception("Exception in appending passwd, file name:", file)
182
183
184def main(args):
185    sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir,
186        os.pardir, os.pardir, os.pardir, os.pardir, "build"))
187    from scripts.util import build_utils  # noqa: E402
188
189    options = parse_args(args)
190    if options.files:
191        depfile_deps = ([options.source_file] + options.files)
192    else:
193        depfile_deps = ([options.source_file])
194
195    with os.fdopen(os.open(options.output, os.O_RDWR | os.O_CREAT, stat.S_IWUSR | stat.S_IRUSR), 'w') as target_f:
196        if operator.contains(options.source_file, "group"):
197            append_group_files(target_f, options)
198        else:
199            append_passwd_files(target_f, options)
200
201    build_utils.write_depfile(options.depfile,
202                options.output, depfile_deps, add_pydeps=False)
203
204
205if __name__ == '__main__':
206    sys.exit(main(sys.argv[1:]))
207