process.go

 1package ollama
 2
 3import (
 4	"context"
 5	"os"
 6	"os/exec"
 7	"os/signal"
 8	"syscall"
 9	"time"
10)
11
12var processManager = &ProcessManager{
13	processes: make(map[string]*exec.Cmd),
14}
15
16// setupProcessCleanup sets up signal handlers to clean up processes on exit
17func setupProcessCleanup() {
18	c := make(chan os.Signal, 1)
19	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
20
21	go func() {
22		<-c
23		cleanupProcesses()
24		os.Exit(0)
25	}()
26}
27
28// cleanupProcesses terminates all Ollama processes started by Crush
29func cleanupProcesses() {
30	processManager.mu.Lock()
31	defer processManager.mu.Unlock()
32
33	// Use CLI approach to stop all running models
34	// This is more reliable than tracking individual processes
35	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
36	defer cancel()
37
38	if err := CLIStopAllModels(ctx); err != nil {
39		// If CLI approach fails, fall back to process tracking
40		// Clean up model processes
41		for modelName, cmd := range processManager.processes {
42			if cmd.Process != nil {
43				cmd.Process.Kill()
44				cmd.Wait() // Wait for the process to actually exit
45			}
46			delete(processManager.processes, modelName)
47		}
48	} else {
49		// CLI approach succeeded, clear our process tracking
50		processManager.processes = make(map[string]*exec.Cmd)
51	}
52
53	// Clean up Ollama server if Crush started it
54	if processManager.crushStartedOllama && processManager.ollamaServer != nil {
55		if processManager.ollamaServer.Process != nil {
56			// Kill the entire process group to ensure all children are terminated
57			syscall.Kill(-processManager.ollamaServer.Process.Pid, syscall.SIGTERM)
58
59			// Give it a moment to shut down gracefully
60			time.Sleep(2 * time.Second)
61
62			// Force kill if still running
63			if processManager.ollamaServer.ProcessState == nil {
64				syscall.Kill(-processManager.ollamaServer.Process.Pid, syscall.SIGKILL)
65			}
66
67			processManager.ollamaServer.Wait() // Wait for the process to actually exit
68		}
69		processManager.ollamaServer = nil
70		processManager.crushStartedOllama = false
71	}
72}