The laptop model's hardware module and a memory-pressure module for any machine (hq research 027/03, 026/05, to-be 42 phase 3). The predecessor's polling auto-profile and mem-guard user scripts become each module's own Go code launched by the node runtime (ADR 0198): a profile switcher woken by the kernel's power-supply uevents, and a guard that warns on RAM, swap or PSI before systemd-oomd acts, on the desktop over the account's bus and always as an event. supergfxctl and triggerhappy are kept as found (research 027 Q1).
73 lines
2.2 KiB
Go
73 lines
2.2 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"syscall"
|
|
)
|
|
|
|
// The kernel announces every change of a power supply — an adapter plugged or pulled, a battery
|
|
// starting or stopping to discharge — as a uevent on a netlink socket that any account may listen
|
|
// on. That is the event the switcher reacts to: no daemon, no bus client, no polling.
|
|
//
|
|
// upower re-announces the same changes on the system bus, and listening there would need a D-Bus
|
|
// client in the bundle; udev's re-broadcast (netlink group 2) carries a libudev header. The kernel's
|
|
// own group (1) is the source both of them read.
|
|
|
|
// powerSupplyEvent says whether a uevent is about a power supply.
|
|
func powerSupplyEvent(msg []byte) bool {
|
|
for _, field := range bytes.Split(msg, []byte{0}) {
|
|
if bytes.Equal(field, []byte("SUBSYSTEM=power_supply")) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// listenPowerSupply opens the kernel's uevent socket and sends on the channel for each power-supply
|
|
// event, never blocking: a burst of events is one wake-up. It stops when ctx ends.
|
|
func listenPowerSupply(ctx context.Context) (<-chan struct{}, error) {
|
|
fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW|syscall.SOCK_CLOEXEC, syscall.NETLINK_KOBJECT_UEVENT)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("opening the kernel's uevent socket: %w", err)
|
|
}
|
|
if err := syscall.Bind(fd, &syscall.SockaddrNetlink{Family: syscall.AF_NETLINK, Groups: 1}); err != nil {
|
|
syscall.Close(fd)
|
|
return nil, fmt.Errorf("joining the kernel's uevent group: %w", err)
|
|
}
|
|
events := make(chan struct{}, 1)
|
|
go func() {
|
|
<-ctx.Done()
|
|
syscall.Close(fd)
|
|
}()
|
|
go func() {
|
|
defer close(events)
|
|
buf := make([]byte, 64*1024)
|
|
for {
|
|
n, _, err := syscall.Recvfrom(fd, buf, 0)
|
|
if err != nil {
|
|
if err == syscall.EINTR || err == syscall.ENOBUFS {
|
|
// ENOBUFS: events were dropped. Treat it as one, since a dropped one may have
|
|
// been the adapter.
|
|
if err == syscall.ENOBUFS {
|
|
select {
|
|
case events <- struct{}{}:
|
|
default:
|
|
}
|
|
}
|
|
continue
|
|
}
|
|
return
|
|
}
|
|
if powerSupplyEvent(buf[:n]) {
|
|
select {
|
|
case events <- struct{}{}:
|
|
default:
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
return events, nil
|
|
}
|