765 lines
19 KiB
Go
765 lines
19 KiB
Go
//nolint:exhaustivestruct,gochecknoglobals
|
|
package prepare
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"os/user"
|
|
"strings"
|
|
"text/template"
|
|
|
|
"github.com/rs/zerolog"
|
|
"github.com/rs/zerolog/log"
|
|
"github.com/spf13/cobra"
|
|
assets "go.xsfx.dev/schnutibox/assets/prepare"
|
|
)
|
|
|
|
const (
|
|
mopidyGroup = "audio"
|
|
mopidyUser = "mopidy"
|
|
serviceFileName = "schnutibox.service"
|
|
serviceLocation = "/etc/systemd/system"
|
|
timesyncGroup = "systemd-timesync"
|
|
timesyncUser = "systemd-timesync"
|
|
schnutiboxUser = "schnutibox"
|
|
schnutboxConfigDir = "/etc/schnutibox"
|
|
upmpdcliUser = "upmpdcli"
|
|
upmpdcliGroup = "nogroup"
|
|
snapserverUser = "snapserver"
|
|
snapserverGroup = "snapserver"
|
|
snapclientUser = "snapclient"
|
|
snapclientGroup = "snapclient"
|
|
)
|
|
|
|
// Cfg represents the structured data for the schnutibox config file.
|
|
var Cfg = struct {
|
|
RFIDReader string
|
|
ReadOnly bool
|
|
Spotify bool
|
|
SpotifyClientID string
|
|
SpotifyClientSecret string
|
|
SpotifyPassword string
|
|
SpotifyUsername string
|
|
StopID string
|
|
System string
|
|
}{}
|
|
|
|
// boxService creates a systemd service for schnutibox.
|
|
func boxService(filename string, enable bool) error {
|
|
logger := log.With().Str("stage", "BoxService").Logger()
|
|
|
|
if err := createUser(); err != nil {
|
|
return fmt.Errorf("could not create user: %w", err)
|
|
}
|
|
|
|
// Create config dir.
|
|
if err := os.MkdirAll(schnutboxConfigDir, os.ModePerm); err != nil {
|
|
return fmt.Errorf("could not create config dir: %w", err)
|
|
}
|
|
|
|
schnutiboxService, err := assets.Files.ReadFile("files/schnutibox.service")
|
|
if err != nil {
|
|
return fmt.Errorf("could not get service file: %w", err)
|
|
}
|
|
|
|
//nolint:gosec,gomnd
|
|
if err := ioutil.WriteFile(filename, schnutiboxService, 0o644); err != nil {
|
|
return fmt.Errorf("could not write service file: %w", err)
|
|
}
|
|
|
|
if enable {
|
|
cmd := exec.Command("systemctl", "daemon-reload")
|
|
logger.Info().Str("cmd", cmd.String()).Msg("running")
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("could not reload service files: %w", err)
|
|
}
|
|
|
|
cmd = exec.Command("systemctl", "enable", "schnutibox.service")
|
|
logger.Info().Str("cmd", cmd.String()).Msg("running")
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("could not enable service: %w", err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func ntp() error {
|
|
logger := log.With().Str("stage", "NTP").Logger()
|
|
|
|
cmd := exec.Command("apt-get", "install", "-y", "ntp", "ntpdate")
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
|
|
logger.Info().Str("cmd", cmd.String()).Msg("running")
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("could not install ntp: %w", err)
|
|
}
|
|
|
|
ntpService, err := assets.Files.ReadFile("files/ntp.service")
|
|
if err != nil {
|
|
return fmt.Errorf("could not get ntp service file: %w", err)
|
|
}
|
|
|
|
// nolint:gosec,gomnd
|
|
if err := ioutil.WriteFile("/etc/systemd/system/ntp.service", ntpService, 0o644); err != nil {
|
|
return fmt.Errorf("could not copy ntp service file: %w", err)
|
|
}
|
|
|
|
cmd = exec.Command("systemctl", "daemon-reload")
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
|
|
logger.Info().Str("cmd", cmd.String()).Msg("running")
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("could not reload systemd service files: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// fstab creates a fstab for a read-only system.
|
|
// nolint:funlen,cyclop
|
|
func fstab(system string) error {
|
|
logger := log.With().Str("stage", "Fstab").Logger()
|
|
logger.Debug().Str("system", system).Msg("ignoring for now")
|
|
|
|
// Getting timesync user and group informations.
|
|
timesyncUser, err := user.Lookup(timesyncUser)
|
|
if err != nil {
|
|
return fmt.Errorf("could not lookup timesync user: %w", err)
|
|
}
|
|
|
|
timesyncGroup, err := user.LookupGroup(timesyncGroup)
|
|
if err != nil {
|
|
return fmt.Errorf("could not lookup timesync group: %w", err)
|
|
}
|
|
|
|
logger.Debug().Str("uid", timesyncUser.Uid).Str("gid", timesyncGroup.Gid).Msg("timesyncd")
|
|
|
|
// Getting mopidy user and group informations.
|
|
mopidyUser, err := user.Lookup(mopidyUser)
|
|
if err != nil {
|
|
return fmt.Errorf("could not lookup mopidy user: %w", err)
|
|
}
|
|
|
|
mopidyGroup, err := user.LookupGroup(mopidyGroup)
|
|
if err != nil {
|
|
return fmt.Errorf("could not lookup mopidy group: %w", err)
|
|
}
|
|
|
|
logger.Debug().Str("uid", mopidyUser.Uid).Str("gid", mopidyGroup.Gid).Msg("mopidy")
|
|
|
|
// Getting upmpd user and group informations.
|
|
upmpdcliUser, err := user.Lookup(upmpdcliUser)
|
|
if err != nil {
|
|
return fmt.Errorf("could not lookup upmpdcli user: %w", err)
|
|
}
|
|
|
|
upmpdcliGroup, err := user.LookupGroup(upmpdcliGroup)
|
|
if err != nil {
|
|
return fmt.Errorf("could not lookup upmpdcli group: %w", err)
|
|
}
|
|
|
|
logger.Debug().Str("uid", upmpdcliUser.Uid).Str("gid", upmpdcliGroup.Gid).Msg("upmpdcli")
|
|
|
|
// Getting snapserver user and group informations.
|
|
snapserverUser, err := user.Lookup(snapserverUser)
|
|
if err != nil {
|
|
return fmt.Errorf("could not lookup snapserver user: %w", err)
|
|
}
|
|
|
|
snapserverGroup, err := user.LookupGroup(snapserverGroup)
|
|
if err != nil {
|
|
return fmt.Errorf("could not lookup snapserver group: %w", err)
|
|
}
|
|
|
|
logger.Debug().Str("uid", snapserverUser.Uid).Str("gid", snapserverGroup.Gid).Msg("snapserver")
|
|
|
|
snapclientUser, err := user.Lookup(snapclientUser)
|
|
if err != nil {
|
|
return fmt.Errorf("could not lookup snapclient user: %w", err)
|
|
}
|
|
|
|
snapclientGroup, err := user.LookupGroup(snapclientGroup)
|
|
if err != nil {
|
|
return fmt.Errorf("could not lookup snapclient group: %w", err)
|
|
}
|
|
|
|
logger.Debug().Str("uid", snapclientUser.Uid).Str("gid", snapclientGroup.Gid).Msg("snapclient")
|
|
|
|
// Chose the right template.
|
|
// In future it should be a switch statement.
|
|
tmpl, err := assets.Templates.ReadFile("templates/fstab.raspbian.tmpl")
|
|
if err != nil {
|
|
return fmt.Errorf("could not get fstab template: %w", err)
|
|
}
|
|
|
|
// Parse template.
|
|
t := template.Must(template.New("fstab").Parse(string(tmpl)))
|
|
|
|
// Open fstab.
|
|
f, err := os.Create("/etc/fstab")
|
|
if err != nil {
|
|
return fmt.Errorf("could not create file to write: %w", err)
|
|
}
|
|
defer f.Close()
|
|
|
|
// Create and write.
|
|
if err := t.Execute(f, struct {
|
|
TimesyncUID string
|
|
TimesyncGID string
|
|
MopidyUID string
|
|
MopidyGID string
|
|
UpmpdcliUID string
|
|
UpmpdcliGID string
|
|
SnapserverUID string
|
|
SnapserverGID string
|
|
SnapclientUID string
|
|
SnapclientGID string
|
|
}{
|
|
timesyncUser.Uid,
|
|
timesyncGroup.Gid,
|
|
mopidyUser.Uid,
|
|
mopidyGroup.Gid,
|
|
upmpdcliUser.Uid,
|
|
upmpdcliGroup.Gid,
|
|
snapserverUser.Uid,
|
|
snapserverGroup.Gid,
|
|
snapclientUser.Uid,
|
|
snapclientGroup.Gid,
|
|
}); err != nil {
|
|
return fmt.Errorf("could not write templated fstab: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// removePkgs removes not needed software in read-only mode.
|
|
func removePkgs(system string) error {
|
|
logger := log.With().Str("stage", "RemovePkgs").Logger()
|
|
if system != "raspbian" {
|
|
logger.Info().Msg("nothing to do")
|
|
|
|
return nil
|
|
}
|
|
|
|
pkgs := []string{
|
|
"cron",
|
|
"logrotate",
|
|
"triggerhappy",
|
|
"dphys-swapfile",
|
|
"fake-hwclock",
|
|
"samba-common",
|
|
}
|
|
|
|
for _, i := range pkgs {
|
|
logger.Debug().Str("pkg", i).Msg("remove package")
|
|
cmd := exec.Command("apt-get", "remove", "-y", i)
|
|
logger.Debug().Str("cmd", cmd.String()).Msg("running")
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("could not remove pkg: %w", err)
|
|
}
|
|
}
|
|
|
|
cmd := exec.Command("apt-get", "autoremove", "--purge", "-y")
|
|
logger.Debug().Str("cmd", cmd.String()).Msg("running")
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("could not do an autoremove: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func udevRules() error {
|
|
logger := log.With().Str("stage", "CreateUDEVrules").Logger()
|
|
logger.Info().Msg("writing udev rule file")
|
|
|
|
// Parse template.
|
|
tmpl, err := assets.Templates.ReadFile("templates/50-neuftech.rules.tmpl")
|
|
if err != nil {
|
|
return fmt.Errorf("could not get udev rules file: %w", err)
|
|
}
|
|
|
|
t := template.Must(template.New("udev").Parse(string(tmpl)))
|
|
|
|
// Open file.
|
|
f, err := os.Create("/etc/udev/rules.d/50-neuftech.rules")
|
|
if err != nil {
|
|
return fmt.Errorf("could not create file to write: %w", err)
|
|
}
|
|
defer f.Close()
|
|
|
|
// Create and write.
|
|
if err := t.Execute(f, struct {
|
|
SchnutiboxGroup string
|
|
}{
|
|
schnutiboxUser,
|
|
}); err != nil {
|
|
return fmt.Errorf("could not write templated udev rules: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// createUser creates schnutibox system user and group.
|
|
func createUser() error {
|
|
logger := log.With().Str("stage", "CreateUser").Logger()
|
|
|
|
cmd := exec.Command("adduser", "--system", "--group", "--no-create-home", schnutiboxUser)
|
|
logger.Debug().Str("cmd", cmd.String()).Msg("running")
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("could not create user: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// symlinks creates all needed symlinks.
|
|
func symlinks(system string) error {
|
|
logger := log.With().Str("stage", "Symlinks").Logger()
|
|
logger.Debug().Str("system", system).Msg("ignoring for now")
|
|
|
|
links := []struct {
|
|
symlink string
|
|
dest string
|
|
}{
|
|
{
|
|
"/var/lib/dhcp",
|
|
"/tmp/dhcp",
|
|
},
|
|
{
|
|
"/var/spool",
|
|
"/tmp/spool",
|
|
},
|
|
{
|
|
"/var/lock",
|
|
"/tmp/lock",
|
|
},
|
|
{
|
|
"/etc/resolv.conf",
|
|
"/tmp/resolv.conf",
|
|
},
|
|
}
|
|
|
|
removeFiles := []string{
|
|
"/var/lib/dhcp",
|
|
"/var/spool",
|
|
"/var/lock",
|
|
"/etc/resolv.conf",
|
|
}
|
|
|
|
for _, i := range removeFiles {
|
|
logger.Debug().Str("item", i).Msg("remove file/directory")
|
|
|
|
if err := os.RemoveAll(i); err != nil {
|
|
return fmt.Errorf("could not remove: %w", err)
|
|
}
|
|
}
|
|
|
|
for _, i := range links {
|
|
logger.Debug().Str("symlink", i.symlink).Str("dest", i.dest).Msg("linking")
|
|
|
|
dest, err := os.Readlink(i.symlink)
|
|
if err == nil {
|
|
if dest == i.dest {
|
|
logger.Debug().Str("dest", dest).Str("expected dest", i.dest).Msg("matches")
|
|
|
|
continue
|
|
}
|
|
}
|
|
|
|
if err := os.Symlink(i.dest, i.symlink); err != nil {
|
|
return fmt.Errorf("could not create symlink: %w", err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// cmdlineTxt modifies the /boot/cmdline.txt.
|
|
func cmdlineTxt() error {
|
|
// Read.
|
|
oldLine, err := ioutil.ReadFile("/boot/cmdline.txt")
|
|
if err != nil {
|
|
return fmt.Errorf("could not read cmdline.txt: %w", err)
|
|
}
|
|
|
|
newLine := strings.TrimSuffix(string(oldLine), "\n") + " " + "fastboot" + " " + "noswap"
|
|
|
|
// Write.
|
|
// nolint:gosec,gomnd
|
|
if err := ioutil.WriteFile("/boot/cmdline.txt", []byte(newLine), 0o644); err != nil {
|
|
return fmt.Errorf("could not write cmdline.txt: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// readOnly executes stuff if a read-only system is wanted.
|
|
func readOnly(system string) error {
|
|
if err := removePkgs(system); err != nil {
|
|
return fmt.Errorf("could not remove pkgs: %w", err)
|
|
}
|
|
|
|
if err := symlinks(system); err != nil {
|
|
return fmt.Errorf("could not create symlinks: %w", err)
|
|
}
|
|
|
|
if err := fstab(system); err != nil {
|
|
return fmt.Errorf("could not create fstab: %w", err)
|
|
}
|
|
|
|
if err := cmdlineTxt(); err != nil {
|
|
return fmt.Errorf("could not modify cmdline.txt: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// mopidy setups mopidy.
|
|
//nolint:funlen,cyclop
|
|
func mopidy() error {
|
|
logger := log.With().Str("stage", "Mopidy").Logger()
|
|
|
|
// GPG Key.
|
|
cmd := exec.Command("/bin/sh", "-c", "wget -q -O - https://apt.mopidy.com/mopidy.gpg | apt-key add -")
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
|
|
logger.Debug().Str("cmd", cmd.String()).Msg("running")
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("could not add mopidy key: %w", err)
|
|
}
|
|
|
|
// Repo.
|
|
cmd = exec.Command(
|
|
"/bin/sh", "-c",
|
|
"wget -q -O /etc/apt/sources.list.d/mopidy.list https://apt.mopidy.com/buster.list",
|
|
)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
|
|
logger.Debug().Str("cmd", cmd.String()).Msg("running")
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("could not download apt repo: %w", err)
|
|
}
|
|
|
|
// Update.
|
|
cmd = exec.Command("apt-get", "update")
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
|
|
logger.Debug().Str("cmd", cmd.String()).Msg("running")
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("could not update apt: %w", err)
|
|
}
|
|
|
|
// Install.
|
|
cmd = exec.Command(
|
|
"apt-get", "install", "-y",
|
|
"libgstreamer-plugins-bad1.0",
|
|
"mopidy",
|
|
"mopidy-alsamixer",
|
|
"mopidy-mpd",
|
|
"mopidy-spotify",
|
|
"python3-pip",
|
|
)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
|
|
logger.Debug().Str("cmd", cmd.String()).Msg("running")
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("could not install mopidy: %w", err)
|
|
}
|
|
|
|
// Extensions.
|
|
cmd = exec.Command(
|
|
"pip3", "install", "--upgrade",
|
|
"Mopidy-YouTube",
|
|
"requests>=2.22",
|
|
)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
|
|
logger.Debug().Str("cmd", cmd.String()).Msg("running")
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("could not install extensions: %w", err)
|
|
}
|
|
|
|
// Enable service.
|
|
cmd = exec.Command("systemctl", "enable", "mopidy.service")
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
|
|
logger.Debug().Str("cmd", cmd.String()).Msg("running")
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("could not enable mopidy service: %w", err)
|
|
}
|
|
|
|
// Config.
|
|
if Cfg.SpotifyUsername != "" &&
|
|
Cfg.SpotifyPassword != "" &&
|
|
Cfg.SpotifyClientID != "" &&
|
|
Cfg.SpotifyClientSecret != "" {
|
|
Cfg.Spotify = true
|
|
}
|
|
|
|
tmpl, err := assets.Templates.ReadFile("templates/mopidy.conf.tmpl")
|
|
if err != nil {
|
|
return fmt.Errorf("could not get mopidy.conf: %w", err)
|
|
}
|
|
|
|
t := template.Must(template.New("mopidyConf").Parse(string(tmpl)))
|
|
|
|
f, err := os.Create("/etc/mopidy/mopidy.conf")
|
|
if err != nil {
|
|
return fmt.Errorf("could not create file to write: %w", err)
|
|
}
|
|
defer f.Close()
|
|
|
|
if err := t.Execute(f, Cfg); err != nil {
|
|
return fmt.Errorf("could not write compiled mopidy config: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Upmpdcli setups upmpdcli.
|
|
//nolint:funlen
|
|
func upmpdcli() error {
|
|
logger := log.With().Str("stage", "Upmpdcli").Logger()
|
|
|
|
// GPG Key.
|
|
cmd := exec.Command(
|
|
"/bin/sh", "-c",
|
|
"wget https://www.lesbonscomptes.com/pages/lesbonscomptes.gpg -O /usr/share/keyrings/lesbonscomptes.gpg",
|
|
)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
|
|
logger.Debug().Str("cmd", cmd.String()).Msg("running")
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("could not add upmpdcli key: %w", err)
|
|
}
|
|
|
|
// Repo.
|
|
cmd = exec.Command(
|
|
"curl",
|
|
"-o",
|
|
"/etc/apt/sources.list.d/upmpdcli.list",
|
|
"https://www.lesbonscomptes.com/upmpdcli/pages/upmpdcli-rbuster.list",
|
|
)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
|
|
logger.Debug().Str("cmd", cmd.String()).Msg("running")
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("could not add sources list for upmpdcli: %w", err)
|
|
}
|
|
|
|
// Update.
|
|
cmd = exec.Command("apt-get", "update")
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
|
|
logger.Debug().Str("cmd", cmd.String()).Msg("running")
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("could not update apt: %w", err)
|
|
}
|
|
|
|
// Install.
|
|
cmd = exec.Command(
|
|
"apt-get", "install", "-y",
|
|
"upmpdcli",
|
|
)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
|
|
logger.Debug().Str("cmd", cmd.String()).Msg("running")
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("could not install mopidy: %w", err)
|
|
}
|
|
|
|
// Enable service.
|
|
cmd = exec.Command("systemctl", "enable", "upmpdcli.service")
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
|
|
logger.Debug().Str("cmd", cmd.String()).Msg("running")
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("could not enable upmpdcli service: %w", err)
|
|
}
|
|
|
|
// Create config.
|
|
upmpdcliConf, err := assets.Files.ReadFile("files/upmpdcli.conf")
|
|
if err != nil {
|
|
return fmt.Errorf("could not get upmpdcli.conf: %w", err)
|
|
}
|
|
|
|
// nolint:gosec,gomnd
|
|
if err := ioutil.WriteFile("/etc/upmpdcli.conf", upmpdcliConf, 0o644); err != nil {
|
|
return fmt.Errorf("could not copy upmpdcli config: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// nolint:funlen
|
|
func snapcast() error {
|
|
logger := log.With().Str("stage", "snapcast").Logger()
|
|
|
|
// Download deb.
|
|
cmd := exec.Command(
|
|
"wget",
|
|
"https://github.com/badaix/snapcast/releases/download/v0.24.0/snapclient_0.24.0-1_without-pulse_armhf.deb",
|
|
"-O", "/tmp/snapclient.deb",
|
|
)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
|
|
logger.Debug().Str("cmd", cmd.String()).Msg("running")
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("could not download snapclient deb: %w", err)
|
|
}
|
|
|
|
// Install deb
|
|
cmd = exec.Command(
|
|
"/bin/sh", "-c",
|
|
"dpkg -i /tmp/snapclient.deb; apt --fix-broken install -y; rm /tmp/snapclient.deb",
|
|
)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
|
|
logger.Debug().Str("cmd", cmd.String()).Msg("running")
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("could not install snapclient deb: %w", err)
|
|
}
|
|
|
|
// Download deb.
|
|
cmd = exec.Command(
|
|
"wget",
|
|
"https://github.com/badaix/snapcast/releases/download/v0.24.0/snapserver_0.24.0-1_armhf.deb",
|
|
"-O", "/tmp/snapserver.deb",
|
|
)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
|
|
logger.Debug().Str("cmd", cmd.String()).Msg("running")
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("could not download snapserver deb: %w", err)
|
|
}
|
|
|
|
// Install deb
|
|
cmd = exec.Command(
|
|
"/bin/sh", "-c",
|
|
"dpkg -i /tmp/snapserver.deb; apt --fix-broken install -y; rm /tmp/snapserver.deb",
|
|
)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
|
|
logger.Debug().Str("cmd", cmd.String()).Msg("running")
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("could not install snapserver deb: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func schnutiboxConfig() error {
|
|
logger := log.With().Str("stage", "schnutiboxConfig").Logger()
|
|
logger.Info().Msg("writing schnutibox config")
|
|
|
|
// Parse template.
|
|
tmpl, err := assets.Templates.ReadFile("templates/schnutibox.yml.tmpl")
|
|
if err != nil {
|
|
return fmt.Errorf("could not get template: %w", err)
|
|
}
|
|
|
|
t := template.Must(template.New("config").Parse(string(tmpl)))
|
|
|
|
// Open file.
|
|
f, err := os.Create("/etc/schnutibox/schnutibox.yml")
|
|
if err != nil {
|
|
return fmt.Errorf("could not create file to write: %w", err)
|
|
}
|
|
defer f.Close()
|
|
|
|
// Create and write.
|
|
if err := t.Execute(f, Cfg); err != nil {
|
|
return fmt.Errorf("could not write templated udev rules: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func Run(cmd *cobra.Command, args []string) {
|
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
|
|
|
// Install schnutibox service.
|
|
if err := boxService(serviceLocation+"/"+serviceFileName, true); err != nil {
|
|
log.Fatal().Err(err).Msg("could not create schnutibox service")
|
|
}
|
|
|
|
// Create schnutibox config.
|
|
if err := schnutiboxConfig(); err != nil {
|
|
log.Fatal().Err(err).Msg("could not create schnutibox config.")
|
|
}
|
|
|
|
// Install udev file.
|
|
if err := udevRules(); err != nil {
|
|
log.Fatal().Err(err).Msg("could not install udev rules")
|
|
}
|
|
|
|
// Setup NTP.
|
|
if err := ntp(); err != nil {
|
|
log.Fatal().Err(err).Msg("could not setup ntp")
|
|
}
|
|
|
|
// Setup mopidy.
|
|
if err := mopidy(); err != nil {
|
|
log.Fatal().Err(err).Msg("could not setup mopidy")
|
|
}
|
|
|
|
// Setup upmpdcli.
|
|
if err := upmpdcli(); err != nil {
|
|
log.Fatal().Err(err).Msg("could not setup upmpdcli")
|
|
}
|
|
|
|
// Setup snapcast.
|
|
if err := snapcast(); err != nil {
|
|
log.Fatal().Err(err).Msg("could not setup snapclient")
|
|
}
|
|
|
|
// Making system read-only.
|
|
if Cfg.ReadOnly {
|
|
if err := readOnly(Cfg.System); err != nil {
|
|
log.Fatal().Err(err).Msg("could not make system read-only")
|
|
}
|
|
}
|
|
}
|