icecast.go 2.44 KiB
package node
import (
"encoding/xml"
"errors"
"io"
"log"
"net/http"
"os"
"os/exec"
"time"
"git.autistici.org/ale/radioai"
)
var (
statusPageUrl = "http://localhost:8000/status-radioai.xsl"
)
type IcecastStatus struct {
XMLName xml.Name `xml:"status",json:"-"`
Mounts []radioai.IcecastMountStatus `xml:"mount"`
Up bool
}
type IcecastController struct {
PublicIp string
ConfigFile string
InitScript string
config *icecastConfig
status *IcecastStatus
stop chan bool
}
func NewIcecastController(publicIp string, stop chan bool) *IcecastController {
return &IcecastController{
PublicIp: publicIp,
ConfigFile: "/etc/icecast2/icecast.xml",
InitScript: "/etc/init.d/icecast2",
config: newIcecastConfig(publicIp),
status: &IcecastStatus{},
stop: make(chan bool, 1),
}
}
func (ic *IcecastController) reload() error {
err := exec.Command(ic.InitScript, "reload").Run()
if err != nil {
err = exec.Command(ic.InitScript, "start").Run()
}
return err
}
// Update reloads the Icecast daemon with a new configuration.
func (ic *IcecastController) Update(conf *ClusterConfig, isMaster bool, masterAddr string) error {
if !isMaster && masterAddr == "" {
return errors.New("unknown system state")
}
tmpf := ic.ConfigFile + ".tmp"
defer os.Remove(tmpf)
ic.config.Update(conf, isMaster, masterAddr)
if err := ic.config.EncodeToFile(tmpf); err != nil {
return err
}
if err := os.Rename(tmpf, ic.ConfigFile); err != nil {
return err
}
return ic.reload()
}
func (ic *IcecastController) GetStatus() *IcecastStatus {
return ic.status
}
func (ic *IcecastController) statusUpdater() {
t := time.NewTicker(3 * time.Second)
downStatus := &IcecastStatus{}
for {
select {
case <-t.C:
if status, err := ic.fetchStatus(); err == nil {
ic.status = status
} else {
log.Printf("bad status from iceast: %v", err)
ic.status = downStatus
}
case <-ic.stop:
return
}
}
}
func (ic *IcecastController) fetchStatus() (*IcecastStatus, error) {
resp, err := http.Get(statusPageUrl)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return ic.parseStatusPage(resp.Body)
}
func (ic *IcecastController) parseStatusPage(input io.Reader) (*IcecastStatus, error) {
var status IcecastStatus
if err := xml.NewDecoder(input).Decode(&status); err != nil {
return nil, err
}
status.Up = true
return &status, nil
}
func (ic *IcecastController) Run() {
ic.statusUpdater()
}