// CheckApply method for Hostname resource. func (obj *HostnameRes) CheckApply(apply bool) (checkOK bool, err error) { conn, err := util.SystemBusPrivateUsable() if err != nil { return false, errwrap.Wrap(err, "Failed to connect to the private system bus") } defer conn.Close() hostnameObject := conn.Object(hostname1Iface, hostname1Path) checkOK = true if obj.PrettyHostname != "" { propertyCheckOK, err := updateHostnameProperty(hostnameObject, obj.PrettyHostname, "PrettyHostname", "SetPrettyHostname", apply) if err != nil { return false, err } checkOK = checkOK && propertyCheckOK } if obj.StaticHostname != "" { propertyCheckOK, err := updateHostnameProperty(hostnameObject, obj.StaticHostname, "StaticHostname", "SetStaticHostname", apply) if err != nil { return false, err } checkOK = checkOK && propertyCheckOK } if obj.TransientHostname != "" { propertyCheckOK, err := updateHostnameProperty(hostnameObject, obj.TransientHostname, "Hostname", "SetHostname", apply) if err != nil { return false, err } checkOK = checkOK && propertyCheckOK } return checkOK, nil }
// NewBus returns a new bus connection. func NewBus() *Conn { // 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 nil } return &Conn{ conn: bus, } }
// CheckApply method for Hostname resource. func (obj *HostnameRes) CheckApply(apply bool) (checkOK bool, err error) { log.Printf("%v[%v]: CheckApply(%t)", obj.Kind(), obj.GetName(), apply) if obj.isStateOK { // cached state return true, nil } conn, err := util.SystemBusPrivateUsable() if err != nil { return false, errwrap.Wrap(err, "Failed to connect to the private system bus") } defer conn.Close() hostnameObject := conn.Object(hostname1Iface, hostname1Path) checkOK = true if obj.PrettyHostname != "" { propertyCheckOK, err := updateHostnameProperty(hostnameObject, obj.PrettyHostname, "PrettyHostname", "SetPrettyHostname", apply) if err != nil { return false, err } checkOK = checkOK && propertyCheckOK } if obj.StaticHostname != "" { propertyCheckOK, err := updateHostnameProperty(hostnameObject, obj.StaticHostname, "StaticHostname", "SetStaticHostname", apply) if err != nil { return false, err } checkOK = checkOK && propertyCheckOK } if obj.TransientHostname != "" { propertyCheckOK, err := updateHostnameProperty(hostnameObject, obj.TransientHostname, "Hostname", "SetHostname", apply) if err != nil { return false, err } checkOK = checkOK && propertyCheckOK } if apply || checkOK { obj.isStateOK = true } return checkOK, 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... } } } }
// Watch is the primary listener for this resource and it outputs events. func (obj *HostnameRes) Watch(processChan chan event.Event) error { if obj.IsWatching() { return nil // TODO: should this be an error? } 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 } // 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.Wrap(err, "Failed to connect to bus") } defer bus.Close() callResult := bus.BusObject().Call( "org.freedesktop.DBus.AddMatch", 0, fmt.Sprintf("type='signal',path='%s',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'", hostname1Path)) if callResult.Err != nil { return errwrap.Wrap(callResult.Err, "Failed to subscribe to DBus events for hostname1") } signals := make(chan *dbus.Signal, 10) // closed by dbus package bus.Signal(signals) var send = false // send event? var dirty = false for { obj.SetState(ResStateWatching) // reset select { case <-signals: cuid.SetConverged(false) send = true dirty = true case event := <-obj.Events(): cuid.SetConverged(false) // we avoid sending events on unpause if exit, _ := obj.ReadEvent(&event); exit { return nil // exit } send = true dirty = true case <-cuid.ConvergedTimer(): cuid.SetConverged(true) // converged! continue case <-Startup(startup): cuid.SetConverged(false) send = true } // do all our event sending all together to avoid duplicate msgs if send { startup = true // startup finished send = false if dirty { dirty = false obj.isStateOK = false // something made state dirty } // only do this on certain types of events if exit, err := obj.DoSend(processChan, ""); exit || err != nil { return err // we exit or bubble up a NACK... } } } }
// Watch for state changes and sends a message to the bus if there is a change func (obj *NspawnRes) 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 } // 1/2 the resolution of converged timeout return time.After(time.Duration(500) * time.Millisecond) } // this resource depends on systemd ensure that it's running if !systemdUtil.IsRunningSystemd() { return fmt.Errorf("Systemd is not running.") } // create a private message bus bus, err := util.SystemBusPrivateUsable() if err != nil { return errwrap.Wrapf(err, "Failed to connect to bus") } // add a match rule to match messages going through the message bus call := bus.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, fmt.Sprintf("type='signal',interface='%s',eavesdrop='true'", dbusInterface)) // <-call.Done if err := call.Err; err != nil { return err } buschan := make(chan *dbus.Signal, 10) bus.Signal(buschan) var send = false var exit = false var dirty = false for { obj.SetState(ResStateWatching) select { case event := <-buschan: // process org.freedesktop.machine1 events for this resource's name if event.Body[0] == obj.GetName() { log.Printf("%s[%s]: Event received: %v", obj.Kind(), obj.GetName(), event.Name) if event.Name == machineNew { log.Printf("%s[%s]: Machine started", obj.Kind(), obj.GetName()) } else if event.Name == machineRemoved { log.Printf("%s[%s]: Machine stopped", obj.Kind(), obj.GetName()) } else { return fmt.Errorf("Unknown event: %s", event.Name) } send = true dirty = true } 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 dirty = true } // do all our event sending all together to avoid duplicate msgs if send || !obj.isStateOK { startup = true // startup finished send = false // only invalid state on certain types of events if dirty { dirty = false obj.isStateOK = false // something made state dirty } if exit, err := obj.DoSend(processChan, ""); exit || err != nil { return err // we exit or bubble up a NACK... } } } }