From 6f3b997decc0e57a2a1fe3bebe8116fdca761239 Mon Sep 17 00:00:00 2001 From: Parth Sareen Date: Mon, 20 Jul 2026 11:24:25 -0700 Subject: [PATCH] cmd: route root command server start through checkServerHeartbeat (#17245) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bare `ollama` command (and `ollama launch` with no integration) used a bespoke `ensureServerRunning` that forked `ollama serve` directly and polled its heartbeat forever (no timeout, no platform-aware launch). Every other subcommand (`ollama run`, `ollama pull`, `ollama launch `, ...) goes through `checkServerHeartbeat` -> `startApp`, so the root command behaved differently and could hang indefinitely. Route `runInteractiveTUI` through `checkServerHeartbeat(cmd, nil)` — the same path `ollama launch ` uses — so the root command is consistent and no longer runs an unbounded server-spawn loop. `ensureServerRunning` and its `backgroundServerSysProcAttr` helpers (only it referenced them) are removed, along with the now-unused `os/exec` import. The platform `startApp`/`waitForServer` paths are unchanged, so behavior on macOS/Windows is identical to the other subcommands, and on Linux the root command now errors the same way the subcommands already do when no server is running. --- cmd/background_unix.go | 13 ------------- cmd/background_windows.go | 12 ------------ cmd/cmd.go | 41 +++------------------------------------ 3 files changed, 3 insertions(+), 63 deletions(-) delete mode 100644 cmd/background_unix.go delete mode 100644 cmd/background_windows.go diff --git a/cmd/background_unix.go b/cmd/background_unix.go deleted file mode 100644 index a4eea48c5b..0000000000 --- a/cmd/background_unix.go +++ /dev/null @@ -1,13 +0,0 @@ -//go:build !windows - -package cmd - -import "syscall" - -// backgroundServerSysProcAttr returns SysProcAttr for running the server in the background on Unix. -// Setpgid prevents the server from being killed when the parent process exits. -func backgroundServerSysProcAttr() *syscall.SysProcAttr { - return &syscall.SysProcAttr{ - Setpgid: true, - } -} diff --git a/cmd/background_windows.go b/cmd/background_windows.go deleted file mode 100644 index fa43b74009..0000000000 --- a/cmd/background_windows.go +++ /dev/null @@ -1,12 +0,0 @@ -package cmd - -import "syscall" - -// backgroundServerSysProcAttr returns SysProcAttr for running the server in the background on Windows. -// CREATE_NO_WINDOW (0x08000000) prevents a console window from appearing. -func backgroundServerSysProcAttr() *syscall.SysProcAttr { - return &syscall.SysProcAttr{ - CreationFlags: 0x08000000, - HideWindow: true, - } -} diff --git a/cmd/cmd.go b/cmd/cmd.go index d30eb1a5b4..4a91eb4505 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -16,7 +16,6 @@ import ( "net" "net/http" "os" - "os/exec" "os/signal" "path" "path/filepath" @@ -2099,40 +2098,6 @@ Environment Variables: cmd.SetUsageTemplate(cmd.UsageTemplate() + envUsage) } -// ensureServerRunning checks if the ollama server is running and starts it in the background if not. -func ensureServerRunning(ctx context.Context) error { - client, err := api.ClientFromEnvironment() - if err != nil { - return err - } - - // Check if server is already running - if err := client.Heartbeat(ctx); err == nil { - return nil // server is already running - } - - // Server not running, start it in the background - exe, err := os.Executable() - if err != nil { - return fmt.Errorf("could not find executable: %w", err) - } - - serverCmd := exec.CommandContext(ctx, exe, "serve") - serverCmd.Env = os.Environ() - serverCmd.SysProcAttr = backgroundServerSysProcAttr() - if err := serverCmd.Start(); err != nil { - return fmt.Errorf("failed to start server: %w", err) - } - - // Wait for the server to be ready - for { - time.Sleep(500 * time.Millisecond) - if err := client.Heartbeat(ctx); err == nil { - return nil // server has started - } - } -} - func launchInteractiveModel(cmd *cobra.Command, modelName string) error { client, err := api.ClientFromEnvironment() if err != nil { @@ -2166,9 +2131,9 @@ func launchInteractiveModel(cmd *cobra.Command, modelName string) error { // runInteractiveTUI runs the main interactive TUI menu. func runInteractiveTUI(cmd *cobra.Command) { - // Ensure the server is running before showing the TUI - if err := ensureServerRunning(cmd.Context()); err != nil { - fmt.Fprintf(os.Stderr, "Error starting server: %v\n", err) + // Ensure the server is running via the shared checkServerHeartbeat path. + if err := checkServerHeartbeat(cmd, nil); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) return }