start_windows.go 2.82 KB
Newer Older
1
2
3
4
5
6
package cmd

import (
	"context"
	"errors"
	"fmt"
7
	"log/slog"
8
9
	"os"
	"os/exec"
10
	"path"
11
12
13
	"path/filepath"
	"strings"
	"syscall"
14
	"unsafe"
15

16
	"github.com/ollama/ollama/api"
17
18
19
20
21
	"golang.org/x/sys/windows"
)

const (
	Installer = "OllamaSetup.exe"
22
23
24
)

func startApp(ctx context.Context, client *api.Client) error {
25
26
27
	if len(isProcRunning(Installer)) > 0 {
		return fmt.Errorf("upgrade in progress...")
	}
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
	AppName := "ollama app.exe"
	exe, err := os.Executable()
	if err != nil {
		return err
	}
	appExe := filepath.Join(filepath.Dir(exe), AppName)
	_, err = os.Stat(appExe)
	if errors.Is(err, os.ErrNotExist) {
		// Try the standard install location
		localAppData := os.Getenv("LOCALAPPDATA")
		appExe = filepath.Join(localAppData, "Ollama", AppName)
		_, err := os.Stat(appExe)
		if errors.Is(err, os.ErrNotExist) {
			// Finally look in the path
			appExe, err = exec.LookPath(AppName)
			if err != nil {
Michael Yang's avatar
lint  
Michael Yang committed
44
				return errors.New("could not locate ollama app")
45
46
47
48
49
			}
		}
	}

	cmd_path := "c:\\Windows\\system32\\cmd.exe"
Daniel Hiltgen's avatar
Daniel Hiltgen committed
50
	cmd := exec.Command(cmd_path, "/c", appExe, "hidden")
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
	cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: 0x08000000, HideWindow: true}

	cmd.Stdin = strings.NewReader("")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	if err := cmd.Start(); err != nil {
		return fmt.Errorf("unable to start ollama app %w", err)
	}

	if cmd.Process != nil {
		defer cmd.Process.Release() //nolint:errcheck
	}
	return waitForServer(ctx, client)
}
66
67
68
69
70
71
72
73

func isProcRunning(procName string) []uint32 {
	pids := make([]uint32, 2048)
	var ret uint32
	if err := windows.EnumProcesses(pids, &ret); err != nil || ret == 0 {
		slog.Debug("failed to check for running installers", "error", err)
		return nil
	}
74
75
76
77
78
79
80
81
82
83
	if ret > uint32(len(pids)) {
		pids = make([]uint32, ret+10)
		if err := windows.EnumProcesses(pids, &ret); err != nil || ret == 0 {
			slog.Debug("failed to check for running installers", "error", err)
			return nil
		}
	}
	if ret < uint32(len(pids)) {
		pids = pids[:ret]
	}
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
	var matches []uint32
	for _, pid := range pids {
		if pid == 0 {
			continue
		}
		hProcess, err := windows.OpenProcess(windows.PROCESS_QUERY_INFORMATION|windows.PROCESS_VM_READ, false, pid)
		if err != nil {
			continue
		}
		defer windows.CloseHandle(hProcess)
		var module windows.Handle
		var cbNeeded uint32
		cb := (uint32)(unsafe.Sizeof(module))
		if err := windows.EnumProcessModules(hProcess, &module, cb, &cbNeeded); err != nil {
			continue
		}
		var sz uint32 = 1024 * 8
		moduleName := make([]uint16, sz)
		cb = uint32(len(moduleName)) * (uint32)(unsafe.Sizeof(uint16(0)))
		if err := windows.GetModuleBaseName(hProcess, module, &moduleName[0], cb); err != nil && err != syscall.ERROR_INSUFFICIENT_BUFFER {
			continue
		}
		exeFile := path.Base(strings.ToLower(syscall.UTF16ToString(moduleName)))
		if strings.EqualFold(exeFile, procName) {
			matches = append(matches, pid)
		}
	}
	return matches
}