...

Source file src/github.com/coreos/go-systemd/v22/import1/dbus.go

Documentation: github.com/coreos/go-systemd/v22/import1

     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/v5"
    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  // Connected returns whether conn is connected
    68  func (c *Conn) Connected() bool {
    69  	return c.conn.Connected()
    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  	// Only use EXTERNAL method, and hardcode the uid (not username)
    80  	// to avoid a username lookup (which requires a dynamically linked
    81  	// libc)
    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.import1", dbus.ObjectPath(dbusPath))
    97  
    98  	return nil
    99  }
   100  
   101  // getResult will return:
   102  //   - transfer object (*Transfer)
   103  //   - err (error)
   104  func (c *Conn) getResult(method string, args ...interface{}) (*Transfer, error) {
   105  	result := c.object.Call(fmt.Sprintf("%s.%s", dbusInterface, method), 0, args...)
   106  	if result.Err != nil {
   107  		return nil, result.Err
   108  	}
   109  
   110  	if len(result.Body) < 2 {
   111  		return nil, fmt.Errorf("invalid number of result fields: %v", result.Body)
   112  	}
   113  
   114  	ok := false
   115  	transfer := &Transfer{}
   116  
   117  	transfer.Id, ok = result.Body[0].(uint32)
   118  	if !ok {
   119  		return nil, fmt.Errorf("unable to convert dbus response '%v' to uint32", result.Body[0])
   120  	}
   121  
   122  	transfer.Path, ok = result.Body[1].(dbus.ObjectPath)
   123  	if !ok {
   124  		return nil, fmt.Errorf("unable to convert dbus response '%v' to dbus.ObjectPath", result.Body[1])
   125  	}
   126  	return transfer, nil
   127  }
   128  
   129  // ImportTar imports a tar into systemd-importd.
   130  func (c *Conn) ImportTar(
   131  	f *os.File, local_name string, force, read_only bool,
   132  ) (*Transfer, error) {
   133  	return c.getResult("ImportTar", dbus.UnixFD(f.Fd()), local_name, force, read_only)
   134  }
   135  
   136  // ImportRaw imports a raw image into systemd-importd.
   137  func (c *Conn) ImportRaw(
   138  	f *os.File, local_name string, force, read_only bool,
   139  ) (*Transfer, error) {
   140  	return c.getResult("ImportRaw", dbus.UnixFD(f.Fd()), local_name, force, read_only)
   141  }
   142  
   143  // ExportTar exports a tar from systemd-importd.
   144  func (c *Conn) ExportTar(
   145  	local_name string, f *os.File, format string,
   146  ) (*Transfer, error) {
   147  	return c.getResult("ExportTar", local_name, dbus.UnixFD(f.Fd()), format)
   148  }
   149  
   150  // ExportRaw exports a raw image from systemd-importd.
   151  func (c *Conn) ExportRaw(
   152  	local_name string, f *os.File, format string,
   153  ) (*Transfer, error) {
   154  	return c.getResult("ExportRaw", local_name, dbus.UnixFD(f.Fd()), format)
   155  }
   156  
   157  // PullTar pulls a tar into systemd-importd.
   158  func (c *Conn) PullTar(
   159  	url, local_name, verify_mode string, force bool,
   160  ) (*Transfer, error) {
   161  	return c.getResult("PullTar", url, local_name, verify_mode, force)
   162  }
   163  
   164  // PullRaw pulls a raw image into systemd-importd.
   165  func (c *Conn) PullRaw(
   166  	url, local_name, verify_mode string, force bool,
   167  ) (*Transfer, error) {
   168  	return c.getResult("PullRaw", url, local_name, verify_mode, force)
   169  }
   170  
   171  // ListTransfers will list ongoing import, export or download operations.
   172  func (c *Conn) ListTransfers() ([]TransferStatus, error) {
   173  	result := make([][]interface{}, 0)
   174  	if err := c.object.Call(dbusInterface+".ListTransfers", 0).Store(&result); err != nil {
   175  		return nil, err
   176  	}
   177  
   178  	transfers := make([]TransferStatus, 0)
   179  	for _, v := range result {
   180  		transfer, err := transferFromInterfaces(v)
   181  		if err != nil {
   182  			return nil, err
   183  		}
   184  		transfers = append(transfers, *transfer)
   185  	}
   186  
   187  	return transfers, nil
   188  }
   189  
   190  // CancelTransfer will cancel an ongoing import, export or download operations.
   191  func (c *Conn) CancelTransfer(transfer_id uint32) error {
   192  	return c.object.Call(dbusInterface+".CancelTransfer", 0, transfer_id).Err
   193  }
   194  
   195  func transferFromInterfaces(transfer []interface{}) (*TransferStatus, error) {
   196  	// Verify may be not defined in response.
   197  	if len(transfer) < 5 {
   198  		return nil, fmt.Errorf("invalid number of transfer fields: %d", len(transfer))
   199  	}
   200  
   201  	ok := false
   202  	ret := &TransferStatus{}
   203  
   204  	ret.Id, ok = transfer[0].(uint32)
   205  	if !ok {
   206  		return nil, fmt.Errorf("failed to typecast transfer field 0 to uint32")
   207  	}
   208  	ret.Local, ok = transfer[1].(string)
   209  	if !ok {
   210  		return nil, fmt.Errorf("failed to typecast transfer field 1 to string")
   211  	}
   212  	ret.Remote, ok = transfer[2].(string)
   213  	if !ok {
   214  		return nil, fmt.Errorf("failed to typecast transfer field 2 to string")
   215  	}
   216  	ret.Type, ok = transfer[3].(string)
   217  	if !ok {
   218  		return nil, fmt.Errorf("failed to typecast transfer field 3 to string")
   219  	}
   220  	// Verify is only defined for download operations.
   221  	// If operation is not pull, we should ignore Verify field.
   222  	if !strings.HasPrefix(ret.Type, "pull-") {
   223  		ret.Progress, ok = transfer[4].(float64)
   224  		if !ok {
   225  			return nil, fmt.Errorf("failed to typecast transfer field 4 to float64")
   226  		}
   227  		return ret, nil
   228  	}
   229  
   230  	ret.Verify, ok = transfer[4].(string)
   231  	if !ok {
   232  		return nil, fmt.Errorf("failed to typecast transfer field 4 to string")
   233  	}
   234  	ret.Progress, ok = transfer[5].(float64)
   235  	if !ok {
   236  		return nil, fmt.Errorf("failed to typecast transfer field 5 to float64")
   237  	}
   238  	return ret, nil
   239  }
   240  

View as plain text