1
16
17
18 package machine1
19
20 import (
21 "fmt"
22 "os"
23 "strconv"
24 "syscall"
25
26 "github.com/godbus/dbus/v5"
27
28 sd_dbus "github.com/coreos/go-systemd/v22/dbus"
29 )
30
31 const (
32 dbusInterface = "org.freedesktop.machine1.Manager"
33 dbusPath = "/org/freedesktop/machine1"
34 )
35
36
37 type Conn struct {
38 conn *dbus.Conn
39 object dbus.BusObject
40 }
41
42
43 type MachineStatus struct {
44 Name string
45 Class string
46 Service string
47 JobPath dbus.ObjectPath
48 }
49
50
51 type ImageStatus struct {
52 Name string
53 ImageType string
54 Readonly bool
55 CreateTime uint64
56 ModifyTime uint64
57 DiskUsage uint64
58 JobPath dbus.ObjectPath
59 }
60
61
62 func New() (*Conn, error) {
63 c := new(Conn)
64
65 if err := c.initConnection(); err != nil {
66 return nil, err
67 }
68
69 return c, nil
70 }
71
72 func (c *Conn) initConnection() error {
73 var err error
74 c.conn, err = dbus.SystemBusPrivate()
75 if err != nil {
76 return err
77 }
78
79
80
81
82 methods := []dbus.Auth{dbus.AuthExternal(strconv.Itoa(os.Getuid()))}
83
84 err = c.conn.Auth(methods)
85 if err != nil {
86 c.conn.Close()
87 return err
88 }
89
90 err = c.conn.Hello()
91 if err != nil {
92 c.conn.Close()
93 return err
94 }
95
96 c.object = c.conn.Object("org.freedesktop.machine1", dbus.ObjectPath(dbusPath))
97
98 return nil
99 }
100
101 func (c *Conn) getPath(method string, args ...interface{}) (dbus.ObjectPath, error) {
102 result := c.object.Call(fmt.Sprintf("%s.%s", dbusInterface, method), 0, args...)
103 if result.Err != nil {
104 return "", result.Err
105 }
106
107 path, typeErr := result.Body[0].(dbus.ObjectPath)
108 if !typeErr {
109 return "", fmt.Errorf("unable to convert dbus response '%v' to dbus.ObjectPath", result.Body[0])
110 }
111
112 return path, nil
113 }
114
115
116 func (c *Conn) Connected() bool {
117 return c.conn.Connected()
118 }
119
120
121 func (c *Conn) CreateMachine(name string, id []byte, service string, class string, pid int, root_directory string, scope_properties []sd_dbus.Property) error {
122 return c.object.Call(dbusInterface+".CreateMachine", 0, name, id, service, class, uint32(pid), root_directory, scope_properties).Err
123 }
124
125
126 func (c *Conn) CreateMachineWithNetwork(name string, id []byte, service string, class string, pid int, root_directory string, ifindices []int, scope_properties []sd_dbus.Property) error {
127 return c.object.Call(dbusInterface+".CreateMachineWithNetwork", 0, name, id, service, class, uint32(pid), root_directory, ifindices, scope_properties).Err
128 }
129
130
131 func (c *Conn) GetMachine(name string) (dbus.ObjectPath, error) {
132 return c.getPath("GetMachine", name)
133 }
134
135
136 func (c *Conn) GetImage(name string) (dbus.ObjectPath, error) {
137 return c.getPath("GetImage", name)
138 }
139
140
141 func (c *Conn) GetMachineByPID(pid uint) (dbus.ObjectPath, error) {
142 return c.getPath("GetMachineByPID", pid)
143 }
144
145
146 func (c *Conn) GetMachineAddresses(name string) (dbus.ObjectPath, error) {
147 return c.getPath("GetMachineAddresses", name)
148 }
149
150
151 func (c *Conn) DescribeMachine(name string) (machineProps map[string]interface{}, err error) {
152 var dbusProps map[string]dbus.Variant
153 path, pathErr := c.GetMachine(name)
154 if pathErr != nil {
155 return nil, pathErr
156 }
157 obj := c.conn.Object("org.freedesktop.machine1", path)
158 err = obj.Call("org.freedesktop.DBus.Properties.GetAll", 0, "").Store(&dbusProps)
159 if err != nil {
160 return nil, err
161 }
162 machineProps = make(map[string]interface{}, len(dbusProps))
163 for key, val := range dbusProps {
164 machineProps[key] = val.Value()
165 }
166 return
167 }
168
169
170 func (c *Conn) KillMachine(name, who string, sig syscall.Signal) error {
171 return c.object.Call(dbusInterface+".KillMachine", 0, name, who, sig).Err
172 }
173
174
175 func (c *Conn) TerminateMachine(name string) error {
176 return c.object.Call(dbusInterface+".TerminateMachine", 0, name).Err
177 }
178
179
180 func (c *Conn) RegisterMachine(name string, id []byte, service string, class string, pid int, root_directory string) error {
181 return c.object.Call(dbusInterface+".RegisterMachine", 0, name, id, service, class, uint32(pid), root_directory).Err
182 }
183
184
185 func (c *Conn) RegisterMachineWithNetwork(name string, id []byte, service string, class string, pid int, root_directory string, ifindices []int) error {
186 return c.object.Call(dbusInterface+".RegisterMachineWithNetwork", 0, name, id, service, class, uint32(pid), root_directory, ifindices).Err
187 }
188
189 func machineFromInterfaces(machine []interface{}) (*MachineStatus, error) {
190 if len(machine) < 4 {
191 return nil, fmt.Errorf("invalid number of machine fields: %d", len(machine))
192 }
193 name, ok := machine[0].(string)
194 if !ok {
195 return nil, fmt.Errorf("failed to typecast machine field 0 to string")
196 }
197 class, ok := machine[1].(string)
198 if !ok {
199 return nil, fmt.Errorf("failed to typecast class field 1 to string")
200 }
201 service, ok := machine[2].(string)
202 if !ok {
203 return nil, fmt.Errorf("failed to typecast service field 2 to string")
204 }
205 jobpath, ok := machine[3].(dbus.ObjectPath)
206 if !ok {
207 return nil, fmt.Errorf("failed to typecast jobpath field 3 to ObjectPath")
208 }
209
210 ret := MachineStatus{Name: name, Class: class, Service: service, JobPath: jobpath}
211 return &ret, nil
212 }
213
214
215 func (c *Conn) ListMachines() ([]MachineStatus, error) {
216 result := make([][]interface{}, 0)
217 if err := c.object.Call(dbusInterface+".ListMachines", 0).Store(&result); err != nil {
218 return nil, err
219 }
220
221 machs := []MachineStatus{}
222 for _, i := range result {
223 machine, err := machineFromInterfaces(i)
224 if err != nil {
225 return nil, err
226 }
227 machs = append(machs, *machine)
228 }
229
230 return machs, nil
231 }
232
233 func imageFromInterfaces(image []interface{}) (*ImageStatus, error) {
234 if len(image) < 7 {
235 return nil, fmt.Errorf("invalid number of image fields: %d", len(image))
236 }
237 name, ok := image[0].(string)
238 if !ok {
239 return nil, fmt.Errorf("failed to typecast image field 0 to string")
240 }
241 imagetype, ok := image[1].(string)
242 if !ok {
243 return nil, fmt.Errorf("failed to typecast imagetype field 1 to string")
244 }
245 readonly, ok := image[2].(bool)
246 if !ok {
247 return nil, fmt.Errorf("failed to typecast readonly field 2 to bool")
248 }
249 createtime, ok := image[3].(uint64)
250 if !ok {
251 return nil, fmt.Errorf("failed to typecast createtime field 3 to uint64")
252 }
253 modifytime, ok := image[4].(uint64)
254 if !ok {
255 return nil, fmt.Errorf("failed to typecast modifytime field 4 to uint64")
256 }
257 diskusage, ok := image[5].(uint64)
258 if !ok {
259 return nil, fmt.Errorf("failed to typecast diskusage field 5 to uint64")
260 }
261 jobpath, ok := image[6].(dbus.ObjectPath)
262 if !ok {
263 return nil, fmt.Errorf("failed to typecast jobpath field 6 to ObjectPath")
264 }
265
266 ret := ImageStatus{Name: name, ImageType: imagetype, Readonly: readonly, CreateTime: createtime, ModifyTime: modifytime, DiskUsage: diskusage, JobPath: jobpath}
267 return &ret, nil
268 }
269
270
271 func (c *Conn) ListImages() ([]ImageStatus, error) {
272 result := make([][]interface{}, 0)
273 if err := c.object.Call(dbusInterface+".ListImages", 0).Store(&result); err != nil {
274 return nil, err
275 }
276
277 images := []ImageStatus{}
278 for _, i := range result {
279 image, err := imageFromInterfaces(i)
280 if err != nil {
281 return nil, err
282 }
283 images = append(images, *image)
284 }
285
286 return images, nil
287 }
288
View as plain text