[Bug 45749] New: Visual Studio 2017 Installer fails due to node.js/ libuv listen(named pipe) error

wine-bugs at winehq.org wine-bugs at winehq.org
Sat Sep 1 05:58:44 CDT 2018


https://bugs.winehq.org/show_bug.cgi?id=45749

            Bug ID: 45749
           Summary: Visual Studio 2017 Installer fails due to
                    node.js/libuv listen(named pipe) error
           Product: Wine
           Version: 3.14
          Hardware: x86-64
                OS: Linux
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: -unknown
          Assignee: wine-bugs at winehq.org
          Reporter: jimbo1qaz at gmail.com
      Distribution: ---

Created attachment 62190
  --> https://bugs.winehq.org/attachment.cgi?id=62190
Visual Studio Installer logs (mostly useless)

Running Kubuntu 18.04 64-bit. I created a 32-bit .wine32 prefix, installed
dotnet46 via winetricks, then the Build Tools for Visual Studio installer
(https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2017
) (which shares the core with VS2017 installer. It's written in node.js and
involves multiple processes communicating via what the source describes as
"pipe"s).

The error is "Hub Controller process exited prematurely with exit code 1
(NodeUncaughtFatalException)."

Interestingly, the installer's source is user-readable and located in
WINEPREFIX / drive_c/Program Files/Microsoft Visual
Studio/Installer/resources/app/node_modules/microsoft-servicehub/host/. I was
able to edit the .js files to add control flow logging:

const fs = require('fs');
function logg(val, args){
        fs.writeFileSync("C:\\"+process.hrtime()[1] + '_'+val, ""+args,
function(err) {});
}

Installer seems to be codenamed "microsoft-servicehub". Strangely, many of the
Installer files appear to test for Windows and/or *nix, despite VS being
Windows-only. It also references open-source VS Code, but I was unable to find
any hits when Googling for quoted strings.

----------

The error message "Hub Controller process exited prematurely with exit code" is
raised from within "drive_c/Program Files/Microsoft Visual
Studio/Installer/resources/app/node_modules/microsoft-servicehub/controllerConnection.js":

ControllerConnection.prototype.startController `var connectPromise` fails,
calling `function (err)` to spawn a process (which crashes).

ControllerConnection.prototype.onControllerProcessExited is responsible for
noticing the process has terminated, and raising the exception.

---------

The actual crash originates from "drive_c/Program Files/Microsoft Visual
Studio/Installer/resources/app/node_modules/microsoft-servicehub/host/HubController.js".

HubController.js is executed by several distinct processes, and `process.pid`
is the same within `function HubController()` and the global scope.

I systematically replaced all calls to `logger.error()` (info, etc.) to
`logg()` (since Logger.error() called within subprocess did not show up in
Visual Studio's debug logs). I get two sequentially numbered files, 1
millisecond apart:

"429988399_Successfully started server on pipe
5d74f51d8cc5d690c261c26d3e9e3408e060bd352875558b9320be9c213e0610"
"430873100_Terminating controller due to some unknown error: " with contents
"listen EINVAL
\\?\pipe\5d74f51d8cc5d690c261c26d3e9e3408e060bd352875558b9320be9c213e0610"

This error is thrown by `function retryStartServer`. `e.code` is EINVAL, while
the code only handles `e.code === ec.EADDRINUSE.code`.

Looking at
https://stackoverflow.com/questions/11040460/get-the-listen-einval-when-starting-up-node-js-server-why
suggests:

> > EINVAL The socket is already connected.
> Make sure something isn't already listening on that port.

`man errno` suggests otherwise: > EINVAL Invalid argument (POSIX.1-2001).

Adding `|| e.code === ec.EINVAL.code` and commenting `process.platform ===
'win32' ||` sends the installer into an endless "kill and restart
HubController" loop, which isn't what we want.

Extra notes on control flow at
https://gist.github.com/jimbo1qaz/1c972422fd5c31706920b9be5c2a0f42#server

--------

The `EINVAL` error comes with a node.js stack trace:
https://gist.github.com/jimbo1qaz/1c972422fd5c31706920b9be5c2a0f42#file-networkstacktrace-txt

(The hexadecimal path originates from
https://gist.github.com/jimbo1qaz/1c972422fd5c31706920b9be5c2a0f42#deterministic-pipe-names
)

https://github.com/nodejs/node/blob/68dff4a67b7222770f90fc5d9bdd884f8db4a24b/lib/internal/errors.js#L340
`const ex = new Error(`${syscall} ${code}${details}`);`
"listen EINVAL
\\?\pipe\5d74f51d8cc5d690c261c26d3e9e3408e060bd352875558b9320be9c213e0610"

I think the call which failed is `listen`, which returns EINVAL for some
reason.

------------

Comparing my stack trace with node.js source code (node.dll 8.9.3):

`Server.listen (net.js:1487:5)` =
https://github.com/nodejs/node/blob/v8.9.3/lib/net.js#L1487
- listenInCluster(server=this, address=pipeName, port=-1, addressType=-1,
    backlog=backlog, fd=undefined, exclusive=options.exclusive);
`listenInCluster (net.js:1392:12)` =
https://github.com/nodejs/node/blob/v8.9.3/lib/net.js#L1392
- server._listen2(=address, =port, =addressType, =backlog, =fd);

Unpeeling layers of abstraction on top of Windows named pipes:

`Server.setupListenHandle [as _listen2] (net.js:1351:14)` =
https://github.com/nodejs/node/blob/v8.9.3/lib/net.js#L1351

(Bullet points are not in stack trace, but I assume they executed)
- `handle = new Pipe(PipeConstants.SERVER);`
https://github.com/nodejs/node/blob/v8.9.3/lib/net.js#L1267
  - `const { Pipe } = process.binding('pipe_wrap');` which refers to C++ code
at https://github.com/nodejs/node/blob/v8.9.3/src/pipe_wrap.cc
    - pipes are handled by https://github.com/libuv/libuv

- `var err = this._handle.listen(backlog || 511);` is the operation which fails
=
https://github.com/nodejs/node/blob/8a44289089a08b7b19fa3c4651b5f1f5d1edd71b/lib/net.js#L1348`
- Routes to https://github.com/nodejs/node/blob/v8.9.3/src/pipe_wrap.cc#L154
- which calls uv_listen(). `return uv_translate_sys_error(err);` and EINVAL is
located at https://github.com/libuv/libuv/blob/v1.x/src/win/error.c#L101 ...
There are 7 Win32 errors all mapping to UV_EINVAL==EINVAL, but only WSAEINVAL
can happen.
- uv_listen() either [assert(0) and sets err=ERROR_INVALID_PARAMETER] (I hope
not), calls uv_tcp_listen (I hope not)
- Most likely calls uv_pipe_listen() =
https://github.com/libuv/libuv/blob/1391a3d9d0996fcf1a116a9c6c58e8b1c7303193/src/win/pipe.c#L938
- It has 4 return paths, only `if (!(handle->flags & UV_HANDLE_BOUND)) { return
WSAEINVAL;` is happening =
https://github.com/libuv/libuv/blob/1391a3d9d0996fcf1a116a9c6c58e8b1c7303193/src/win/pipe.c#L947
- What initializes `handle->flags` without |=UV_HANDLE_BOUND?

ok i'm getting a headache

-- 
Do not reply to this email, post in Bugzilla using the
above URL to reply.
You are receiving this mail because:
You are watching all bug changes.



More information about the wine-bugs mailing list