farming-code 2.2.22 → 2.2.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -77,6 +77,15 @@ function sanitizeAgentEnv(env) {
|
|
|
77
77
|
});
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
function socketIdentity(socketPath) {
|
|
81
|
+
const stat = fs.statSync(socketPath, { bigint: true });
|
|
82
|
+
return { dev: stat.dev, ino: stat.ino };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function sameSocketIdentity(left, right) {
|
|
86
|
+
return Boolean(left && right && left.dev === right.dev && left.ino === right.ino);
|
|
87
|
+
}
|
|
88
|
+
|
|
80
89
|
class NativePtyHost {
|
|
81
90
|
constructor(options = {}) {
|
|
82
91
|
this.configDir = options.configDir || process.env.FARMING_CONFIG_DIR || path.join(os.homedir(), '.farming');
|
|
@@ -84,6 +93,13 @@ class NativePtyHost {
|
|
|
84
93
|
this.ownerPid = Number(options.ownerPid || process.env.FARMING_NATIVE_PTY_HOST_OWNER_PID || 0);
|
|
85
94
|
this.runtimeIdentity = options.runtimeIdentity || HOST_RUNTIME_IDENTITY;
|
|
86
95
|
this.exitOnShutdown = options.exitOnShutdown !== false;
|
|
96
|
+
this.boundSocketPath = process.platform === 'win32'
|
|
97
|
+
? this.socketPath
|
|
98
|
+
: path.join(
|
|
99
|
+
path.dirname(this.socketPath),
|
|
100
|
+
`.fpty-${process.pid}-${crypto.createHash('sha256').update(this.socketPath).digest('hex').slice(0, 8)}-${crypto.randomBytes(4).toString('hex')}.sock`,
|
|
101
|
+
);
|
|
102
|
+
this.boundSocketIdentity = null;
|
|
87
103
|
this.sessions = new Map();
|
|
88
104
|
this.clients = new Set();
|
|
89
105
|
this.ownerCheckTimer = null;
|
|
@@ -130,13 +146,22 @@ class NativePtyHost {
|
|
|
130
146
|
this.server = net.createServer(socket => this.handleConnection(socket));
|
|
131
147
|
await new Promise((resolve, reject) => {
|
|
132
148
|
this.server.once('error', reject);
|
|
133
|
-
this.server.listen(this.
|
|
149
|
+
this.server.listen(this.boundSocketPath, () => {
|
|
134
150
|
this.server.off('error', reject);
|
|
135
151
|
resolve();
|
|
136
152
|
});
|
|
137
153
|
});
|
|
138
154
|
if (process.platform !== 'win32') {
|
|
139
|
-
|
|
155
|
+
this.boundSocketIdentity = socketIdentity(this.boundSocketPath);
|
|
156
|
+
fs.chmodSync(this.boundSocketPath, 0o600);
|
|
157
|
+
try {
|
|
158
|
+
fs.linkSync(this.boundSocketPath, this.socketPath);
|
|
159
|
+
} catch (error) {
|
|
160
|
+
await new Promise(resolve => this.server.close(() => resolve()));
|
|
161
|
+
this.server = null;
|
|
162
|
+
this.boundSocketIdentity = null;
|
|
163
|
+
throw error;
|
|
164
|
+
}
|
|
140
165
|
}
|
|
141
166
|
console.error(`[${new Date().toISOString()}] Native PTY host listening on ${this.socketPath} ownerPid=${this.ownerPid || ''}`);
|
|
142
167
|
this.startOwnerWatch();
|
|
@@ -173,6 +198,11 @@ class NativePtyHost {
|
|
|
173
198
|
} catch (error) {
|
|
174
199
|
if (!error || error.code !== 'ENOENT') throw error;
|
|
175
200
|
}
|
|
201
|
+
try {
|
|
202
|
+
fs.unlinkSync(this.boundSocketPath);
|
|
203
|
+
} catch (error) {
|
|
204
|
+
if (!error || error.code !== 'ENOENT') throw error;
|
|
205
|
+
}
|
|
176
206
|
}
|
|
177
207
|
|
|
178
208
|
handleConnection(socket) {
|
|
@@ -1476,16 +1506,22 @@ class NativePtyHost {
|
|
|
1476
1506
|
}
|
|
1477
1507
|
}
|
|
1478
1508
|
await this.screenWorkerPool.dispose();
|
|
1479
|
-
if (this.server) {
|
|
1480
|
-
await new Promise(resolve => this.server.close(() => resolve()));
|
|
1481
|
-
this.server = null;
|
|
1482
|
-
}
|
|
1483
|
-
if (process.platform !== 'win32') {
|
|
1509
|
+
if (this.server && process.platform !== 'win32') {
|
|
1484
1510
|
try {
|
|
1485
|
-
|
|
1511
|
+
const currentSocketIdentity = socketIdentity(this.socketPath);
|
|
1512
|
+
if (sameSocketIdentity(currentSocketIdentity, this.boundSocketIdentity)) {
|
|
1513
|
+
// Unlink while this listener is still active. A competing host cannot
|
|
1514
|
+
// replace an active socket between the ownership check and unlink.
|
|
1515
|
+
fs.unlinkSync(this.socketPath);
|
|
1516
|
+
}
|
|
1486
1517
|
} catch (error) {
|
|
1487
1518
|
if (!error || error.code !== 'ENOENT') throw error;
|
|
1488
1519
|
}
|
|
1520
|
+
this.boundSocketIdentity = null;
|
|
1521
|
+
}
|
|
1522
|
+
if (this.server) {
|
|
1523
|
+
await new Promise(resolve => this.server.close(() => resolve()));
|
|
1524
|
+
this.server = null;
|
|
1489
1525
|
}
|
|
1490
1526
|
}
|
|
1491
1527
|
}
|