Module: SeccompTools::Util

Defined in:
lib/seccomp-tools/util.rb

Overview

Utility methods shared across the library: architecture detection and terminal colorizing.

Constant Summary collapse

ELF_MACHINE =

ELF e_machine values (the halfword at offset 18) of the architectures seccomp-tools supports, as they are laid out in the file - so the big-endian s390x reads the other way around. The keys are binary strings (+.b+) to match what is read off the file: this source is UTF-8, where a literal such as "\xb7\x00" would never compare equal to those bytes.

{
  "\x03\x00".b => :i386,
  "\x3e\x00".b => :amd64,
  "\xb7\x00".b => :aarch64,
  "\xf3\x00".b => :riscv64,
  "\x00\x16".b => :s390x
}.freeze
LIGHT_YELLOW =

color code of light yellow

"\e[38;5;230m"
COLOR_CODE =

Color codes for pretty print. error, warn and info are severities rather than parts of a filter, shading from alarming to quiet; Logger colors its own levels by the same names.

{
  esc_m: "\e[0m",
  syscall: "\e[38;5;120m", # light green
  arch: LIGHT_YELLOW,
  args: LIGHT_YELLOW,
  gray: "\e[2m",
  error: "\e[38;5;196m", # heavy red
  warn: LIGHT_YELLOW,
  info: "\e[38;5;110m" # light blue
}.freeze

Class Method Summary collapse

Class Method Details

.colorize(s, t: nil) ⇒ String

Wrap contents with terminal color codes.

Returns s unchanged when colorize_enabled? is false.

Parameters:

  • s (#to_s)

    Contents to be wrapped.

  • t (Symbol?) (defaults to: nil)

    Which kind of color to use, valid symbols are the keys of COLOR_CODE.

Returns:

  • (String)

    s wrapped with color codes.



108
109
110
111
112
113
114
115
# File 'lib/seccomp-tools/util.rb', line 108

def colorize(s, t: nil)
  s = s.to_s
  return s unless colorize_enabled?

  cc = COLOR_CODE
  color = cc[t]
  "#{color}#{s.sub(cc[:esc_m], cc[:esc_m] + color)}#{cc[:esc_m]}"
end

.colorize_enabled?Boolean

Is colorize enabled?

Returns:

  • (Boolean)

    true only if colors have not been disabled by disable_color! and $stdout is a tty.



80
81
82
# File 'lib/seccomp-tools/util.rb', line 80

def colorize_enabled?
  !@disable_color && $stdout.tty?
end

.disable_color!void

This method returns an undefined value.

Disable colorize, colorize becomes a no-op regardless of the output being a tty.



73
74
75
# File 'lib/seccomp-tools/util.rb', line 73

def disable_color!
  @disable_color = true
end

.elf?(path) ⇒ Boolean

Does the file at path start with the ELF magic bytes?

Used to tell an executable apart from a raw BPF blob.

Parameters:

  • path (String)

    Path to the file to check.

Returns:

  • (Boolean)

    false when the file cannot be read.



124
125
126
127
128
# File 'lib/seccomp-tools/util.rb', line 124

def elf?(path)
  File.binread(path, 4) == "\x7fELF".b
rescue SystemCallError
  false
end

.enable_color!void

This method returns an undefined value.

Enable colorize.

Colors are still only emitted when the output is a tty, see colorize_enabled?.



67
68
69
# File 'lib/seccomp-tools/util.rb', line 67

def enable_color!
  @disable_color = false
end

.process_arch(pid) ⇒ Symbol?

The architecture a running process was built for, read from the ELF header of its executable.

Filters dumped from another process are numbered for its architecture, so this is what makes the syscall names right when it differs from the host.

Parameters:

  • pid (Integer)

    Process identifier.

Returns:

  • (Symbol?)

    One of supported_archs, or nil when the executable cannot be read (no /proc, the process is gone, permission denied) or its machine type is not one we know.



54
55
56
57
58
59
60
61
# File 'lib/seccomp-tools/util.rb', line 54

def process_arch(pid)
  File.open("/proc/#{pid}/exe", 'rb') do |f|
    f.pos = 18
    ELF_MACHINE[f.read(2)]
  end
rescue SystemCallError
  nil
end

.supported_archsArray<Symbol>

Get currently supported architectures.

Derived from the syscall tables shipped under consts/sys_nr/.

Returns:

  • (Array<Symbol>)

    Architecture names, sorted.



13
14
15
16
17
# File 'lib/seccomp-tools/util.rb', line 13

def supported_archs
  @supported_archs ||= Dir.glob(File.join(__dir__, 'consts', 'sys_nr', '*.rb'))
                          .map { |f| File.basename(f, '.rb').to_sym }
                          .sort
end

.system_archSymbol

Detect system architecture.

Returns:

  • (Symbol)

    One of supported_archs, or :unknown if the host CPU is not supported.



22
23
24
25
26
27
28
29
30
31
# File 'lib/seccomp-tools/util.rb', line 22

def system_arch
  case RbConfig::CONFIG['host_cpu']
  when /x86_64/ then :amd64
  when /i386/ then :i386
  when /aarch64/ then :aarch64
  when /riscv64/ then :riscv64
  when /s390x/ then :s390x
  else :unknown
  end
end

.template(filename) ⇒ String

Get content of filename under directory templates/.

Parameters:

  • filename (String)

    Basename of a file under lib/seccomp-tools/templates/.

Returns:

  • (String)

    Content of the file.

Raises:

  • (Errno::ENOENT)

    If no such template exists.



140
141
142
# File 'lib/seccomp-tools/util.rb', line 140

def template(filename)
  File.binread(File.join(__dir__, 'templates', filename))
end