# | |
# Licensed to the Apache Software Foundation (ASF) under one or more | |
# contributor license agreements. See the NOTICE file distributed with | |
# this work for additional information regarding copyright ownership. | |
# The ASF licenses this file to You under the Apache License, Version 2.0 | |
# (the "License"); you may not use this file except in compliance with | |
# the License. You may obtain a copy of the License at | |
# | |
# http://www.apache.org/licenses/LICENSE-2.0 | |
# | |
# Unless required by applicable law or agreed to in writing, software | |
# distributed under the License is distributed on an "AS IS" BASIS, | |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
# See the License for the specific language governing permissions and | |
# limitations under the License. | |
param ( | |
[string]$p, | |
[string]$batchpid, | |
[switch]$f, | |
[switch]$silent, | |
[switch]$help | |
) | |
#----------------------------------------------------------------------------- | |
Function ValidateArguments | |
{ | |
if (!$p) | |
{ | |
PrintUsage | |
} | |
if ($help) | |
{ | |
PrintUsage | |
} | |
} | |
#----------------------------------------------------------------------------- | |
Function PrintUsage | |
{ | |
echo @" | |
usage: stop-server.ps1 -p pidfile -f[-help] | |
-p pidfile tracked by server and removed on close. | |
-s Silent. Don't print success/failure data. | |
-f force kill. | |
"@ | |
exit | |
} | |
#----------------------------------------------------------------------------- | |
Function KillProcess | |
{ | |
if (-Not (Test-Path $p)) | |
{ | |
if (-Not ($silent)) | |
{ | |
echo "Error - pidfile not found. Aborting." | |
} | |
exit | |
} | |
$t = @" | |
using System; | |
using System.Diagnostics; | |
using System.IO; | |
using System.Runtime.InteropServices; | |
using System.Threading; | |
namespace PowerStopper | |
{ | |
public static class Stopper | |
{ | |
delegate bool ConsoleCtrlDelegate(CtrlTypes CtrlType); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
static extern bool AttachConsole(uint dwProcessId); | |
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] | |
static extern bool FreeConsole(); | |
enum CtrlTypes : uint | |
{ | |
CTRL_C_EVENT = 0, | |
CTRL_BREAK_EVENT, | |
CTRL_CLOSE_EVENT, | |
CTRL_LOGOFF_EVENT = 5, | |
CTRL_SHUTDOWN_EVENT | |
} | |
[DllImport("kernel32.dll")] | |
[return: MarshalAs(UnmanagedType.Bool)] | |
private static extern bool GenerateConsoleCtrlEvent(CtrlTypes dwCtrlEvent, uint dwProcessGroupId); | |
[DllImport("kernel32.dll")] | |
static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate HandlerRoutine, bool Add); | |
// Our output gets swallowed on ms-dos as we can't re-attach our console to the output of the cmd | |
// running the batch file. | |
public static void StopProgram(int pidToKill, int consolePid, bool silent) | |
{ | |
Process proc = null; | |
try | |
{ | |
proc = Process.GetProcessById(pidToKill); | |
} | |
catch (ArgumentException) | |
{ | |
if (!silent) | |
System.Console.WriteLine("Process " + pidToKill + " not found. Aborting."); | |
return; | |
} | |
if (!FreeConsole()) | |
{ | |
if (!silent) | |
System.Console.WriteLine("Failed to FreeConsole to attach to running cassandra process. Aborting."); | |
return; | |
} | |
if (AttachConsole((uint)pidToKill)) | |
{ | |
//Disable Ctrl-C handling for our program | |
SetConsoleCtrlHandler(null, true); | |
GenerateConsoleCtrlEvent(CtrlTypes.CTRL_C_EVENT, 0); | |
// Must wait here. If we don't and re-enable Ctrl-C | |
// handling below too fast, we might terminate ourselves. | |
bool exited = proc.WaitForExit(30000); | |
if(!exited) | |
proc.Kill(); | |
FreeConsole(); | |
// Re-attach to current console to write output | |
if (consolePid >= 0) | |
AttachConsole((uint)consolePid); | |
// Re-enable Ctrl-C handling or any subsequently started | |
// programs will inherit the disabled state. | |
SetConsoleCtrlHandler(null, false); | |
if (!silent) | |
{ | |
if(exited) | |
System.Console.WriteLine("Successfully sent ctrl+c to process with id: " + pidToKill + "."); | |
else | |
System.Console.WriteLine("Process with id: " + pidToKill + " did not exit after 30 seconds, killed."); | |
} | |
} | |
else | |
{ | |
if (!silent) | |
{ | |
string errorMsg = new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()).Message; | |
System.Console.WriteLine("Error attaching to pid: " + pidToKill + ": " + Marshal.GetLastWin32Error() + " - " + errorMsg); | |
} | |
} | |
} | |
} | |
} | |
"@ | |
# cygwin assumes environment variables are case sensitive which causes problems when | |
# the type dictionary references 'tmp' or 'temp' and throws a System.ArgumentException | |
$oldTmp = $env:TMP | |
$oldTemp = $env:Temp | |
$env:TMP='' | |
$env:TEMP='' | |
Add-Type -TypeDefinition $t | |
$env:TMP = $oldTmp | |
$env:TEMP = $oldTemp | |
$pidToKill = Get-Content $p | |
# If run in cygwin, we don't get the TITLE / pid combo in stop-server.bat but also don't need | |
# to worry about reattaching console output as it gets stderr/stdout even after the C#/C++ | |
# FreeConsole calls. | |
if ($batchpid -eq "No") | |
{ | |
$batchpid = -1 | |
} | |
if ($f) | |
{ | |
taskkill /f /pid $pidToKill | |
} | |
else | |
{ | |
[PowerStopper.Stopper]::StopProgram($pidToKill, $batchpid, $silent) | |
} | |
} | |
#----------------------------------------------------------------------------- | |
ValidateArguments | |
KillProcess |