Class: Snapsync::PartitionsMonitor

Inherits:
Object
  • Object
show all
Defined in:
lib/snapsync/partitions_monitor.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(machine = nil) ⇒ PartitionsMonitor

Returns a new instance of PartitionsMonitor.

Parameters:

  • machine (RemotePathname) (defaults to: nil)

    Remote machine to connect to



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/snapsync/partitions_monitor.rb', line 16

def initialize(machine = nil)
    if machine.nil?
        dbus = DBus::SystemBus.instance
    else
        sock_path = '/tmp/snapsync_%04d_remote.sock' % rand(10000)


        ready = Concurrent::AtomicBoolean.new(false)
        @ssh_thr = Thread.new do
            machine.dup_ssh do |ssh|
                @ssh = ssh
                if Snapsync.SSH_DEBUG
                    log = Logger.new(STDOUT)
                    log.level = Logger::DEBUG
                    ssh.logger = log
                    ssh.logger.sev_threshold=Logger::Severity::DEBUG
                end
                ssh.forward.local_socket(sock_path, '/var/run/dbus/system_bus_socket')
                ObjectSpace.define_finalizer(@ssh, proc {
                    File.delete sock_path
                })
                ready.make_true
                ssh.loop { true }
            end
        end
        while ready.false?
            sleep 0.001
        end

        dbus = DBus::RemoteBus.new "unix:path=#{sock_path}"
    end
    @udisk = dbus.service('org.freedesktop.UDisks2')
    udisk.introspect

    @dirty = Concurrent::AtomicBoolean.new(false)
    # udisk.on_signal('InterfacesAdded') do
    #     dirty!
    # end
    # udisk.on_signal('InterfacesRemoved') do
    #     dirty!
    # end

    @monitored_partitions = Set.new
    @known_partitions = Hash.new
    @partition_table = Hash.new
end

Instance Attribute Details

#dirtyObject (readonly)

Returns the value of attribute dirty.



5
6
7
# File 'lib/snapsync/partitions_monitor.rb', line 5

def dirty
  @dirty
end

#known_partitionsHash<String, Dev> (readonly)

Returns:

  • (Hash<String, Dev>)


10
11
12
# File 'lib/snapsync/partitions_monitor.rb', line 10

def known_partitions
  @known_partitions
end

#monitored_partitionsSet<String> (readonly)

Returns:

  • (Set<String>)


8
9
10
# File 'lib/snapsync/partitions_monitor.rb', line 8

def monitored_partitions
  @monitored_partitions
end

#partition_tableHash<String, DBus::ProxyObjectInterface> (readonly)

Returns:

  • (Hash<String, DBus::ProxyObjectInterface>)


13
14
15
# File 'lib/snapsync/partitions_monitor.rb', line 13

def partition_table
  @partition_table
end

#udiskObject (readonly)

Returns the value of attribute udisk.



3
4
5
# File 'lib/snapsync/partitions_monitor.rb', line 3

def udisk
  @udisk
end

Instance Method Details

#dirty!Object



113
114
115
# File 'lib/snapsync/partitions_monitor.rb', line 113

def dirty!
    dirty.set
end

#dirty?Boolean

Returns:

  • (Boolean)


117
118
119
# File 'lib/snapsync/partitions_monitor.rb', line 117

def dirty?
    dirty.set?
end

#each_partition_with_filesystem {|the, the| ... } ⇒ Object

Yields the udev objects representing block devices that support an underlying filesystem

Yield Parameters:

  • the (String)

    block device name (e.g. sda3)

  • the

    block device’s udev object



182
183
184
185
186
187
188
189
190
# File 'lib/snapsync/partitions_monitor.rb', line 182

def each_partition_with_filesystem
    return enum_for(__method__) if !block_given?
    udisk.root['org']['freedesktop']['UDisks2']['block_devices'].each do |device_name, _|
        dev = udisk.object("/org/freedesktop/UDisks2/block_devices/#{device_name}")
        if dev.has_iface?('org.freedesktop.UDisks2.Block') && dev.has_iface?('org.freedesktop.UDisks2.Filesystem')
            yield(device_name, dev)
        end
    end
end

#emit_added(uuid, fs) ⇒ Object



171
172
# File 'lib/snapsync/partitions_monitor.rb', line 171

def emit_added(uuid, fs)
end

#emit_removed(uuid) ⇒ Object



174
175
# File 'lib/snapsync/partitions_monitor.rb', line 174

def emit_removed(uuid)
end

#monitor_for(partition_uuid) ⇒ Object



79
80
81
# File 'lib/snapsync/partitions_monitor.rb', line 79

def monitor_for(partition_uuid)
    monitored_partitions << partition_uuid.to_str
end

#mountpoint_of_uuid(partition_uuid) ⇒ Object



73
74
75
76
77
# File 'lib/snapsync/partitions_monitor.rb', line 73

def mountpoint_of_uuid(partition_uuid)
    mounts = mountpoints(known_partitions[partition_uuid])
    raise "Ambiguous mountpoints: #{mounts}" if mounts.length > 1
    mounts[0]
end

#mountpoints(fs) ⇒ Array<String>

Parameters:

  • fs (DBus::ProxyObjectInterface)

Returns:

  • (Array<String>)


65
66
67
68
69
70
71
# File 'lib/snapsync/partitions_monitor.rb', line 65

def mountpoints(fs)
    raise "Not mounted?" if fs.nil?
    mount_points = fs['MountPoints'].map do |str|
        str[0..-2].pack("U*")
    end
    return mount_points
end

#partition_of(dir) ⇒ String, ...

Returns uuid dir rel.

Returns:

  • (String, Snapsync::Path, Pathname)

    uuid dir rel

Raises:

  • (ArgumentError)


84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/snapsync/partitions_monitor.rb', line 84

def partition_of(dir)
    rel = Pathname.new("")
    dir = dir.expand_path
    while !dir.mountpoint?
        rel = dir.basename + rel
        dir = dir.dirname
    end

    # Collect partitions list from udisk
    parts = []
    each_partition_with_filesystem do |name, dev|
        partition = dev['org.freedesktop.UDisks2.Block']
        uuid = partition['IdUUID']
        fs = dev['org.freedesktop.UDisks2.Filesystem']
        mount_points = fs['MountPoints'].map do |str|
            str[0..-2].pack("U*")
        end
        parts.push([name, uuid, mount_points])
    end

    # Find any partition that is a parent of the folder we are looking at
    parts.each do |name, uuid, mount_points|
        if mount_points.include?(dir.path_part)
            return uuid, dir, rel
        end
    end
    raise ArgumentError, "cannot guess the partition UUID of the mountpoint #{dir} for #{dir + rel}"
end

#partition_uuid_for_dir(dir) ⇒ Object



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/snapsync/partitions_monitor.rb', line 121

def partition_uuid_for_dir(dir)
    dir = dir.expand_path
    # Find the dir's mountpoint
    while !dir.mountpoint?
        dir = dir.parent
    end
    dir = dir.to_s

    each_partition_with_filesystem.find do |name, dev|
        fs = dev['org.freedesktop.UDisks2.Filesystem']
        mp = fs['MountPoints']
        # .map { |str| Pathname.new(str) }
        mp.include?(dir)
    end
end

#pollObject



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/snapsync/partitions_monitor.rb', line 137

def poll
    udisk.introspect
    dirty.make_false

    all = Hash.new
    each_partition_with_filesystem do |name, dev|
        # @type [DBus::ProxyObjectInterface]
        partition = dev['org.freedesktop.UDisks2.Block']
        # @type [String]
        uuid = partition['IdUUID']

        if monitored_partitions.include?(uuid)
            fs = dev['org.freedesktop.UDisks2.Filesystem']

            # If it is a btrfs raid, it will have multiple partitions with the same uuid, but only one will be
            # mounted.
            next if all.has_key?(uuid) and all[uuid]['MountPoints'].size > 0

            all[uuid] = fs
        end
    end

    added = Hash.new
    (all.keys - known_partitions.keys).each do |uuid|
        fs = added[uuid] = all[uuid]
        emit_added(uuid, fs)
    end
    removed = (known_partitions.keys - all.keys)
    removed.each { |uuid| emit_removed(uuid) }

    @known_partitions = all
    return added, removed
end