1""" 2Win System Utils 3 4Functions shared with salt.modules.win_system and salt.grains.pending_reboot 5 6.. versionadded:: 3001 7""" 8# NOTE: DO NOT USE RAW STRINGS IN THIS MODULE! UNICODE_LITERALS DOES NOT PLAY 9# NICELY WITH RAW STRINGS CONTAINING \u or \U. 10 11import logging 12 13import salt.utils.win_reg 14import salt.utils.win_update 15 16try: 17 import win32api 18 import win32con 19 20 HAS_WIN32_MODS = True 21except ImportError: 22 HAS_WIN32_MODS = False 23 24 25log = logging.getLogger(__name__) 26 27# Define the module's virtual name 28__virtualname__ = "win_system" 29MINION_VOLATILE_KEY = "SYSTEM\\CurrentControlSet\\Services\\salt-minion\\Volatile-Data" 30REBOOT_REQUIRED_NAME = "Reboot required" 31 32 33def __virtual__(): 34 """ 35 Only works on Windows systems 36 """ 37 if not salt.utils.platform.is_windows(): 38 return ( 39 False, 40 "win_system salt util failed to load: " 41 "The util will only run on Windows systems", 42 ) 43 if not HAS_WIN32_MODS: 44 return ( 45 False, 46 "win_system salt util failed to load: " 47 "The util will only run on Windows systems", 48 ) 49 return __virtualname__ 50 51 52def get_computer_name(): 53 """ 54 Get the Windows computer name. Uses the win32api to get the current computer 55 name. 56 57 .. versionadded:: 3001 58 59 Returns: 60 str: Returns the computer name if found. Otherwise returns ``False``. 61 62 Example: 63 64 .. code-block:: python 65 66 import salt.utils.win_system 67 salt.utils.win_system.get_computer_name() 68 """ 69 name = win32api.GetComputerNameEx(win32con.ComputerNamePhysicalDnsHostname) 70 return name if name else False 71 72 73def get_pending_computer_name(): 74 """ 75 Get a pending computer name. If the computer name has been changed, and the 76 change is pending a system reboot, this function will return the pending 77 computer name. Otherwise, ``None`` will be returned. If there was an error 78 retrieving the pending computer name, ``False`` will be returned, and an 79 error message will be logged to the minion log. 80 81 .. versionadded:: 3001 82 83 Returns: 84 str: 85 Returns the pending name if pending restart. Returns ``None`` if not 86 pending restart. 87 88 Example: 89 90 .. code-block:: python 91 92 import salt.utils.win_system 93 salt.utils.win_system.get_pending_computer_name() 94 """ 95 current = get_computer_name() 96 try: 97 pending = salt.utils.win_reg.read_value( 98 hive="HKLM", 99 key="SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters", 100 vname="NV Hostname", 101 )["vdata"] 102 except TypeError: 103 # This should never happen as the above key and vname are system names 104 # and should always be present 105 return None 106 if pending: 107 return pending if pending.lower() != current.lower() else None 108 109 110def get_pending_component_servicing(): 111 """ 112 Determine whether there are pending Component Based Servicing tasks that 113 require a reboot. 114 115 If any the following registry keys exist then a reboot is pending: 116 117 ``HKLM:\\\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootPending`` 118 ``HKLM:\\\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootInProgress`` 119 ``HKLM:\\\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\PackagesPending`` 120 121 .. versionadded:: 3001 122 123 Returns: 124 bool: ``True`` if there are pending Component Based Servicing tasks, 125 otherwise ``False`` 126 127 CLI Example: 128 129 .. code-block:: bash 130 131 salt '*' system.get_pending_component_servicing 132 """ 133 # So long as one of the registry keys exists, a reboot is pending 134 base_key = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing" 135 sub_keys = ("RebootPending", "RebootInProgress", "PackagesPending") 136 for sub_key in sub_keys: 137 key = "\\".join((base_key, sub_key)) 138 if salt.utils.win_reg.key_exists(hive="HKLM", key=key): 139 return True 140 141 return False 142 143 144def get_pending_domain_join(): 145 """ 146 Determine whether there is a pending domain join action that requires a 147 reboot. 148 149 If any the following registry keys exist then a reboot is pending: 150 151 ``HKLM:\\\\SYSTEM\\CurrentControlSet\\Services\\Netlogon\\AvoidSpnSet`` 152 ``HKLM:\\\\SYSTEM\\CurrentControlSet\\Services\\Netlogon\\JoinDomain`` 153 154 .. versionadded:: 3001 155 156 Returns: 157 bool: ``True`` if there is a pending domain join action, otherwise 158 ``False`` 159 160 Example: 161 162 .. code-block:: python 163 164 import salt.utils.win_system 165 salt.utils.win_system.get_pending_domain_join() 166 """ 167 base_key = "SYSTEM\\CurrentControlSet\\Services\\Netlogon" 168 sub_keys = ("AvoidSpnSet", "JoinDomain") 169 170 # If any keys are present then there is a reboot pending. 171 for sub_key in sub_keys: 172 key = "\\".join((base_key, sub_key)) 173 if salt.utils.win_reg.key_exists(hive="HKLM", key=key): 174 return True 175 176 return False 177 178 179def get_pending_file_rename(): 180 """ 181 Determine whether there are pending file rename operations that require a 182 reboot. 183 184 A reboot is pending if any of the following value names exist and have value 185 data set: 186 187 - ``PendingFileRenameOperations`` 188 - ``PendingFileRenameOperations2`` 189 190 in the following registry key: 191 192 ``HKLM:\\\\SYSTEM\\CurrentControlSet\\Control\\Session Manager`` 193 194 .. versionadded:: 3001 195 196 Returns: 197 bool: ``True`` if there are pending file rename operations, otherwise 198 ``False`` 199 200 Example: 201 202 .. code-block:: python 203 204 import salt.utils.win_system 205 salt.utils.win_system.get_pending_file_rename() 206 """ 207 vnames = ("PendingFileRenameOperations", "PendingFileRenameOperations2") 208 key = "SYSTEM\\CurrentControlSet\\Control\\Session Manager" 209 for vname in vnames: 210 reg_ret = salt.utils.win_reg.read_value(hive="HKLM", key=key, vname=vname) 211 if reg_ret["success"]: 212 if reg_ret["vdata"] and (reg_ret["vdata"] != "(value not set)"): 213 return True 214 return False 215 216 217def get_pending_servermanager(): 218 """ 219 Determine whether there are pending Server Manager tasks that require a 220 reboot. 221 222 A reboot is pending if the ``CurrentRebootAttempts`` value name exists and 223 has an integer value. The value name resides in the following registry key: 224 225 ``HKLM:\\\\SOFTWARE\\Microsoft\\ServerManager`` 226 227 .. versionadded:: 3001 228 229 Returns: 230 bool: ``True`` if there are pending Server Manager tasks, otherwise 231 ``False`` 232 233 Example: 234 235 .. code-block:: python 236 237 import salt.utils.win_system 238 salt.utils.win_system.get_pending_servermanager() 239 """ 240 vname = "CurrentRebootAttempts" 241 key = "SOFTWARE\\Microsoft\\ServerManager" 242 243 # There are situations where it's possible to have '(value not set)' as 244 # the value data, and since an actual reboot won't be pending in that 245 # instance, just catch instances where we try unsuccessfully to cast as int. 246 247 reg_ret = salt.utils.win_reg.read_value(hive="HKLM", key=key, vname=vname) 248 if reg_ret["success"]: 249 try: 250 if int(reg_ret["vdata"]) > 0: 251 return True 252 except ValueError: 253 pass 254 return False 255 256 257def get_pending_dvd_reboot(): 258 """ 259 Determine whether the DVD Reboot flag is set. 260 261 The system requires a reboot if the ``DVDRebootSignal`` value name exists 262 at the following registry location: 263 264 ``HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce`` 265 266 .. versionadded:: 3001 267 268 Returns: 269 bool: ``True`` if the above condition is met, otherwise ``False`` 270 271 Example: 272 273 .. code-block:: python 274 275 import salt.utils.win_system 276 salt.utils.win_system.get_pending_dvd_reboot() 277 """ 278 # So long as the registry key exists, a reboot is pending. 279 return salt.utils.win_reg.value_exists( 280 hive="HKLM", 281 key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", 282 vname="DVDRebootSignal", 283 ) 284 285 286def get_pending_update(): 287 """ 288 Determine whether there are pending updates that require a reboot. 289 290 If either of the following registry keys exists, a reboot is pending: 291 292 ``HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Auto Update\\RebootRequired`` 293 ``HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Auto Update\\PostRebootReporting`` 294 295 .. versionadded:: 3001 296 297 Returns: 298 bool: ``True`` if any of the above conditions are met, otherwise 299 ``False`` 300 301 Example: 302 303 .. code-block:: python 304 305 import salt.utils.win_system 306 salt.utils.win_system.get_pending_update() 307 """ 308 # So long as any of the registry keys exists, a reboot is pending. 309 base_key = ( 310 "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Auto Update" 311 ) 312 sub_keys = ("RebootRequired", "PostRebootReporting") 313 for sub_key in sub_keys: 314 key = "\\".join((base_key, sub_key)) 315 if salt.utils.win_reg.key_exists(hive="HKLM", key=key): 316 return True 317 318 return False 319 320 321def get_reboot_required_witnessed(): 322 """ 323 Determine if at any time during the current boot session the salt minion 324 witnessed an event indicating that a reboot is required. 325 326 This function will return ``True`` if an install completed with exit 327 code 3010 during the current boot session and can be extended where 328 appropriate in the future. 329 330 If the ``Reboot required`` value name exists in the following location and 331 has a value of ``1`` then the system is pending reboot: 332 333 ``HKLM:\\\\SYSTEM\\CurrentControlSet\\Services\\salt-minion\\Volatile-Data`` 334 335 .. versionadded:: 3001 336 337 Returns: 338 bool: ``True`` if the ``Requires reboot`` registry flag is set to ``1``, 339 otherwise ``False`` 340 341 Example: 342 343 .. code-block:: python 344 345 import salt.utils.win_system 346 salt.utils.win_system.get_reboot_required_witnessed() 347 348 """ 349 value_dict = salt.utils.win_reg.read_value( 350 hive="HKLM", key=MINION_VOLATILE_KEY, vname=REBOOT_REQUIRED_NAME 351 ) 352 return value_dict["vdata"] == 1 353 354 355def set_reboot_required_witnessed(): 356 """ 357 This function is used to remember that an event indicating that a reboot is 358 required was witnessed. This function relies on the salt-minion's ability to 359 create the following volatile registry key in the *HKLM* hive: 360 361 *SYSTEM\\CurrentControlSet\\Services\\salt-minion\\Volatile-Data* 362 363 Because this registry key is volatile, it will not persist beyond the 364 current boot session. Also, in the scope of this key, the name *'Reboot 365 required'* will be assigned the value of *1*. 366 367 For the time being, this function is being used whenever an install 368 completes with exit code 3010 and can be extended where appropriate in the 369 future. 370 371 .. versionadded:: 3001 372 373 Returns: 374 bool: ``True`` if successful, otherwise ``False`` 375 376 Example: 377 378 .. code-block:: python 379 380 import salt.utils.win_system 381 salt.utils.win_system.set_reboot_required_witnessed() 382 """ 383 return salt.utils.win_reg.set_value( 384 hive="HKLM", 385 key=MINION_VOLATILE_KEY, 386 volatile=True, 387 vname=REBOOT_REQUIRED_NAME, 388 vdata=1, 389 vtype="REG_DWORD", 390 ) 391 392 393def get_pending_update_exe_volatile(): 394 """ 395 Determine whether there is a volatile update exe that requires a reboot. 396 397 Checks ``HKLM:\\Microsoft\\Updates``. If the ``UpdateExeVolatile`` value 398 name is anything other than 0 there is a reboot pending 399 400 .. versionadded:: 3001 401 402 Returns: 403 bool: ``True`` if there is a volatile exe, otherwise ``False`` 404 405 Example: 406 407 .. code-block:: python 408 409 import salt.utils.win_system 410 salt.utils.win_system.get_pending_update_exe_volatile() 411 """ 412 key = "SOFTWARE\\Microsoft\\Updates" 413 reg_ret = salt.utils.win_reg.read_value( 414 hive="HKLM", key=key, vname="UpdateExeVolatile" 415 ) 416 if reg_ret["success"]: 417 try: 418 if int(reg_ret["vdata"]) != 0: 419 return True 420 except ValueError: 421 pass 422 return False 423 424 425def get_pending_windows_update(): 426 """ 427 Check the Windows Update system for a pending reboot state. 428 429 This leverages the Windows Update System to determine if the system is 430 pending a reboot. 431 432 .. versionadded:: 3001 433 434 Returns: 435 bool: ``True`` if the Windows Update system reports a pending update, 436 otherwise ``False`` 437 438 Example: 439 440 .. code-block:: python 441 442 import salt.utils.win_system 443 salt.utils.win_system.get_pending_windows_update() 444 """ 445 return salt.utils.win_update.needs_reboot() 446 447 448def get_pending_reboot(): 449 """ 450 Determine whether there is a reboot pending. 451 452 .. versionadded:: 3001 453 454 Returns: 455 bool: ``True`` if the system is pending reboot, otherwise ``False`` 456 457 Example: 458 459 .. code-block:: python 460 461 import salt.utils.win_system 462 salt.utils.win_system.get_pending_reboot() 463 """ 464 # Order the checks for reboot pending in most to least likely. 465 checks = ( 466 get_pending_update, 467 get_pending_windows_update, 468 get_pending_update_exe_volatile, 469 get_pending_file_rename, 470 get_pending_servermanager, 471 get_pending_component_servicing, 472 get_pending_dvd_reboot, 473 get_reboot_required_witnessed, 474 get_pending_computer_name, 475 get_pending_domain_join, 476 ) 477 478 for check in checks: 479 if check(): 480 return True 481 482 return False 483 484 485def get_pending_reboot_details(): 486 """ 487 Determine which check is signalling that the system is pending a reboot. 488 Useful in determining why your system is signalling that it needs a reboot. 489 490 .. versionadded:: 3001 491 492 Returns: 493 dict: A dictionary of the results of each function that checks for a 494 pending reboot 495 496 Example: 497 498 .. code-block:: python 499 500 import salt.utils.win_system 501 salt.utils.win_system.get_pending_reboot_details() 502 """ 503 return { 504 "Pending Component Servicing": get_pending_component_servicing(), 505 "Pending Computer Rename": get_pending_computer_name() is not None, 506 "Pending DVD Reboot": get_pending_dvd_reboot(), 507 "Pending File Rename": get_pending_file_rename(), 508 "Pending Join Domain": get_pending_domain_join(), 509 "Pending ServerManager": get_pending_servermanager(), 510 "Pending Update": get_pending_update(), 511 "Pending Windows Update": get_pending_windows_update(), 512 "Reboot Required Witnessed": get_reboot_required_witnessed(), 513 "Volatile Update Exe": get_pending_update_exe_volatile(), 514 } 515