2024-02-16 01:15:09 +00:00
|
|
|
package gpu
|
|
|
|
|
|
|
|
import (
|
2024-03-13 18:43:45 +00:00
|
|
|
"errors"
|
2024-02-16 01:15:09 +00:00
|
|
|
"fmt"
|
|
|
|
"log/slog"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"runtime"
|
2024-03-13 18:43:45 +00:00
|
|
|
"strconv"
|
2024-02-16 01:15:09 +00:00
|
|
|
"strings"
|
2024-03-08 17:45:55 +00:00
|
|
|
"sync"
|
2024-03-13 18:43:45 +00:00
|
|
|
"syscall"
|
2024-03-14 17:24:13 +00:00
|
|
|
"time"
|
2024-05-04 18:46:01 +00:00
|
|
|
|
2024-05-24 21:57:15 +00:00
|
|
|
"github.com/ollama/ollama/envconfig"
|
2024-03-08 17:45:55 +00:00
|
|
|
)
|
2024-02-16 01:15:09 +00:00
|
|
|
|
2024-03-08 17:45:55 +00:00
|
|
|
var (
|
|
|
|
lock sync.Mutex
|
|
|
|
payloadsDir = ""
|
2024-02-16 01:15:09 +00:00
|
|
|
)
|
|
|
|
|
2024-03-08 17:45:55 +00:00
|
|
|
func PayloadsDir() (string, error) {
|
|
|
|
lock.Lock()
|
|
|
|
defer lock.Unlock()
|
2024-03-28 21:26:17 +00:00
|
|
|
var err error
|
2024-03-08 17:45:55 +00:00
|
|
|
if payloadsDir == "" {
|
2024-07-04 02:30:19 +00:00
|
|
|
runnersDir := envconfig.RunnersDir()
|
2024-04-26 23:14:08 +00:00
|
|
|
|
2024-04-23 19:19:17 +00:00
|
|
|
if runnersDir != "" {
|
|
|
|
payloadsDir = runnersDir
|
|
|
|
return payloadsDir, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// The remainder only applies on non-windows where we still carry payloads in the main executable
|
2024-03-13 18:43:45 +00:00
|
|
|
cleanupTmpDirs()
|
2024-07-04 02:30:19 +00:00
|
|
|
tmpDir := envconfig.TmpDir()
|
2024-03-28 21:26:17 +00:00
|
|
|
if tmpDir == "" {
|
|
|
|
tmpDir, err = os.MkdirTemp("", "ollama")
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("failed to generate tmp dir: %w", err)
|
|
|
|
}
|
|
|
|
} else {
|
2024-08-01 21:52:15 +00:00
|
|
|
err = os.MkdirAll(tmpDir, 0o755)
|
2024-03-28 21:26:17 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("failed to generate tmp dir %s: %w", tmpDir, err)
|
|
|
|
}
|
2024-03-08 17:45:55 +00:00
|
|
|
}
|
2024-03-13 18:43:45 +00:00
|
|
|
|
|
|
|
// Track our pid so we can clean up orphaned tmpdirs
|
2024-08-05 07:34:09 +00:00
|
|
|
n := filepath.Join(tmpDir, "ollama.pid")
|
|
|
|
if err := os.WriteFile(n, []byte(strconv.Itoa(os.Getpid())), 0o644); err != nil {
|
|
|
|
return "", fmt.Errorf("failed to write pid file %s: %w", n, err)
|
2024-03-13 18:43:45 +00:00
|
|
|
}
|
|
|
|
|
2024-03-11 15:45:57 +00:00
|
|
|
// We create a distinct subdirectory for payloads within the tmpdir
|
|
|
|
// This will typically look like /tmp/ollama3208993108/runners on linux
|
|
|
|
payloadsDir = filepath.Join(tmpDir, "runners")
|
2024-02-16 01:15:09 +00:00
|
|
|
}
|
2024-03-08 17:45:55 +00:00
|
|
|
return payloadsDir, nil
|
|
|
|
}
|
|
|
|
|
2024-03-13 18:43:45 +00:00
|
|
|
// Best effort to clean up prior tmpdirs
|
|
|
|
func cleanupTmpDirs() {
|
2024-08-05 07:02:47 +00:00
|
|
|
matches, err := filepath.Glob(filepath.Join(os.TempDir(), "ollama*", "ollama.pid"))
|
2024-03-13 18:43:45 +00:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2024-08-05 07:02:47 +00:00
|
|
|
|
|
|
|
for _, match := range matches {
|
|
|
|
raw, err := os.ReadFile(match)
|
|
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
|
|
slog.Debug("not a ollama runtime directory, skipping", "path", match)
|
2024-03-13 18:43:45 +00:00
|
|
|
continue
|
2024-08-05 07:02:47 +00:00
|
|
|
} else if err != nil {
|
|
|
|
slog.Warn("could not read ollama.pid, skipping", "path", match, "error", err)
|
2024-06-20 15:51:35 +00:00
|
|
|
continue
|
2024-03-13 18:43:45 +00:00
|
|
|
}
|
2024-06-20 16:23:43 +00:00
|
|
|
|
|
|
|
pid, err := strconv.Atoi(string(raw))
|
2024-06-20 16:30:59 +00:00
|
|
|
if err != nil {
|
2024-08-05 07:02:47 +00:00
|
|
|
slog.Warn("invalid pid, skipping", "path", match, "error", err)
|
2024-06-20 16:30:59 +00:00
|
|
|
continue
|
2024-06-20 16:23:43 +00:00
|
|
|
}
|
|
|
|
|
2024-08-05 07:02:47 +00:00
|
|
|
p, err := os.FindProcess(pid)
|
|
|
|
if err == nil && !errors.Is(p.Signal(syscall.Signal(0)), os.ErrProcessDone) {
|
|
|
|
slog.Warn("process still running, skipping", "pid", pid, "path", match)
|
2024-06-20 16:30:59 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2024-08-05 07:02:47 +00:00
|
|
|
if err := os.Remove(match); err != nil {
|
|
|
|
slog.Warn("could not cleanup stale pidfile", "path", match, "error", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
runners := filepath.Join(filepath.Dir(match), "runners")
|
|
|
|
if err := os.RemoveAll(runners); err != nil {
|
|
|
|
slog.Warn("could not cleanup stale runners", "path", runners, "error", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := os.Remove(filepath.Dir(match)); err != nil {
|
|
|
|
slog.Warn("could not cleanup stale tmpdir", "path", filepath.Dir(match), "error", err)
|
2024-03-13 18:43:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-08 17:45:55 +00:00
|
|
|
func Cleanup() {
|
|
|
|
lock.Lock()
|
|
|
|
defer lock.Unlock()
|
2024-07-04 02:30:19 +00:00
|
|
|
runnersDir := envconfig.RunnersDir()
|
2024-04-23 19:19:17 +00:00
|
|
|
if payloadsDir != "" && runnersDir == "" && runtime.GOOS != "windows" {
|
2024-03-11 15:45:57 +00:00
|
|
|
// We want to fully clean up the tmpdir parent of the payloads dir
|
|
|
|
tmpDir := filepath.Clean(filepath.Join(payloadsDir, ".."))
|
|
|
|
slog.Debug("cleaning up", "dir", tmpDir)
|
|
|
|
err := os.RemoveAll(tmpDir)
|
2024-03-08 17:45:55 +00:00
|
|
|
if err != nil {
|
2024-03-14 17:24:13 +00:00
|
|
|
// On windows, if we remove too quickly the llama.dll may still be in-use and fail to remove
|
|
|
|
time.Sleep(1000 * time.Millisecond)
|
|
|
|
err = os.RemoveAll(tmpDir)
|
|
|
|
if err != nil {
|
|
|
|
slog.Warn("failed to clean up", "dir", tmpDir, "err", err)
|
|
|
|
}
|
2024-02-16 01:15:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func UpdatePath(dir string) {
|
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
tmpDir := filepath.Dir(dir)
|
|
|
|
pathComponents := strings.Split(os.Getenv("PATH"), ";")
|
|
|
|
i := 0
|
|
|
|
for _, comp := range pathComponents {
|
|
|
|
if strings.EqualFold(comp, dir) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// Remove any other prior paths to our temp dir
|
|
|
|
if !strings.HasPrefix(strings.ToLower(comp), strings.ToLower(tmpDir)) {
|
|
|
|
pathComponents[i] = comp
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
newPath := strings.Join(append([]string{dir}, pathComponents...), ";")
|
2024-03-30 16:50:05 +00:00
|
|
|
slog.Info("updating", "PATH", newPath)
|
2024-02-16 01:15:09 +00:00
|
|
|
os.Setenv("PATH", newPath)
|
|
|
|
}
|
|
|
|
// linux and darwin rely on rpath
|
|
|
|
}
|