| 281 | } |
| 282 | |
| 283 | func (c *Core) Stop() { |
| 284 | c.mu.Lock() |
| 285 | defer c.mu.Unlock() |
| 286 | |
| 287 | started := c.Started() |
| 288 | if !started && c.process == nil && c.cancelFunc == nil && c.logger == nil && len(c.unixSocketPaths) == 0 { |
| 289 | return |
| 290 | } |
| 291 | |
| 292 | if started { |
| 293 | pid := c.process.Process.Pid |
| 294 | c.processPID = pid |
| 295 | waitDone := c.waitDone |
| 296 | c.stopping = true |
| 297 | |
| 298 | // Kill the process |
| 299 | _ = c.process.Process.Kill() |
| 300 | |
| 301 | // Wait for the process watcher to reap the process. |
| 302 | select { |
| 303 | case <-waitDone: |
| 304 | // Process terminated |
| 305 | case <-time.After(5 * time.Second): |
| 306 | // Timeout - try force kill |
| 307 | log.Printf("xray process %d did not terminate within timeout, force killing", pid) |
| 308 | _ = killProcessTree(pid) |
| 309 | } |
| 310 | |
| 311 | // Verify process is actually dead |
| 312 | if err := verifyProcessDead(pid); err != nil { |
| 313 | log.Printf("warning: xray process %d may still be running: %v", pid, err) |
| 314 | // Try one more time to kill it |
| 315 | _ = killProcessTree(pid) |
| 316 | } |
| 317 | } |
| 318 | socketPaths := append([]string(nil), c.unixSocketPaths...) |
| 319 | c.process = nil |
| 320 | c.processPID = 0 |
| 321 | c.stopping = false |
| 322 | c.waitDone = nil |
| 323 | c.unixSocketPaths = nil |
| 324 | removeUnixSocketFiles(socketPaths) |
| 325 | |
| 326 | if c.cancelFunc != nil { |
| 327 | c.cancelFunc() |
| 328 | c.cancelFunc = nil |
| 329 | } |
| 330 | |
| 331 | if c.logger != nil { |
| 332 | c.logger.Close() |
| 333 | c.logger = nil |
| 334 | } |
| 335 | c.SwitchToRuntimeLogPhase() |
| 336 | |
| 337 | log.Println("xray core stopped") |
| 338 | } |
| 339 | |
| 340 | func (c *Core) Restart(config *Config, debugMode bool) error { |