morganstanley/winss

Svscan not killing children

toenuff opened this issue · 3 comments

I was able to create a scenario where svscan is not killing its children when it is killed.

c:\winss/test/log/run
C:\winss\winss-log.exe -b n20 s1000000 .

c:\winss/test/run
powershell.exe -noprofile -file c:\winss\test\doit.ps1

c:\winss/test/doit.ps1

git clone https://github.com/toenuff/flancy.git
import-module ./flancy/flancy.psd1
$url = "http://localhost:8001"

new-flancy -url $url -webschema @(
    Get '/' { "Welcome to Flancy!" }
    Get '/processes' {
        $processes = Get-Process
        $processes |select name, id |convertto-json
    }
)
while ($true) {
    "stayin alive"
    sleep 1000
}

I'm running a command prompt and then calling

cd c:\winss
winss-svscan

Everything works, but when I Ctrl-C from svscan, it is not killing the children.

Another weird thing is that the Flancy web server is getting started in the cmd.exe process rather than in the Powershell process I'm spawning in test/run. When I kill the supervisors manually, the web server is still running until I close the cmd window. Also the cmd window cannot exit cleanly - I need to force it.

It appears that Ctrl-C is passing Ctrl-Break as it should by winss. However, it's not killing PowerShell. Running with powershell.exe -version 2 appears to work fine (note flancy won't work in that case, but you can duplicate this issue with a simpler doit.ps1

c:\winss\test\doit.ps1

while ($true) {
    "stayin alive"
    sleep 1000
}

This is either a PowerShell issue or we should enable a configuration mode where we can have ctrl-c pass a kill instead of ctrl-break signal.

Some background; we need to tell the child process to exit gracefully and because of the lack of signals on Windows we use the CTRL+C event. The CTRL+C event will occur in all child processes. The way we can reliably close the parent and child and exit gracefully is to create the child processes in a new process group. Unfortunately, for no apparent reason, the parent process cannot send a CTRL+C event to the child in the new process group:

https://msdn.microsoft.com/en-us/library/windows/desktop/ms683155(v=vs.85).aspx
Generates a CTRL+C signal. This signal cannot be generated for process groups. If dwProcessGroupId is nonzero, this function will succeed, but the CTRL+C signal will not be received by processes within the specified process group.

So the only option is to capture the CTRL+C and send CTRL+BREAK to children.

Closing this ticket as it appears to be a PowerShell issue.

Attempting this:

$code = @"

using System;
using System.Runtime.InteropServices;
using System.Management.Automation;
using System.Management.Automation.Runspaces;

namespace MyNamespace
{
    public static class MyClass
    {
        public static void SetHandler()
        {
            SetConsoleCtrlHandler(new HandlerRoutine(ConsoleCtrlCheck), true);
        }

        private static bool ConsoleCtrlCheck(CtrlTypes ctrlType)
        {
            switch (ctrlType)
            {
                case CtrlTypes.CTRL_C_EVENT:
                    Console.WriteLine("CTRL+C received!");
                    return true;

                case CtrlTypes.CTRL_BREAK_EVENT:
                    Console.WriteLine("CTRL+BREAK received!");
                    return true;
            }

            return false;
        }

        [DllImport("Kernel32")]
        public static extern bool SetConsoleCtrlHandler(HandlerRoutine Handler, bool Add);

        public delegate bool HandlerRoutine(CtrlTypes CtrlType);

        public enum CtrlTypes
        {
            CTRL_C_EVENT = 0,
            CTRL_BREAK_EVENT,
            CTRL_CLOSE_EVENT,
            CTRL_LOGOFF_EVENT = 5,
            CTRL_SHUTDOWN_EVENT
        }
    }
}

"@

Add-Type -TypeDefinition $code -Language CSharp
[MyNamespace.MyClass]::SetHandler()

while ($true) {
"stayin alive"
sleep 1000
}