first commit
This commit is contained in:
394
main.py
Normal file
394
main.py
Normal file
@@ -0,0 +1,394 @@
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
|
||||
from pyutils import zxic_firmware_tools, check_and_delete_files, nv_compare, nv_replace, nv_sort, adb_tools
|
||||
|
||||
current_script_name = os.path.basename(__file__)
|
||||
mifi_tools_base_dirs = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
default_config_path = os.path.join(mifi_tools_base_dirs, 'configs.ini')
|
||||
|
||||
work_space_path = mifi_tools_base_dirs
|
||||
arm_tools_path = os.path.join(mifi_tools_base_dirs, "arm-tools")
|
||||
|
||||
default_firmware_name = 'full.bin'
|
||||
|
||||
# 输出目录
|
||||
output_dir_name = 'output'
|
||||
rootfs_dir_name = 'squashfs-root'
|
||||
|
||||
output_path = os.path.join(work_space_path, output_dir_name)
|
||||
|
||||
|
||||
def clear_screen():
|
||||
if os.name == 'nt': # 如果是 Windows 系统
|
||||
os.system('cls')
|
||||
else: # 如果是类 Unix 系统
|
||||
os.system('clear')
|
||||
|
||||
|
||||
def print_soft_info():
|
||||
print(f'############################### MIFI-TOOLS ##################################')
|
||||
print(f'【PS】仅支持 zxic 中兴微固件\n')
|
||||
print(f'功能菜单: 固件目录:{work_space_path} 固件名称: {default_firmware_name}')
|
||||
|
||||
|
||||
def print_author_info():
|
||||
print(f'####################### by: anysoft ##########################')
|
||||
|
||||
|
||||
def main_menu():
|
||||
sub_menu_firmware_tools()
|
||||
|
||||
|
||||
def sub_menu_firmware_tools(msg=''):
|
||||
clear_screen()
|
||||
print_soft_info()
|
||||
print('----------------------------------固件工具箱---------------------------------------')
|
||||
print(f'【环境及备份】')
|
||||
print(f'O) 设定工作目录/选择编程器固件 BACK) 备份分区及文件\n')
|
||||
|
||||
print(f'【分区拆分/合并】')
|
||||
print(f'S) 编程器固件 拆分为 mtd 分区 M) mtds 分区合并为 编程器固件\n')
|
||||
|
||||
print(f'【squashfs 镜像提取/回写】')
|
||||
print(f'R) rootfs(squashfs镜像提取) W) squashfs 格式的 rootfs 回写\n')
|
||||
# print(f' 只能从(8MB flash) 编程器固件 或 MTD4(rootfs)分区 提取squashfs格式的rootfs')
|
||||
# print(f' 不支持 16MB falsh 的固件,16MB falsh 固件本身也无需/无法提取,其rootfs分区为jffs2文件系统,直接到linux上挂载编辑即可\n')
|
||||
# print(f' 将squashfs格式的rootfs回写到 `编程器固件` 或 `mtd4` 分区\n')
|
||||
|
||||
print(f'【squashfs 解压/压缩】')
|
||||
print(f'UNPACK) `squashfs`镜像解压为`squashfs_root`目录 PACK) `squashfs_root`目录 压缩为 `squashfs`镜像文件\n')
|
||||
|
||||
print(f'【rootfs目录修改】')
|
||||
print(f'1) 对比输出文件目录树及删除特定文件')
|
||||
print(f'2) 根据 `pyutils/default_parameter_default` 修改 默认配置 `/etc_ro/default/default_parameter_*`')
|
||||
print(f'3) 替换 `web` ')
|
||||
print(f'4) 对比 2 个配置文件 ')
|
||||
print(f'5) 对指定配置文件根据配置项的首字母排序输出 \n')
|
||||
|
||||
print(f'AUTO) 一键制作全功能后台编程器固件及mtd4(基于squashfs的rootfs分区) \n')
|
||||
|
||||
print(f'PART) ★★进入分区刷写菜单★★ ')
|
||||
|
||||
print_author_info()
|
||||
|
||||
print(msg)
|
||||
|
||||
|
||||
user_input = input("请输入命令('exit'退出程序): ").upper()
|
||||
if user_input == 'EXIT':
|
||||
sys.exit()
|
||||
elif user_input == 'O':
|
||||
set_work_space_and_bin()
|
||||
continue_any_key()
|
||||
|
||||
elif user_input == 'BACK':
|
||||
print('备份编程分区及固件')
|
||||
backup_firmware()
|
||||
continue_any_key()
|
||||
|
||||
elif user_input == 'S':
|
||||
print('编程器固件拆分为 mtd 分区')
|
||||
zxic_firmware_tools.split_firmware(os.path.join(work_space_path, default_firmware_name))
|
||||
continue_any_key()
|
||||
|
||||
elif user_input == 'M':
|
||||
print('合并 mtd 分区为 编程器固件')
|
||||
zxic_firmware_tools.merge_firmware(work_space_path)
|
||||
continue_any_key()
|
||||
|
||||
elif user_input == 'R':
|
||||
print('rootfs(squashfs镜像提取)')
|
||||
user_input = input(
|
||||
f'默认将从{os.path.join(work_space_path, default_firmware_name)} 中提取,如需选择其他文件请输入字母 O :').upper()
|
||||
|
||||
unpack_file_path = os.path.join(work_space_path, default_firmware_name)
|
||||
if user_input == 'O':
|
||||
unpack_file_path = input(f'请输入待提取文件路径:')
|
||||
zxic_firmware_tools.unpack_firmware(unpack_file_path)
|
||||
continue_any_key()
|
||||
|
||||
elif user_input == 'W':
|
||||
print('squashfs 格式的 rootfs 回写')
|
||||
rootfs_image_path = input(f'请输入待回写的 squashfs 格式的 rootfs 镜像文件:')
|
||||
target_image_path = input(f'请输入回写目标文件(编程器固件/mtd4分区文件):')
|
||||
zxic_firmware_tools.repack_firmware(rootfs_image_path, target_image_path)
|
||||
continue_any_key()
|
||||
|
||||
elif user_input == 'UNPACK':
|
||||
print('squashfs 镜像解压')
|
||||
rootfs_image_path = input(f'请输入需要解压的 squashfs 镜像文件路径:')
|
||||
if os.path.exists(rootfs_image_path):
|
||||
zxic_firmware_tools.unsquashfs(rootfs_image_path)
|
||||
continue_any_key()
|
||||
|
||||
elif user_input == 'PACK':
|
||||
print('squashfs-root 打包镜像')
|
||||
squashfs_rootfs_dir_path = input(f'请输入需要压缩打包的 squashfs-root 目录:')
|
||||
if os.path.exists(squashfs_rootfs_dir_path):
|
||||
zxic_firmware_tools.mksquashfs(squashfs_rootfs_dir_path)
|
||||
continue_any_key()
|
||||
|
||||
elif user_input == '1':
|
||||
print('对比输出文件目录树及删除特定文件')
|
||||
check_and_delete_files.main(default_config_path ,os.path.join(output_path, rootfs_dir_name))
|
||||
continue_any_key()
|
||||
|
||||
elif user_input == '2':
|
||||
print('替换默认配置')
|
||||
# target_image_path = input(f'请输入目标配置目录:')
|
||||
nv_replace.main(os.path.join(output_path, rootfs_dir_name))
|
||||
continue_any_key()
|
||||
|
||||
elif user_input == '3':
|
||||
print('替换web')
|
||||
print('正在努力实现.......')
|
||||
# target_image_path = input(f'请输入目标配置目录:')
|
||||
# nv_replace.main(os.path.join(output_path, rootfs_dir_name))
|
||||
continue_any_key()
|
||||
|
||||
elif user_input == '4':
|
||||
print('2 个配置文件对比')
|
||||
|
||||
source_config_file = input(f'请输入来源配置文件路径:')
|
||||
target_config_file = input(f'请输入目标配置文件路径:')
|
||||
nv_compare.main(source_config_file, target_config_file)
|
||||
continue_any_key()
|
||||
|
||||
elif user_input == '4':
|
||||
print('配置项排序')
|
||||
target_image_path = input(f'请输入目标配置文件路径:')
|
||||
nv_sort.print_sorted_config(target_image_path)
|
||||
continue_any_key()
|
||||
elif user_input == 'AUTO':
|
||||
print('一键制作全功能后台编程器固件及mtd4(基于squashfs的rootfs分区)')
|
||||
print('正在努力实现......')
|
||||
continue_any_key()
|
||||
|
||||
elif user_input == 'PART':
|
||||
sub_menu_active_device()
|
||||
else:
|
||||
# print("无法识别的命令。")
|
||||
sub_menu_firmware_tools(f'无法识别的命令。{user_input}')
|
||||
|
||||
|
||||
def sub_menu_active_device(msg=''):
|
||||
clear_screen()
|
||||
print_soft_info()
|
||||
print('-----------------------------------分区刷写----------------------------------------')
|
||||
print(f'R) 刷写 rootfs 分区 I) 刷写 imagefs 分区 U) 刷写 userdata 分区 \n')
|
||||
|
||||
|
||||
print(f'W) 刷指定 web(仅支持可读写 16MB 设备,adb 刷写) \n')
|
||||
|
||||
print(f'MAIN) ★★进入固件工具菜单★★')
|
||||
|
||||
print_author_info()
|
||||
|
||||
print(msg)
|
||||
user_input = input("请输入命令('exit'退出程序): ").upper()
|
||||
if user_input == 'EXIT':
|
||||
sys.exit()
|
||||
|
||||
elif user_input == 'R':
|
||||
print('刷写 rootfs 分区')
|
||||
mtd_file_path = input("请输入 mtd4 文件路径:")
|
||||
flash_mtd(mtd_file_path)
|
||||
continue_any_key()
|
||||
|
||||
elif user_input == 'N':
|
||||
print('刷写 nvrofs 分区')
|
||||
mtd_file_path = input("请输入 mtd1 文件路径:")
|
||||
flash_mtd(mtd_file_path, mtd='mtd1')
|
||||
continue_any_key()
|
||||
|
||||
elif user_input == 'I':
|
||||
print('刷写 imagefs 分区')
|
||||
mtd_file_path = input("请输入 mtd3 文件路径:")
|
||||
flash_mtd(mtd_file_path, mtd='mtd3')
|
||||
continue_any_key()
|
||||
|
||||
elif user_input == 'U':
|
||||
print('刷写 userdata 分区')
|
||||
mtd_file_path = input("请输入 mtd5 文件路径:")
|
||||
flash_mtd(mtd_file_path, mtd='mtd5')
|
||||
continue_any_key()
|
||||
|
||||
elif user_input == 'W':
|
||||
print('刷指定 web')
|
||||
print('正在努力实现......')
|
||||
continue_any_key()
|
||||
|
||||
elif user_input == 'MAIN':
|
||||
print('跳转到固件工具箱菜单')
|
||||
sub_menu_firmware_tools()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def continue_any_key():
|
||||
input("按下Enter键继续......")
|
||||
|
||||
|
||||
def set_work_space_and_bin():
|
||||
global work_space_path
|
||||
global default_firmware_name
|
||||
firmware_file_path = input("请输入固件完整路径: ")
|
||||
if os.path.exists(firmware_file_path):
|
||||
work_space_path = os.path.dirname(firmware_file_path)
|
||||
default_firmware_name = os.path.basename(firmware_file_path)
|
||||
else:
|
||||
print(f'{firmware_file_path} 文件不存在,请重新输入!')
|
||||
set_work_space_and_bin()
|
||||
|
||||
|
||||
def pull_dirs(path, target):
|
||||
if path.startswith('/'):
|
||||
path = path.replace('\\', '/')
|
||||
os.makedirs(target, exist_ok=True)
|
||||
dirs = adb_tools.get_sub_dirs(path)
|
||||
for dir in dirs:
|
||||
if path == '/' and dir in ['dev', 'media', 'proc', 'sys', 'tmp']:
|
||||
print(f'Skip system dirs /{dir}')
|
||||
continue # 跳过系统目录
|
||||
|
||||
# if path != '/':
|
||||
# target = os.path.join(target, dir)
|
||||
pull_dirs(os.path.join(path, dir), os.path.join(target, dir))
|
||||
files = adb_tools.get_sub_files(path)
|
||||
print(f'Start to scan {path}......')
|
||||
for file in files:
|
||||
file_path = os.path.join(path, file)
|
||||
target_path = os.path.join(target, file)
|
||||
if file_path.startswith('/'):
|
||||
file_path = file_path.replace('\\', '/')
|
||||
adb_tools.pull_file(file_path, target_path)
|
||||
print(f'Pull {file_path} to {target_path}')
|
||||
print('\n')
|
||||
|
||||
|
||||
def backup_firmware():
|
||||
firmware_name = input("请输入要保存的固件名称: ")
|
||||
if len(firmware_name) < 1:
|
||||
print(f'{firmware_name} 未输入名称!')
|
||||
backup_firmware()
|
||||
|
||||
backups_dir = os.path.join(mifi_tools_base_dirs, 'backups')
|
||||
firmware_path = os.path.join(backups_dir, firmware_name)
|
||||
mtds_path = os.path.join(firmware_path, 'mtd')
|
||||
blk_path = os.path.join(firmware_path, 'blk0')
|
||||
pull_path = os.path.join(firmware_path, 'pull')
|
||||
info_path = os.path.join(firmware_path, 'info')
|
||||
|
||||
if os.path.exists(firmware_path):
|
||||
print(f'{firmware_name} 名称已存在,请重新输入!')
|
||||
backup_firmware()
|
||||
|
||||
os.makedirs(firmware_path, exist_ok=True)
|
||||
os.makedirs(mtds_path, exist_ok=True)
|
||||
os.makedirs(blk_path, exist_ok=True)
|
||||
os.makedirs(pull_path, exist_ok=True)
|
||||
os.makedirs(info_path, exist_ok=True)
|
||||
|
||||
# 获取 mtds 分区信息
|
||||
adb_mtds = adb_tools.run_adb_command("cat /proc/mtd |grep -v \"dev:\"|awk '{print $4}'")
|
||||
mtd_names = adb_mtds.decode('utf-8').replace('\r\r\n', '\r\n').replace('"', '').split('\r\n')
|
||||
mtd_names = [item for item in mtd_names if item]
|
||||
|
||||
adb_mtds = adb_tools.run_adb_command("cat /proc/mtd |grep -v \"dev:\"|awk '{print $1}'")
|
||||
mtd_ids = adb_mtds.decode('utf-8').replace('\r\r\n', '\r\n').replace(':', '').split('\r\n')
|
||||
mtd_ids = [item for item in mtd_ids if item]
|
||||
|
||||
# 拉取 mtds 分区并合并写入 full.bin
|
||||
firmware_data = b'';
|
||||
for i in range(len(mtd_ids)):
|
||||
mtd_id = mtd_ids[i]
|
||||
mtd_name = mtd_names[i]
|
||||
|
||||
mtd_file_name = f'{i}.{mtd_name}'
|
||||
mtd_file_path = os.path.join(mtds_path, mtd_file_name)
|
||||
partitions_file_path = os.path.join(mtds_path, 'packages.json')
|
||||
adb_tools.pull_file(f'/dev/{mtd_id}', mtd_file_path)
|
||||
|
||||
with open(mtd_file_path, 'rb') as f:
|
||||
firmware_data += f.read()
|
||||
if mtd_name == 'zloader':
|
||||
# partitions = zxic_firmware_tools.get_partions_info_from_firmware(mtd_file_path)
|
||||
partitions = adb_tools.get_partitions_from_system()
|
||||
with open(partitions_file_path, 'w') as json_file:
|
||||
json.dump(partitions, json_file)
|
||||
# 合并 mtds 分区
|
||||
with open(os.path.join(blk_path, 'full.bin'), 'wb') as f:
|
||||
f.write(firmware_data)
|
||||
|
||||
# 拉取 info
|
||||
file_list = ['mtd', 'cmdline', 'cpuinfo', 'meminfo', 'partitions']
|
||||
|
||||
for file_name in file_list:
|
||||
source = f'/proc/{file_name}'
|
||||
target = os.path.join(info_path, f'{file_name}.txt')
|
||||
adb_tools.pull_file(source, target)
|
||||
|
||||
with open(os.path.join(info_path, 'nv.txt'), 'w') as nv_file:
|
||||
nv_file.write(adb_tools.run_adb_command('nv show').decode('utf-8').replace('\r\r\n', '\r\n'))
|
||||
|
||||
# 拉取文件
|
||||
pull_dirs('/', pull_path)
|
||||
|
||||
|
||||
def push_arm_tools(applets=['flashcp', 'nohup']):
|
||||
adb_tools.run_adb_command('mount -o remount,rw /')
|
||||
adb_tools.run_adb_command('mount -o remount,rw,suid,exec /tmp')
|
||||
|
||||
for applet in applets:
|
||||
adb_tools.push_file(os.path.join(arm_tools_path, applet), '/tmp')
|
||||
adb_tools.run_adb_command(f'chmod +x /tmp/{applet}')
|
||||
|
||||
|
||||
|
||||
|
||||
def flash_mtd(mtd_file_path, mtd='mtd4', mode='flashcp'):
|
||||
mtd_file_name = os.path.basename(mtd_file_path)
|
||||
if os.path.exists(mtd_file_path):
|
||||
# todo 校验各分区 magic 防止刷错分区
|
||||
if mtd == 'mtd4':
|
||||
if not zxic_firmware_tools.is_mtd4_squashfs_file(mtd_file_path):
|
||||
print(f'{mtd} 必须是 squashfs 镜像格式才支持刷写!')
|
||||
return
|
||||
# todo 校验分区大小是否一致 防止刷错分区
|
||||
adb_tools.run_adb_command('mount -o remount,rw /')
|
||||
print(f'当前刷机模式为:{mode},刷写分区:{mtd}....')
|
||||
flash_command = f'/tmp/nohup /tmp/dd if=/tmp/{mtd_file_name} of=/dev/{mtd} conv=fsync 2>&1 >/mnt/userdata/{mtd}.log'
|
||||
if mode == 'dd':
|
||||
push_arm_tools(['dd', 'nohup'])
|
||||
elif mode == 'flashcp':
|
||||
push_arm_tools(['flashcp', 'nohup'])
|
||||
flash_command = f'/tmp/nohup /tmp/flashcp /tmp/{mtd_file_name} /dev/{mtd} -v 2>&1 >/mnt/userdata/{mtd}.log'
|
||||
|
||||
adb_tools.push_file(mtd_file_path, f'/tmp/{mtd_file_name}')
|
||||
adb_tools.run_adb_command('/sbin/fota_release_space.sh')
|
||||
adb_tools.run_adb_command("kill -9 $(ps -ef | grep -v init | grep -v adbd | grep -ve '\[' | grep -v 'sh -l' | grep -v '/bin/login' |grep -v PID| grep -v grep |awk '{print $1}')")
|
||||
print("已开始刷写分区,请耐心等待(耗时大概30~50s)......")
|
||||
adb_tools.run_adb_command(flash_command)
|
||||
print(f'{mtd_file_name} 已刷入 {mtd}分区,开始校验文件一致性....')
|
||||
out = adb_tools.run_adb_command(f"md5sum /tmp/{mtd_file_name}" + " |awk '{print $1}'")
|
||||
md5_mtd_file = out.decode('utf-8').split('\r\r\n')[0]
|
||||
out = adb_tools.run_adb_command(f"md5sum /dev/{mtd}" + " |awk '{print $1}'")
|
||||
md5_mtd_block = out.decode('utf-8').split('\r\r\n')[0]
|
||||
if md5_mtd_block != md5_mtd_file:
|
||||
print(f"分区刷写校验失败,{md5_mtd_block} != {md5_mtd_file} 请重新刷写该分区")
|
||||
else:
|
||||
print(f"分区刷写校验成功,{md5_mtd_block} ==> {md5_mtd_file} ")
|
||||
|
||||
|
||||
def main():
|
||||
while True:
|
||||
main_menu()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
pass
|
||||
Reference in New Issue
Block a user