package main import ( "fmt" "os" "os/exec" "runtime" "strings" "sync" "time" ) // Package work runs in a transient systemd unit, as do existing agent update // actions. It must not inherit ProtectSystem=strict from the reporting agent. // A started package transaction is allowed to finish if collection is disabled. type networkDependencyState struct { sync.Mutex running bool nextAttempt time.Time message string } var networkDependency networkDependencyState func (d *networkDependencyState) ensure(now time.Time, present bool, install func() error) string { d.Lock() defer d.Unlock() if present { return "" } if d.running { return "Автоустановка conntrack… Сбор начнётся после установки пакета." } if now.Before(d.nextAttempt) { return d.message + " Повторная попытка не раньше " + d.nextAttempt.Format("15:04:05") + " (время агента)." } d.running = true d.nextAttempt = now.Add(30 * time.Minute) go func() { err := install() d.Lock() defer d.Unlock() d.running = false if err != nil { d.message = "Не удалось установить conntrack: " + trafficText(err.Error(), 400) } else { d.message = "Установка завершена; ожидается появление conntrack в PATH" } }() return "Автоустановка conntrack… Сбор начнётся после установки пакета." } func ensureNetworkDependency() string { if runtime.GOOS != "linux" { return "Сбор conntrack поддерживается только на Linux" } _, err := exec.LookPath("conntrack") return networkDependency.ensure(time.Now(), err == nil, installNetworkDependency) } func installNetworkDependency() error { if os.Geteuid() != 0 { return fmt.Errorf("для автоустановки нужны права root") } if apk, err := exec.LookPath("apk"); err == nil { out := runAgentCombinedCommand(10*time.Minute, apk, "add", "--no-cache", "conntrack-tools") if strings.HasPrefix(out, "ERROR: ") { return fmt.Errorf("%s", out) } if _, err := exec.LookPath("conntrack"); err != nil { return fmt.Errorf("apk завершился, но conntrack не найден") } return nil } apt, err := exec.LookPath("apt-get") if err != nil { return fmt.Errorf("автоустановка поддерживается на Debian/Ubuntu с apt-get; установите conntrack вручную") } run := func(suffix string, args ...string) error { out := runPrivilegedTransient(10*time.Minute, suffix, apt, args...) if strings.HasPrefix(out, "ERROR: ") { return fmt.Errorf("%s", explainSystemUpdateError(out)) } return nil } return installConntrackPackage(run, func() bool { _, err := exec.LookPath("conntrack"); return err == nil }) } func installConntrackPackage(run func(string, ...string) error, present func() bool) error { // No upgrade, autoremove, firewall changes, or runtime sysctl changes. options := []string{"-o", "DPkg::Lock::Timeout=30", "-o", "Acquire::Retries=1", "-o", "Acquire::http::Timeout=20", "-o", "Acquire::https::Timeout=20"} if err := run("conntrack-lists", append(append([]string{}, options...), "update")...); err != nil { return err } if err := run("conntrack-install", append(append([]string{}, options...), "-y", "--no-install-recommends", "--no-remove", "install", "conntrack")...); err != nil { return err } if !present() { return fmt.Errorf("apt-get завершился, но conntrack не найден в PATH") } return nil }