func (n Nginx) Reload() error { n.Lock() defer n.Unlock() switch cfg.Current.Nginx.Mode { case "systemd": if n.Systemd == nil { c, err := dbus.NewSystemdConnection() if err != nil { return err } n.Systemd = c } ch := make(chan string) _, err := n.Systemd.ReloadUnit(cfg.Current.Nginx.Systemd.Service, "replace", ch) if err != nil { return err } <-ch return nil default: return errors.New("unknown Nginx mode") } panic("unreachable") }
func (obj *ServiceType) Apply() bool { log.Printf("%v[%v]: Apply", obj.GetType(), obj.GetName()) if !util.IsRunningSystemd() { log.Fatal("Systemd is not running.") } conn, err := systemd.NewSystemdConnection() // needs root access if err != nil { log.Fatal("Failed to connect to systemd: ", err) } defer conn.Close() var service = fmt.Sprintf("%v.service", obj.Name) // systemd name var files = []string{service} // the service represented in a list if obj.Startup == "enabled" { _, _, err = conn.EnableUnitFiles(files, false, true) } else if obj.Startup == "disabled" { _, err = conn.DisableUnitFiles(files, false) } else { err = nil } if err != nil { log.Printf("Unable to change startup status: %v", err) return false } result := make(chan string, 1) // catch result information if obj.State == "running" { _, err := conn.StartUnit(service, "fail", result) if err != nil { log.Fatal("Failed to start unit: ", err) return false } } else if obj.State == "stopped" { _, err = conn.StopUnit(service, "fail", result) if err != nil { log.Fatal("Failed to stop unit: ", err) return false } } else { log.Fatal("Unknown state: ", obj.State) } status := <-result if &status == nil { log.Fatal("Result is nil") return false } if status != "done" { log.Fatal("Unknown return string: ", status) return false } // XXX: also set enabled on boot return true }
// WaitOnDevices waits for the devices named in devs to be plugged before returning. func WaitOnDevices(devs []string, stage string) error { conn, err := dbus.NewSystemdConnection() if err != nil { return err } results := map[string]chan string{} for _, dev := range devs { unitName := unit.UnitNamePathEscape(dev + ".device") results[unitName] = make(chan string) if _, err = conn.StartUnit(unitName, "replace", results[unitName]); err != nil { return fmt.Errorf("failed starting device unit %s: %v", unitName, err) } } for unitName, result := range results { s := <-result if s != "done" { return fmt.Errorf("device unit %s %s", unitName, s) } } return nil }
// Reload tells haproxy to reload its configuration func Reload() (err error) { conn, err := dbus.NewSystemdConnection() if err != nil { return err } _, err = conn.RestartUnit("haproxy.cycore@"+instanceID+".service", "ignore-dependencies", nil) return err }
func (obj *ServiceType) StateOK() bool { if obj.isStateOK { // cache the state return true } if !util.IsRunningSystemd() { log.Fatal("Systemd is not running.") } conn, err := systemd.NewSystemdConnection() // needs root access if err != nil { log.Fatal("Failed to connect to systemd: ", err) } defer conn.Close() var service = fmt.Sprintf("%v.service", obj.Name) // systemd name loadstate, err := conn.GetUnitProperty(service, "LoadState") if err != nil { log.Printf("Failed to get load state: %v", err) return false } // NOTE: we have to compare variants with other variants, they are really strings... var notFound = (loadstate.Value == dbus.MakeVariant("not-found")) if notFound { log.Printf("Failed to find service: %v", service) return false } // XXX: check service "enabled at boot" or not status... //conn.GetUnitProperties(service) activestate, err := conn.GetUnitProperty(service, "ActiveState") if err != nil { log.Fatal("Failed to get active state: ", err) } var running = (activestate.Value == dbus.MakeVariant("active")) if obj.State == "running" { if !running { return false // we are in the wrong state } } else if obj.State == "stopped" { if running { return false } } else { log.Fatal("Unknown state: ", obj.State) } return true // all is good, no state change needed }
func (s *ServiceStatus) Init() error { var err error if s.Name == "" { return e.New(ErrInvServName) } s.conn, err = sd.NewSystemdConnection() if err != nil { return e.Forward(err) } err = s.conn.Subscribe() if err != nil { return e.Forward(err) } ch, cherr := s.conn.SubscribeUnits(s.Interval) go func() { for { select { case status := <-ch: if status == nil { continue } st, found := status[s.Name] if !found { log.Tag("systemd").Printf("Status of %v is unknow.", s.Name) s.setStatus(Unknow) continue } if s == nil { s.setStatus(Deleted) continue } if st.LoadState == "loaded" && st.ActiveState == "active" && st.SubState == "running" { s.setStatus(Running) } else if st.LoadState == "loaded" && st.ActiveState == "active" && st.SubState == "active" { s.setStatus(Running) } else if st.LoadState == "not-found" && st.ActiveState == "active" && st.SubState == "exited" { s.setStatus(Stopped) } else if st.LoadState == "loaded" && st.ActiveState == "inactive" && st.SubState == "dead" { s.setStatus(Stopped) } else { //ActiveState: deactivating, LoadState: loaded, SubState: stop-sigterm log.Tag("systemd").Printf("ActiveState: %v, LoadState: %v, SubState: %v", st.ActiveState, st.LoadState, st.SubState) s.setStatus(Other) } case err := <-cherr: log.Tag("systemd", "service", "status").Fatalln("SubscribeUnits error:", err) } } }() return nil }
// Watch is the primary listener for this resource and it outputs events. func (obj *SvcRes) Watch(processChan chan event.Event) error { if obj.IsWatching() { return nil } obj.SetWatching(true) defer obj.SetWatching(false) cuid := obj.converger.Register() defer cuid.Unregister() var startup bool Startup := func(block bool) <-chan time.Time { if block { return nil // blocks forever //return make(chan time.Time) // blocks forever } return time.After(time.Duration(500) * time.Millisecond) // 1/2 the resolution of converged timeout } // obj.Name: svc name if !systemdUtil.IsRunningSystemd() { return fmt.Errorf("Systemd is not running.") } conn, err := systemd.NewSystemdConnection() // needs root access if err != nil { return errwrap.Wrapf(err, "Failed to connect to systemd") } defer conn.Close() // if we share the bus with others, we will get each others messages!! bus, err := util.SystemBusPrivateUsable() // don't share the bus connection! if err != nil { return errwrap.Wrapf(err, "Failed to connect to bus") } // XXX: will this detect new units? bus.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, "type='signal',interface='org.freedesktop.systemd1.Manager',member='Reloading'") buschan := make(chan *dbus.Signal, 10) bus.Signal(buschan) var svc = fmt.Sprintf("%s.service", obj.Name) // systemd name var send = false // send event? var exit = false var invalid = false // does the svc exist or not? var previous bool // previous invalid value set := conn.NewSubscriptionSet() // no error should be returned subChannel, subErrors := set.Subscribe() var activeSet = false for { // XXX: watch for an event for new units... // XXX: detect if startup enabled/disabled value changes... previous = invalid invalid = false // firstly, does svc even exist or not? loadstate, err := conn.GetUnitProperty(svc, "LoadState") if err != nil { log.Printf("Failed to get property: %v", err) invalid = true } if !invalid { var notFound = (loadstate.Value == dbus.MakeVariant("not-found")) if notFound { // XXX: in the loop we'll handle changes better... log.Printf("Failed to find svc: %s", svc) invalid = true // XXX: ? } } if previous != invalid { // if invalid changed, send signal send = true obj.StateOK(false) // dirty } if invalid { log.Printf("Waiting for: %s", svc) // waiting for svc to appear... if activeSet { activeSet = false set.Remove(svc) // no return value should ever occur } obj.SetState(ResStateWatching) // reset select { case <-buschan: // XXX: wait for new units event to unstick cuid.SetConverged(false) // loop so that we can see the changed invalid signal log.Printf("Svc[%s]->DaemonReload()", svc) case event := <-obj.Events(): cuid.SetConverged(false) if exit, send = obj.ReadEvent(&event); exit { return nil // exit } case <-cuid.ConvergedTimer(): cuid.SetConverged(true) // converged! continue case <-Startup(startup): cuid.SetConverged(false) send = true obj.StateOK(false) // dirty } } else { if !activeSet { activeSet = true set.Add(svc) // no return value should ever occur } log.Printf("Watching: %s", svc) // attempting to watch... obj.SetState(ResStateWatching) // reset select { case event := <-subChannel: log.Printf("Svc event: %+v", event) // NOTE: the value returned is a map for some reason... if event[svc] != nil { // event[svc].ActiveState is not nil switch event[svc].ActiveState { case "active": log.Printf("Svc[%s]->Started", svc) case "inactive": log.Printf("Svc[%s]->Stopped", svc) case "reloading": log.Printf("Svc[%s]->Reloading", svc) default: log.Fatalf("Unknown svc state: %s", event[svc].ActiveState) } } else { // svc stopped (and ActiveState is nil...) log.Printf("Svc[%s]->Stopped", svc) } send = true obj.StateOK(false) // dirty case err := <-subErrors: cuid.SetConverged(false) return errwrap.Wrapf(err, "Unknown %s[%s] error", obj.Kind(), obj.GetName()) case event := <-obj.Events(): cuid.SetConverged(false) if exit, send = obj.ReadEvent(&event); exit { return nil // exit } case <-cuid.ConvergedTimer(): cuid.SetConverged(true) // converged! continue case <-Startup(startup): cuid.SetConverged(false) send = true obj.StateOK(false) // dirty } } if send { startup = true // startup finished send = false if exit, err := obj.DoSend(processChan, ""); exit || err != nil { return err // we exit or bubble up a NACK... } } } }
// CheckApply checks the resource state and applies the resource if the bool // input is true. It returns error info and if the state check passed or not. func (obj *SvcRes) CheckApply(apply bool) (checkOK bool, err error) { if !systemdUtil.IsRunningSystemd() { return false, fmt.Errorf("Systemd is not running.") } conn, err := systemd.NewSystemdConnection() // needs root access if err != nil { return false, errwrap.Wrapf(err, "Failed to connect to systemd") } defer conn.Close() var svc = fmt.Sprintf("%s.service", obj.Name) // systemd name loadstate, err := conn.GetUnitProperty(svc, "LoadState") if err != nil { return false, errwrap.Wrapf(err, "Failed to get load state") } // NOTE: we have to compare variants with other variants, they are really strings... var notFound = (loadstate.Value == dbus.MakeVariant("not-found")) if notFound { return false, errwrap.Wrapf(err, "Failed to find svc: %s", svc) } // XXX: check svc "enabled at boot" or not status... //conn.GetUnitProperties(svc) activestate, err := conn.GetUnitProperty(svc, "ActiveState") if err != nil { return false, errwrap.Wrapf(err, "Failed to get active state") } var running = (activestate.Value == dbus.MakeVariant("active")) var stateOK = ((obj.State == "") || (obj.State == "running" && running) || (obj.State == "stopped" && !running)) var startupOK = true // XXX: DETECT AND SET var refresh = obj.Refresh() // do we have a pending reload to apply? if stateOK && startupOK && !refresh { return true, nil // we are in the correct state } // state is not okay, no work done, exit, but without error if !apply { return false, nil } // apply portion log.Printf("%s[%s]: Apply", obj.Kind(), obj.GetName()) var files = []string{svc} // the svc represented in a list if obj.Startup == "enabled" { _, _, err = conn.EnableUnitFiles(files, false, true) } else if obj.Startup == "disabled" { _, err = conn.DisableUnitFiles(files, false) } if err != nil { return false, errwrap.Wrapf(err, "Unable to change startup status") } // XXX: do we need to use a buffered channel here? result := make(chan string, 1) // catch result information if obj.State == "running" { _, err = conn.StartUnit(svc, "fail", result) if err != nil { return false, errwrap.Wrapf(err, "Failed to start unit") } if refresh { log.Printf("%s[%s]: Skipping reload, due to pending start", obj.Kind(), obj.GetName()) } refresh = false // we did a start, so a reload is not needed } else if obj.State == "stopped" { _, err = conn.StopUnit(svc, "fail", result) if err != nil { return false, errwrap.Wrapf(err, "Failed to stop unit") } if refresh { log.Printf("%s[%s]: Skipping reload, due to pending stop", obj.Kind(), obj.GetName()) } refresh = false // we did a stop, so a reload is not needed } status := <-result if &status == nil { return false, fmt.Errorf("Systemd service action result is nil") } if status != "done" { return false, fmt.Errorf("Unknown systemd return string: %v", status) } if refresh { // we need to reload the service // XXX: run a svc reload here! log.Printf("%s[%s]: Reloading...", obj.Kind(), obj.GetName()) } // XXX: also set enabled on boot return false, nil // success }
// Service watcher func (obj *ServiceType) Watch() { if obj.IsWatching() { return } obj.SetWatching(true) defer obj.SetWatching(false) // obj.Name: service name //vertex := obj.GetVertex() // stored with SetVertex if !util.IsRunningSystemd() { log.Fatal("Systemd is not running.") } conn, err := systemd.NewSystemdConnection() // needs root access if err != nil { log.Fatal("Failed to connect to systemd: ", err) } defer conn.Close() bus, err := dbus.SystemBus() if err != nil { log.Fatal("Failed to connect to bus: ", err) } // XXX: will this detect new units? bus.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, "type='signal',interface='org.freedesktop.systemd1.Manager',member='Reloading'") buschan := make(chan *dbus.Signal, 10) bus.Signal(buschan) var service = fmt.Sprintf("%v.service", obj.Name) // systemd name var send = false // send event? var dirty = false var invalid = false // does the service exist or not? var previous bool // previous invalid value set := conn.NewSubscriptionSet() // no error should be returned subChannel, subErrors := set.Subscribe() var activeSet = false for { // XXX: watch for an event for new units... // XXX: detect if startup enabled/disabled value changes... previous = invalid invalid = false // firstly, does service even exist or not? loadstate, err := conn.GetUnitProperty(service, "LoadState") if err != nil { log.Printf("Failed to get property: %v", err) invalid = true } if !invalid { var notFound = (loadstate.Value == dbus.MakeVariant("not-found")) if notFound { // XXX: in the loop we'll handle changes better... log.Printf("Failed to find service: %v", service) invalid = true // XXX ? } } if previous != invalid { // if invalid changed, send signal send = true dirty = true } if invalid { log.Printf("Waiting for: %v", service) // waiting for service to appear... if activeSet { activeSet = false set.Remove(service) // no return value should ever occur } obj.SetState(typeWatching) // reset select { case _ = <-buschan: // XXX wait for new units event to unstick obj.SetConvergedState(typeConvergedNil) // loop so that we can see the changed invalid signal log.Printf("Service[%v]->DaemonReload()", service) case event := <-obj.events: obj.SetConvergedState(typeConvergedNil) if ok := obj.ReadEvent(&event); !ok { return // exit } if event.GetActivity() { dirty = true } send = true case _ = <-TimeAfterOrBlock(obj.ctimeout): obj.SetConvergedState(typeConvergedTimeout) obj.converged <- true continue } } else { if !activeSet { activeSet = true set.Add(service) // no return value should ever occur } log.Printf("Watching: %v", service) // attempting to watch... obj.SetState(typeWatching) // reset select { case event := <-subChannel: log.Printf("Service event: %+v", event) // NOTE: the value returned is a map for some reason... if event[service] != nil { // event[service].ActiveState is not nil if event[service].ActiveState == "active" { log.Printf("Service[%v]->Started()", service) } else if event[service].ActiveState == "inactive" { log.Printf("Service[%v]->Stopped!()", service) } else { log.Fatal("Unknown service state: ", event[service].ActiveState) } } else { // service stopped (and ActiveState is nil...) log.Printf("Service[%v]->Stopped", service) } send = true dirty = true case err := <-subErrors: obj.SetConvergedState(typeConvergedNil) // XXX ? log.Println("error:", err) log.Fatal(err) //vertex.events <- fmt.Sprintf("service: %v", "error") // XXX: how should we handle errors? case event := <-obj.events: obj.SetConvergedState(typeConvergedNil) if ok := obj.ReadEvent(&event); !ok { return // exit } if event.GetActivity() { dirty = true } send = true } } if send { send = false if dirty { dirty = false obj.isStateOK = false // something made state dirty } Process(obj) // XXX: rename this function } } }
func (c *systemdCollector) newDbus() (*dbus.Conn, error) { if *systemdPrivate { return dbus.NewSystemdConnection() } return dbus.New() }
func New(cfg Config) (*Collector, error) { var conn *dbus.Conn var err error switch cfg.ConnType { case ConnTypeDefault, ConnTypeDbus: conn, err = dbus.New() case ConnTypeDirect: conn, err = dbus.NewSystemdConnection() default: return nil, fmt.Errorf("unhandled ConnType: %v", cfg.ConnType) } if err != nil { return nil, fmt.Errorf("could not connect to systemd: %v", err) } c := &Collector{ conn: conn, units: make(map[string]*unitMetrics), } if cfg.DisableLoaded { c.loaded = nullmetric.NoopGaugeVec{} } else { c.loaded = c.metrics.NewGaugeVec( prometheus.GaugeOpts{ Namespace: "systemd", Subsystem: "units", Name: "loaded", Help: "1 if the unit is loaded, 0 otherwise.", ConstLabels: cfg.ConstLabels, }, []string{"unit"}, ) } if cfg.DisableActive { c.active = nullmetric.NoopGaugeVec{} } else { c.active = c.metrics.NewGaugeVec( prometheus.GaugeOpts{ Namespace: "systemd", Subsystem: "units", Name: "active", Help: "1 if the unit is active, 0 otherwise.", ConstLabels: cfg.ConstLabels, }, []string{"unit"}, ) } if cfg.DisableFailed { c.failed = nullmetric.NoopGaugeVec{} } else { c.failed = c.metrics.NewGaugeVec( prometheus.GaugeOpts{ Namespace: "systemd", Subsystem: "units", Name: "failed", Help: "1 if the unit has failed, 0 otherwise.", ConstLabels: cfg.ConstLabels, }, []string{"unit"}, ) } if len(c.metrics) == 0 { return nil, fmt.Errorf("cannot disable all systemd metrics - remove [systemd] configuration instead") } return c, nil }
// Service watcher func (obj ServiceType) Watch(v *Vertex) { // obj.Name: service name if !util.IsRunningSystemd() { log.Fatal("Systemd is not running.") } conn, err := systemd.NewSystemdConnection() // needs root access if err != nil { log.Fatal("Failed to connect to systemd: ", err) } defer conn.Close() bus, err := dbus.SystemBus() if err != nil { log.Fatal("Failed to connect to bus: %v\n", err) } // XXX: will this detect new units? bus.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, "type='signal',interface='org.freedesktop.systemd1.Manager',member='Reloading'") buschan := make(chan *dbus.Signal, 10) bus.Signal(buschan) var service = fmt.Sprintf("%v.service", obj.Name) // systemd name var send = false // send event? var invalid = false // does the service exist or not? var previous bool // previous invalid value set := conn.NewSubscriptionSet() // no error should be returned subChannel, subErrors := set.Subscribe() var activeSet = false for { // XXX: watch for an event for new units... // XXX: detect if startup enabled/disabled value changes... previous = invalid invalid = false // firstly, does service even exist or not? loadstate, err := conn.GetUnitProperty(service, "LoadState") if err != nil { log.Printf("Failed to get property: %v\n", err) invalid = true } if !invalid { var notFound = (loadstate.Value == dbus.MakeVariant("not-found")) if notFound { // XXX: in the loop we'll handle changes better... log.Printf("Failed to find service: %v\n", service) invalid = true // XXX ? } } if previous != invalid { // if invalid changed, send signal send = true } if invalid { log.Printf("Waiting for: %v\n", service) // waiting for service to appear... if activeSet { activeSet = false set.Remove(service) // no return value should ever occur } select { case _ = <-buschan: // XXX wait for new units event to unstick // loop so that we can see the changed invalid signal log.Printf("Service[%v]->DaemonReload()\n", service) case exit := <-obj.Events: if exit == "exit" { return } else { log.Fatal("Unknown event: %v\n", exit) } } } else { if !activeSet { activeSet = true set.Add(service) // no return value should ever occur } log.Printf("Watching: %v\n", service) // attempting to watch... select { case event := <-subChannel: log.Printf("Service event: %+v\n", event) // NOTE: the value returned is a map for some reason... if event[service] != nil { // event[service].ActiveState is not nil if event[service].ActiveState == "active" { log.Printf("Service[%v]->Started()\n", service) } else if event[service].ActiveState == "inactive" { log.Printf("Service[%v]->Stopped!()\n", service) } else { log.Fatal("Unknown service state: ", event[service].ActiveState) } } else { // service stopped (and ActiveState is nil...) log.Printf("Service[%v]->Stopped\n", service) } send = true case err := <-subErrors: log.Println("error:", err) log.Fatal(err) v.Events <- fmt.Sprintf("service: %v", "error") case exit := <-obj.Events: if exit == "exit" { return } else { log.Fatal("Unknown event: %v\n", exit) } } } if send { send = false //log.Println("Sending event!") v.Events <- fmt.Sprintf("service(%v): %v", obj.Name, "event!") // FIXME: use struct } } }