Skip to content

Commit

Permalink
Set session socket into environment variable
Browse files Browse the repository at this point in the history
While I was at it I added a CLI flag to override the default.  I also
swapped the default to --user-data-dir.

The value is set on an environment variable so it can be used by the
extension host similar to VSCODE_IPC_HOOK_CLI.
  • Loading branch information
code-asher committed Jun 21, 2023
1 parent 56d10d8 commit def0807
Show file tree
Hide file tree
Showing 9 changed files with 65 additions and 48 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ Code v99.99.999

Code v1.79.2

### Fixed

- Fix being unable to launch multiple instances of code-server for different
users.

### Added

- `--session-socket` CLI flag to configure the location of the session socket.
By default it will be placed in `--user-data-dir`.

## [4.14.0](https://github.com/coder/code-server/releases/tag/v4.14.0) - 2023-06-16

Code v1.79.2
Expand Down
8 changes: 4 additions & 4 deletions patches/store-socket.diff
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ Index: code-server/lib/vscode/src/vs/workbench/api/node/extHostExtensionService.

+ (async () => {
+ const socketPath = process.env['VSCODE_IPC_HOOK_CLI'];
+ if (!socketPath) {
+ const codeServerSocketPath = process.env['CODE_SERVER_SESSION_SOCKET']
+ if (!socketPath || !codeServerSocketPath) {
+ return;
+ }
+ const workspace = this._instaService.invokeFunction((accessor) => {
Expand All @@ -52,7 +53,6 @@ Index: code-server/lib/vscode/src/vs/workbench/api/node/extHostExtensionService.
+ socketPath
+ };
+ const message = JSON.stringify({entry});
+ const codeServerSocketPath = path.join(os.tmpdir(), 'code-server-ipc.sock');
+ await new Promise<void>((resolve, reject) => {
+ const opts: _http.RequestOptions = {
+ path: '/add-session',
Expand Down Expand Up @@ -109,12 +109,12 @@ Index: code-server/lib/vscode/src/vs/workbench/api/node/extensionHostProcess.ts
+ onTerminate = (reason: string) => {
+ extensionHostMain.terminate(reason);
+
+ const codeServerSocketPath = process.env['CODE_SERVER_SESSION_SOCKET']
+ const socketPath = process.env['VSCODE_IPC_HOOK_CLI'];
+ if (!socketPath) {
+ if (!socketPath || !codeServerSocketPath) {
+ return;
+ }
+ const message = JSON.stringify({socketPath});
+ const codeServerSocketPath = path.join(os.tmpdir(), 'code-server-ipc.sock');
+ const opts: _http.RequestOptions = {
+ path: '/delete-session',
+ socketPath: codeServerSocketPath,
Expand Down
4 changes: 2 additions & 2 deletions src/node/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import * as util from "../common/util"
import { DefaultedArgs } from "./cli"
import { disposer } from "./http"
import { isNodeJSErrnoException } from "./util"
import { DEFAULT_SOCKET_PATH, EditorSessionManager, makeEditorSessionManagerServer } from "./vscodeSocket"
import { EditorSessionManager, makeEditorSessionManagerServer } from "./vscodeSocket"
import { handleUpgrade } from "./wsRouter"

type SocketOptions = { socket: string; "socket-mode"?: string }
Expand Down Expand Up @@ -88,7 +88,7 @@ export const createApp = async (args: DefaultedArgs): Promise<App> => {
handleUpgrade(wsRouter, server)

const editorSessionManager = new EditorSessionManager()
const editorSessionManagerServer = await makeEditorSessionManagerServer(DEFAULT_SOCKET_PATH, editorSessionManager)
const editorSessionManagerServer = await makeEditorSessionManagerServer(args["session-socket"], editorSessionManager)
const disposeEditorSessionManagerServer = disposer(editorSessionManagerServer)

const dispose = async () => {
Expand Down
19 changes: 16 additions & 3 deletions src/node/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { load } from "js-yaml"
import * as os from "os"
import * as path from "path"
import { generateCertificate, generatePassword, humanPath, paths, splitOnFirstEquals } from "./util"
import { DEFAULT_SOCKET_PATH, EditorSessionManagerClient } from "./vscodeSocket"
import { EditorSessionManagerClient } from "./vscodeSocket"

export enum Feature {
// No current experimental features!
Expand Down Expand Up @@ -51,6 +51,7 @@ export interface UserProvidedCodeArgs {
"disable-file-downloads"?: boolean
"disable-workspace-trust"?: boolean
"disable-getting-started-override"?: boolean
"session-socket"?: string
}

/**
Expand Down Expand Up @@ -160,6 +161,9 @@ export const options: Options<Required<UserProvidedArgs>> = {
"Disable update check. Without this flag, code-server checks every 6 hours against the latest github release and \n" +
"then notifies you once every week that a new release is available.",
},
"session-socket": {
type: "string",
},
"disable-file-downloads": {
type: "boolean",
description:
Expand Down Expand Up @@ -459,6 +463,7 @@ export interface DefaultedArgs extends ConfigArgs {
usingEnvHashedPassword: boolean
"extensions-dir": string
"user-data-dir": string
"session-socket": string
/* Positional arguments. */
_: string[]
}
Expand All @@ -479,6 +484,11 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config
args["extensions-dir"] = path.join(args["user-data-dir"], "extensions")
}

if (!args["session-socket"]) {
args["session-socket"] = path.join(args["user-data-dir"], "code-server-ipc.sock")
}
process.env.CODE_SERVER_SESSION_SOCKET = args["session-socket"]

// --verbose takes priority over --log and --log takes priority over the
// environment variable.
if (args.verbose) {
Expand Down Expand Up @@ -739,15 +749,18 @@ function bindAddrFromAllSources(...argsConfig: UserProvidedArgs[]): Addr {
* existing instance. The arguments here should be the arguments the user
* explicitly passed on the command line, *NOT DEFAULTS* or the configuration.
*/
export const shouldOpenInExistingInstance = async (args: UserProvidedArgs): Promise<string | undefined> => {
export const shouldOpenInExistingInstance = async (
args: UserProvidedArgs,
sessionSocket: string,
): Promise<string | undefined> => {
// Always use the existing instance if we're running from VS Code's terminal.
if (process.env.VSCODE_IPC_HOOK_CLI) {
logger.debug("Found VSCODE_IPC_HOOK_CLI")
return process.env.VSCODE_IPC_HOOK_CLI
}

const paths = getResolvedPathsFromArgs(args)
const client = new EditorSessionManagerClient(DEFAULT_SOCKET_PATH)
const client = new EditorSessionManagerClient(sessionSocket)

// If we can't connect to the socket then there's no existing instance.
if (!(await client.canConnect())) {
Expand Down
2 changes: 1 addition & 1 deletion src/node/entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ async function entry(): Promise<void> {
return runCodeCli(args)
}

const socketPath = await shouldOpenInExistingInstance(cliArgs)
const socketPath = await shouldOpenInExistingInstance(cliArgs, args["session-socket"])
if (socketPath) {
logger.debug("Trying to open in existing instance")
return openInExistingInstance(args, socketPath)
Expand Down
5 changes: 1 addition & 4 deletions src/node/vscodeSocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@ import * as http from "http"
import * as path from "path"
import { HttpCode } from "../common/http"
import { listen } from "./app"
import { canConnect, paths } from "./util"

// Socket path of the daemonized code-server instance.
export const DEFAULT_SOCKET_PATH = path.join(paths.data, `code-server-ipc.sock`)
import { canConnect } from "./util"

export interface EditorSessionEntry {
workspace: {
Expand Down
51 changes: 29 additions & 22 deletions test/unit/node/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import {
import { shouldSpawnCliProcess } from "../../../src/node/main"
import { generatePassword, paths } from "../../../src/node/util"
import {
DEFAULT_SOCKET_PATH,
EditorSessionManager,
EditorSessionManagerClient,
makeEditorSessionManagerServer,
Expand All @@ -37,6 +36,7 @@ const defaults = {
usingEnvHashedPassword: false,
"extensions-dir": path.join(paths.data, "extensions"),
"user-data-dir": paths.data,
"session-socket": path.join(paths.data, "code-server-ipc.sock"),
_: [],
}

Expand Down Expand Up @@ -103,6 +103,8 @@ describe("parser", () => {

"--disable-getting-started-override",

["--session-socket", "/tmp/override-code-server-ipc-socket"],

["--host", "0.0.0.0"],
"4",
"--",
Expand Down Expand Up @@ -136,6 +138,7 @@ describe("parser", () => {
"welcome-text": "welcome to code",
version: true,
"bind-addr": "192.169.0.1:8080",
"session-socket": "/tmp/override-code-server-ipc-socket",
})
})

Expand Down Expand Up @@ -504,22 +507,23 @@ describe("cli", () => {
it("should use existing if inside code-server", async () => {
process.env.VSCODE_IPC_HOOK_CLI = "test"
const args: UserProvidedArgs = {}
expect(await shouldOpenInExistingInstance(args)).toStrictEqual("test")
expect(await shouldOpenInExistingInstance(args, "")).toStrictEqual("test")

args.port = 8081
args._ = ["./file"]
expect(await shouldOpenInExistingInstance(args)).toStrictEqual("test")
expect(await shouldOpenInExistingInstance(args, "")).toStrictEqual("test")
})

it("should use existing if --reuse-window is set", async () => {
const server = await makeEditorSessionManagerServer(DEFAULT_SOCKET_PATH, new EditorSessionManager())
const sessionSocket = path.join(tmpDirPath, "session-socket")
const server = await makeEditorSessionManagerServer(sessionSocket, new EditorSessionManager())

const args: UserProvidedArgs = {}
args["reuse-window"] = true
await expect(shouldOpenInExistingInstance(args)).resolves.toStrictEqual(undefined)
await expect(shouldOpenInExistingInstance(args, sessionSocket)).resolves.toStrictEqual(undefined)

const socketPath = path.join(tmpDirPath, "socket")
const client = new EditorSessionManagerClient(DEFAULT_SOCKET_PATH)
const client = new EditorSessionManagerClient(sessionSocket)
await client.addSession({
entry: {
workspace: {
Expand All @@ -537,24 +541,25 @@ describe("cli", () => {
})
const vscodeSockets = listenOn(socketPath)

await expect(shouldOpenInExistingInstance(args)).resolves.toStrictEqual(socketPath)
await expect(shouldOpenInExistingInstance(args, sessionSocket)).resolves.toStrictEqual(socketPath)

args.port = 8081
await expect(shouldOpenInExistingInstance(args)).resolves.toStrictEqual(socketPath)
await expect(shouldOpenInExistingInstance(args, sessionSocket)).resolves.toStrictEqual(socketPath)

server.close()
vscodeSockets.close()
})

it("should use existing if --new-window is set", async () => {
const server = await makeEditorSessionManagerServer(DEFAULT_SOCKET_PATH, new EditorSessionManager())
const sessionSocket = path.join(tmpDirPath, "session-socket")
const server = await makeEditorSessionManagerServer(sessionSocket, new EditorSessionManager())

const args: UserProvidedArgs = {}
args["new-window"] = true
await expect(shouldOpenInExistingInstance(args)).resolves.toStrictEqual(undefined)
await expect(shouldOpenInExistingInstance(args, sessionSocket)).resolves.toStrictEqual(undefined)

const socketPath = path.join(tmpDirPath, "socket")
const client = new EditorSessionManagerClient(DEFAULT_SOCKET_PATH)
const client = new EditorSessionManagerClient(sessionSocket)
await client.addSession({
entry: {
workspace: {
Expand All @@ -572,25 +577,26 @@ describe("cli", () => {
})
const vscodeSockets = listenOn(socketPath)

expect(await shouldOpenInExistingInstance(args)).toStrictEqual(socketPath)
expect(await shouldOpenInExistingInstance(args, sessionSocket)).toStrictEqual(socketPath)

args.port = 8081
expect(await shouldOpenInExistingInstance(args)).toStrictEqual(socketPath)
expect(await shouldOpenInExistingInstance(args, sessionSocket)).toStrictEqual(socketPath)

server.close()
vscodeSockets.close()
})

it("should use existing if no unrelated flags are set, has positional, and socket is active", async () => {
const server = await makeEditorSessionManagerServer(DEFAULT_SOCKET_PATH, new EditorSessionManager())
const sessionSocket = path.join(tmpDirPath, "session-socket")
const server = await makeEditorSessionManagerServer(sessionSocket, new EditorSessionManager())

const args: UserProvidedArgs = {}
expect(await shouldOpenInExistingInstance(args)).toStrictEqual(undefined)
expect(await shouldOpenInExistingInstance(args, sessionSocket)).toStrictEqual(undefined)

args._ = ["./file"]
expect(await shouldOpenInExistingInstance(args)).toStrictEqual(undefined)
expect(await shouldOpenInExistingInstance(args, sessionSocket)).toStrictEqual(undefined)

const client = new EditorSessionManagerClient(DEFAULT_SOCKET_PATH)
const client = new EditorSessionManagerClient(sessionSocket)
const socketPath = path.join(tmpDirPath, "socket")
await client.addSession({
entry: {
Expand All @@ -609,18 +615,19 @@ describe("cli", () => {
})
const vscodeSockets = listenOn(socketPath)

expect(await shouldOpenInExistingInstance(args)).toStrictEqual(socketPath)
expect(await shouldOpenInExistingInstance(args, sessionSocket)).toStrictEqual(socketPath)

args.port = 8081
expect(await shouldOpenInExistingInstance(args)).toStrictEqual(undefined)
expect(await shouldOpenInExistingInstance(args, sessionSocket)).toStrictEqual(undefined)

server.close()
vscodeSockets.close()
})

it("should prefer matching sessions for only the first path", async () => {
const server = await makeEditorSessionManagerServer(DEFAULT_SOCKET_PATH, new EditorSessionManager())
const client = new EditorSessionManagerClient(DEFAULT_SOCKET_PATH)
const sessionSocket = path.join(tmpDirPath, "session-socket")
const server = await makeEditorSessionManagerServer(sessionSocket, new EditorSessionManager())
const client = new EditorSessionManagerClient(sessionSocket)
await client.addSession({
entry: {
workspace: {
Expand Down Expand Up @@ -655,7 +662,7 @@ describe("cli", () => {

const args: UserProvidedArgs = {}
args._ = ["/aaa/file", "/bbb/file"]
expect(await shouldOpenInExistingInstance(args)).toStrictEqual(`${tmpDirPath}/vscode-ipc-aaa.sock`)
expect(await shouldOpenInExistingInstance(args, sessionSocket)).toStrictEqual(`${tmpDirPath}/vscode-ipc-aaa.sock`)

server.close()
})
Expand Down
1 change: 1 addition & 0 deletions test/unit/node/plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ describe("plugin", () => {
usingEnvHashedPassword: false,
"extensions-dir": "",
"user-data-dir": "",
"session-socket": "",
}
next()
}
Expand Down
13 changes: 1 addition & 12 deletions test/unit/node/vscodeSocket.test.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,8 @@
import { logger } from "@coder/logger"
import * as app from "../../../src/node/app"
import { paths } from "../../../src/node/util"
import {
DEFAULT_SOCKET_PATH,
EditorSessionManager,
makeEditorSessionManagerServer,
} from "../../../src/node/vscodeSocket"
import { EditorSessionManager, makeEditorSessionManagerServer } from "../../../src/node/vscodeSocket"
import { clean, tmpdir, listenOn, mockLogger } from "../../utils/helpers"

describe("DEFAULT_SOCKET_PATH", () => {
it("should be a unique path per user", () => {
expect(DEFAULT_SOCKET_PATH.startsWith(paths.data)).toBe(true)
})
})

describe("makeEditorSessionManagerServer", () => {
let tmpDirPath: string

Expand Down

0 comments on commit def0807

Please sign in to comment.