isLockFileStale reads the lock file and checks whether the owning process is dead or has a mismatched start time. Returns (true, content, nil) if stale, (false, content, nil) if actively held, or an error if the file cannot be read.
(path string)
| 219 | // stale, (false, content, nil) if actively held, or an error if the file |
| 220 | // cannot be read. |
| 221 | func isLockFileStale(path string) (bool, lockContent, error) { |
| 222 | data, err := os.ReadFile(path) // nolint: gosec |
| 223 | if err != nil { |
| 224 | return false, lockContent{}, err |
| 225 | } |
| 226 | var content lockContent |
| 227 | if err := json.Unmarshal(data, &content); err != nil { |
| 228 | // corrupt or empty file (treat as stale) |
| 229 | return true, lockContent{}, nil |
| 230 | } |
| 231 | |
| 232 | p, err := process.NewProcess(content.PID) |
| 233 | if err != nil { |
| 234 | return true, content, nil // process does not exist |
| 235 | } |
| 236 | // CreateTime reads /proc/{pid}/stat on Linux (world-readable, always works). |
| 237 | // On Windows and macOS it can fail for processes owned by a different user, |
| 238 | // but cloudflared instances sharing a lock file are always running as the |
| 239 | // same user (the lock directory is derived from ~ via go-homedir). |
| 240 | ct, err := p.CreateTime() |
| 241 | if err != nil { |
| 242 | return true, content, nil // cannot query process (treat as stale) |
| 243 | } |
| 244 | |
| 245 | diff := ct - content.StartTime |
| 246 | if diff < 0 { |
| 247 | diff = -diff |
| 248 | } |
| 249 | if diff > startTimeTolerance { |
| 250 | return true, content, nil // PID was recycled (different process) |
| 251 | } |
| 252 | |
| 253 | // If the lock file is older than lockTimeout, the auth flow is |
| 254 | // definitely complete and the process is no longer doing auth work. |
| 255 | info, err := os.Stat(path) |
| 256 | if err == nil && time.Since(info.ModTime()) > lockTimeout { |
| 257 | return true, content, nil |
| 258 | } |
| 259 | |
| 260 | return false, content, nil // process is alive and actively authenticating |
| 261 | } |
| 262 | |
| 263 | func Init(version string) { |
| 264 | userAgent = fmt.Sprintf("cloudflared/%s", version) |