6
6
# - Verifying the existence of dependencies like PowerShellGet
7
7
# - Verifying that the expected version of the PowerShellEditorServices module is installed
8
8
# - Installing the PowerShellEditorServices module if confirmed by the user
9
- # - Finding unused TCP port numbers for the language and debug services to use
9
+ # - Creating named pipes for the language and debug services to use (if using named pipes)
10
10
# - Starting the language and debug services from the PowerShellEditorServices module
11
11
#
12
12
# NOTE: If editor integration authors make modifications to this
39
39
[ValidateNotNullOrEmpty ()]
40
40
$LogPath ,
41
41
42
- [ValidateSet (" Diagnostic" , " Normal" , " Verbose" , " Error" , " Diagnostic " )]
42
+ [ValidateSet (" Diagnostic" , " Normal" , " Verbose" , " Error" )]
43
43
$LogLevel ,
44
44
45
45
[Parameter (Mandatory = $true )]
75
75
$DebugServicePipeName = $null
76
76
)
77
77
78
- $minPortNumber = 10000
79
- $maxPortNumber = 30000
78
+ $DEFAULT_USER_MODE = " 600"
80
79
81
80
if ($LogLevel -eq " Diagnostic" ) {
82
81
$VerbosePreference = ' Continue'
@@ -168,67 +167,71 @@ function Test-ModuleAvailable($ModuleName, $ModuleVersion) {
168
167
return $false ;
169
168
}
170
169
171
- function Test-PortAvailability {
170
+ function New-NamedPipeName {
171
+
172
+ # We try 10 times to find a valid pipe name
173
+ for ($i = 0 ; $i -lt 10 ; $i ++ ) {
174
+ # add a guid to make the pipe unique
175
+ $PipeName = " PSES_$ ( (New-Guid ).Guid) "
176
+
177
+ if ((Test-NamedPipeName - PipeName $PipeName )) {
178
+ return $PipeName
179
+ }
180
+ }
181
+ ExitWithError " Could not find valid a pipe name."
182
+ }
183
+
184
+ function Get-NamedPipePath {
172
185
param (
173
186
[Parameter (Mandatory = $true )]
174
- [int ]
175
- $PortNumber
187
+ [ValidateNotNullOrEmpty ()]
188
+ [string ]
189
+ $PipeName
176
190
)
177
191
178
- $portAvailable = $true
179
-
180
- try {
181
- # After some research, I don't believe we should run into problems using an IPv4 port
182
- # that happens to be in use via an IPv6 address. That is based on this info:
183
- # https://www.ibm.com/support/knowledgecenter/ssw_i5_54/rzai2/rzai2compipv4ipv6.htm#rzai2compipv4ipv6__compports
184
- $ipAddress = [System.Net.IPAddress ]::Loopback
185
- Log " Testing availability of port ${PortNumber} at address ${ipAddress} / $ ( $ipAddress.AddressFamily ) "
186
-
187
- $tcpListener = Microsoft.PowerShell.Utility\New-Object System.Net.Sockets.TcpListener @ ($ipAddress , $PortNumber )
188
- $tcpListener.Start ()
189
- $tcpListener.Stop ()
192
+ if (-not $IsLinux -and -not $IsMacOS ) {
193
+ return " \\.\pipe\$PipeName " ;
190
194
}
191
- catch [System.Net.Sockets.SocketException ] {
192
- $portAvailable = $false
193
-
194
- # Check the SocketErrorCode to see if it's the expected exception
195
- if ($_.Exception.SocketErrorCode -eq [System.Net.Sockets.SocketError ]::AddressAlreadyInUse) {
196
- Log " Port $PortNumber is in use."
197
- }
198
- else {
199
- Log " SocketException on port ${PortNumber} : $ ( $_.Exception ) "
200
- }
195
+ else {
196
+ # Windows uses NamedPipes where non-Windows platforms use Unix Domain Sockets.
197
+ # the Unix Domain Sockets live in the tmp directory and are prefixed with "CoreFxPipe_"
198
+ return (Join-Path - Path ([System.IO.Path ]::GetTempPath()) - ChildPath " CoreFxPipe_$PipeName " )
201
199
}
202
200
203
- $portAvailable
204
201
}
205
202
206
- $portsInUse = @ {}
207
- $rand = Microsoft.PowerShell.Utility\New-Object System.Random
208
- function Get-AvailablePort () {
209
- $triesRemaining = 10 ;
210
-
211
- while ($triesRemaining -gt 0 ) {
212
- do {
213
- $port = $rand.Next ($minPortNumber , $maxPortNumber )
214
- }
215
- while ($portsInUse.ContainsKey ($port ))
216
-
217
- # Whether we succeed or fail, don't try this port again
218
- $portsInUse [$port ] = 1
203
+ # Returns True if it's a valid pipe name
204
+ # A valid pipe name is a file that does not exist either
205
+ # in the temp directory (macOS & Linux) or in the pipe directory (Windows)
206
+ function Test-NamedPipeName {
207
+ param (
208
+ [Parameter (Mandatory = $true )]
209
+ [ValidateNotNullOrEmpty ()]
210
+ [string ]
211
+ $PipeName
212
+ )
219
213
220
- Log " Checking port: $port , attempts remaining $triesRemaining --------------------"
221
- if ((Test-PortAvailability - PortNumber $port ) -eq $true ) {
222
- Log " Port: $port is available"
223
- return $port
224
- }
214
+ $path = Get-NamedPipePath - PipeName $PipeName
215
+ return -not (Test-Path $path )
216
+ }
225
217
226
- Log " Port: $port is NOT available"
227
- $triesRemaining -- ;
218
+ function Set-NamedPipeMode {
219
+ param (
220
+ [Parameter (Mandatory = $true )]
221
+ [ValidateNotNullOrEmpty ()]
222
+ [string ]
223
+ $PipeFile
224
+ )
225
+ chmod $DEFAULT_USER_MODE $PipeFile
226
+ if ($IsLinux ) {
227
+ $mode = stat - c " %A" $PipeFile
228
+ }
229
+ else {
230
+ $mode = stat -f " %A" $PipeFile
231
+ }
232
+ if ($mode -ne $DEFAULT_USER_MODE ) {
233
+ ExitWithError " Permissions to the pipe file were not set properly. Expected: $DEFAULT_USER_MODE Actual: $mode for file: $PipeFile "
228
234
}
229
-
230
- Log " Did not find any available ports!!"
231
- return $null
232
235
}
233
236
234
237
# Add BundledModulesPath to $env:PSModulePath
@@ -251,21 +254,36 @@ try {
251
254
252
255
Microsoft.PowerShell.Core\Import-Module PowerShellEditorServices - ErrorAction Stop
253
256
254
- # Locate available port numbers for services
255
- # There could be only one service on Stdio channel
256
-
257
- $languageServiceTransport = $null
258
- $debugServiceTransport = $null
257
+ # Locate available port numbers for services
258
+ # There could be only one service on Stdio channel
259
259
260
- if ($Stdio.IsPresent -and -not $DebugServiceOnly.IsPresent ) { $languageServiceTransport = " Stdio" }
261
- elseif ($LanguageServicePipeName ) { $languageServiceTransport = " NamedPipe" ; $languageServicePipeName = " $LanguageServicePipeName " }
262
- elseif ($languageServicePort = Get-AvailablePort ) { $languageServiceTransport = " Tcp" }
263
- else { ExitWithError " Failed to find an open socket port for language service." }
260
+ $languageServiceTransport = $null
261
+ $debugServiceTransport = $null
264
262
265
- if ($Stdio.IsPresent -and $DebugServiceOnly.IsPresent ) { $debugServiceTransport = " Stdio" }
266
- elseif ($DebugServicePipeName ) { $debugServiceTransport = " NamedPipe" ; $debugServicePipeName = " $DebugServicePipeName " }
267
- elseif ($debugServicePort = Get-AvailablePort ) { $debugServiceTransport = " Tcp" }
268
- else { ExitWithError " Failed to find an open socket port for debug service." }
263
+ if ($Stdio.IsPresent ) {
264
+ $languageServiceTransport = " Stdio"
265
+ $debugServiceTransport = " Stdio"
266
+ }
267
+ else {
268
+ $languageServiceTransport = " NamedPipe"
269
+ $debugServiceTransport = " NamedPipe"
270
+ if (-not $LanguageServicePipeName ) {
271
+ $LanguageServicePipeName = New-NamedPipeName
272
+ }
273
+ else {
274
+ if (-not (Test-NamedPipeName - PipeName $LanguageServicePipeName )) {
275
+ ExitWithError " Pipe name supplied is already taken: $LanguageServicePipeName "
276
+ }
277
+ }
278
+ if (-not $DebugServicePipeName ) {
279
+ $DebugServicePipeName = New-NamedPipeName
280
+ }
281
+ else {
282
+ if (-not (Test-NamedPipeName - PipeName $DebugServicePipeName )) {
283
+ ExitWithError " Pipe name supplied is already taken: $DebugServicePipeName "
284
+ }
285
+ }
286
+ }
269
287
270
288
if ($EnableConsoleRepl ) {
271
289
Write-Host " PowerShell Integrated Console`n "
@@ -281,11 +299,9 @@ try {
281
299
- LogPath $LogPath `
282
300
- LogLevel $LogLevel `
283
301
- AdditionalModules $AdditionalModules `
284
- - LanguageServicePort $languageServicePort `
285
- - DebugServicePort $debugServicePort `
286
302
- LanguageServiceNamedPipe $LanguageServicePipeName `
287
303
- DebugServiceNamedPipe $DebugServicePipeName `
288
- - Stdio:$ Stdio.IsPresent `
304
+ - Stdio:( $TransportType -eq " Stdio" ) `
289
305
- BundledModulesPath $BundledModulesPath `
290
306
- EnableConsoleRepl:$EnableConsoleRepl.IsPresent `
291
307
- DebugServiceOnly:$DebugServiceOnly.IsPresent `
@@ -300,11 +316,18 @@ try {
300
316
" debugServiceTransport" = $debugServiceTransport ;
301
317
};
302
318
303
- if ($languageServicePipeName ) { $resultDetails [" languageServicePipeName" ] = " $languageServicePipeName " }
304
- if ($debugServicePipeName ) { $resultDetails [" debugServicePipeName" ] = " $debugServicePipeName " }
305
-
306
- if ($languageServicePort ) { $resultDetails [" languageServicePort" ] = $languageServicePort }
307
- if ($debugServicePort ) { $resultDetails [" debugServicePort" ] = $debugServicePort }
319
+ if ($LanguageServicePipeName ) {
320
+ $resultDetails [" languageServicePipeName" ] = Get-NamedPipePath - PipeName $LanguageServicePipeName
321
+ if ($IsLinux -or $IsMacOS ) {
322
+ Set-NamedPipeMode - PipeFile $resultDetails [" languageServicePipeName" ]
323
+ }
324
+ }
325
+ if ($DebugServicePipeName ) {
326
+ $resultDetails [" debugServicePipeName" ] = Get-NamedPipePath - PipeName $DebugServicePipeName
327
+ if ($IsLinux -or $IsMacOS ) {
328
+ Set-NamedPipeMode - PipeFile $resultDetails [" debugServicePipeName" ]
329
+ }
330
+ }
308
331
309
332
# Notify the client that the services have started
310
333
WriteSessionFile $resultDetails
0 commit comments