Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
lima-vm
GitHub Repository: lima-vm/lima
Path: blob/master/pkg/instance/stop.go
2611 views
1
// SPDX-FileCopyrightText: Copyright The Lima Authors
2
// SPDX-License-Identifier: Apache-2.0
3
4
package instance
5
6
import (
7
"context"
8
"errors"
9
"fmt"
10
"os"
11
"path/filepath"
12
"strings"
13
"time"
14
15
"github.com/sirupsen/logrus"
16
17
"github.com/lima-vm/lima/v2/pkg/autostart"
18
hostagentevents "github.com/lima-vm/lima/v2/pkg/hostagent/events"
19
"github.com/lima-vm/lima/v2/pkg/limatype"
20
"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
21
"github.com/lima-vm/lima/v2/pkg/osutil"
22
"github.com/lima-vm/lima/v2/pkg/store"
23
)
24
25
func StopGracefully(ctx context.Context, inst *limatype.Instance, isRestart bool) error {
26
if inst.Status != limatype.StatusRunning {
27
if isRestart {
28
logrus.Warn("The instance is not running, continuing with the restart")
29
return nil
30
}
31
return fmt.Errorf("expected status %q, got %q (maybe use `limactl stop -f`?)", limatype.StatusRunning, inst.Status)
32
}
33
34
begin := time.Now() // used for logrus propagation
35
if requested, err := autostart.RequestStop(ctx, inst); err != nil && !errors.Is(err, autostart.ErrNotSupported) {
36
return fmt.Errorf("failed to request stop via autostart manager: %w", err)
37
} else if !requested {
38
logrus.Infof("Sending SIGINT to hostagent process %d", inst.HostAgentPID)
39
if err := osutil.SysKill(inst.HostAgentPID, osutil.SigInt); err != nil {
40
logrus.Error(err)
41
}
42
}
43
44
logrus.Info("Waiting for the host agent and the driver processes to shut down")
45
err := waitForHostAgentTermination(ctx, inst, begin)
46
if err != nil {
47
return err
48
}
49
50
logrus.Info("Waiting for the instance to shut down")
51
return waitForInstanceShutdown(ctx, inst)
52
}
53
54
func waitForHostAgentTermination(ctx context.Context, inst *limatype.Instance, begin time.Time) error {
55
ctx, cancel := context.WithTimeout(ctx, 3*time.Minute+10*time.Second)
56
defer cancel()
57
58
var receivedExitingEvent bool
59
onEvent := func(ev hostagentevents.Event) bool {
60
if len(ev.Status.Errors) > 0 {
61
logrus.Errorf("%+v", ev.Status.Errors)
62
}
63
if ev.Status.Exiting {
64
receivedExitingEvent = true
65
return true
66
}
67
return false
68
}
69
70
haStdoutPath := filepath.Join(inst.Dir, filenames.HostAgentStdoutLog)
71
haStderrPath := filepath.Join(inst.Dir, filenames.HostAgentStderrLog)
72
73
if err := hostagentevents.Watch(ctx, haStdoutPath, haStderrPath, begin, true, onEvent); err != nil {
74
return err
75
}
76
77
if !receivedExitingEvent {
78
return errors.New("did not receive an event with the \"exiting\" status")
79
}
80
81
return nil
82
}
83
84
func waitForInstanceShutdown(ctx context.Context, inst *limatype.Instance) error {
85
ctx, cancel := context.WithTimeout(ctx, 3*time.Minute)
86
defer cancel()
87
88
ticker := time.NewTicker(500 * time.Millisecond)
89
defer ticker.Stop()
90
91
for {
92
select {
93
case <-ticker.C:
94
updatedInst, err := store.Inspect(ctx, inst.Name)
95
if err != nil {
96
return fmt.Errorf("failed to inspect instance status: %w", err)
97
}
98
99
if updatedInst.Status == limatype.StatusStopped {
100
logrus.Infof("The instance %s has shut down", updatedInst.Name)
101
return nil
102
}
103
case <-ctx.Done():
104
return errors.New("timed out waiting for instance to shut down after 3 minutes")
105
}
106
}
107
}
108
109
func StopForcibly(inst *limatype.Instance) {
110
if inst.DriverPID > 0 {
111
logrus.Infof("Sending SIGKILL to the %s driver process %d", inst.VMType, inst.DriverPID)
112
if err := osutil.SysKill(inst.DriverPID, osutil.SigKill); err != nil {
113
logrus.Error(err)
114
}
115
} else {
116
logrus.Infof("The %s driver process seems already stopped", inst.VMType)
117
}
118
119
for _, d := range inst.AdditionalDisks {
120
diskName := d.Name
121
disk, err := store.InspectDisk(diskName, d.FSType)
122
if err != nil {
123
logrus.Warnf("Disk %q does not exist", diskName)
124
continue
125
}
126
if err := disk.Unlock(); err != nil {
127
logrus.Warnf("Failed to unlock disk %q. To use, run `limactl disk unlock %v`", diskName, diskName)
128
}
129
}
130
131
if inst.HostAgentPID > 0 {
132
logrus.Infof("Sending SIGKILL to the host agent process %d", inst.HostAgentPID)
133
if err := osutil.SysKill(inst.HostAgentPID, osutil.SigKill); err != nil {
134
logrus.Error(err)
135
}
136
} else {
137
logrus.Info("The host agent process seems already stopped")
138
}
139
140
globPatterns := strings.ReplaceAll(strings.Join(filenames.TmpFileSuffixes, " "), ".", "*.")
141
logrus.Infof("Removing %s under %q", globPatterns, inst.Dir)
142
143
fi, err := os.ReadDir(inst.Dir)
144
if err != nil {
145
logrus.Error(err)
146
return
147
}
148
for _, f := range fi {
149
path := filepath.Join(inst.Dir, f.Name())
150
for _, suffix := range filenames.TmpFileSuffixes {
151
if strings.HasSuffix(path, suffix) {
152
logrus.Infof("Removing %q", path)
153
if err := os.Remove(path); err != nil {
154
if errors.Is(err, os.ErrNotExist) {
155
logrus.Debug(err.Error())
156
} else {
157
logrus.Error(err)
158
}
159
}
160
}
161
}
162
}
163
}
164
165