// MsgSyncReq: install packages on the remote host (Debian) // Copyright (C) 2021 Simon Ruderich // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . package sync import ( "fmt" "os" "os/exec" "strings" "ruderich.org/simon/safcm" ) func (s *Sync) syncPackagesDebian() error { s.log.Debugf("packages: detected debian") installed, err := s.debianInstalledPackages() if err != nil { return err } s.log.Debugf("packages: checking %s", strings.Join(s.req.Packages, " ")) var install []string for _, x := range s.req.Packages { if !installed[x] { install = append(install, x) } } if len(install) == 0 { return nil } for _, x := range install { s.resp.PackageChanges = append(s.resp.PackageChanges, safcm.PackageChange{ Name: x, }) } if s.req.DryRun { return nil } s.log.Verbosef("packages: installing %s", strings.Join(install, " ")) cmd := exec.Command("/usr/bin/apt-get", append([]string{"install", // Don't require further acknowledgment; this won't perform // dangerous actions "--assume-yes", // Don't perform upgrades "--no-upgrade", // Nobody needs those "--no-install-recommends", // Don't overwrite existing config files "-o", "Dpkg::Options::=--force-confdef", "-o", "Dpkg::Options::=--force-confold", }, install...)...) cmd.Env = append(os.Environ(), // Don't ask questions during installation "DEBIAN_FRONTEND=noninteractive", ) _, err = s.cmd.CombinedOutputCmd("packages", cmd) if err != nil { return err } return nil } func (s *Sync) debianInstalledPackages() (map[string]bool, error) { out, _, err := s.cmd.Run("packages", "/usr/bin/dpkg-query", "--show", `--showformat=${Status}\t${Package}\n`, ) if err != nil { return nil, err } lines := strings.Split(strings.TrimSpace(string(out)), "\n") res := make(map[string]bool) for _, line := range lines { xs := strings.Split(line, "\t") if len(xs) != 2 { return nil, fmt.Errorf("invalid dpkg-query line %q", line) } // We only care if the package is currently successfully // installed (last two fields). If a package is on hold (first // field) this is fine as well. if strings.HasSuffix(xs[0], " ok installed") { res[xs[1]] = true } } return res, nil }