90 lines
3.1 KiB
Go
90 lines
3.1 KiB
Go
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")
|
||
}
|
||
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
|
||
}
|