// CreateItem (IN Dict<String,Variant> properties, IN Secret secret, IN Boolean replace, OUT ObjectPath item, OUT ObjectPath prompt); func (collection *Collection) CreateItem(label string, secret *Secret, replace bool) (*Item, error) { properties := make(map[string]dbus.Variant) attributes := make(map[string]string) attributes["profile"] = label properties["org.freedesktop.Secret.Item.Label"] = dbus.MakeVariant(label) properties["org.freedesktop.Secret.Item.Attributes"] = dbus.MakeVariant(attributes) var path dbus.ObjectPath var prompt dbus.ObjectPath err := collection.dbus.Call("org.freedesktop.Secret.Collection.CreateItem", 0, properties, secret, replace).Store(&path, &prompt) if err != nil { return &Item{}, err } if isPrompt(prompt) { prompt := NewPrompt(collection.conn, prompt) result, err := prompt.Prompt() if err != nil { return &Item{}, err } path = result.Value().(dbus.ObjectPath) } return NewItem(collection.conn, path), nil }
func (adapter *blob) SetDiscoveryFilter(uuids ...string) error { log.Printf("%s: setting discovery filter %v", adapter.Name(), uuids) return adapter.call( "SetDiscoveryFilter", map[string]dbus.Variant{ "Transport": dbus.MakeVariant("le"), "UUIDs": dbus.MakeVariant(uuids), }, ) }
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 }
// Show sends the information in the notification object to the server to be // displayed. func (n Notification) Show() (id uint32, err error) { conn, err := dbus.SessionBus() if err != nil { return } // We need to convert the interface type of the map to dbus.Variant as // people dont want to have to import the dbus package just to make use // of the notification hints. hints := map[string]dbus.Variant{} for k, v := range n.Hints { hints[k] = dbus.MakeVariant(v) } obj := conn.Object(interfacePath, objectPath) call := obj.Call( notify, 0, n.AppName, n.ReplacesID, n.AppIcon, n.Summary, n.Body, n.Actions, hints, n.Timeout) if err = call.Err; err != nil { return } err = call.Store(&id) return }
// ToMapVariant recasts a list of args to map[string]dbus.Variant as requested by the DBus API. // func ToMapVariant(input map[string]interface{}) map[string]dbus.Variant { vars := make(map[string]dbus.Variant) for k, v := range input { vars[k] = dbus.MakeVariant(v) } return vars }
// CreateCollection (IN Dict<String,Variant> properties, IN String alias, OUT ObjectPath collection, OUT ObjectPath prompt); func (service *Service) CreateCollection(label string) (*Collection, error) { properties := make(map[string]dbus.Variant) properties["org.freedesktop.Secret.Collection.Label"] = dbus.MakeVariant(label) var path dbus.ObjectPath var prompt dbus.ObjectPath err := service.dbus.Call("org.freedesktop.Secret.Service.CreateCollection", 0, properties, "").Store(&path, &prompt) if err != nil { return &Collection{}, err } if isPrompt(prompt) { prompt := NewPrompt(service.conn, prompt) result, err := prompt.Prompt() if err != nil { return &Collection{}, err } path = result.Value().(dbus.ObjectPath) } return NewCollection(service.conn, path), nil }
func newNotification() notify.Notification { return notify.Notification{ AppName: "GNOMEConnect", Hints: map[string]dbus.Variant{ "desktop-entry": dbus.MakeVariant("gnomeconnect"), }, } }
// OpenSession (IN String algorithm, IN Variant input, OUT Variant output, OUT ObjectPath result); func (service *Service) Open() (*Session, error) { var output dbus.Variant var path dbus.ObjectPath err := service.dbus.Call("org.freedesktop.Secret.Service.OpenSession", 0, "plain", dbus.MakeVariant("")).Store(&output, &path) if err != nil { return &Session{}, err } return NewSession(service.conn, path), nil }
// Get implements org.freedesktop.DBus.Properties.Get. func (p *Properties) Get(iface, property string) (dbus.Variant, *dbus.Error) { p.mut.RLock() defer p.mut.RUnlock() m, ok := p.m[iface] if !ok { return dbus.Variant{}, ErrIfaceNotFound } prop, ok := m[property] if !ok { return dbus.Variant{}, ErrPropNotFound } return dbus.MakeVariant(prop.Value), nil }
// GetAll implements org.freedesktop.DBus.Properties.GetAll. func (p *Properties) GetAll(iface string) (map[string]dbus.Variant, *dbus.Error) { p.mut.RLock() defer p.mut.RUnlock() m, ok := p.m[iface] if !ok { return nil, ErrIfaceNotFound } rm := make(map[string]dbus.Variant, len(m)) for k, v := range m { rm[k] = dbus.MakeVariant(v.Value) } return rm, nil }
// PropExecStart sets the ExecStart service property. The first argument is a // slice with the binary path to execute followed by the arguments to pass to // the executed command. See // http://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart= func PropExecStart(command []string, uncleanIsFailure bool) Property { execStarts := []execStart{ execStart{ Path: command[0], Args: command, UncleanIsFailure: uncleanIsFailure, }, } return Property{ Name: "ExecStart", Value: dbus.MakeVariant(execStarts), } }
// set sets the given property and emits PropertyChanged if appropiate. p.mut // must already be locked. func (p *Properties) set(iface, property string, v interface{}) { prop := p.m[iface][property] prop.Value = v switch prop.Emit { case EmitFalse: // do nothing case EmitInvalidates: p.conn.Emit(p.path, "org.freedesktop.DBus.Properties.PropertiesChanged", iface, map[string]dbus.Variant{}, []string{property}) case EmitTrue: p.conn.Emit(p.path, "org.freedesktop.DBus.Properties.PropertiesChanged", iface, map[string]dbus.Variant{property: dbus.MakeVariant(v)}, []string{}) default: panic("invalid value for EmitType") } }
// TestSetUnitProperties changes a cgroup setting on the `tmp.mount` // which should exist on all systemd systems and ensures that the // property was set. func TestSetUnitProperties(t *testing.T) { conn := setupConn(t) unit := "tmp.mount" if err := conn.SetUnitProperties(unit, true, Property{"CPUShares", dbus.MakeVariant(uint64(1023))}); err != nil { t.Fatal(err) } info, err := conn.GetUnitTypeProperties(unit, "Mount") if err != nil { t.Fatal(err) } value := info["CPUShares"].(uint64) if value != 1023 { t.Fatal("CPUShares of unit is not 1023, %s", value) } }
func SetupScope(pid uint32) error { unit = fmt.Sprintf("poe-sandbox-%d.scope", pid) props := []sdbus.Property{ {"PIDs", dbus.MakeVariant([]uint32{pid})}, {"Description", dbus.MakeVariant(fmt.Sprintf("poe sandbox (pid: %d)", pid))}, {"MemoryLimit", dbus.MakeVariant(uint64(1024 * 1024 * 128))}, {"TasksMax", dbus.MakeVariant(uint64(16))}, {"CPUShares", dbus.MakeVariant(uint64(512))}, {"BlockIOWeight", dbus.MakeVariant(uint64(10))}, {"DevicePolicy", dbus.MakeVariant("strict")}, } if _, err := conn.StartTransientUnit(unit, "fail", props, nil); err != nil { return err } else { return waitScopeToBeCreated(pid, unit) } }
// Show sends the information in the notification object to the server to be displayed. func (n Notification) Show() (id uint32, err error) { // We need to convert the interface type of the map to dbus.Variant as people // dont want to have to import the dbus package just to make use of the notification // hints. hints := map[string]dbus.Variant{} if len(n.Hints) != 0 { for k, v := range n.Hints { hints[k] = dbus.MakeVariant(v) } } connection, err := dbus.SessionBus() if err != nil { return } obj := connection.Object("org.freedesktop.Notifications", "/org/freedesktop/Notifications") call := obj.Call( "org.freedesktop.Notifications.Notify", 0, n.AppName, n.ReplacesID, n.AppIcon, n.Summary, n.Body, n.Actions, hints, n.Timeout) if call.Err != nil { return 0, call.Err } if err = call.Store(&id); err != nil { return } return }
// 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 Apply(c *cgroups.Cgroup, pid int) (cgroups.ActiveCgroup, error) { var ( unitName = getUnitName(c) slice = "system.slice" properties []systemd1.Property res = &systemdCgroup{} ) res.cgroup = c if c.Slice != "" { slice = c.Slice } properties = append(properties, systemd1.Property{"Slice", dbus.MakeVariant(slice)}, systemd1.Property{"Description", dbus.MakeVariant("docker container " + c.Name)}, systemd1.Property{"PIDs", dbus.MakeVariant([]uint32{uint32(pid)})}, ) // Always enable accounting, this gets us the same behaviour as the fs implementation, // plus the kernel has some problems with joining the memory cgroup at a later time. properties = append(properties, systemd1.Property{"MemoryAccounting", dbus.MakeVariant(true)}, systemd1.Property{"CPUAccounting", dbus.MakeVariant(true)}, systemd1.Property{"BlockIOAccounting", dbus.MakeVariant(true)}) if c.Memory != 0 { properties = append(properties, systemd1.Property{"MemoryLimit", dbus.MakeVariant(uint64(c.Memory))}) } // TODO: MemoryReservation and MemorySwap not available in systemd if c.CpuShares != 0 { properties = append(properties, systemd1.Property{"CPUShares", dbus.MakeVariant(uint64(c.CpuShares))}) } if _, err := theConn.StartTransientUnit(unitName, "replace", properties...); err != nil { return nil, err } if !c.AllowAllDevices { if err := joinDevices(c, pid); err != nil { return nil, err } } // -1 disables memorySwap if c.MemorySwap >= 0 && (c.Memory != 0 || c.MemorySwap > 0) { if err := joinMemory(c, pid); err != nil { return nil, err } } // we need to manually join the freezer cgroup in systemd because it does not currently support it // via the dbus api if err := joinFreezer(c, pid); err != nil { return nil, err } if c.CpusetCpus != "" { if err := joinCpuset(c, pid); err != nil { return nil, err } } return res, nil }
func systemdApply(c *Cgroup, pid int) (ActiveCgroup, error) { unitName := c.Parent + "-" + c.Name + ".scope" slice := "system.slice" var properties []systemd1.Property for _, v := range c.UnitProperties { switch v[0] { case "Slice": slice = v[1] default: return nil, fmt.Errorf("Unknown unit propery %s", v[0]) } } properties = append(properties, systemd1.Property{"Slice", dbus.MakeVariant(slice)}, systemd1.Property{"Description", dbus.MakeVariant("docker container " + c.Name)}, systemd1.Property{"PIDs", dbus.MakeVariant([]uint32{uint32(pid)})}) if !c.DeviceAccess { properties = append(properties, systemd1.Property{"DevicePolicy", dbus.MakeVariant("strict")}, systemd1.Property{"DeviceAllow", dbus.MakeVariant([]DeviceAllow{ {"/dev/null", "rwm"}, {"/dev/zero", "rwm"}, {"/dev/full", "rwm"}, {"/dev/random", "rwm"}, {"/dev/urandom", "rwm"}, {"/dev/tty", "rwm"}, {"/dev/console", "rwm"}, {"/dev/tty0", "rwm"}, {"/dev/tty1", "rwm"}, {"/dev/pts/ptmx", "rwm"}, // There is no way to add /dev/pts/* here atm, so we hack this manually below // /dev/pts/* (how to add this?) // Same with tuntap, which doesn't exist as a node most of the time })}) } if c.Memory != 0 { properties = append(properties, systemd1.Property{"MemoryLimit", dbus.MakeVariant(uint64(c.Memory))}) } // TODO: MemorySwap not available in systemd if c.CpuShares != 0 { properties = append(properties, systemd1.Property{"CPUShares", dbus.MakeVariant(uint64(c.CpuShares))}) } if _, err := theConn.StartTransientUnit(unitName, "replace", properties...); err != nil { return nil, err } // To work around the lack of /dev/pts/* support above we need to manually add these // so, ask systemd for the cgroup used props, err := theConn.GetUnitTypeProperties(unitName, getIfaceForUnit(unitName)) if err != nil { return nil, err } cgroup := props["ControlGroup"].(string) if !c.DeviceAccess { mountpoint, err := FindCgroupMountpoint("devices") if err != nil { return nil, err } path := filepath.Join(mountpoint, cgroup) // /dev/pts/* if err := writeFile(path, "devices.allow", "c 136:* rwm"); err != nil { return nil, err } // tuntap if err := writeFile(path, "devices.allow", "c 10:200 rwm"); err != nil { return nil, err } } return &systemdCgroup{}, nil }
func Apply(c *cgroups.Cgroup, pid int) (cgroups.ActiveCgroup, error) { var ( unitName = c.Parent + "-" + c.Name + ".scope" slice = "system.slice" properties []systemd1.Property cpuArgs []cgroupArg cpusetArgs []cgroupArg memoryArgs []cgroupArg res systemdCgroup ) // First set up things not supported by systemd // -1 disables memorySwap if c.MemorySwap >= 0 && (c.Memory != 0 || c.MemorySwap > 0) { memorySwap := c.MemorySwap if memorySwap == 0 { // By default, MemorySwap is set to twice the size of RAM. memorySwap = c.Memory * 2 } memoryArgs = append(memoryArgs, cgroupArg{"memory.memsw.limit_in_bytes", strconv.FormatInt(memorySwap, 10)}) } if c.CpusetCpus != "" { cpusetArgs = append(cpusetArgs, cgroupArg{"cpuset.cpus", c.CpusetCpus}) } if c.Slice != "" { slice = c.Slice } properties = append(properties, systemd1.Property{"Slice", dbus.MakeVariant(slice)}, systemd1.Property{"Description", dbus.MakeVariant("docker container " + c.Name)}, systemd1.Property{"PIDs", dbus.MakeVariant([]uint32{uint32(pid)})}, ) if !c.DeviceAccess { properties = append(properties, systemd1.Property{"DevicePolicy", dbus.MakeVariant("strict")}, systemd1.Property{"DeviceAllow", dbus.MakeVariant([]DeviceAllow{ {"/dev/null", "rwm"}, {"/dev/zero", "rwm"}, {"/dev/full", "rwm"}, {"/dev/random", "rwm"}, {"/dev/urandom", "rwm"}, {"/dev/tty", "rwm"}, {"/dev/console", "rwm"}, {"/dev/tty0", "rwm"}, {"/dev/tty1", "rwm"}, {"/dev/pts/ptmx", "rwm"}, // There is no way to add /dev/pts/* here atm, so we hack this manually below // /dev/pts/* (how to add this?) // Same with tuntap, which doesn't exist as a node most of the time })}) } // Always enable accounting, this gets us the same behaviour as the fs implementation, // plus the kernel has some problems with joining the memory cgroup at a later time. properties = append(properties, systemd1.Property{"MemoryAccounting", dbus.MakeVariant(true)}, systemd1.Property{"CPUAccounting", dbus.MakeVariant(true)}, systemd1.Property{"BlockIOAccounting", dbus.MakeVariant(true)}) if c.Memory != 0 { properties = append(properties, systemd1.Property{"MemoryLimit", dbus.MakeVariant(uint64(c.Memory))}) } // TODO: MemoryReservation and MemorySwap not available in systemd if c.CpuShares != 0 { properties = append(properties, systemd1.Property{"CPUShares", dbus.MakeVariant(uint64(c.CpuShares))}) } if _, err := theConn.StartTransientUnit(unitName, "replace", properties...); err != nil { return nil, err } // To work around the lack of /dev/pts/* support above we need to manually add these // so, ask systemd for the cgroup used props, err := theConn.GetUnitTypeProperties(unitName, getIfaceForUnit(unitName)) if err != nil { return nil, err } cgroup := props["ControlGroup"].(string) if !c.DeviceAccess { mountpoint, err := cgroups.FindCgroupMountpoint("devices") if err != nil { return nil, err } path := filepath.Join(mountpoint, cgroup) // /dev/pts/* if err := ioutil.WriteFile(filepath.Join(path, "devices.allow"), []byte("c 136:* rwm"), 0700); err != nil { return nil, err } // tuntap if err := ioutil.WriteFile(filepath.Join(path, "devices.allow"), []byte("c 10:200 rwm"), 0700); err != nil { return nil, err } } if len(cpuArgs) != 0 { mountpoint, err := cgroups.FindCgroupMountpoint("cpu") if err != nil { return nil, err } path := filepath.Join(mountpoint, cgroup) for _, arg := range cpuArgs { if err := ioutil.WriteFile(filepath.Join(path, arg.File), []byte(arg.Value), 0700); err != nil { return nil, err } } } if len(memoryArgs) != 0 { mountpoint, err := cgroups.FindCgroupMountpoint("memory") if err != nil { return nil, err } path := filepath.Join(mountpoint, cgroup) for _, arg := range memoryArgs { if err := ioutil.WriteFile(filepath.Join(path, arg.File), []byte(arg.Value), 0700); err != nil { return nil, err } } } if len(cpusetArgs) != 0 { // systemd does not atm set up the cpuset controller, so we must manually // join it. Additionally that is a very finicky controller where each // level must have a full setup as the default for a new directory is "no cpus", // so we avoid using any hierarchies here, creating a toplevel directory. mountpoint, err := cgroups.FindCgroupMountpoint("cpuset") if err != nil { return nil, err } initPath, err := cgroups.GetInitCgroupDir("cpuset") if err != nil { return nil, err } rootPath := filepath.Join(mountpoint, initPath) path := filepath.Join(mountpoint, initPath, c.Parent+"-"+c.Name) res.cleanupDirs = append(res.cleanupDirs, path) if err := os.MkdirAll(path, 0755); err != nil && !os.IsExist(err) { return nil, err } foundCpus := false foundMems := false for _, arg := range cpusetArgs { if arg.File == "cpuset.cpus" { foundCpus = true } if arg.File == "cpuset.mems" { foundMems = true } if err := ioutil.WriteFile(filepath.Join(path, arg.File), []byte(arg.Value), 0700); err != nil { return nil, err } } // These are required, if not specified inherit from parent if !foundCpus { s, err := ioutil.ReadFile(filepath.Join(rootPath, "cpuset.cpus")) if err != nil { return nil, err } if err := ioutil.WriteFile(filepath.Join(path, "cpuset.cpus"), s, 0700); err != nil { return nil, err } } // These are required, if not specified inherit from parent if !foundMems { s, err := ioutil.ReadFile(filepath.Join(rootPath, "cpuset.mems")) if err != nil { return nil, err } if err := ioutil.WriteFile(filepath.Join(path, "cpuset.mems"), s, 0700); err != nil { return nil, err } } if err := ioutil.WriteFile(filepath.Join(path, "cgroup.procs"), []byte(strconv.Itoa(pid)), 0700); err != nil { return nil, err } } return &res, nil }
func (dec *decoder) decode(s string, depth int) interface{} { dec.align(alignment(typeFor(s))) switch s[0] { case 'y': var b [1]byte if _, err := dec.in.Read(b[:]); err != nil { panic(err) } dec.pos++ return b[0] case 'b': i := dec.decode("u", depth).(uint32) switch { case i == 0: return false case i == 1: return true default: panic(FormatError("invalid value for boolean")) } case 'n': var i int16 dec.binread(&i) dec.pos += 2 return i case 'i': var i int32 dec.binread(&i) dec.pos += 4 return i case 'x': var i int64 dec.binread(&i) dec.pos += 8 return i case 'q': var i uint16 dec.binread(&i) dec.pos += 2 return i case 'u': var i uint32 dec.binread(&i) dec.pos += 4 return i case 't': var i uint64 dec.binread(&i) dec.pos += 8 return i case 'd': var f float64 dec.binread(&f) dec.pos += 8 return f case 's': length := dec.decode("u", depth).(uint32) b := make([]byte, int(length)+1) if _, err := io.ReadFull(dec.in, b); err != nil { panic(err) } dec.pos += int(length) + 1 return string(b[:len(b)-1]) case 'o': return dbus.ObjectPath(dec.decode("s", depth).(string)) case 'g': length := dec.decode("y", depth).(byte) b := make([]byte, int(length)+1) if _, err := io.ReadFull(dec.in, b); err != nil { panic(err) } dec.pos += int(length) + 1 sig, err := dbus.ParseSignature(string(b[:len(b)-1])) if err != nil { panic(err) } return sig case 'v': if depth >= 64 { panic(FormatError("input exceeds container depth limit")) } var variant dbus.Variant sig := dec.decode("g", depth).(dbus.Signature) if len(sig.String()) == 0 { panic(FormatError("variant signature is empty")) } err, rem := validSingle(sig.String(), 0) if err != nil { panic(err) } if rem != "" { panic(FormatError("variant signature has multiple types")) } //variant.sig = sig //variant.value = dec.decode(sig.String(), depth+1) variant = dbus.MakeVariant(dec.decode(sig.String(), depth+1)) return variant case 'h': return dbus.UnixFDIndex(dec.decode("u", depth).(uint32)) case 'a': if len(s) > 1 && s[1] == '{' { ksig := s[2:3] vsig := s[3 : len(s)-1] v := reflect.MakeMap(reflect.MapOf(typeFor(ksig), typeFor(vsig))) if depth >= 63 { panic(FormatError("input exceeds container depth limit")) } length := dec.decode("u", depth).(uint32) // Even for empty maps, the correct padding must be included dec.align(8) spos := dec.pos for dec.pos < spos+int(length) { dec.align(8) if !isKeyType(v.Type().Key()) { panic(dbus.InvalidTypeError{v.Type()}) } kv := dec.decode(ksig, depth+2) vv := dec.decode(vsig, depth+2) v.SetMapIndex(reflect.ValueOf(kv), reflect.ValueOf(vv)) } return v.Interface() } if depth >= 64 { panic(FormatError("input exceeds container depth limit")) } length := dec.decode("u", depth).(uint32) v := reflect.MakeSlice(reflect.SliceOf(typeFor(s[1:])), 0, int(length)) // Even for empty arrays, the correct padding must be included dec.align(alignment(typeFor(s[1:]))) spos := dec.pos for dec.pos < spos+int(length) { ev := dec.decode(s[1:], depth+1) v = reflect.Append(v, reflect.ValueOf(ev)) } return v.Interface() case '(': if depth >= 64 { panic(FormatError("input exceeds container depth limit")) } dec.align(8) v := make([]interface{}, 0) s = s[1 : len(s)-1] for s != "" { err, rem := validSingle(s, 0) if err != nil { panic(err) } ev := dec.decode(s[:len(s)-len(rem)], depth+1) v = append(v, ev) s = rem } return v default: panic(dbus.SignatureError{Sig: s}) } }
// PropSlice sets the Slice unit property. See // http://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#Slice= func PropSlice(slice string) Property { return Property{ Name: "Slice", Value: dbus.MakeVariant(slice), } }
func (a *BasicService) GetAboutData(languageTag string) (aboutData map[string]dbus.Variant, err *dbus.Error) { data := make(map[string]dbus.Variant) data["DeviceName"] = dbus.MakeVariant("Golang-device") return data, nil }
func propDependency(name string, units []string) Property { return Property{ Name: name, Value: dbus.MakeVariant(units), } }
// PropDescription sets the Description unit property. See // http://www.freedesktop.org/software/systemd/man/systemd.unit#Description= func PropDescription(desc string) Property { return Property{ Name: "Description", Value: dbus.MakeVariant(desc), } }
// PropType sets the Type service property. See // http://www.freedesktop.org/software/systemd/man/systemd.service.html#Type= func PropType(t string) Property { return Property{ Name: "Type", Value: dbus.MakeVariant(t), } }
// PropPids sets the PIDs field of scope units used in the initial construction // of the scope only and specifies the initial PIDs to add to the scope object. // See https://www.freedesktop.org/wiki/Software/systemd/ControlGroupInterface/#properties func PropPids(pids ...uint32) Property { return Property{ Name: "PIDs", Value: dbus.MakeVariant(pids), } }
// 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... } } } }
func newProp(name string, units interface{}) systemdDbus.Property { return systemdDbus.Property{ Name: name, Value: dbus.MakeVariant(units), } }
// PropRemainAfterExit sets the RemainAfterExit service property. See // http://www.freedesktop.org/software/systemd/man/systemd.service.html#RemainAfterExit= func PropRemainAfterExit(b bool) Property { return Property{ Name: "RemainAfterExit", Value: dbus.MakeVariant(b), } }