openubmc-bingo 0.6.45__py3-none-any.whl → 0.6.99__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bmcgo/__init__.py +1 -1
- bmcgo/bmcgo.py +9 -3
- bmcgo/bmcgo_config.py +16 -0
- bmcgo/cli/cli.py +72 -21
- bmcgo/codegen/__init__.py +1 -1
- bmcgo/codegen/lua/codegen.py +2 -2
- bmcgo/codegen/lua/script/check_intfs.py +1 -0
- bmcgo/codegen/lua/script/dto/options.py +1 -0
- bmcgo/codegen/lua/script/gen_db_json.py +4 -3
- bmcgo/codegen/lua/script/gen_rpc_msg_json.py +78 -11
- bmcgo/codegen/lua/script/model_consistency_check.py +1 -1
- bmcgo/codegen/lua/script/render_utils/db_lua.py +5 -6
- bmcgo/codegen/lua/script/render_utils/model_lua.py +5 -1
- bmcgo/codegen/lua/script/template.py +5 -0
- bmcgo/codegen/lua/script/utils.py +50 -8
- bmcgo/codegen/lua/templates/apps/Makefile +2 -2
- bmcgo/codegen/lua/templates/apps/client.lua.mako +1 -1
- bmcgo/codegen/lua/templates/apps/model.lua.mako +4 -3
- bmcgo/codegen/lua/templates/apps/service.lua.mako +1 -1
- bmcgo/codegen/lua/templates/apps/utils/mdb_intf.lua.mako +4 -0
- bmcgo/codegen/lua/templates/new_app_v2/CMakeLists.txt.mako +26 -0
- bmcgo/codegen/lua/templates/new_app_v2/conanfile.py.mako +9 -0
- bmcgo/codegen/lua/v1/script/render_utils/db_lua.py +5 -6
- bmcgo/codegen/lua/v1/script/render_utils/model_lua.py +13 -1
- bmcgo/codegen/lua/v1/templates/apps/client.lua.mako +1 -1
- bmcgo/codegen/lua/v1/templates/apps/local_db.lua.mako +0 -4
- bmcgo/codegen/lua/v1/templates/apps/message.lua.mako +3 -0
- bmcgo/codegen/lua/v1/templates/apps/model.lua.mako +3 -0
- bmcgo/codegen/lua/v1/templates/apps/utils/mdb_intf.lua.mako +6 -4
- bmcgo/component/analysis/analysis.py +9 -4
- bmcgo/component/analysis/dep-rules.json +20 -8
- bmcgo/component/analysis/dep_node.py +2 -0
- bmcgo/component/analysis/intf_validation.py +8 -7
- bmcgo/component/analysis/sr_validation.py +5 -4
- bmcgo/component/busctl_log_parse/busctl_log_parser.py +809 -0
- bmcgo/component/busctl_log_parse/mock_data_save.py +170 -0
- bmcgo/component/busctl_log_parse/test_data_save.py +49 -0
- bmcgo/component/component_helper.py +29 -0
- bmcgo/component/coverage/incremental_cov.py +5 -0
- bmcgo/component/fixture/__init__.py +29 -0
- bmcgo/component/fixture/auto_case_generator.py +490 -0
- bmcgo/component/fixture/busctl_type_converter.py +1081 -0
- bmcgo/component/fixture/common_config.py +15 -0
- bmcgo/component/fixture/dbus_gateway.py +669 -0
- bmcgo/component/fixture/dbus_library.py +250 -0
- bmcgo/component/fixture/dbus_mock_utils.py +514 -0
- bmcgo/component/fixture/dbus_response_handler.py +138 -0
- bmcgo/component/fixture/dbus_signature.py +110 -0
- bmcgo/component/template_v2/conanbase.py.mako +1 -5
- bmcgo/component/test.py +69 -10
- bmcgo/error_analyzer/__init__.py +0 -0
- bmcgo/error_analyzer/case_matcher.py +114 -0
- bmcgo/error_analyzer/log_parser.py +128 -0
- bmcgo/error_analyzer/unified_error_analyzer.py +359 -0
- bmcgo/error_cases/cases.yml +59 -0
- bmcgo/error_cases/cases_template_valid.json +71 -0
- bmcgo/error_cases/conanfile.py +58 -0
- bmcgo/frame.py +0 -4
- bmcgo/functional/analysis.py +18 -12
- bmcgo/functional/bmc_studio_action.py +21 -10
- bmcgo/functional/check.py +86 -42
- bmcgo/functional/conan_index_build.py +1 -1
- bmcgo/functional/config.py +22 -18
- bmcgo/functional/csr_build.py +63 -34
- bmcgo/functional/deploy.py +4 -3
- bmcgo/functional/diff.py +51 -34
- bmcgo/functional/full_component.py +16 -5
- bmcgo/functional/hpm_signer.py +484 -0
- bmcgo/functional/new.py +8 -2
- bmcgo/functional/schema_valid.py +111 -15
- bmcgo/functional/upgrade.py +6 -6
- bmcgo/misc.py +1 -0
- bmcgo/tasks/task_build_conan.py +27 -6
- bmcgo/tasks/task_build_rootfs_img.py +120 -83
- bmcgo/tasks/task_buildgppbin.py +30 -13
- bmcgo/tasks/task_buildhpm_ext4.py +5 -3
- bmcgo/tasks/task_download_buildtools.py +20 -11
- bmcgo/tasks/task_download_dependency.py +29 -20
- bmcgo/tasks/task_hpm_envir_prepare.py +32 -53
- bmcgo/tasks/task_packet_to_supporte.py +12 -4
- bmcgo/tasks/task_prepare.py +1 -1
- bmcgo/tasks/task_sign_and_pack_hpm.py +15 -7
- bmcgo/utils/component_version_check.py +4 -4
- bmcgo/utils/config.py +3 -0
- bmcgo/utils/fetch_component_code.py +148 -17
- bmcgo/utils/install_manager.py +2 -2
- bmcgo/utils/installations/base_installer.py +10 -27
- bmcgo/utils/installations/install_plans/studio.yml +3 -0
- bmcgo/utils/mapping_config_patch.py +5 -4
- bmcgo/utils/tools.py +49 -7
- {openubmc_bingo-0.6.45.dist-info → openubmc_bingo-0.6.99.dist-info}/METADATA +1 -1
- {openubmc_bingo-0.6.45.dist-info → openubmc_bingo-0.6.99.dist-info}/RECORD +95 -74
- bmcgo/tasks/download_buildtools_hm.py +0 -124
- {openubmc_bingo-0.6.45.dist-info → openubmc_bingo-0.6.99.dist-info}/WHEEL +0 -0
- {openubmc_bingo-0.6.45.dist-info → openubmc_bingo-0.6.99.dist-info}/entry_points.txt +0 -0
- {openubmc_bingo-0.6.45.dist-info → openubmc_bingo-0.6.99.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# encoding=utf-8
|
|
3
|
+
# 描述:HPM 文件重签名功能
|
|
4
|
+
# Copyright (c) 2025 Huawei Technologies Co., Ltd.
|
|
5
|
+
# openUBMC is licensed under Mulan PSL v2.
|
|
6
|
+
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
7
|
+
# You may obtain a copy of Mulan PSL v2 at:
|
|
8
|
+
# http://license.coscl.org.cn/MulanPSL2
|
|
9
|
+
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
10
|
+
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
11
|
+
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
12
|
+
# See the Mulan PSL v2 for more details.
|
|
13
|
+
|
|
14
|
+
import os
|
|
15
|
+
import struct
|
|
16
|
+
import subprocess
|
|
17
|
+
import shutil
|
|
18
|
+
import argparse
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
from bmcgo import misc
|
|
22
|
+
from bmcgo.utils.tools import Tools
|
|
23
|
+
from bmcgo.bmcgo_config import BmcgoConfig
|
|
24
|
+
|
|
25
|
+
tools = Tools("HPMResigner")
|
|
26
|
+
logger = tools.log
|
|
27
|
+
|
|
28
|
+
#========== 可调默认项 ==========
|
|
29
|
+
CMD = "bingo"
|
|
30
|
+
# DEFAULT_CA_DIR = "/root/ca" # 证书目录默认值(可被 --ca-dir 覆盖)
|
|
31
|
+
DEFAULT_CA_DIR = str(Path.home() / "ca") # 证书目录默认位于用户家目录下
|
|
32
|
+
DEFAULT_OUT_SUFFIX = "_signed.hpm" # 输出文件名默认后缀(可按需改)
|
|
33
|
+
TMP_DIR_NAME = "hpm_tmp" # 源文件同目录下的临时目录名
|
|
34
|
+
|
|
35
|
+
command_info: misc.CommandInfo = misc.CommandInfo(
|
|
36
|
+
group=misc.GRP_MISC,
|
|
37
|
+
name="hpm_signer",
|
|
38
|
+
description=["HPM 文件重签名"],
|
|
39
|
+
hidden=False
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def if_available(bconfig: BmcgoConfig):
|
|
44
|
+
return True
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def get_desc(cmd):
|
|
48
|
+
return f"""
|
|
49
|
+
HPM 文件重签名工具:
|
|
50
|
+
|
|
51
|
+
>> {cmd} hpm_resign <HPM文件> [--ca-dir /path/to/ca] [--out new_name.hpm] [--keep-tmp] [--signer-pass <签名证书加密密码>]
|
|
52
|
+
|
|
53
|
+
说明:
|
|
54
|
+
1) 请确保已安装 hpm_verify 和 hpm_signer 工具
|
|
55
|
+
2) 证书目录默认位于用户家目录 ca 目录下 (可使用 --ca-dir 参数指定其他目录)
|
|
56
|
+
3) 证书目录(--ca-dir)下需包含: rootca.der, rootca.crl, signer.pem, (可选) ts_signer.pem, tsa.cnf
|
|
57
|
+
4) 如果签名证书已加密,需要使用 --signer-pass 参数提供密码
|
|
58
|
+
5) 临时文件与输出文件默认位于源 HPM 文件同目录
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class BmcgoCommand:
|
|
63
|
+
def __init__(self, bconfig: BmcgoConfig, *args):
|
|
64
|
+
self.bconfig = bconfig
|
|
65
|
+
parser = argparse.ArgumentParser(
|
|
66
|
+
prog=f"{CMD} HPM重签名",
|
|
67
|
+
description=get_desc(CMD),
|
|
68
|
+
add_help=True,
|
|
69
|
+
formatter_class=argparse.RawTextHelpFormatter
|
|
70
|
+
)
|
|
71
|
+
parser.add_argument(
|
|
72
|
+
"hpm_file",
|
|
73
|
+
type=Path,
|
|
74
|
+
help="HPM文件路径"
|
|
75
|
+
)
|
|
76
|
+
parser.add_argument(
|
|
77
|
+
"--keep-tmp",
|
|
78
|
+
action="store_true",
|
|
79
|
+
help="保留临时文件"
|
|
80
|
+
)
|
|
81
|
+
parser.add_argument(
|
|
82
|
+
"--ca-dir",
|
|
83
|
+
type=Path,
|
|
84
|
+
default=Path(DEFAULT_CA_DIR),
|
|
85
|
+
help=f"证书目录(默认: {DEFAULT_CA_DIR})"
|
|
86
|
+
)
|
|
87
|
+
parser.add_argument(
|
|
88
|
+
"--out",
|
|
89
|
+
type=str,
|
|
90
|
+
default=None,
|
|
91
|
+
help=f"输出文件名(默认: <源名>{DEFAULT_OUT_SUFFIX})"
|
|
92
|
+
)
|
|
93
|
+
parser.add_argument(
|
|
94
|
+
"--signer-pass",
|
|
95
|
+
type=str,
|
|
96
|
+
default=None,
|
|
97
|
+
help="签名证书的加密密码(如果证书已加密)"
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
self.args, self.kwargs = parser.parse_known_args(*args)
|
|
101
|
+
self.logger = tools.log
|
|
102
|
+
|
|
103
|
+
# 运行期路径属性(在 run/main 中初始化)
|
|
104
|
+
self.base_dir: Path = None
|
|
105
|
+
self.tmp_dir: Path = None
|
|
106
|
+
|
|
107
|
+
@staticmethod
|
|
108
|
+
def write_ascii_hex(fp, value: int):
|
|
109
|
+
"""按 8 位 0 补齐十六进制,写入 ASCII 字符串"""
|
|
110
|
+
fp.write(f"{value:08x}".encode())
|
|
111
|
+
|
|
112
|
+
def run(self):
|
|
113
|
+
hpm_file = self.args.hpm_file
|
|
114
|
+
if not hpm_file.exists():
|
|
115
|
+
self.logger.error(f"ERROR: 文件 {hpm_file} 不存在")
|
|
116
|
+
return 1
|
|
117
|
+
|
|
118
|
+
# 源文件同目录
|
|
119
|
+
self.base_dir = hpm_file.parent.resolve()
|
|
120
|
+
# 临时目录放在源文件同目录
|
|
121
|
+
self.tmp_dir = (self.base_dir / TMP_DIR_NAME).resolve()
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
# 在主要操作前进行权限检查
|
|
125
|
+
if not self.check_permissions(hpm_file):
|
|
126
|
+
self.logger.error("权限检查失败,程序退出")
|
|
127
|
+
return 1
|
|
128
|
+
|
|
129
|
+
self.main(hpm_file)
|
|
130
|
+
return 0
|
|
131
|
+
except Exception as e:
|
|
132
|
+
self.logger.error(f"处理过程中发生错误: {str(e)}")
|
|
133
|
+
import traceback
|
|
134
|
+
self.logger.debug(f"详细堆栈: {traceback.format_exc()}")
|
|
135
|
+
return 1
|
|
136
|
+
finally:
|
|
137
|
+
if not self.args.keep_tmp:
|
|
138
|
+
self.clear_temp()
|
|
139
|
+
|
|
140
|
+
def check_permissions(self, hpm_file: Path) -> bool:
|
|
141
|
+
"""检查必要的文件和目录权限"""
|
|
142
|
+
self.logger.info(">>> 开始权限检查...")
|
|
143
|
+
|
|
144
|
+
# 检查源文件读取权限
|
|
145
|
+
if not os.access(hpm_file, os.R_OK):
|
|
146
|
+
self.logger.error(f"ERROR: 无读取权限: {hpm_file}")
|
|
147
|
+
return False
|
|
148
|
+
self.logger.info(f"SUCCESS: 源文件可读: {hpm_file}")
|
|
149
|
+
|
|
150
|
+
# 检查源文件所在目录的写入权限(用于创建临时目录和输出文件)
|
|
151
|
+
if not os.access(self.base_dir, os.W_OK):
|
|
152
|
+
self.logger.error(f"ERROR: 无写入权限: {self.base_dir}")
|
|
153
|
+
return False
|
|
154
|
+
self.logger.info(f"SUCCESS: 目录可写: {self.base_dir}")
|
|
155
|
+
|
|
156
|
+
# 检查证书目录权限
|
|
157
|
+
ca_dir = self.args.ca_dir.resolve()
|
|
158
|
+
if not ca_dir.exists():
|
|
159
|
+
self.logger.error(f"ERROR: 证书目录不存在: {ca_dir}")
|
|
160
|
+
return False
|
|
161
|
+
|
|
162
|
+
if not os.access(ca_dir, os.R_OK):
|
|
163
|
+
self.logger.error(f"ERROR: 无读取权限: {ca_dir}")
|
|
164
|
+
return False
|
|
165
|
+
self.logger.info(f"SUCCESS: 证书目录可读: {ca_dir}")
|
|
166
|
+
|
|
167
|
+
# 检查必要的证书文件
|
|
168
|
+
required_ca_files = [
|
|
169
|
+
("rootca.der", "根证书"),
|
|
170
|
+
("rootca.crl", "CRL文件"),
|
|
171
|
+
("signer.pem", "签名证书")
|
|
172
|
+
]
|
|
173
|
+
|
|
174
|
+
for filename, desc in required_ca_files:
|
|
175
|
+
file_path = ca_dir / filename
|
|
176
|
+
if not file_path.exists():
|
|
177
|
+
self.logger.error(f"ERROR: {desc}不存在: {file_path}")
|
|
178
|
+
return False
|
|
179
|
+
if not os.access(file_path, os.R_OK):
|
|
180
|
+
self.logger.error(f"ERROR: 无读取权限: {file_path}")
|
|
181
|
+
return False
|
|
182
|
+
self.logger.info(f"SUCCESS: {desc}可读: {filename}")
|
|
183
|
+
|
|
184
|
+
# 检查可选的时间戳证书文件
|
|
185
|
+
optional_files = [
|
|
186
|
+
("ts_signer.pem", "时间戳证书"),
|
|
187
|
+
("tsa.cnf", "时间戳配置")
|
|
188
|
+
]
|
|
189
|
+
|
|
190
|
+
for filename, desc in optional_files:
|
|
191
|
+
file_path = ca_dir / filename
|
|
192
|
+
if file_path.exists():
|
|
193
|
+
if not os.access(file_path, os.R_OK):
|
|
194
|
+
self.logger.warning(f"WARNING: 无读取权限(可选): {file_path}")
|
|
195
|
+
else:
|
|
196
|
+
self.logger.info(f"SUCCESS: {desc}可读: {filename}")
|
|
197
|
+
else:
|
|
198
|
+
self.logger.warning(f"WARNING: {desc}不存在(可选): {filename}")
|
|
199
|
+
|
|
200
|
+
# 检查输出文件权限(如果文件已存在)
|
|
201
|
+
out_name = self.args.out if self.args.out else f"{hpm_file.stem}{DEFAULT_OUT_SUFFIX}"
|
|
202
|
+
new_hpm = self.base_dir / out_name
|
|
203
|
+
if new_hpm.exists():
|
|
204
|
+
if not os.access(new_hpm, os.W_OK):
|
|
205
|
+
self.logger.error(f"ERROR: 输出文件已存在且无写入权限: {new_hpm}")
|
|
206
|
+
return False
|
|
207
|
+
self.logger.info(f"SUCCESS: 输出文件可覆盖: {new_hpm}")
|
|
208
|
+
|
|
209
|
+
self.logger.info(">>> 权限检查通过!")
|
|
210
|
+
return True
|
|
211
|
+
|
|
212
|
+
def main(self, hpm_file: Path):
|
|
213
|
+
# 创建必要目录(源目录下)
|
|
214
|
+
self.tmp_dir.mkdir(parents=True, exist_ok=True)
|
|
215
|
+
|
|
216
|
+
base_name = hpm_file.stem
|
|
217
|
+
self.logger.info(f"源文件: {hpm_file}")
|
|
218
|
+
|
|
219
|
+
# 计算默认输出文件路径(同目录)
|
|
220
|
+
out_name = self.args.out if self.args.out else f"{base_name}{DEFAULT_OUT_SUFFIX}"
|
|
221
|
+
new_hpm = (self.base_dir / out_name).resolve()
|
|
222
|
+
|
|
223
|
+
# 1. 解析HPM文件头
|
|
224
|
+
self.logger.info("\n>>> 开始解析HPM文件...")
|
|
225
|
+
with open(hpm_file, 'rb') as f:
|
|
226
|
+
header_data = f.read(56)
|
|
227
|
+
if len(header_data) != 56:
|
|
228
|
+
self.logger.error(f"ERROR: 文件太小 ({len(header_data)}字节),无法解析头部信息!")
|
|
229
|
+
return
|
|
230
|
+
|
|
231
|
+
try:
|
|
232
|
+
header_bin = bytes.fromhex(header_data.decode('ascii'))
|
|
233
|
+
except Exception as e:
|
|
234
|
+
raise RuntimeError("头部数据不是合法的 ASCII Hex") from e
|
|
235
|
+
|
|
236
|
+
if len(header_bin) != 28:
|
|
237
|
+
raise RuntimeError(f"头部数据长度不合法 {len(header_bin)},期望 56")
|
|
238
|
+
|
|
239
|
+
header_ints = struct.unpack('>7I', header_bin)
|
|
240
|
+
magic, section_count, filelist_len, _, cms_len, _, crl_len = header_ints
|
|
241
|
+
|
|
242
|
+
skip1 = 56
|
|
243
|
+
skip2 = skip1 + filelist_len
|
|
244
|
+
skip3 = skip2 + cms_len
|
|
245
|
+
skip4 = skip3 + crl_len
|
|
246
|
+
|
|
247
|
+
self.logger.info(f"\n>>> 开始计算偏移量...")
|
|
248
|
+
|
|
249
|
+
# 2. 提取各部分(放到源目录的 tmp 下)
|
|
250
|
+
self.logger.info(f"\n>>> 开始提取文件...")
|
|
251
|
+
|
|
252
|
+
filelist_path = self.tmp_dir / f"{base_name}.filelist"
|
|
253
|
+
cms_path = self.tmp_dir / f"{base_name}.cms"
|
|
254
|
+
crl_path = self.tmp_dir / f"{base_name}.crl"
|
|
255
|
+
bin_path = self.tmp_dir / f"{base_name}.bin"
|
|
256
|
+
|
|
257
|
+
if self.extract_part(hpm_file, skip1, filelist_len, filelist_path):
|
|
258
|
+
self.logger.info(f"SUCCESS: 成功提取filelist到 {filelist_path}")
|
|
259
|
+
if self.extract_part(hpm_file, skip2, cms_len, cms_path):
|
|
260
|
+
self.logger.info(f"SUCCESS: 成功提取cms到 {cms_path}")
|
|
261
|
+
if self.extract_part(hpm_file, skip3, crl_len, crl_path):
|
|
262
|
+
self.logger.info(f"SUCCESS: 成功提取crl到 {crl_path}")
|
|
263
|
+
if self.extract_to_end(hpm_file, skip4, bin_path):
|
|
264
|
+
self.logger.info(f"SUCCESS: 成功提取bin到 {bin_path}")
|
|
265
|
+
|
|
266
|
+
required_files = [filelist_path, cms_path, crl_path, bin_path]
|
|
267
|
+
if not self.all_files_exist(required_files):
|
|
268
|
+
self.logger.error(f"ERROR: 部分文件提取失败!")
|
|
269
|
+
return
|
|
270
|
+
|
|
271
|
+
# --- 执行 openssl 证书和 CRL 转换(来自 --ca-dir)---
|
|
272
|
+
ca_dir: Path = self.args.ca_dir.resolve()
|
|
273
|
+
der_cert = ca_dir / "rootca.der"
|
|
274
|
+
der_crl = ca_dir / "rootca.crl"
|
|
275
|
+
pem_cert = self.tmp_dir / "rootca.pem"
|
|
276
|
+
pem_crl = self.tmp_dir / "cms.crl.pem"
|
|
277
|
+
|
|
278
|
+
# 转换 rootca.der 为 rootca.pem
|
|
279
|
+
if der_cert.exists():
|
|
280
|
+
cmd_cert = [
|
|
281
|
+
"openssl", "x509",
|
|
282
|
+
"-in", str(der_cert),
|
|
283
|
+
"-inform", "der",
|
|
284
|
+
"-outform", "pem",
|
|
285
|
+
"-out", str(pem_cert)
|
|
286
|
+
]
|
|
287
|
+
self.logger.info("\n>>> " + " ".join(cmd_cert))
|
|
288
|
+
try:
|
|
289
|
+
subprocess.run(cmd_cert, check=True)
|
|
290
|
+
self.logger.info(f"SUCCESS: 生成 PEM 证书 {pem_cert}")
|
|
291
|
+
except Exception as e:
|
|
292
|
+
self.logger.error(f"ERROR: 证书转换失败: {e}")
|
|
293
|
+
else:
|
|
294
|
+
self.logger.error(f"ERROR: 未找到 DER 证书文件 {der_cert}")
|
|
295
|
+
|
|
296
|
+
# 转换 rootca.crl 为 cms.crl.pem
|
|
297
|
+
if der_crl.exists():
|
|
298
|
+
cmd_crl = [
|
|
299
|
+
"openssl", "crl",
|
|
300
|
+
"-in", str(der_crl),
|
|
301
|
+
"-inform", "der",
|
|
302
|
+
"-outform", "pem",
|
|
303
|
+
"-out", str(pem_crl)
|
|
304
|
+
]
|
|
305
|
+
self.logger.info(">>> " + " ".join(cmd_crl))
|
|
306
|
+
try:
|
|
307
|
+
subprocess.run(cmd_crl, check=True)
|
|
308
|
+
self.logger.info(f"SUCCESS: 生成 PEM CRL {pem_crl}")
|
|
309
|
+
except Exception as e:
|
|
310
|
+
self.logger.error(f"ERROR: CRL转换失败: {e}")
|
|
311
|
+
else:
|
|
312
|
+
self.logger.error(f"ERROR: 未找到 DER CRL 文件 {der_crl}")
|
|
313
|
+
|
|
314
|
+
# 3. 重新签名filelist
|
|
315
|
+
self.logger.info(f"\n>>> 开始重新签名...")
|
|
316
|
+
signer_pem = ca_dir / "signer.pem"
|
|
317
|
+
ts_signer_pem = ca_dir / "ts_signer.pem"
|
|
318
|
+
tsa_cnf = ca_dir / "tsa.cnf"
|
|
319
|
+
cms1_path = self.tmp_dir / f"{base_name}.cms1"
|
|
320
|
+
|
|
321
|
+
if not signer_pem.exists():
|
|
322
|
+
self.logger.error(f"ERROR: 签名证书 {signer_pem} 不存在")
|
|
323
|
+
return
|
|
324
|
+
|
|
325
|
+
# 构建签名命令 - 只在时间戳证书存在时添加时间戳参数
|
|
326
|
+
cmd = [
|
|
327
|
+
"hpm_signer",
|
|
328
|
+
"-s", str(signer_pem),
|
|
329
|
+
"-i", str(filelist_path),
|
|
330
|
+
"-o", str(cms1_path)
|
|
331
|
+
]
|
|
332
|
+
|
|
333
|
+
# 只有在时间戳证书和配置文件都存在时才添加时间戳参数
|
|
334
|
+
if ts_signer_pem.exists() and tsa_cnf.exists():
|
|
335
|
+
cmd.extend(["-t", str(ts_signer_pem), "-T", str(tsa_cnf)])
|
|
336
|
+
self.logger.info(">>> 使用时间戳签名")
|
|
337
|
+
else:
|
|
338
|
+
self.logger.info(">>> 使用基础签名(无时间戳)")
|
|
339
|
+
|
|
340
|
+
# 添加密码参数(如果提供了密码)
|
|
341
|
+
if self.args.signer_pass:
|
|
342
|
+
cmd.extend(["-p", self.args.signer_pass])
|
|
343
|
+
self.logger.info(">>> 使用加密签名证书")
|
|
344
|
+
|
|
345
|
+
self.logger.info(">>> " + " ".join(cmd))
|
|
346
|
+
|
|
347
|
+
try:
|
|
348
|
+
subprocess.run(cmd, check=True)
|
|
349
|
+
self.logger.info(f">>> SUCCESS: 重新签名成功!")
|
|
350
|
+
# --- 签名成功后自动验证 ---
|
|
351
|
+
self.logger.info(f"\n>>> 开始验证签名...")
|
|
352
|
+
verify_cmd = [
|
|
353
|
+
"hpm_verify",
|
|
354
|
+
"-r", str(pem_cert),
|
|
355
|
+
"-C", str(pem_crl),
|
|
356
|
+
"-c", str(filelist_path),
|
|
357
|
+
"-s", str(cms1_path)
|
|
358
|
+
]
|
|
359
|
+
self.logger.info(">>> " + " ".join(verify_cmd))
|
|
360
|
+
try:
|
|
361
|
+
subprocess.run(verify_cmd, check=True)
|
|
362
|
+
self.logger.info(f">>> SUCCESS: 签名验证通过!")
|
|
363
|
+
except subprocess.CalledProcessError:
|
|
364
|
+
self.logger.error(f">>> ERROR: 签名验证失败!")
|
|
365
|
+
except FileNotFoundError:
|
|
366
|
+
self.logger.error(f">>> ERROR: hpm_verify 命令未找到")
|
|
367
|
+
except subprocess.CalledProcessError:
|
|
368
|
+
self.logger.error(f">>> ERROR: 重新签名失败!")
|
|
369
|
+
return
|
|
370
|
+
except FileNotFoundError:
|
|
371
|
+
self.logger.error(f">>> ERROR: hpm_signer 命令未找到")
|
|
372
|
+
return
|
|
373
|
+
|
|
374
|
+
# 4. 重新打包HPM文件(输出到源目录)
|
|
375
|
+
self.logger.info(f"\n>>> 开始重新打包HPM文件...")
|
|
376
|
+
rootca_crl = der_crl
|
|
377
|
+
filelist_size = filelist_path.stat().st_size
|
|
378
|
+
cms1_size = cms1_path.stat().st_size
|
|
379
|
+
crl_size = rootca_crl.stat().st_size
|
|
380
|
+
bin_size = bin_path.stat().st_size
|
|
381
|
+
|
|
382
|
+
# 检查输出文件写入权限
|
|
383
|
+
try:
|
|
384
|
+
with open(new_hpm, "wb") as out_f:
|
|
385
|
+
# 写入头部 (ASCII hex)
|
|
386
|
+
out_f.write(b"00000003") # magic
|
|
387
|
+
out_f.write(b"00000001") # section_count
|
|
388
|
+
self.write_ascii_hex(out_f, filelist_size) # filelist长度
|
|
389
|
+
out_f.write(b"00000002")
|
|
390
|
+
self.write_ascii_hex(out_f, cms1_size) # cms长度
|
|
391
|
+
out_f.write(b"00000003")
|
|
392
|
+
self.write_ascii_hex(out_f, crl_size) # crl长度
|
|
393
|
+
|
|
394
|
+
# 依次写入各部分内容
|
|
395
|
+
for part in [filelist_path, cms1_path, rootca_crl, bin_path]:
|
|
396
|
+
with open(part, "rb") as pf:
|
|
397
|
+
shutil.copyfileobj(pf, out_f)
|
|
398
|
+
|
|
399
|
+
self.logger.info(f">>> SUCCESS: 成功打包HPM文件到: {new_hpm}")
|
|
400
|
+
except PermissionError as e:
|
|
401
|
+
self.logger.error(f">>> ERROR: 无权限写入输出文件: {new_hpm}")
|
|
402
|
+
raise
|
|
403
|
+
except Exception as e:
|
|
404
|
+
self.logger.error(f">>> ERROR: 写入输出文件失败: {e}")
|
|
405
|
+
raise
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
def clear_temp(self):
|
|
409
|
+
"""清理临时目录(源目录下的 tmp)"""
|
|
410
|
+
if self.tmp_dir and self.tmp_dir.exists():
|
|
411
|
+
self.logger.info(f"\n>>> 清理临时文件和目录 {self.tmp_dir} 及同目录序列文件(serial)...")
|
|
412
|
+
try:
|
|
413
|
+
# 检查临时目录删除权限
|
|
414
|
+
if os.access(self.tmp_dir, os.W_OK):
|
|
415
|
+
shutil.rmtree(self.tmp_dir)
|
|
416
|
+
self.logger.info(f"SUCCESS: 已删除 {self.tmp_dir} 及其所有文件")
|
|
417
|
+
else:
|
|
418
|
+
self.logger.warning(f"WARNING: 无权限删除临时目录: {self.tmp_dir}")
|
|
419
|
+
|
|
420
|
+
serial_file = (self.base_dir / "serial")
|
|
421
|
+
if serial_file.exists() and serial_file.is_file():
|
|
422
|
+
if os.access(serial_file, os.W_OK):
|
|
423
|
+
serial_file.unlink()
|
|
424
|
+
self.logger.info(f"SUCCESS: 已删除序列文件: {serial_file}")
|
|
425
|
+
else:
|
|
426
|
+
self.logger.warning(f"WARNING: 无权限删除序列文件: {serial_file}")
|
|
427
|
+
except Exception as e:
|
|
428
|
+
self.logger.error(f"ERROR: 删除临时目录和文件失败: {e}")
|
|
429
|
+
else:
|
|
430
|
+
self.logger.info("INFO: 临时目录不存在,无需清理")
|
|
431
|
+
|
|
432
|
+
def extract_part(self, source: Path, skip: int, count: int, dest: Path):
|
|
433
|
+
"""提取文件的指定部分"""
|
|
434
|
+
self.logger.info(f"提取文件: {dest} (skip={skip}, count={count})")
|
|
435
|
+
cmd = ["dd", f"if={str(source)}", f"of={str(dest)}", "bs=1",
|
|
436
|
+
f"skip={skip}", f"count={count}", "status=none"]
|
|
437
|
+
try:
|
|
438
|
+
subprocess.run(cmd, check=True)
|
|
439
|
+
actual_size = dest.stat().st_size
|
|
440
|
+
if actual_size == count:
|
|
441
|
+
return True
|
|
442
|
+
else:
|
|
443
|
+
self.logger.error(f"ERROR:文件大小不匹配 ({actual_size} != {count}),文件名: {dest}")
|
|
444
|
+
return False
|
|
445
|
+
except subprocess.CalledProcessError:
|
|
446
|
+
self.logger.error(f"ERROR: 提取文件失败,文件名: {dest}")
|
|
447
|
+
return False
|
|
448
|
+
except Exception as e:
|
|
449
|
+
self.logger.error(f"ERROR: 提取文件异常,文件名: {dest},原因: {str(e)}")
|
|
450
|
+
return False
|
|
451
|
+
|
|
452
|
+
def extract_to_end(self, source: Path, skip: int, dest: Path):
|
|
453
|
+
"""提取文件从指定位置到末尾"""
|
|
454
|
+
self.logger.info(f"提取文件: {dest}")
|
|
455
|
+
total_size = source.stat().st_size
|
|
456
|
+
count = total_size - skip
|
|
457
|
+
cmd = ["dd", f"if={str(source)}", f"of={str(dest)}", "bs=1",
|
|
458
|
+
f"skip={skip}", f"count={count}", "status=none"]
|
|
459
|
+
try:
|
|
460
|
+
subprocess.run(cmd, check=True)
|
|
461
|
+
actual_size = dest.stat().st_size
|
|
462
|
+
if actual_size == count:
|
|
463
|
+
return True
|
|
464
|
+
else:
|
|
465
|
+
self.logger.warning(f"WARNING: 文件大小不匹配 ({actual_size} != {count}),文件名: {dest}")
|
|
466
|
+
return True
|
|
467
|
+
except subprocess.CalledProcessError:
|
|
468
|
+
self.logger.error(f"ERROR: 提取文件失败,文件名: {dest}")
|
|
469
|
+
return False
|
|
470
|
+
except Exception as e:
|
|
471
|
+
self.logger.error(f"ERROR: 提取文件异常,文件名: {dest},原因: {str(e)}")
|
|
472
|
+
return False
|
|
473
|
+
|
|
474
|
+
def all_files_exist(self, file_list):
|
|
475
|
+
"""检查所有文件是否存在"""
|
|
476
|
+
missing = [f for f in file_list if not Path(f).exists()]
|
|
477
|
+
if missing:
|
|
478
|
+
self.logger.error("\nERROR: 以下文件缺失:")
|
|
479
|
+
for f in missing:
|
|
480
|
+
self.logger.error(f" - {f}")
|
|
481
|
+
return False
|
|
482
|
+
self.logger.info("\nSUCCESS: 所有文件提取成功!")
|
|
483
|
+
|
|
484
|
+
return True
|
bmcgo/functional/new.py
CHANGED
|
@@ -42,11 +42,13 @@ _DEFAULT = ""
|
|
|
42
42
|
# 必选项:选项缩写,选项,选项的中文说明
|
|
43
43
|
_REQUIRES = [["n", "name", "组件名"]]
|
|
44
44
|
# 可选项:选项缩写,选项,选项的中文说明,可选值,默认值
|
|
45
|
-
_OPTIONS = [["t", "type", "组件类型", ["application"], "application"],
|
|
45
|
+
_OPTIONS = [["t", "type", "组件类型", ["application"], "application"],
|
|
46
|
+
["l", "language", "组件编程语言", ["lua"], "lua"],
|
|
47
|
+
["conan", "conan_version", "组件支持的conan版本", ["1.0", "2.0"], "1.0"]]
|
|
46
48
|
# 环境中存放bmcgo自动生成工具相对的目录,组件中存放bmcgo自动生成工具的临时相对目录,模板脚本的相对路径,模板的相对目录
|
|
47
49
|
_TEMPLATES = {
|
|
48
50
|
_LUA : {
|
|
49
|
-
_APP
|
|
51
|
+
_APP: ['codegen/lua', 'temp/lua_codegen', 'script/template.py', 'templates/new_app', 'templates/new_app_v2']
|
|
50
52
|
}
|
|
51
53
|
}
|
|
52
54
|
|
|
@@ -66,6 +68,7 @@ class BmcgoCommand:
|
|
|
66
68
|
self.name = parsed_args.name
|
|
67
69
|
self.type = parsed_args.type
|
|
68
70
|
self.language = parsed_args.language
|
|
71
|
+
self.conan_version = parsed_args.conan_version
|
|
69
72
|
self.path = os.path.join(cwd, self.name)
|
|
70
73
|
|
|
71
74
|
def run(self):
|
|
@@ -134,6 +137,9 @@ class BmcgoCommand:
|
|
|
134
137
|
|
|
135
138
|
script_file = os.path.join(gen_tool_dir, template[2])
|
|
136
139
|
template_dir = os.path.join(gen_tool_dir, template[3])
|
|
140
|
+
if self.conan_version == "2.0":
|
|
141
|
+
conan2_template_dir = os.path.join(gen_tool_dir, template[4])
|
|
142
|
+
shutil.copytree(conan2_template_dir, template_dir, dirs_exist_ok=True)
|
|
137
143
|
|
|
138
144
|
for root, _, files in os.walk(template_dir):
|
|
139
145
|
rel_dir = os.path.relpath(root, template_dir)
|