Skip to content

Commit 1708e8b

Browse files
committed
Add DBus node for swap size and swappiness config
Add /io/hass/os/Config/Swap object that exposes interface with properties controlling the swap size and swappiness configuration added in [1] and [2]. There are no checks whether this supported on the target system (while swappiness would probably have effect also on Supervised installs, swap size can't be controlled there). These checks should be implemented on upper layer (i.e. Supervisor). [1] home-assistant/operating-system#3882 [2] home-assistant/operating-system#3884
1 parent 856e157 commit 1708e8b

File tree

3 files changed

+165
-3
lines changed

3 files changed

+165
-3
lines changed

config/swap/swap.go

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
package swap
2+
3+
import (
4+
"fmt"
5+
"github.com/godbus/dbus/v5"
6+
"github.com/godbus/dbus/v5/introspect"
7+
"github.com/godbus/dbus/v5/prop"
8+
"github.com/home-assistant/os-agent/utils/lineinfile"
9+
logging "github.com/home-assistant/os-agent/utils/log"
10+
"regexp"
11+
"strconv"
12+
)
13+
14+
const (
15+
objectPath = "/io/hass/os/Config/Swap"
16+
ifaceName = "io.hass.os.Config.Swap"
17+
swapPath = "/etc/default/haos-swapfile"
18+
swappinessPath = "/etc/sysctl.d/15-swappiness.conf"
19+
defaultSwappiness = 1
20+
)
21+
22+
var (
23+
optSwapSize string
24+
optSwappiness int
25+
swapFileEditor = lineinfile.LineInFile{FilePath: swapPath}
26+
swappinessEditor = lineinfile.LineInFile{FilePath: swappinessPath}
27+
)
28+
29+
type swap struct {
30+
conn *dbus.Conn
31+
props *prop.Properties
32+
}
33+
34+
func getSwapSize() string {
35+
found, err := swapFileEditor.Find(`^SWAPSIZE=`, "", true)
36+
if found == nil || err != nil {
37+
return ""
38+
}
39+
40+
matches := regexp.MustCompile(`^SWAPSIZE=(.*)`).FindStringSubmatch(*found)
41+
if len(matches) > 1 {
42+
return matches[1]
43+
}
44+
45+
return ""
46+
}
47+
48+
func getSwappiness() int {
49+
found, err := swappinessEditor.Find(`^vm.swappiness\s*=`, "", true)
50+
if found == nil || err != nil {
51+
return defaultSwappiness
52+
}
53+
54+
matches := regexp.MustCompile(`^vm.swappiness\s*=\s*(\d+)`).FindStringSubmatch(*found)
55+
if len(matches) > 1 {
56+
if swappiness, err := strconv.Atoi(matches[1]); err == nil {
57+
return swappiness
58+
}
59+
}
60+
61+
return defaultSwappiness
62+
}
63+
64+
func setSwapSize(c *prop.Change) *dbus.Error {
65+
swapSize, ok := c.Value.(string)
66+
if !ok {
67+
return dbus.MakeFailedError(fmt.Errorf("invalid type for swap size"))
68+
}
69+
70+
re := regexp.MustCompile(`^\d+([KMG]?(i?B)?)?$`)
71+
if !re.MatchString(swapSize) {
72+
return dbus.MakeFailedError(fmt.Errorf("invalid swap size format"))
73+
}
74+
75+
params := lineinfile.NewPresentParams(fmt.Sprintf("SWAPSIZE=%s", swapSize))
76+
params.Regexp, _ = regexp.Compile(`^[#\s]*SWAPSIZE=`)
77+
78+
if err := swapFileEditor.Present(params); err != nil {
79+
return dbus.MakeFailedError(fmt.Errorf("failed to set swap size: %w", err))
80+
}
81+
82+
return nil
83+
}
84+
85+
func setSwappiness(c *prop.Change) *dbus.Error {
86+
swappiness, ok := c.Value.(int32)
87+
if !ok {
88+
return dbus.MakeFailedError(fmt.Errorf("swappiness must be int32, got %T", c.Value))
89+
}
90+
91+
if swappiness < 0 || swappiness > 100 {
92+
return dbus.MakeFailedError(fmt.Errorf("swappiness must be between 0 and 100"))
93+
}
94+
95+
params := lineinfile.NewPresentParams(fmt.Sprintf("vm.swappiness=%d", swappiness))
96+
params.Regexp, _ = regexp.Compile(`^[#\s]*vm.swappiness\s*=`)
97+
98+
if err := swappinessEditor.Present(params); err != nil {
99+
return dbus.MakeFailedError(fmt.Errorf("failed to set swappiness: %w", err))
100+
}
101+
102+
return nil
103+
}
104+
105+
func InitializeDBus(conn *dbus.Conn) {
106+
d := swap{
107+
conn: conn,
108+
}
109+
110+
optSwapSize = getSwapSize()
111+
optSwappiness = getSwappiness()
112+
113+
propsSpec := map[string]map[string]*prop.Prop{
114+
ifaceName: {
115+
"SwapSize": {
116+
Value: optSwapSize,
117+
Writable: true,
118+
Emit: prop.EmitTrue,
119+
Callback: setSwapSize,
120+
},
121+
"Swappiness": {
122+
Value: optSwappiness,
123+
Writable: true,
124+
Emit: prop.EmitTrue,
125+
Callback: setSwappiness,
126+
},
127+
},
128+
}
129+
130+
props, err := prop.Export(conn, objectPath, propsSpec)
131+
if err != nil {
132+
logging.Critical.Panic(err)
133+
}
134+
d.props = props
135+
136+
err = conn.Export(d, objectPath, ifaceName)
137+
if err != nil {
138+
logging.Critical.Panic(err)
139+
}
140+
141+
node := &introspect.Node{
142+
Name: objectPath,
143+
Interfaces: []introspect.Interface{
144+
introspect.IntrospectData,
145+
prop.IntrospectData,
146+
{
147+
Name: ifaceName,
148+
Methods: introspect.Methods(d),
149+
Properties: props.Introspection(ifaceName),
150+
},
151+
},
152+
}
153+
154+
err = conn.Export(introspect.NewIntrospectable(node), objectPath, "org.freedesktop.DBus.Introspectable")
155+
if err != nil {
156+
logging.Critical.Panic(err)
157+
}
158+
159+
logging.Info.Printf("Exposing object %s with interface %s ...", objectPath, ifaceName)
160+
}

config/timesyncd.go renamed to config/timesyncd/timesyncd.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package config
1+
package timesyncd
22

33
import (
44
"fmt"

main.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import (
1212
"github.com/home-assistant/os-agent/apparmor"
1313
"github.com/home-assistant/os-agent/boards"
1414
"github.com/home-assistant/os-agent/cgroup"
15-
"github.com/home-assistant/os-agent/config"
15+
"github.com/home-assistant/os-agent/config/swap"
16+
"github.com/home-assistant/os-agent/config/timesyncd"
1617
"github.com/home-assistant/os-agent/datadisk"
1718
"github.com/home-assistant/os-agent/system"
1819
logging "github.com/home-assistant/os-agent/utils/log"
@@ -71,7 +72,8 @@ func main() {
7172
apparmor.InitializeDBus(conn)
7273
cgroup.InitializeDBus(conn)
7374
boards.InitializeDBus(conn, board)
74-
config.InitializeDBus(conn)
75+
swap.InitializeDBus(conn)
76+
timesyncd.InitializeDBus(conn)
7577

7678
_, err = daemon.SdNotify(false, daemon.SdNotifyReady)
7779
if err != nil {

0 commit comments

Comments
 (0)