Skip to content

Commit e64a0ec

Browse files
author
Luca Bruno
authored
Merge pull request #310 from Xuanwo/add-importd
import1: Implement importd methods
2 parents ff7011e + 915e66a commit e64a0ec

File tree

5 files changed

+411
-2
lines changed

5 files changed

+411
-2
lines changed

fixtures/image.raw.xz

252 Bytes
Binary file not shown.

fixtures/image.tar.xz

252 Bytes
Binary file not shown.

import1/dbus.go

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
// Copyright 2019 CoreOS, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Package import1 provides integration with the systemd-importd API. See https://www.freedesktop.org/wiki/Software/systemd/importd/
16+
// Note: Requires systemd v231 or higher
17+
package import1
18+
19+
import (
20+
"fmt"
21+
"os"
22+
"strconv"
23+
"strings"
24+
25+
"github.com/godbus/dbus"
26+
)
27+
28+
const (
29+
dbusInterface = "org.freedesktop.import1.Manager"
30+
dbusPath = "/org/freedesktop/import1"
31+
)
32+
33+
// Conn is a connection to systemds dbus endpoint.
34+
type Conn struct {
35+
conn *dbus.Conn
36+
object dbus.BusObject
37+
}
38+
39+
// Transfer is an object in dbus for an import, export or download operation.
40+
type Transfer struct {
41+
Id uint32 // The numeric transfer ID of the transfer object
42+
Path dbus.ObjectPath // The dbus objectPath for the transfer
43+
}
44+
45+
// TransferStatus is the status for an import, export or download operation.
46+
type TransferStatus struct {
47+
Id uint32 // The numeric transfer ID of the transfer object
48+
Local string // The local container name of this transfer
49+
Remote string // The remote source (in case of download: the URL, in case of import/export a string describing the file descriptor passed in)
50+
Type string // The type of operation
51+
Verify string // The selected verification setting, and is only defined for download operations
52+
Progress float64 // The current progress of the transfer, as a value between 0.0 and 1.0
53+
}
54+
55+
// New establishes a connection to the system bus and authenticates.
56+
// Note: systemd-importd will be activated via D-Bus, we don't need to check service status.
57+
func New() (*Conn, error) {
58+
c := new(Conn)
59+
60+
if err := c.initConnection(); err != nil {
61+
return nil, err
62+
}
63+
64+
return c, nil
65+
}
66+
67+
func (c *Conn) initConnection() error {
68+
var err error
69+
c.conn, err = dbus.SystemBusPrivate()
70+
if err != nil {
71+
return err
72+
}
73+
74+
// Only use EXTERNAL method, and hardcode the uid (not username)
75+
// to avoid a username lookup (which requires a dynamically linked
76+
// libc)
77+
methods := []dbus.Auth{dbus.AuthExternal(strconv.Itoa(os.Getuid()))}
78+
79+
err = c.conn.Auth(methods)
80+
if err != nil {
81+
c.conn.Close()
82+
return err
83+
}
84+
85+
err = c.conn.Hello()
86+
if err != nil {
87+
c.conn.Close()
88+
return err
89+
}
90+
91+
c.object = c.conn.Object("org.freedesktop.import1", dbus.ObjectPath(dbusPath))
92+
93+
return nil
94+
}
95+
96+
// getResult will return:
97+
// - transfer object (*Transfer)
98+
// - err (error)
99+
func (c *Conn) getResult(method string, args ...interface{}) (*Transfer, error) {
100+
result := c.object.Call(fmt.Sprintf("%s.%s", dbusInterface, method), 0, args...)
101+
if result.Err != nil {
102+
return nil, result.Err
103+
}
104+
105+
if len(result.Body) < 2 {
106+
return nil, fmt.Errorf("invalid number of result fields: %v", result.Body)
107+
}
108+
109+
ok := false
110+
transfer := &Transfer{}
111+
112+
transfer.Id, ok = result.Body[0].(uint32)
113+
if !ok {
114+
return nil, fmt.Errorf("unable to convert dbus response '%v' to uint32", result.Body[0])
115+
}
116+
117+
transfer.Path, ok = result.Body[1].(dbus.ObjectPath)
118+
if !ok {
119+
return nil, fmt.Errorf("unable to convert dbus response '%v' to dbus.ObjectPath", result.Body[1])
120+
}
121+
return transfer, nil
122+
}
123+
124+
// ImportTar imports a tar into systemd-importd.
125+
func (c *Conn) ImportTar(
126+
f *os.File, local_name string, force, read_only bool,
127+
) (*Transfer, error) {
128+
return c.getResult("ImportTar", dbus.UnixFD(f.Fd()), local_name, force, read_only)
129+
}
130+
131+
// ImportRaw imports a raw image into systemd-importd.
132+
func (c *Conn) ImportRaw(
133+
f *os.File, local_name string, force, read_only bool,
134+
) (*Transfer, error) {
135+
return c.getResult("ImportRaw", dbus.UnixFD(f.Fd()), local_name, force, read_only)
136+
}
137+
138+
// ExportTar exports a tar from systemd-importd.
139+
func (c *Conn) ExportTar(
140+
local_name string, f *os.File, format string,
141+
) (*Transfer, error) {
142+
return c.getResult("ExportTar", local_name, dbus.UnixFD(f.Fd()), format)
143+
}
144+
145+
// ExportRaw exports a raw image from systemd-importd.
146+
func (c *Conn) ExportRaw(
147+
local_name string, f *os.File, format string,
148+
) (*Transfer, error) {
149+
return c.getResult("ExportRaw", local_name, dbus.UnixFD(f.Fd()), format)
150+
}
151+
152+
// PullTar pulls a tar into systemd-importd.
153+
func (c *Conn) PullTar(
154+
url, local_name, verify_mode string, force bool,
155+
) (*Transfer, error) {
156+
return c.getResult("PullTar", url, local_name, verify_mode, force)
157+
}
158+
159+
// PullRaw pulls a raw image into systemd-importd.
160+
func (c *Conn) PullRaw(
161+
url, local_name, verify_mode string, force bool,
162+
) (*Transfer, error) {
163+
return c.getResult("PullRaw", url, local_name, verify_mode, force)
164+
}
165+
166+
// ListTransfers will list ongoing import, export or download operations.
167+
func (c *Conn) ListTransfers() ([]TransferStatus, error) {
168+
result := make([][]interface{}, 0)
169+
if err := c.object.Call(dbusInterface+".ListTransfers", 0).Store(&result); err != nil {
170+
return nil, err
171+
}
172+
173+
transfers := make([]TransferStatus, 0)
174+
for _, v := range result {
175+
transfer, err := transferFromInterfaces(v)
176+
if err != nil {
177+
return nil, err
178+
}
179+
transfers = append(transfers, *transfer)
180+
}
181+
182+
return transfers, nil
183+
}
184+
185+
// CancelTransfer will cancel an ongoing import, export or download operations.
186+
func (c *Conn) CancelTransfer(transfer_id uint32) error {
187+
return c.object.Call(dbusInterface+".CancelTransfer", 0, transfer_id).Err
188+
}
189+
190+
func transferFromInterfaces(transfer []interface{}) (*TransferStatus, error) {
191+
// Verify may be not defined in response.
192+
if len(transfer) < 5 {
193+
return nil, fmt.Errorf("invalid number of transfer fields: %d", len(transfer))
194+
}
195+
196+
ok := false
197+
ret := &TransferStatus{}
198+
199+
ret.Id, ok = transfer[0].(uint32)
200+
if !ok {
201+
return nil, fmt.Errorf("failed to typecast transfer field 0 to uint32")
202+
}
203+
ret.Local, ok = transfer[1].(string)
204+
if !ok {
205+
return nil, fmt.Errorf("failed to typecast transfer field 1 to string")
206+
}
207+
ret.Remote, ok = transfer[2].(string)
208+
if !ok {
209+
return nil, fmt.Errorf("failed to typecast transfer field 2 to string")
210+
}
211+
ret.Type, ok = transfer[3].(string)
212+
if !ok {
213+
return nil, fmt.Errorf("failed to typecast transfer field 3 to string")
214+
}
215+
// Verify is only defined for download operations.
216+
// If operation is not pull, we should ignore Verify field.
217+
if !strings.HasPrefix(ret.Type, "pull-") {
218+
ret.Progress, ok = transfer[4].(float64)
219+
if !ok {
220+
return nil, fmt.Errorf("failed to typecast transfer field 4 to float64")
221+
}
222+
return ret, nil
223+
}
224+
225+
ret.Verify, ok = transfer[4].(string)
226+
if !ok {
227+
return nil, fmt.Errorf("failed to typecast transfer field 4 to string")
228+
}
229+
ret.Progress, ok = transfer[5].(float64)
230+
if !ok {
231+
return nil, fmt.Errorf("failed to typecast transfer field 5 to float64")
232+
}
233+
return ret, nil
234+
}

0 commit comments

Comments
 (0)