@logictan/dsh-config-manager 0.1.60 → 0.1.61

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.
@@ -322,16 +322,37 @@ function defaultIo(): EnvLockIo {
322
322
  }
323
323
  }
324
324
 
325
+ /**
326
+ * 从 /proc/<pid>/stat 文本提取进程创建身份(starttime,字段 22)。
327
+ *
328
+ * 字段布局(man 5 proc_pid_stat):1 pid, 2 comm, 3 state, …, 22 starttime, 23 vsize, 24 rss。
329
+ * comm 可能含空格与括号,故必须按**最后一个** ')' 切片;切片后 index 0 对应字段 3,
330
+ * 于是字段 N 的索引是 N-3 —— **starttime 的索引是 19**。
331
+ *
332
+ * 历史缺陷:此处曾取 index 21(= 字段 24 rss)。rss 随进程内存占用变化,
333
+ * 使同一进程的两次读取得到不同身份,被误判为「PID 复用」→ STALE_LOCK_DETECTED,
334
+ * 进而可能让 recoverStaleLock() 捕获一个**仍存活**的持有者的锁。
335
+ * 该路径仅在 Linux 生效(macOS/Windows 无 /proc),故只在 Linux CI 上暴露。
336
+ *
337
+ * @param statText /proc/<pid>/stat 全文
338
+ * @returns starttime 字符串;畸形/字段不足 → null(不抛错,也不编造身份)
339
+ */
340
+ export function parseLinuxProcStartTime(statText: string): string | null {
341
+ const close = statText.lastIndexOf(')')
342
+ if (close < 0) return null
343
+ const fields = statText.slice(close + 1).trim().split(/\s+/)
344
+ const starttime = fields[19]
345
+ return typeof starttime === 'string' && starttime !== '' ? starttime : null
346
+ }
347
+
325
348
  /** 默认进程探测(跨平台 best-effort;OS identity 能力由平台决定) */
326
349
  function defaultProbe(): ProcessIdentityProbe {
327
350
  const selfOsIdentity = (() => {
328
351
  try {
329
352
  if (process.platform === 'linux') {
330
- // /proc/<pid>/stat 第 22 字段 = starttime(tick 数)
331
353
  const l = fssync.readFileSync(`/proc/${process.pid}/stat`, 'utf8').toString()
332
- const afterComm = l.slice(l.lastIndexOf(')') + 1).trim().split(/\s+/)
333
- // 格式: state ppid ... starttime:comm 后第一字段是 state,starttime 是第 22 个(index 21 起)
334
- return `linux:${afterComm[21] ?? 'unknown'}`
354
+ const starttime = parseLinuxProcStartTime(l)
355
+ return starttime === null ? null : `linux:${starttime}`
335
356
  }
336
357
  if (process.platform === 'darwin') return `darwin:${process.pid}:${Date.now()}` // 不可靠 → 保守返回占位
337
358
  if (process.platform === 'win32') {
@@ -377,8 +398,8 @@ function defaultProbe(): ProcessIdentityProbe {
377
398
  if (process.platform === 'linux') {
378
399
  try {
379
400
  const l = fssync.readFileSync(`/proc/${pid}/stat`, 'utf8').toString()
380
- const afterComm = l.slice(l.lastIndexOf(')') + 1).trim().split(/\s+/)
381
- osIdentity = `linux:${afterComm[21] ?? 'unknown'}`
401
+ const starttime = parseLinuxProcStartTime(l)
402
+ osIdentity = starttime === null ? null : `linux:${starttime}`
382
403
  } catch { osIdentity = null }
383
404
  } else if (process.platform === 'win32') {
384
405
  // best-effort:Node 无法直接读其它进程 creation time;留给注入实现。这里返回 null = 无法验证。