parent
247c4ec776
commit
016daf2fdd
@ -154,19 +154,22 @@ const main = async (args: DefaultedArgs): Promise<void> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function entry(): Promise<void> {
|
async function entry(): Promise<void> {
|
||||||
const cliArgs = parse(process.argv.slice(2))
|
|
||||||
const configArgs = await readConfigFile(cliArgs.config)
|
|
||||||
const args = await setDefaults(cliArgs, configArgs)
|
|
||||||
|
|
||||||
// There's no need to check flags like --help or to spawn in an existing
|
// There's no need to check flags like --help or to spawn in an existing
|
||||||
// instance for the child process because these would have already happened in
|
// instance for the child process because these would have already happened in
|
||||||
// the parent and the child wouldn't have been spawned.
|
// the parent and the child wouldn't have been spawned. We also get the
|
||||||
|
// arguments from the parent so we don't have to parse twice and to account
|
||||||
|
// for environment manipulation (like how PASSWORD gets removed to avoid
|
||||||
|
// leaking to child processes).
|
||||||
if (isChild(wrapper)) {
|
if (isChild(wrapper)) {
|
||||||
await wrapper.handshake()
|
const args = await wrapper.handshake()
|
||||||
wrapper.preventExit()
|
wrapper.preventExit()
|
||||||
return main(args)
|
return main(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cliArgs = parse(process.argv.slice(2))
|
||||||
|
const configArgs = await readConfigFile(cliArgs.config)
|
||||||
|
const args = await setDefaults(cliArgs, configArgs)
|
||||||
|
|
||||||
if (args.help) {
|
if (args.help) {
|
||||||
console.log("code-server", version, commit)
|
console.log("code-server", version, commit)
|
||||||
console.log("")
|
console.log("")
|
||||||
@ -201,7 +204,7 @@ async function entry(): Promise<void> {
|
|||||||
return openInExistingInstance(args, socketPath)
|
return openInExistingInstance(args, socketPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
return wrapper.start()
|
return wrapper.start(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
entry().catch((error) => {
|
entry().catch((error) => {
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { Logger, field, logger } from "@coder/logger"
|
import { field, Logger, logger } from "@coder/logger"
|
||||||
import * as cp from "child_process"
|
import * as cp from "child_process"
|
||||||
import * as path from "path"
|
import * as path from "path"
|
||||||
import * as rfs from "rotating-file-stream"
|
import * as rfs from "rotating-file-stream"
|
||||||
import { Emitter } from "../common/emitter"
|
import { Emitter } from "../common/emitter"
|
||||||
|
import { DefaultedArgs } from "./cli"
|
||||||
import { paths } from "./util"
|
import { paths } from "./util"
|
||||||
|
|
||||||
const timeoutInterval = 10000 // 10s, matches VS Code's timeouts.
|
const timeoutInterval = 10000 // 10s, matches VS Code's timeouts.
|
||||||
@ -58,7 +59,12 @@ export function onMessage<M, T extends M>(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
interface HandshakeMessage {
|
interface ParentHandshakeMessage {
|
||||||
|
type: "handshake"
|
||||||
|
args: DefaultedArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChildHandshakeMessage {
|
||||||
type: "handshake"
|
type: "handshake"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +73,8 @@ interface RelaunchMessage {
|
|||||||
version: string
|
version: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Message = RelaunchMessage | HandshakeMessage
|
type ChildMessage = RelaunchMessage | ChildHandshakeMessage
|
||||||
|
type ParentMessage = ParentHandshakeMessage
|
||||||
|
|
||||||
class ProcessError extends Error {
|
class ProcessError extends Error {
|
||||||
public constructor(message: string, public readonly code: number | undefined) {
|
public constructor(message: string, public readonly code: number | undefined) {
|
||||||
@ -164,15 +171,16 @@ class ChildProcess extends Process {
|
|||||||
/**
|
/**
|
||||||
* Initiate the handshake and wait for a response from the parent.
|
* Initiate the handshake and wait for a response from the parent.
|
||||||
*/
|
*/
|
||||||
public async handshake(): Promise<void> {
|
public async handshake(): Promise<DefaultedArgs> {
|
||||||
this.send({ type: "handshake" })
|
this.send({ type: "handshake" })
|
||||||
await onMessage<Message, HandshakeMessage>(
|
const message = await onMessage<ParentMessage, ParentHandshakeMessage>(
|
||||||
process,
|
process,
|
||||||
(message): message is HandshakeMessage => {
|
(message): message is ParentHandshakeMessage => {
|
||||||
return message.type === "handshake"
|
return message.type === "handshake"
|
||||||
},
|
},
|
||||||
this.logger,
|
this.logger,
|
||||||
)
|
)
|
||||||
|
return message.args
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -185,7 +193,7 @@ class ChildProcess extends Process {
|
|||||||
/**
|
/**
|
||||||
* Send a message to the parent.
|
* Send a message to the parent.
|
||||||
*/
|
*/
|
||||||
private send(message: Message): void {
|
private send(message: ChildMessage): void {
|
||||||
if (!process.send) {
|
if (!process.send) {
|
||||||
throw new Error("not spawned with IPC")
|
throw new Error("not spawned with IPC")
|
||||||
}
|
}
|
||||||
@ -211,12 +219,19 @@ export class ParentProcess extends Process {
|
|||||||
private readonly logStdoutStream: rfs.RotatingFileStream
|
private readonly logStdoutStream: rfs.RotatingFileStream
|
||||||
private readonly logStderrStream: rfs.RotatingFileStream
|
private readonly logStderrStream: rfs.RotatingFileStream
|
||||||
|
|
||||||
protected readonly _onChildMessage = new Emitter<Message>()
|
protected readonly _onChildMessage = new Emitter<ChildMessage>()
|
||||||
protected readonly onChildMessage = this._onChildMessage.event
|
protected readonly onChildMessage = this._onChildMessage.event
|
||||||
|
|
||||||
|
private args?: DefaultedArgs
|
||||||
|
|
||||||
public constructor(private currentVersion: string, private readonly options?: WrapperOptions) {
|
public constructor(private currentVersion: string, private readonly options?: WrapperOptions) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
|
process.on("SIGUSR1", async () => {
|
||||||
|
this.logger.info("Received SIGUSR1; hotswapping")
|
||||||
|
this.relaunch()
|
||||||
|
})
|
||||||
|
|
||||||
const opts = {
|
const opts = {
|
||||||
size: "10M",
|
size: "10M",
|
||||||
maxFiles: 10,
|
maxFiles: 10,
|
||||||
@ -253,21 +268,17 @@ export class ParentProcess extends Process {
|
|||||||
private async relaunch(): Promise<void> {
|
private async relaunch(): Promise<void> {
|
||||||
this.disposeChild()
|
this.disposeChild()
|
||||||
try {
|
try {
|
||||||
await this.start()
|
this.started = this._start()
|
||||||
|
await this.started
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(error.message)
|
this.logger.error(error.message)
|
||||||
this.exit(typeof error.code === "number" ? error.code : 1)
|
this.exit(typeof error.code === "number" ? error.code : 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public start(): Promise<void> {
|
public start(args: DefaultedArgs): Promise<void> {
|
||||||
// If we have a process then we've already bound this.
|
// Store for relaunches.
|
||||||
if (!this.child) {
|
this.args = args
|
||||||
process.on("SIGUSR1", async () => {
|
|
||||||
this.logger.info("Received SIGUSR1; hotswapping")
|
|
||||||
this.relaunch()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (!this.started) {
|
if (!this.started) {
|
||||||
this.started = this._start()
|
this.started = this._start()
|
||||||
}
|
}
|
||||||
@ -320,14 +331,24 @@ export class ParentProcess extends Process {
|
|||||||
* Wait for a handshake from the child then reply.
|
* Wait for a handshake from the child then reply.
|
||||||
*/
|
*/
|
||||||
private async handshake(child: cp.ChildProcess): Promise<void> {
|
private async handshake(child: cp.ChildProcess): Promise<void> {
|
||||||
await onMessage<Message, HandshakeMessage>(
|
if (!this.args) {
|
||||||
|
throw new Error("started without args")
|
||||||
|
}
|
||||||
|
await onMessage<ChildMessage, ChildHandshakeMessage>(
|
||||||
child,
|
child,
|
||||||
(message): message is HandshakeMessage => {
|
(message): message is ChildHandshakeMessage => {
|
||||||
return message.type === "handshake"
|
return message.type === "handshake"
|
||||||
},
|
},
|
||||||
this.logger,
|
this.logger,
|
||||||
)
|
)
|
||||||
child.send({ type: "handshake" })
|
this.send(child, { type: "handshake", args: this.args })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a message to the child.
|
||||||
|
*/
|
||||||
|
private send(child: cp.ChildProcess, message: ParentMessage): void {
|
||||||
|
child.send(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user