I wrote a program called winestreamproxy [1] that proxys data between a named pipe in Wine and a unix domain socket outside of Wine. This program should automatically exit when no other non-system program is running in the bottle/prefix if the parameter --system is supplied. For this purpose it marks itself as a system process using __wine_make_process_system() from ntdll, and waits on the returned event handle to exit.
This worked fine, until a change was introduced that seemingly made every non-system process spawn a conhost.exe instance. Since conhost.exe is not a system process, this breaks the auto-exit functionality of winestreamproxy. I understand that using Wine-internal functions is not supported, however the desired behavior seems hard if not impossible to implement otherwise.
Making conhost mark itself as a system process would allow the automatic exit to work again. I don't think this would break anything, because at the point in the bottle/prefix lifecycle where this becomes relevant there are no non-system processes anymore, and anything else has either explicitly opted into this behavior or not received a conhost instance in the first place.
As to why winestreamproxy is necessary at all: It allows Windows games running under Wine to communicate with Linux-native Discord [2] and show status messages and invite buttons on a user's profile.
[1] https://github.com/openglfreak/winestreamproxy [2] https://discord.com/
Signed-off-by: Torge Matthies openglfreak@googlemail.com --- programs/conhost/conhost.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/programs/conhost/conhost.c b/programs/conhost/conhost.c index 6985a9ca0b0..46b48d086d4 100644 --- a/programs/conhost/conhost.c +++ b/programs/conhost/conhost.c @@ -42,6 +42,8 @@ static CRITICAL_SECTION console_section = { &critsect_debug, -1, 0, 0, 0, 0 }; static void *ioctl_buffer; static size_t ioctl_buffer_size;
+extern HANDLE CDECL __wine_make_process_system(void); + static void *alloc_ioctl_buffer( size_t size ) { if (size > ioctl_buffer_size) @@ -2526,7 +2528,7 @@ static NTSTATUS process_console_ioctls( struct console *console ) static int main_loop( struct console *console, HANDLE signal ) { HANDLE signal_event = NULL; - HANDLE wait_handles[3]; + HANDLE wait_handles[4]; unsigned int wait_cnt = 0; unsigned short signal_id; IO_STATUS_BLOCK signal_io; @@ -2544,6 +2546,7 @@ static int main_loop( struct console *console, HANDLE signal )
if (!alloc_ioctl_buffer( 4096 )) return 1;
+ wait_handles[wait_cnt++] = __wine_make_process_system(); wait_handles[wait_cnt++] = console->server; if (signal) wait_handles[wait_cnt++] = signal_event; if (console->input_thread) wait_handles[wait_cnt++] = console->input_thread; @@ -2570,13 +2573,14 @@ static int main_loop( struct console *console, HANDLE signal ) switch (res) { case WAIT_OBJECT_0: + case WAIT_OBJECT_0 + 1: EnterCriticalSection( &console_section ); status = process_console_ioctls( console ); LeaveCriticalSection( &console_section ); - if (status) return 0; + if (status || res == WAIT_OBJECT_0) return 0; break;
- case WAIT_OBJECT_0 + 1: + case WAIT_OBJECT_0 + 2: if (signal_io.Status || signal_io.Information != sizeof(signal_id)) { TRACE( "signaled quit\n" );
Hi Torge,
On 11/2/20 10:34 PM, Torge Matthies wrote:
I understand that using Wine-internal functions is not supported, however the desired behavior seems hard if not impossible to implement otherwise.
You should be able to achieve that by implementing your application as a Windows service.
For a quick workaround would be to call FreeConsole() in your application. This way console will become unused and conhost will exit earlier.
If you wrap execution with a script, you could also make sure that none of std handles (in, out, err) is a terminal. In this case conhost will not be created in the first place.
Jacek
Hello Jacek, thank you very much for your reply!
On 11/4/20 6:04 PM, Jacek Caban wrote:
Hi Torge,
On 11/2/20 10:34 PM, Torge Matthies wrote:
I understand that using Wine-internal functions is not supported, however the desired behavior seems hard if not impossible to implement otherwise.
You should be able to achieve that by implementing your application as a Windows service.
I tried that a long time ago, but it turned out that Wine does not wait for services to finish starting up before starting the game process, so the proxy was usually only ready when it was too late, and the game had already given up on connecting to Discord at that point.
For a quick workaround would be to call FreeConsole() in your application. This way console will become unused and conhost will exit earlier.
This was also one of the first things I tried when I noticed the change in behavior. It did not work for some reason, the conhost process stayed around. The process also daemonizes itself after becoming ready to accept connections, by CreateProcess'ing its executable and in the child process signaling the parent process to exit after becoming ready. But I have also tried with this functionality disabled and got the same conhost process.
If you wrap execution with a script, you could also make sure that none of std handles (in, out, err) is a terminal. In this case conhost will not be created in the first place.
This works, but it's not ideal since I would like to print log messages to stdout/stderr. This has also been broken for some time with the daemonize option enabled, and I've actually been meaning to e-mail you about that. But I'm bad at initiating conversation, so I never sent that e-mail... Do you happen to know why I can't write to stdout/stderr in a child process?
Jacek
I'm also reachable on freenode #winehackers under the name openglfreak, if you want to reach me there. You can ping me even if I'm not online.
Best regards, Torge Matthies
On 11/4/20 11:34 PM, Torge Matthies wrote:
Hello Jacek, thank you very much for your reply!
On 11/4/20 6:04 PM, Jacek Caban wrote:
Hi Torge,
On 11/2/20 10:34 PM, Torge Matthies wrote:
I understand that using Wine-internal functions is not supported, however the desired behavior seems hard if not impossible to implement otherwise.
You should be able to achieve that by implementing your application as a Windows service.
I tried that a long time ago, but it turned out that Wine does not wait for services to finish starting up before starting the game process, so the proxy was usually only ready when it was too late, and the game had already given up on connecting to Discord at that point.
wineboot waits until all autostart services are started, see SVCCTL_STARTED_EVENT event handing. You just need to make sure to create the pipe before signaling service as started.
For a quick workaround would be to call FreeConsole() in your application. This way console will become unused and conhost will exit earlier.
This was also one of the first things I tried when I noticed the change in behavior. It did not work for some reason, the conhost process stayed around. The process also daemonizes itself after becoming ready to accept connections, by CreateProcess'ing its executable and in the child process signaling the parent process to exit after becoming ready. But I have also tried with this functionality disabled and got the same conhost process.
conhost should terminate in that case, assuming that no process is connected to it anymore. If it doesn't, that's a bug. I have some fixes planned for that area.
If you wrap execution with a script, you could also make sure that none of std handles (in, out, err) is a terminal. In this case conhost will not be created in the first place.
This works, but it's not ideal since I would like to print log messages to stdout/stderr. This has also been broken for some time with the daemonize option enabled, and I've actually been meaning to e-mail you about that. But I'm bad at initiating conversation, so I never sent that e-mail... Do you happen to know why I can't write to stdout/stderr in a child process?
You can also pipe output so that it's not a terminal, something like |cat should do.
Jacek