One machine ran another time zone and a German console keymap with no record why. The module writes /etc/locale.conf and /etc/vconsole.conf whole and sets the zone through a run-once step of its own Go binary (timedatectl, read back): /etc/localtime is a link the mesh may not write (hq ADR 0012) and a module may not declare an action (ADR 0005). Tools: localization_get, _time_zone, _locales, _keymaps (to-be 42 Phase 1).
107 lines
3.9 KiB
Go
107 lines
3.9 KiB
Go
// localization's tools bundle (novox/hq to-be 42 Phase 1, research 026/05), and its step.
|
|
//
|
|
// Served by the node's runtime over MCP on stdio through the Go SDK (ADR 0188, ADR 0193) when it is
|
|
// started with no arguments. Started as `localization-tools set-time-zone <zone>` it is instead the
|
|
// module's step, which the host runs once as root for every version of the bundle (localization.go
|
|
// says why the time zone is a step and not a file).
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
|
|
stdio "git.novox.be/novox/mesh-sdk/go"
|
|
)
|
|
|
|
// binaryName is what the build names this bundle's executable: the manifest's `binary`.
|
|
const binaryName = "localization-tools"
|
|
|
|
func bg() context.Context { return context.Background() }
|
|
|
|
func main() {
|
|
m := ThisMachine()
|
|
if len(os.Args) > 1 {
|
|
if len(os.Args) != 3 || os.Args[1] != "set-time-zone" {
|
|
fmt.Fprintf(os.Stderr, "usage: %s [set-time-zone <zone>]\n", binaryName)
|
|
os.Exit(2)
|
|
}
|
|
said, err := m.SetTimeZoneStep(os.Args[2])
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
fmt.Println(said)
|
|
return
|
|
}
|
|
// An empty name serves as the module the runtime names (MESH_SERVED_MODULE): localization.
|
|
if err := stdio.Serve("", tools(m)); err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func tools(m *Machine) []stdio.Tool {
|
|
return []stdio.Tool{
|
|
{
|
|
Name: "localization_get",
|
|
Description: "The machine's locale (LANG and every LC_* localed reports), whether LANG is generated, its time zone, " +
|
|
"whether the clock keeps local time, whether NTP is on and synchronised, the console keymap and the X11 keyboard " +
|
|
"settings — and for each of locale, zone and keymap whether it is what the module declares " +
|
|
"(en_US.UTF-8, Europe/Brussels, us).",
|
|
Input: schema(map[string]any{}),
|
|
Run: func(map[string]any) (any, error) { return m.Get() },
|
|
},
|
|
{
|
|
Name: "localization_time_zone",
|
|
Description: "The time zone: read it; list the zones matching a word (match); or set one (set), through the time " +
|
|
"daemon with sudo -n, read back after. The module declares Europe/Brussels and its step sets it again " +
|
|
"whenever the module's bundle changes, which the answer says when another zone is set.",
|
|
Input: schema(map[string]any{
|
|
"set": map[string]any{"type": "string", "description": "a zone to set, as timedatectl list-timezones names it (optional)"},
|
|
"match": map[string]any{"type": "string", "description": "list the zones whose name holds this word (optional)"},
|
|
}),
|
|
Run: func(args map[string]any) (any, error) {
|
|
set, err := text(args, "set", false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
match, err := text(args, "match", false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return m.TimeZone(set, match)
|
|
},
|
|
},
|
|
{
|
|
Name: "localization_locales",
|
|
Description: "The locales: generated (locale -a), enabled in /etc/locale.gen, the LANG of /etc/locale.conf and " +
|
|
"whether it is generated, and how many glibc can generate — listed when a word narrows them (match).",
|
|
Input: schema(map[string]any{
|
|
"match": map[string]any{"type": "string", "description": "list the generatable locales whose name holds this word (optional)"},
|
|
}),
|
|
Run: func(args map[string]any) (any, error) {
|
|
match, err := text(args, "match", false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return m.Locales(match)
|
|
},
|
|
},
|
|
{
|
|
Name: "localization_keymaps",
|
|
Description: "The console keymap in force and the keymaps this machine has (localectl list-keymaps), narrowed to a word when given; at most 500 listed.",
|
|
Input: schema(map[string]any{
|
|
"match": map[string]any{"type": "string", "description": "list only keymaps whose name holds this word (optional)"},
|
|
}),
|
|
Run: func(args map[string]any) (any, error) {
|
|
match, err := text(args, "match", false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return m.Keymaps(match)
|
|
},
|
|
},
|
|
}
|
|
}
|