1## @file 2# Install distribution package. 3# 4# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR> 5# 6# SPDX-License-Identifier: BSD-2-Clause-Patent 7# 8 9''' 10RmPkg 11''' 12 13## 14# Import Modules 15# 16import os.path 17from stat import S_IWUSR 18from traceback import format_exc 19from platform import python_version 20from hashlib import md5 21from sys import stdin 22from sys import platform 23 24from Core.DependencyRules import DependencyRules 25from Library import GlobalData 26from Logger import StringTable as ST 27import Logger.Log as Logger 28from Logger.ToolError import OPTION_MISSING 29from Logger.ToolError import UNKNOWN_ERROR 30from Logger.ToolError import ABORT_ERROR 31from Logger.ToolError import CODE_ERROR 32from Logger.ToolError import FatalError 33 34 35## CheckDpDepex 36# 37# Check if the Depex is satisfied 38# @param Dep: Dep 39# @param Guid: Guid of Dp 40# @param Version: Version of Dp 41# @param WorkspaceDir: Workspace Dir 42# 43def CheckDpDepex(Dep, Guid, Version, WorkspaceDir): 44 (Removable, DependModuleList) = Dep.CheckDpDepexForRemove(Guid, Version) 45 if not Removable: 46 Logger.Info(ST.MSG_CONFIRM_REMOVE) 47 Logger.Info(ST.MSG_USER_DELETE_OP) 48 Input = stdin.readline() 49 Input = Input.replace('\r', '').replace('\n', '') 50 if Input.upper() != 'Y': 51 Logger.Error("RmPkg", UNKNOWN_ERROR, ST.ERR_USER_INTERRUPT) 52 return 1 53 else: 54 # 55 # report list of modules that are not valid due to force 56 # remove, 57 # also generate a log file for reference 58 # 59 Logger.Info(ST.MSG_INVALID_MODULE_INTRODUCED) 60 LogFilePath = os.path.normpath(os.path.join(WorkspaceDir, GlobalData.gINVALID_MODULE_FILE)) 61 Logger.Info(ST.MSG_CHECK_LOG_FILE % LogFilePath) 62 try: 63 LogFile = open(LogFilePath, 'w') 64 try: 65 for ModulePath in DependModuleList: 66 LogFile.write("%s\n"%ModulePath) 67 Logger.Info(ModulePath) 68 except IOError: 69 Logger.Warn("\nRmPkg", ST.ERR_FILE_WRITE_FAILURE, 70 File=LogFilePath) 71 except IOError: 72 Logger.Warn("\nRmPkg", ST.ERR_FILE_OPEN_FAILURE, 73 File=LogFilePath) 74 finally: 75 LogFile.close() 76 77## Remove Path 78# 79# removing readonly file on windows will get "Access is denied" 80# error, so before removing, change the mode to be writeable 81# 82# @param Path: The Path to be removed 83# 84def RemovePath(Path): 85 Logger.Info(ST.MSG_REMOVE_FILE % Path) 86 if not os.access(Path, os.W_OK): 87 os.chmod(Path, S_IWUSR) 88 os.remove(Path) 89 try: 90 os.removedirs(os.path.split(Path)[0]) 91 except OSError: 92 pass 93## GetCurrentFileList 94# 95# @param DataBase: DataBase of UPT 96# @param Guid: Guid of Dp 97# @param Version: Version of Dp 98# @param WorkspaceDir: Workspace Dir 99# 100def GetCurrentFileList(DataBase, Guid, Version, WorkspaceDir): 101 NewFileList = [] 102 for Dir in DataBase.GetDpInstallDirList(Guid, Version): 103 RootDir = os.path.normpath(os.path.join(WorkspaceDir, Dir)) 104 for Root, Dirs, Files in os.walk(RootDir): 105 Logger.Debug(0, Dirs) 106 for File in Files: 107 FilePath = os.path.join(Root, File) 108 if FilePath not in NewFileList: 109 NewFileList.append(FilePath) 110 return NewFileList 111 112 113## Tool entrance method 114# 115# This method mainly dispatch specific methods per the command line options. 116# If no error found, return zero value so the caller of this tool can know 117# if it's executed successfully or not. 118# 119# @param Options: command option 120# 121def Main(Options = None): 122 123 try: 124 DataBase = GlobalData.gDB 125 if not Options.DistributionFile: 126 Logger.Error("RmPkg", 127 OPTION_MISSING, 128 ExtraData=ST.ERR_SPECIFY_PACKAGE) 129 WorkspaceDir = GlobalData.gWORKSPACE 130 # 131 # Prepare check dependency 132 # 133 Dep = DependencyRules(DataBase) 134 135 # 136 # Get the Dp information 137 # 138 StoredDistFile, Guid, Version = GetInstalledDpInfo(Options.DistributionFile, Dep, DataBase, WorkspaceDir) 139 140 # 141 # Check Dp depex 142 # 143 CheckDpDepex(Dep, Guid, Version, WorkspaceDir) 144 145 # 146 # remove distribution 147 # 148 RemoveDist(Guid, Version, StoredDistFile, DataBase, WorkspaceDir, Options.Yes) 149 150 Logger.Quiet(ST.MSG_FINISH) 151 152 ReturnCode = 0 153 154 except FatalError as XExcept: 155 ReturnCode = XExcept.args[0] 156 if Logger.GetLevel() <= Logger.DEBUG_9: 157 Logger.Quiet(ST.MSG_PYTHON_ON % (python_version(), platform) + \ 158 format_exc()) 159 except KeyboardInterrupt: 160 ReturnCode = ABORT_ERROR 161 if Logger.GetLevel() <= Logger.DEBUG_9: 162 Logger.Quiet(ST.MSG_PYTHON_ON % (python_version(), platform) + \ 163 format_exc()) 164 except: 165 Logger.Error( 166 "\nRmPkg", 167 CODE_ERROR, 168 ST.ERR_UNKNOWN_FATAL_REMOVING_ERR, 169 ExtraData=ST.MSG_SEARCH_FOR_HELP % ST.MSG_EDKII_MAIL_ADDR, 170 RaiseError=False 171 ) 172 Logger.Quiet(ST.MSG_PYTHON_ON % (python_version(), platform) + \ 173 format_exc()) 174 ReturnCode = CODE_ERROR 175 return ReturnCode 176 177## GetInstalledDpInfo method 178# 179# Get the installed distribution information 180# 181# @param DistributionFile: the name of the distribution 182# @param Dep: the instance of DependencyRules 183# @param DataBase: the internal database 184# @param WorkspaceDir: work space directory 185# @retval StoredDistFile: the distribution file that backed up 186# @retval Guid: the Guid of the distribution 187# @retval Version: the Version of distribution 188# 189def GetInstalledDpInfo(DistributionFile, Dep, DataBase, WorkspaceDir): 190 (Guid, Version, NewDpFileName) = DataBase.GetDpByName(os.path.split(DistributionFile)[1]) 191 if not Guid: 192 Logger.Error("RmPkg", UNKNOWN_ERROR, ST.ERR_PACKAGE_NOT_INSTALLED % DistributionFile) 193 194 # 195 # Check Dp existing 196 # 197 if not Dep.CheckDpExists(Guid, Version): 198 Logger.Error("RmPkg", UNKNOWN_ERROR, ST.ERR_DISTRIBUTION_NOT_INSTALLED) 199 # 200 # Check for Distribution files existence in /conf/upt, if not exist, 201 # Warn user and go on. 202 # 203 StoredDistFile = os.path.normpath(os.path.join(WorkspaceDir, GlobalData.gUPT_DIR, NewDpFileName)) 204 if not os.path.isfile(StoredDistFile): 205 Logger.Warn("RmPkg", ST.WRN_DIST_NOT_FOUND%StoredDistFile) 206 StoredDistFile = None 207 208 return StoredDistFile, Guid, Version 209 210## RemoveDist method 211# 212# remove a distribution 213# 214# @param Guid: the Guid of the distribution 215# @param Version: the Version of distribution 216# @param StoredDistFile: the distribution file that backed up 217# @param DataBase: the internal database 218# @param WorkspaceDir: work space directory 219# @param ForceRemove: whether user want to remove file even it is modified 220# 221def RemoveDist(Guid, Version, StoredDistFile, DataBase, WorkspaceDir, ForceRemove): 222 # 223 # Get Current File List 224 # 225 NewFileList = GetCurrentFileList(DataBase, Guid, Version, WorkspaceDir) 226 227 # 228 # Remove all files 229 # 230 MissingFileList = [] 231 for (Path, Md5Sum) in DataBase.GetDpFileList(Guid, Version): 232 if os.path.isfile(Path): 233 if Path in NewFileList: 234 NewFileList.remove(Path) 235 if not ForceRemove: 236 # 237 # check whether modified by users 238 # 239 Md5Signature = md5(open(str(Path), 'rb').read()) 240 if Md5Sum != Md5Signature.hexdigest(): 241 Logger.Info(ST.MSG_CONFIRM_REMOVE2 % Path) 242 Input = stdin.readline() 243 Input = Input.replace('\r', '').replace('\n', '') 244 if Input.upper() != 'Y': 245 continue 246 RemovePath(Path) 247 else: 248 MissingFileList.append(Path) 249 250 for Path in NewFileList: 251 if os.path.isfile(Path): 252 if (not ForceRemove) and (not os.path.split(Path)[1].startswith('.')): 253 Logger.Info(ST.MSG_CONFIRM_REMOVE3 % Path) 254 Input = stdin.readline() 255 Input = Input.replace('\r', '').replace('\n', '') 256 if Input.upper() != 'Y': 257 continue 258 RemovePath(Path) 259 260 # 261 # Remove distribution files in /Conf/.upt 262 # 263 if StoredDistFile is not None: 264 os.remove(StoredDistFile) 265 266 # 267 # update database 268 # 269 Logger.Quiet(ST.MSG_UPDATE_PACKAGE_DATABASE) 270 DataBase.RemoveDpObj(Guid, Version) 271