package main // The mirror list and its refresher (novox/hq to-be 42 Phase 1). On 2026-10-04 every machine's list // had been generated once — by a tool no longer installed, or by a hosting provider's installer — and // never again. The module installs reflector, owns its configuration and enables its weekly timer; // this reads the list and the refresher's last run, and starts a refresh on demand. import ( "strings" ) // Where the list is and how reflector is told to write it. const ( Mirrorlist = "/etc/pacman.d/mirrorlist" ReflectorConf = "/etc/xdg/reflector/reflector.conf" ) // Mirrors is the mirror list and its refresher. type Mirrors struct { Servers []string `json:"servers"` Commented int `json:"commented_servers"` GeneratedBy string `json:"generated_by,omitempty"` When string `json:"generated_when,omitempty"` Reflector []string `json:"reflector_options"` Timer map[string]string `json:"reflector_timer,omitempty"` LastRun map[string]string `json:"reflector_last_run,omitempty"` Refreshing bool `json:"refresh_started"` Note string `json:"note,omitempty"` } // ParseMirrorlist reads the servers in force, those commented out, and the generator's header. func ParseMirrorlist(text string) Mirrors { m := Mirrors{Servers: []string{}, Reflector: []string{}} for _, l := range lines(text) { l = strings.TrimSpace(l) switch { case strings.HasPrefix(l, "Server"): if _, v, ok := strings.Cut(l, "="); ok { m.Servers = append(m.Servers, strings.TrimSpace(v)) } case strings.HasPrefix(strings.TrimLeft(l, "# "), "Server"): m.Commented++ case strings.Contains(l, "generated by Reflector"): m.GeneratedBy = "reflector" case strings.HasPrefix(l, "# When:"): m.When = strings.TrimSpace(strings.TrimPrefix(l, "# When:")) case strings.HasPrefix(l, "## Generated on"): m.GeneratedBy, m.When = "the distribution's mirrorlist", strings.TrimSpace(strings.TrimPrefix(l, "## Generated on")) } } return m } // MirrorList answers the list, reflector's options, its timer and its last run; refresh starts // reflector now, without waiting, since ranking mirrors by rate takes longer than a call may. func (m *Machine) MirrorList(refresh bool) (Mirrors, error) { text, err := m.ReadFile(Mirrorlist) if err != nil { return Mirrors{}, err } out := ParseMirrorlist(string(text)) if conf, err := m.ReadFile(ReflectorConf); err == nil { for _, l := range lines(string(conf)) { if l = strings.TrimSpace(l); !strings.HasPrefix(l, "#") { out.Reflector = append(out.Reflector, l) } } } if t, err := m.unitProps("reflector.timer", "LoadState", "ActiveState", "UnitFileState", "LastTriggerUSec", "NextElapseUSecRealtime"); err == nil { out.Timer = t } if s, err := m.unitProps("reflector.service", "LoadState", "ActiveState", "Result", "ExecMainExitTimestamp", "ExecMainStatus"); err == nil { out.LastRun = s } if out.Timer["LoadState"] == "not-found" { out.Note = "reflector is not installed here; the module installs it" } if refresh { if _, err := m.Root("systemctl", "start", "--no-block", "reflector.service"); err != nil { return out, err } out.Refreshing = true out.Note = "reflector is ranking mirrors now; call again in a minute for the new list" } return out, nil }