diff --git a/node/icecast.go b/node/icecast.go index 403a9219f3f8ceb4395bbc224e30451b44afeaf7..ac490f8e78f397e4705f71cd1320905fb7e0cac6 100644 --- a/node/icecast.go +++ b/node/icecast.go @@ -73,24 +73,57 @@ func (ic *IcecastController) reload() error { return cmd.Run() } +// Kill sources connected to local streams. +func (ic *IcecastController) killSources(conf *ClusterConfig) error { + var anyErr error + client := &http.Client{} + for _, m := range conf.ListMounts() { + req, err := http.NewRequest("GET", fmt.Sprintf("http://localhost:%d/admin/killsource?mount=%s", autoradio.IcecastPort, autoradio.MountNameToIcecastPath(m.Name)), nil) + if err != nil { + anyErr = err + continue + } + req.SetBasicAuth("admin", getIcecastAdminPassword()) + resp, err := client.Do(req) + if err != nil { + anyErr = err + continue + } + resp.Body.Close() + if resp.StatusCode != 200 { + anyErr = fmt.Errorf("HTTP status %s", resp.Status) + } + } + return anyErr +} + // 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 := icecastConfigFile + ".tmp" - defer os.Remove(tmpf) + // Try to kill sources connected to the local icecast daemon + // before reloading, otherwise we'll have problems on master + // -> slave transitions (for example, on a restart of radiod) + // as sources will be "stuck" preventing the new configuration + // from taking effect. + if err := ic.killSources(conf); err != nil { + log.Printf("error killing sources: %v", err) + } + // Write a new configuration (atomically). ic.config.Update(conf, isMaster, masterAddr) + tmpf := icecastConfigFile + ".tmp" + defer os.Remove(tmpf) if err := ic.config.EncodeToFile(tmpf); err != nil { return err } - if err := os.Rename(tmpf, icecastConfigFile); err != nil { return err } + // Tell the Icecast daemon to reload its configuration. return ic.reload() }