...

Source file src/github.com/opencontainers/runc/libcontainer/cgroups/systemd/dbus.go

Documentation: github.com/opencontainers/runc/libcontainer/cgroups/systemd

     1  package systemd
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"sync"
     8  
     9  	systemdDbus "github.com/coreos/go-systemd/v22/dbus"
    10  	dbus "github.com/godbus/dbus/v5"
    11  )
    12  
    13  var (
    14  	dbusC        *systemdDbus.Conn
    15  	dbusMu       sync.RWMutex
    16  	dbusInited   bool
    17  	dbusRootless bool
    18  )
    19  
    20  type dbusConnManager struct{}
    21  
    22  // newDbusConnManager initializes systemd dbus connection manager.
    23  func newDbusConnManager(rootless bool) *dbusConnManager {
    24  	dbusMu.Lock()
    25  	defer dbusMu.Unlock()
    26  	if dbusInited && rootless != dbusRootless {
    27  		panic("can't have both root and rootless dbus")
    28  	}
    29  	dbusInited = true
    30  	dbusRootless = rootless
    31  	return &dbusConnManager{}
    32  }
    33  
    34  // getConnection lazily initializes and returns systemd dbus connection.
    35  func (d *dbusConnManager) getConnection() (*systemdDbus.Conn, error) {
    36  	// In the case where dbusC != nil
    37  	// Use the read lock the first time to ensure
    38  	// that Conn can be acquired at the same time.
    39  	dbusMu.RLock()
    40  	if conn := dbusC; conn != nil {
    41  		dbusMu.RUnlock()
    42  		return conn, nil
    43  	}
    44  	dbusMu.RUnlock()
    45  
    46  	// In the case where dbusC == nil
    47  	// Use write lock to ensure that only one
    48  	// will be created
    49  	dbusMu.Lock()
    50  	defer dbusMu.Unlock()
    51  	if conn := dbusC; conn != nil {
    52  		return conn, nil
    53  	}
    54  
    55  	conn, err := d.newConnection()
    56  	if err != nil {
    57  		// When dbus-user-session is not installed, we can't detect whether we should try to connect to user dbus or system dbus, so d.dbusRootless is set to false.
    58  		// This may fail with a cryptic error "read unix @->/run/systemd/private: read: connection reset by peer: unknown."
    59  		// https://github.com/moby/moby/issues/42793
    60  		return nil, fmt.Errorf("failed to connect to dbus (hint: for rootless containers, maybe you need to install dbus-user-session package, see https://github.com/opencontainers/runc/blob/master/docs/cgroup-v2.md): %w", err)
    61  	}
    62  	dbusC = conn
    63  	return conn, nil
    64  }
    65  
    66  func (d *dbusConnManager) newConnection() (*systemdDbus.Conn, error) {
    67  	if dbusRootless {
    68  		return newUserSystemdDbus()
    69  	}
    70  	return systemdDbus.NewWithContext(context.TODO())
    71  }
    72  
    73  // resetConnection resets the connection to its initial state
    74  // (so it can be reconnected if necessary).
    75  func (d *dbusConnManager) resetConnection(conn *systemdDbus.Conn) {
    76  	dbusMu.Lock()
    77  	defer dbusMu.Unlock()
    78  	if dbusC != nil && dbusC == conn {
    79  		dbusC.Close()
    80  		dbusC = nil
    81  	}
    82  }
    83  
    84  // retryOnDisconnect calls op, and if the error it returns is about closed dbus
    85  // connection, the connection is re-established and the op is retried. This helps
    86  // with the situation when dbus is restarted and we have a stale connection.
    87  func (d *dbusConnManager) retryOnDisconnect(op func(*systemdDbus.Conn) error) error {
    88  	for {
    89  		conn, err := d.getConnection()
    90  		if err != nil {
    91  			return err
    92  		}
    93  		err = op(conn)
    94  		if err == nil {
    95  			return nil
    96  		}
    97  		if !errors.Is(err, dbus.ErrClosed) {
    98  			return err
    99  		}
   100  		d.resetConnection(conn)
   101  	}
   102  }
   103  

View as plain text