Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 91 additions & 91 deletions .pre-commit-config.yaml

Large diffs are not rendered by default.

73 changes: 55 additions & 18 deletions pkg/windows/msi/CustomAction01/CustomAction01.cs
Original file line number Diff line number Diff line change
Expand Up @@ -465,26 +465,63 @@ public static ActionResult kill_python_exe(Session session) {
// Get full path and command line from running process
// see https://github.com/saltstack/salt/issues/42862
session.Log("...BEGIN kill_python_exe (CustomAction01.cs)");
using (
var wmi_searcher = new ManagementObjectSearcher(
"SELECT ProcessID, ExecutablePath, CommandLine FROM Win32_Process WHERE CommandLine LIKE '%salt-minion%' AND NOT CommandLine LIKE '%msiexec%'"
)
) {
foreach (ManagementObject wmi_obj in wmi_searcher.Get()) {
String ProcessID = wmi_obj["ProcessID"].ToString();
Int32 pid = Int32.Parse(ProcessID);
String ExecutablePath = wmi_obj["ExecutablePath"].ToString();
String CommandLine = wmi_obj["CommandLine"].ToString();
session.Log("...kill_python_exe " + ExecutablePath + " " + CommandLine);
Process proc11 = Process.GetProcessById(pid);
try {
proc11.Kill();
} catch (Exception exc) {
session.Log("...kill_python_exe " + ExecutablePath + " " + CommandLine);
session.Log(exc.ToString());
// ignore wmiresults without these properties

// Give the minion enough time to finish its internal stop_async (graceful shutdown).
// salt/minion.py:MinionManager.stop_async has a static 5-second sleep to allow
// the I/O loop to process and send any remaining "return" messages to the Master.
// We wait 6 seconds here to ensure that we don't aggressively kill the process
// while it is still performing its legitimate cleanup. After this window,
// we proceed to kill any lingering or orphan processes that would otherwise
// lock DLLs (like pywin32 or cryptography) and cause a "Frankenstein" installation.
session.Log("...Waiting 6 seconds for graceful shutdown...");
System.Threading.Thread.Sleep(6000);

// This is an immediate custom action, access properties directly
string installDir = "";
try {
installDir = cutil.get_property_IMCAC(session, "INSTALLDIR");
} catch (Exception) {
session.Log("...INSTALLDIR not found. Falling back to default WMI search.");
}
string wmi_query = "SELECT ProcessID, ExecutablePath, CommandLine FROM Win32_Process WHERE (CommandLine LIKE '%salt-minion%' OR CommandLine LIKE '%salt-call%' OR CommandLine LIKE '%ssm.exe%') AND NOT CommandLine LIKE '%msiexec%'";
if (!string.IsNullOrEmpty(installDir)) {
session.Log("...Targeting processes in: " + installDir);
// Broaden the query to include anything running from the installation directory OR explicitly named ssm
wmi_query = "SELECT ProcessID, ExecutablePath, CommandLine FROM Win32_Process WHERE (ExecutablePath LIKE '" + installDir.Replace("\\", "\\\\") + "%' OR CommandLine LIKE '%salt-minion%' OR CommandLine LIKE '%salt-call%' OR CommandLine LIKE '%ssm.exe%' OR ExecutablePath LIKE '%ssm.exe') AND NOT CommandLine LIKE '%msiexec%'";
}

// Perform multiple passes to ensure stubborn or child processes are caught
for (int attempt = 1; attempt <= 3; attempt++) {
session.Log("...Kill attempt " + attempt + " of 3");
using (var wmi_searcher = new ManagementObjectSearcher(wmi_query)) {
int killedCount = 0;
foreach (ManagementObject wmi_obj in wmi_searcher.Get()) {
try {
if (wmi_obj["ProcessID"] == null) continue;
String ProcessID = wmi_obj["ProcessID"].ToString();
Int32 pid = Int32.Parse(ProcessID);

// Don't kill ourselves or the installer
if (pid == Process.GetCurrentProcess().Id) continue;

String ExecutablePath = wmi_obj["ExecutablePath"] != null ? wmi_obj["ExecutablePath"].ToString() : "Unknown";
session.Log("...killing process: PID=" + ProcessID + " Path=" + ExecutablePath);
Process proc = Process.GetProcessById(pid);
proc.Kill();
killedCount++;
} catch (Exception exc) {
session.Log("...failed to kill process: " + exc.Message);
}
}
if (killedCount == 0) {
session.Log("...No matching processes found to kill.");
break;
}
}
if (attempt < 3) {
session.Log("...Waiting 2 seconds before next kill attempt...");
System.Threading.Thread.Sleep(2000);
}
}
session.Log("...END kill_python_exe");
return ActionResult.Success;
Expand Down
10 changes: 4 additions & 6 deletions pkg/windows/msi/Product.wxs
Original file line number Diff line number Diff line change
Expand Up @@ -197,16 +197,14 @@ IMCAC - Immediate Custom Action - It's immediate
stopSalt to release log file, installValidate requires access to all
files, including the log file
-->
<Custom Action="stopSalt" Before="InstallValidate" >1</Custom>
<Custom Action="stopSalt" Before="kill_python_exe" >1</Custom>

<!--
On uninstall or upgrade: stop salt python.exe processes that would lock dll's
- This will only run on silent installs. /quiet or /qn
- All other installs will display a dialog box of process that need to be closed
- This must run before InstallValidate to ensure that the installer does not see files in use
-->
<Custom Action="kill_python_exe" After="StopServices" >(REMOVE ~= "ALL") or WIX_UPGRADE_DETECTED</Custom>
<Custom Action="kill_python_exe" Before="InstallValidate" >(REMOVE ~= "ALL") or WIX_UPGRADE_DETECTED</Custom>

<!-- ReadConfig_IMCAC must be called before CostInitialize so features can depend on properties set-->
<Custom Action="ReadConfig_IMCAC" Before="CostInitialize" >NOT Installed</Custom>
<Custom Action="del_NSIS_DECAC" After="InstallInitialize" >nsis_install_found</Custom>

Expand Down Expand Up @@ -242,7 +240,7 @@ IMCAC - Immediate Custom Action - It's immediate
<CustomAction Id="DeleteConfig_DECAC" BinaryKey="MinionConfigExt" DllEntry="DeleteConfig_DECAC" Execute="deferred" Return="check" Impersonate="no" />
<CustomAction Id="DeleteConfig2_DECAC" BinaryKey="MinionConfigExt" DllEntry="DeleteConfig_DECAC" Execute="deferred" Return="check" Impersonate="no" />
<CustomAction Id="BackupConfig_DECAC" BinaryKey="MinionConfigExt" DllEntry="BackupConfig_DECAC" Execute="deferred" Return="check" Impersonate="no" />
<CustomAction Id="kill_python_exe" BinaryKey="MinionConfigExt" DllEntry="kill_python_exe" Execute="deferred" Return="check" Impersonate="no" />
<CustomAction Id="kill_python_exe" BinaryKey="MinionConfigExt" DllEntry="kill_python_exe" Execute="immediate" Return="ignore" />
<!-- Custom Action Data Helper for deferred custom actions -->
<!-- master and id must be named like in YAML configuration -->
<!-- Send all this stuff down to the sandbox -->
Expand Down
85 changes: 74 additions & 11 deletions pkg/windows/nsis/installer/Salt-Minion-Setup.nsi
Original file line number Diff line number Diff line change
Expand Up @@ -1241,9 +1241,57 @@ Function ${un}uninstallSalt
Abort
${EndIf}

# Give the minion enough time to finish its internal stop_async (graceful shutdown).

# salt/minion.py:MinionManager.stop_async has a static 5-second sleep to allow

# the I/O loop to process and send any remaining "return" messages to the Master.

# We wait 6 seconds here to ensure that we don't aggressively kill the process

# while it is still performing its legitimate cleanup. After this window,

# we proceed to kill any lingering or orphan processes that would otherwise

# lock DLLs (like pywin32 or cryptography) and cause a "Frankenstein" installation.

${LogMsg} "Waiting 6 seconds for graceful shutdown..."

Sleep 6000



# Perform multiple passes to ensure stubborn or child processes are caught
# Pass 1: Aggressive taskkill
${LogMsg} "Killing remaining processes (Pass 1 of 3)"
nsExec::ExecToStack 'taskkill /F /IM ssm.exe /T'
nsExec::ExecToStack 'taskkill /F /IM salt-minion.exe /T'
nsExec::ExecToStack 'taskkill /F /IM salt-call.exe /T'
nsExec::ExecToStack `powershell -Command "$p = '$INSTDIR'.Replace('\', '\\'); Get-Process | Where-Object { ($_.Path -like '$p*') -or ($_.Name -eq 'ssm') } | ForEach-Object { Write-Output \"Killing: $($_.Name) ($($_.Id))\"; Stop-Process -Id $_.Id -Force -ErrorAction SilentlyContinue }"`
pop $0
pop $1
${LogMsg} "Kill log: $1"
Sleep 2000

# Pass 2: PowerShell follow-up
${LogMsg} "Killing remaining processes (Pass 2 of 3)"
nsExec::ExecToStack `powershell -Command "$p = '$INSTDIR'.Replace('\', '\\'); Get-Process | Where-Object { ($_.Path -like '$p*') -or ($_.Name -eq 'ssm') } | ForEach-Object { Write-Output \"Killing: $($_.Name) ($($_.Id))\"; Stop-Process -Id $_.Id -Force -ErrorAction SilentlyContinue }"`
pop $0
pop $1
${LogMsg} "Kill log: $1"
Sleep 2000

# Pass 3: Final check
${LogMsg} "Killing remaining processes (Pass 3 of 3)"
nsExec::ExecToStack `powershell -Command "$p = '$INSTDIR'.Replace('\', '\\'); Get-Process | Where-Object { ($_.Path -like '$p*') -or ($_.Name -eq 'ssm') } | ForEach-Object { Write-Output \"Killing: $($_.Name) ($($_.Id))\"; Stop-Process -Id $_.Id -Force -ErrorAction SilentlyContinue }"`
pop $0
pop $1
${LogMsg} "Kill log: $1"

doneSSM:

# Remove files

# Remove files
${LogMsg} "Deleting files"
ClearErrors
${LogMsg} "Deleting files: $INSTDIR\multi-minion*"
Expand All @@ -1255,15 +1303,21 @@ Function ${un}uninstallSalt
ClearErrors
${LogMsg} "Deleting files: $INSTDIR\salt*"
Delete "$INSTDIR\salt*"
IfErrors 0 ssmBin
${LogMsg} "FAILED"
${If} ${Errors}
${LogMsg} "FAILED to delete critical Salt binaries in $INSTDIR. Files might be locked."
MessageBox MB_OK|MB_ICONEXCLAMATION "FAILED to delete critical Salt binaries in $INSTDIR. Files might be locked. Please ensure all Salt processes are stopped and try again." /SD IDOK IDOK
Abort
${EndIf}

ssmBin:
ClearErrors
${LogMsg} "Deleting file: $SSMBin"
Delete "$SSMBin"
IfErrors 0 uninstBin
${LogMsg} "FAILED"
${If} ${Errors}
${LogMsg} "FAILED to delete $SSMBin. File might be locked."
MessageBox MB_OK|MB_ICONEXCLAMATION "FAILED to delete critical Salt service manager ($SSMBin). File might be locked. Please ensure all Salt processes are stopped and try again." /SD IDOK IDOK
Abort
${EndIf}

uninstBin:
ClearErrors
Expand Down Expand Up @@ -1299,8 +1353,11 @@ Function ${un}uninstallSalt
ClearErrors
${LogMsg} "Deleting directory: $INSTDIR\Lib"
RMDir /r "$INSTDIR\Lib"
IfErrors 0 removeLibs
${LogMsg} "FAILED"
${If} ${Errors}
${LogMsg} "FAILED to delete $INSTDIR\Lib. Files might be locked."
MessageBox MB_OK|MB_ICONEXCLAMATION "FAILED to delete critical Salt libraries in $INSTDIR\Lib. Files might be locked. Please ensure all Salt processes are stopped and try again." /SD IDOK IDOK
Abort
${EndIf}

removeLibs:
ClearErrors
Expand All @@ -1313,15 +1370,21 @@ Function ${un}uninstallSalt
ClearErrors
${LogMsg} "Deleting directory: $INSTDIR\Scripts"
RMDir /r "$INSTDIR\Scripts" # Relenv puts bins in Scripts
IfErrors 0 removeBin
${LogMsg} "FAILED"
${If} ${Errors}
${LogMsg} "FAILED to delete $INSTDIR\Scripts. Files might be locked."
MessageBox MB_OK|MB_ICONEXCLAMATION "FAILED to delete critical Salt scripts in $INSTDIR\Scripts. Files might be locked. Please ensure all Salt processes are stopped and try again." /SD IDOK IDOK
Abort
${EndIf}

removeBin:
ClearErrors
${LogMsg} "Deleting directory: $INSTDIR\bin"
RMDir /r "$INSTDIR\bin" # Older versions use bin
IfErrors 0 removeConfigs
${LogMsg} "FAILED"
${If} ${Errors}
${LogMsg} "FAILED to delete $INSTDIR\bin. Files might be locked."
MessageBox MB_OK|MB_ICONEXCLAMATION "FAILED to delete critical Salt binaries in $INSTDIR\bin. Files might be locked. Please ensure all Salt processes are stopped and try again." /SD IDOK IDOK
Abort
${EndIf}

removeConfigs:
ClearErrors
Expand Down
8 changes: 4 additions & 4 deletions requirements/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,22 @@
# Multiple entries for the same package (with different version constraints) are grouped together.


certifi==2023.07.22; python_version < '3.10'
certifi>=2024.7.4; python_version >= '3.10'
certifi>=2024.7.4
cffi>=2.0.0
# We need contextvars for salt-ssh
contextvars
croniter>=0.3.0,!=0.3.22; sys_platform != 'win32'
cryptography>=42.0.0,<43.0.0
cryptography>=46.0.5
distro>=1.0.1
frozenlist>=1.3.0; python_version < '3.11'
frozenlist>=1.5.0; python_version >= '3.11'
# immutables is a requirement of contextvars
immutables>=0.21
jaraco.functools>=4.1.0
jaraco.context>=6.1.0
jaraco.text>=4.0.0
Jinja2>=3.1.5
jmespath
jmespath>=1.1.0
looseversion
MarkupSafe<3.0.0
msgpack>=1.0.0
Expand Down
2 changes: 1 addition & 1 deletion requirements/darwin.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ idna>=2.8
linode-python>=1.1.1
pyasn1>=0.6.2
pycparser>=2.21
pyopenssl>=23.2.0,<25.0.0
pyopenssl>=25.0.0
python-dateutil>=2.8.0
python-gnupg>=0.4.4
setproctitle>=1.2.3
Expand Down
6 changes: 3 additions & 3 deletions requirements/static/ci/common.in
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
apache-libcloud>=1.5.0; sys_platform != 'win32'
boto3>=1.25.0
boto>=2.47.0
cassandra-driver>=3.25.0
cryptography>=46.0.5
cffi>=1.14.6
cherrypy>=17.4.1
clustershell
Expand All @@ -18,7 +18,7 @@ aiohttp>=3.10.2
filelock>=3.19.1 ; python_version < '3.10'
filelock>=3.20.3 ; python_version >= '3.10'
gitpython>=3.1.37
jmespath
jmespath>=1.1.0
jsonschema
junos-eznc; sys_platform != 'win32' and python_version <= '3.10'
jxmlease; sys_platform != 'win32'
Expand All @@ -43,7 +43,7 @@ rfc3987
sqlparse>=0.4.4
strict_rfc3339>=0.7
toml
vcert~=0.7.0; sys_platform != 'win32'
vcert~=0.9.0; sys_platform != 'win32'
virtualenv>=20.36.1
watchdog>=0.9.0
websocket-client>=1.3.3
Expand Down
11 changes: 5 additions & 6 deletions requirements/static/ci/py3.10/changelog.txt
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
#
# This file is autogenerated by pip-compile
# To update, run:
# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile --cert='' --client-cert='' --index-url='' --no-emit-index-url --output-file=requirements/static/ci/py3.10/changelog.txt --pip-args='' requirements/static/ci/changelog.in
# pip-compile --no-emit-index-url --output-file=requirements/static/ci/py3.10/changelog.txt requirements/static/ci/changelog.in
#
click-default-group==1.2.2
# via towncrier
click==7.1.1
# via
# -c requirements/static/ci/py3.10/linux.txt
# click-default-group
# towncrier
click-default-group==1.2.2
# via towncrier
incremental==17.5.0
# via towncrier
jinja2==3.1.6
Expand Down
Loading
Loading