localization: locale, time zone and console keymap as one module
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).
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
// 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)
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user