Class: OsCtld::Devices::Manager

Inherits:
Object
  • Object
show all
Defined in:
lib/osctld/devices/manager.rb

Direct Known Subclasses

ContainerManager, GroupManager

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(owner, devices: []) ⇒ Manager

Returns a new instance of Manager

Parameters:



9
10
11
12
13
# File 'lib/osctld/devices/manager.rb', line 9

def initialize(owner, devices: [])
  @owner = owner
  @devices = devices
  @cleared = false
end

Instance Attribute Details

#devicesObject (readonly, protected)

Returns the value of attribute devices



291
292
293
# File 'lib/osctld/devices/manager.rb', line 291

def devices
  @devices
end

#ownerObject (readonly, protected)

Returns the value of attribute owner



291
292
293
# File 'lib/osctld/devices/manager.rb', line 291

def owner
  @owner
end

Class Method Details

.load(owner, cfg) ⇒ Object



3
4
5
# File 'lib/osctld/devices/manager.rb', line 3

def self.load(owner, cfg)
  new(owner, devices: cfg.map { |v| Devices::Device.load(v) })
end

Instance Method Details

#add(device, parent = nil) ⇒ Object

Add new device and ensure that parent groups provide it

Parameters:

  • device (Device)
  • parent (Group, nil) (defaults to: nil)


40
41
42
43
44
45
# File 'lib/osctld/devices/manager.rb', line 40

def add(device, parent = nil)
  sync do
    parent.devices.provide(device) if parent
    do_add(device)
  end
end

#add_new(type, major, minor, mode, opts = {}) ⇒ Object



33
34
35
# File 'lib/osctld/devices/manager.rb', line 33

def add_new(type, major, minor, mode, opts = {})
  add(Devices::Device.new(type, major.to_s, minor.to_s, mode, opts))
end

#apply(opts = {}) ⇒ Object

Configure devices in cgroup

Parameters:

  • opts (Hash) (defaults to: {})


131
132
133
134
135
136
# File 'lib/osctld/devices/manager.rb', line 131

def apply(opts = {})
  sync do
    clear
    devices.each { |dev| do_allow_dev(dev) }
  end
end

#check_availability!(device, group, mode: nil) ⇒ Object

Check whether the device is available in parent groups



195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/osctld/devices/manager.rb', line 195

def check_availability!(device, group, mode: nil)
  sync do
    ([group] + group.parents.reverse).each do |grp|
      dev = grp.devices.detect { |v| v == device }
      raise DeviceNotAvailable.new(device, grp) unless dev

      unless dev.mode.compatible?(mode || device.mode)
        raise DeviceModeInsufficient.new(device, grp, dev.mode)
      end
    end
  end
end

#check_descendants!(device, mode: nil) ⇒ Object

Check whether descendants do not have broader mode requirements

Raises:

  • (NotImplementedError)


209
210
211
# File 'lib/osctld/devices/manager.rb', line 209

def check_descendants!(device, mode: nil)
  raise NotImplementedError
end

#chmod(device, mode, opts = {}) ⇒ Hash

Returns changes

Parameters:

Options Hash (opts):

  • :promote (Boolean)
  • :parents (Boolean)

Returns:

  • (Hash)

    changes



78
79
80
81
82
83
84
85
86
# File 'lib/osctld/devices/manager.rb', line 78

def chmod(device, mode, opts = {})
  sync do
    changes = device.chmod(mode)
    device.inherited = false if opts[:promote] && device.inherited?
    owner.save_config

    do_apply_changes(changes)
  end
end

#clearObject (protected)



293
294
295
296
297
298
# File 'lib/osctld/devices/manager.rb', line 293

def clear
  return if @cleared

  do_deny_all
  @cleared = true
end

#detect(&block) ⇒ Object



261
262
263
# File 'lib/osctld/devices/manager.rb', line 261

def detect(&block)
  sync { devices.detect(&block) }
end

#do_add(device) ⇒ Object (protected)



343
344
345
# File 'lib/osctld/devices/manager.rb', line 343

def do_add(device)
  devices << device
end

#do_allow_dev(device) ⇒ Object (protected)



314
315
316
317
318
319
# File 'lib/osctld/devices/manager.rb', line 314

def do_allow_dev(device)
  CGroup.set_param(
    File.join(owner.abs_cgroup_path('devices'), 'devices.allow'),
    [device.to_s]
  )
end

#do_apply_changes(changes, path: nil) ⇒ Object (protected)

Parameters:

  • changes (Hash<Symbol, String>)
  • path (String, nil)


323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
# File 'lib/osctld/devices/manager.rb', line 323

def do_apply_changes(changes, path: nil)
  path ||= owner.abs_cgroup_path('devices')

  changes.each do |action, value|
    case action
    when :allow
      CGroup.set_param(
        File.join(path, 'devices.allow'),
        [value]
      )

    when :deny
      CGroup.set_param(
        File.join(path, 'devices.deny'),
        [value]
      )
    end
  end
end

#do_deny_allObject (protected)



300
301
302
303
304
305
# File 'lib/osctld/devices/manager.rb', line 300

def do_deny_all
  CGroup.set_param(
    File.join(owner.abs_cgroup_path('devices'), 'devices.deny'),
    ['a']
  )
end

#do_deny_dev(device) ⇒ Object (protected)



307
308
309
310
311
312
# File 'lib/osctld/devices/manager.rb', line 307

def do_deny_dev(device)
  CGroup.set_param(
    File.join(owner.abs_cgroup_path('devices'), 'devices.deny'),
    [device.to_s]
  )
end

#dumpArray<Hash>

Dump device configuration into the config

Returns:

  • (Array<Hash>)


277
278
279
# File 'lib/osctld/devices/manager.rb', line 277

def dump
  sync { devices.reject(&:inherited?).map { |dev| dev.dump } }
end

#dup(new_owner) ⇒ Object



281
282
283
284
285
286
287
288
# File 'lib/osctld/devices/manager.rb', line 281

def dup(new_owner)
  sync do
    ret = super()
    ret.instance_variable_set('@owner', new_owner)
    ret.instance_variable_set('@devices', devices.map(&:clone))
    ret
  end
end

#each(&block) ⇒ Object



257
258
259
# File 'lib/osctld/devices/manager.rb', line 257

def each(&block)
  sync { devices.each(&block) }
end

#exportArray<Hash>

Export devices to clients

Returns:

  • (Array<Hash>)


271
272
273
# File 'lib/osctld/devices/manager.rb', line 271

def export
  sync { devices.map { |dev| dev.export } }
end

#find(type, major, minor) ⇒ Devices::Device?

Find device by `type` and `major` and `minor` numbers

Parameters:

  • type (Symbol)

    `:char`, `:block`

  • major (String)
  • minor (String)

Returns:



218
219
220
221
222
223
224
# File 'lib/osctld/devices/manager.rb', line 218

def find(type, major, minor)
  sync do
    devices.detect do |dev|
      dev.type == type && dev.major == major && dev.minor == minor
    end
  end
end

#get(device) ⇒ Devices::Device?

Find and return device

The point of this method is to return the manager's own device's instance. Devices equality is tested by comparing its type and major and minor numbers, but its devnode name or mode can be different.

Returns:



232
233
234
235
236
237
# File 'lib/osctld/devices/manager.rb', line 232

def get(device)
  sync do
    i = devices.index(device)
    i ? devices[i] : nil
  end
end

#include?(device) ⇒ Boolean

Check if we have a particular device

Parameters:

Returns:

  • (Boolean)


242
243
244
# File 'lib/osctld/devices/manager.rb', line 242

def include?(device)
  sync { devices.include?(device) }
end

#inherit(device, opts = {}) ⇒ Object

Inherit device from a parent group

Parameters:

  • device (Device)
  • opts (Hash) (defaults to: {})

    can be used by subclasses



50
51
52
53
54
55
56
# File 'lib/osctld/devices/manager.rb', line 50

def inherit(device, opts = {})
  sync do
    dev = device.clone
    dev.inherited = true
    do_add(dev)
  end
end

#inherit_all_from(parent, opts = {}) ⇒ Object

Inherit all inheritable devices from parent group

Parameters:

  • parent (Group)
  • opts (Hash) (defaults to: {})

    options passed to #inherit



24
25
26
27
28
29
30
31
# File 'lib/osctld/devices/manager.rb', line 24

def inherit_all_from(parent, opts = {})
  sync do
    parent.devices.each do |dev|
      next if !dev.inherit? || include?(dev)
      inherit(dev, opts)
    end
  end
end

#inherit_promoted(device) ⇒ Object

Inherit a promoted device

Parameters:

Raises:

  • (NotImplementedError)


97
98
99
# File 'lib/osctld/devices/manager.rb', line 97

def inherit_promoted(device)
  raise NotImplementedError
end

#init(opts = {}) ⇒ Object

Initialize device list

Parameters:

  • opts (Hash) (defaults to: {})

    can be used by subclasses



17
18
19
# File 'lib/osctld/devices/manager.rb', line 17

def init(opts = {})

end

#promote(device) ⇒ Object

Promote device, i.e. remove its inherited status and save it in config

Parameters:



90
91
92
93
# File 'lib/osctld/devices/manager.rb', line 90

def promote(device)
  device.inherited = false
  owner.save_config
end

#remove(device) ⇒ Object

Remove device from self

Parameters:



60
61
62
63
64
65
66
67
68
69
70
# File 'lib/osctld/devices/manager.rb', line 60

def remove(device)
  sync { devices.delete(device) }
  owner.save_config

  begin
    do_deny_dev(device)

  rescue CGroupFileNotFound
    # does not matter
  end
end

#replace(new_devices) ⇒ Object

Replace configured devices by a new set

`new_devices` has to contain devices that are to be promoted. Devices that were promoted but are no longer in `new_devices` will be removed. Devices that are inherited from parent groups are promoted if they're in `new_devices`, otherwire they're left alone.

Note that this method does not enforce nor manage proper parent/descendant dependencies. It is possible to add a device which is not provided by parents or to remove a device that is needed by descendants.

Parameters:



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/osctld/devices/manager.rb', line 150

def replace(new_devices)
  sync do
    to_add = []
    to_inherit = []
    to_promote = []
    to_chmod = []

    # Find devices to promote, chmod and remove/inherit
    devices.each do |cur_dev|
      found = new_devices.detect { |new_dev| new_dev == cur_dev }

      if found.nil?
        to_inherit << cur_dev unless cur_dev.inherited?

      elsif found.inherited?
        if found.mode == cur_dev.mode
          to_promote << cur_dev
        else
          to_chmod << [cur_dev, found.mode]
        end

      elsif found.mode != cur_dev.mode
        to_chmod << [cur_dev, found.mode]
      end
    end

    # Find devices to add
    new_devices.each do |new_dev|
      found = devices.detect { |cur_dev| cur_dev == new_dev }
      to_add << new_dev if found.nil?
    end

    # Apply changes
    to_add.each { |dev| add(dev) }
    to_promote.each { |dev| promote(dev) }
    to_chmod.each do |dev, mode|
      chmod(dev, mode, promote: true, descendants: true, containers: true)
    end
    to_inherit.each { |dev| inherit_promoted(dev) }

    apply(descendants: true, containers: true)
  end
end

#select(&block) ⇒ Object



265
266
267
# File 'lib/osctld/devices/manager.rb', line 265

def select(&block)
  sync { devices.select(&block) }
end

#set_inherit(device) ⇒ Object

Mark device as inheritable

Parameters:



117
118
119
120
# File 'lib/osctld/devices/manager.rb', line 117

def set_inherit(device)
  device.inherit = true
  owner.save_config
end

#sync(&block) ⇒ Object (protected)



347
348
349
# File 'lib/osctld/devices/manager.rb', line 347

def sync(&block)
  Devices::Lock.sync(owner.pool, &block)
end

#unset_inherit(device) ⇒ Object

Remove inheritable mark

Parameters:



124
125
126
127
# File 'lib/osctld/devices/manager.rb', line 124

def unset_inherit(device)
  device.inherit = false
  owner.save_config
end

#update_inherited_mode(device, mode, changes) ⇒ Object

Called when the access mode of the device in the parent group changes

This method should update the mode and pass the information to its own descendants.

Parameters:



109
110
111
112
113
# File 'lib/osctld/devices/manager.rb', line 109

def update_inherited_mode(device, mode, changes)
  device.mode = mode
  owner.save_config
  do_apply_changes(changes)
end

#used?(device) ⇒ Boolean

Check if device exists and is used, not just inherited

Parameters:

Returns:

  • (Boolean)


249
250
251
252
253
254
255
# File 'lib/osctld/devices/manager.rb', line 249

def used?(device)
  sync do
    dev = devices.detect { |v| v == device }
    next(false) unless dev
    !dev.inherited?
  end
end