Class: OsCtl::Lib::OsProcess

Inherits:
Object
  • Object
show all
Defined in:
lib/libosctl/os_process.rb

Overview

Interface to system processes, reading information from ‘/proc`

Constant Summary collapse

TICS_PER_SECOND =
SysConf.tics_per_second
PAGE_SIZE =
SysConf.page_size

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(pid, **opts) ⇒ OsProcess

Returns a new instance of OsProcess.

Parameters:

  • pid (Integer)
  • opts (Hash)

Options Hash (**opts):

  • parse_stat (Boolean) — default: true
  • parse_status (Boolean) — default: true


78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/libosctl/os_process.rb', line 78

def initialize(pid, **opts)
  @path = File.join('/proc', pid.to_s)
  @pid = pid
  @cache = {}
  @id_maps = {}
  @opts = opts

  volatile do
    parse_stat if opts.fetch(:parse_stat, true)
    parse_status if opts.fetch(:parse_status, true)
  end
end

Instance Attribute Details

#egidInteger (readonly)

Returns:

  • (Integer)


40
41
42
# File 'lib/libosctl/os_process.rb', line 40

def egid
  @egid
end

#euidInteger (readonly)

Returns:

  • (Integer)


34
35
36
# File 'lib/libosctl/os_process.rb', line 34

def euid
  @euid
end

#nameString (readonly)

Returns:

  • (String)


46
47
48
# File 'lib/libosctl/os_process.rb', line 46

def name
  @name
end

#niceInteger (readonly)

Returns:

  • (Integer)


57
58
59
# File 'lib/libosctl/os_process.rb', line 57

def nice
  @nice
end

#nspidArray (readonly)

Returns:

  • (Array)


28
29
30
# File 'lib/libosctl/os_process.rb', line 28

def nspid
  @nspid
end

#num_threadsInteger (readonly)

Returns:

  • (Integer)


60
61
62
# File 'lib/libosctl/os_process.rb', line 60

def num_threads
  @num_threads
end

#optsObject (readonly, protected)

Returns the value of attribute opts.



189
190
191
# File 'lib/libosctl/os_process.rb', line 189

def opts
  @opts
end

#pathObject (readonly, protected)

Returns the value of attribute path.



189
190
191
# File 'lib/libosctl/os_process.rb', line 189

def path
  @path
end

#pgrpInteger (readonly)

Process group ID

Returns:

  • (Integer)


25
26
27
# File 'lib/libosctl/os_process.rb', line 25

def pgrp
  @pgrp
end

#pidInteger (readonly)

Process ID

Returns:

  • (Integer)


17
18
19
# File 'lib/libosctl/os_process.rb', line 17

def pid
  @pid
end

#ppidInteger (readonly)

Parent process ID

Returns:

  • (Integer)


21
22
23
# File 'lib/libosctl/os_process.rb', line 21

def ppid
  @ppid
end

#rgidInteger (readonly)

Returns:

  • (Integer)


37
38
39
# File 'lib/libosctl/os_process.rb', line 37

def rgid
  @rgid
end

#rssInteger (readonly)

Resident set size in bytes

Returns:

  • (Integer)


54
55
56
# File 'lib/libosctl/os_process.rb', line 54

def rss
  @rss
end

#ruidInteger (readonly)

Returns:

  • (Integer)


31
32
33
# File 'lib/libosctl/os_process.rb', line 31

def ruid
  @ruid
end

#start_timeTime (readonly)

Time when the process was started

Returns:

  • (Time)


72
73
74
# File 'lib/libosctl/os_process.rb', line 72

def start_time
  @start_time
end

#stateString (readonly)

Returns:

  • (String)


43
44
45
# File 'lib/libosctl/os_process.rb', line 43

def state
  @state
end

#sys_timeInteger (readonly)

Number of seconds this process was scheduled in kernel mode

Returns:

  • (Integer)


68
69
70
# File 'lib/libosctl/os_process.rb', line 68

def sys_time
  @sys_time
end

#user_timeInteger (readonly)

Number of seconds this process was scheduled in user mode

Returns:

  • (Integer)


64
65
66
# File 'lib/libosctl/os_process.rb', line 64

def user_time
  @user_time
end

#vmsizeInteger (readonly)

Virtual memory size in bytes

Returns:

  • (Integer)


50
51
52
# File 'lib/libosctl/os_process.rb', line 50

def vmsize
  @vmsize
end

Class Method Details

.system_start_timeObject



9
10
11
12
13
# File 'lib/libosctl/os_process.rb', line 9

def self.system_start_time
  @system_start_time ||= Time.now - File.read('/proc/uptime').strip.split.first.to_i

  @system_start_time
end

Instance Method Details

#cache(key) ⇒ Object (protected)



266
267
268
269
270
271
272
# File 'lib/libosctl/os_process.rb', line 266

def cache(key)
  if @cache.has_key?(key)
    @cache[key]
  else
    @cache[key] = yield
  end
end

#cmdlineString

Read /proc/<pid>/cmdline

Returns:

  • (String)


175
176
177
178
179
# File 'lib/libosctl/os_process.rb', line 175

def cmdline
  cache(:cmdline) do
    volatile { File.read(File.join(path, 'cmdline')).gsub("\0", ' ').strip }
  end
end

#ct_egidInteger?

Returns:

  • (Integer, nil)


169
170
171
# File 'lib/libosctl/os_process.rb', line 169

def ct_egid
  uns_host_to_ns('gid', egid)
end

#ct_euidInteger?

Returns:

  • (Integer, nil)


164
165
166
# File 'lib/libosctl/os_process.rb', line 164

def ct_euid
  uns_host_to_ns('uid', euid)
end

#ct_idArray?

Return container pool and id as a tuple or nil

Returns:

  • (Array, nil)


117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/libosctl/os_process.rb', line 117

def ct_id
  cache(:ct_id) do
    volatile do
      File.open(File.join(path, 'cgroup'), 'r') do |f|
        # It's not enough to check the first cgroup, it can happen that
        # the process remains only in some cgroups that belong to
        # the container, e.g. on an incorrect shutdown.
        f.each_line do |line|
          begin
            _id, _subsys, path = line.split(':')
          rescue ArgumentError
            # We have a kernel bug in 5.10.147.7, which malforms
            # /proc/<pid>/cgroup contents, raising:
            #
            #   invalid byte sequence in US-ASCII (ArgumentError)
            #
            break
          end

          next if %r{^/osctl/pool\.([^/]+)} !~ path

          pool = ::Regexp.last_match(1)

          next if %r{ct\.([^/]+)/user-owned(/|$)} !~ path

          ctid = ::Regexp.last_match(1)

          return [pool, ctid]
        end
      end

      nil
    end
  end
end

#ct_pidInteger

Return PID as seen in the container’s PID namespace

Returns:

  • (Integer)


111
112
113
# File 'lib/libosctl/os_process.rb', line 111

def ct_pid
  nspid[1]
end

#ct_rgidInteger?

Returns:

  • (Integer, nil)


159
160
161
# File 'lib/libosctl/os_process.rb', line 159

def ct_rgid
  uns_host_to_ns('gid', rgid)
end

#ct_ruidInteger?

Returns:

  • (Integer, nil)


154
155
156
# File 'lib/libosctl/os_process.rb', line 154

def ct_ruid
  uns_host_to_ns('uid', ruid)
end

#flushObject

Flush cache and read fresh information from ‘/proc`



182
183
184
185
# File 'lib/libosctl/os_process.rb', line 182

def flush
  @cache.clear
  @id_maps.clear
end

#grandparentOsProcess

Returns:



105
106
107
# File 'lib/libosctl/os_process.rb', line 105

def grandparent
  parent.parent
end

#id_map(type) ⇒ Object (protected)



242
243
244
# File 'lib/libosctl/os_process.rb', line 242

def id_map(type)
  @id_maps[type] ||= parse_id_map(type)
end

#parentOsProcess

Returns:



100
101
102
# File 'lib/libosctl/os_process.rb', line 100

def parent
  self.class.new(ppid, **opts)
end

#parseObject

Menually invoke parsing of ‘/proc/<pid>/stat` and `/proc/<pid>/status`



92
93
94
95
96
97
# File 'lib/libosctl/os_process.rb', line 92

def parse
  volatile do
    parse_stat
    parse_status
  end
end

#parse_id_map(type) ⇒ Object (protected)



246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/libosctl/os_process.rb', line 246

def parse_id_map(type)
  volatile do
    id_map = OsCtl::Lib::IdMap.new

    File.open(File.join(path, "#{type}_map"), 'r') do |f|
      f.each_line do |line|
        id_map.add_from_string(line, separator: ' ')
      end
    end

    id_map
  end
end

#parse_statObject (protected)



191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/libosctl/os_process.rb', line 191

def parse_stat
  File.open(File.join(path, 'stat'), 'r') do |f|
    line = f.readline
    fields = line[line.rindex(')') + 1..].split

    # The third field in /proc/<pid>/stat (State) is the first in here, i.e.
    # substract 3 from field index documented in man proc to get
    # the appropriate field
    @state = fields[0]
    @ppid = fields[1].to_i
    @pgrp = fields[2].to_i
    @user_time = fields[11].to_i / TICS_PER_SECOND
    @sys_time = fields[12].to_i / TICS_PER_SECOND
    @nice = fields[16].to_i
    @num_threads = fields[17].to_i
    @start_time = self.class.system_start_time + (fields[19].to_i / TICS_PER_SECOND)
    @vmsize = fields[20].to_i
    @rss = fields[21].to_i * PAGE_SIZE
  end
end

#parse_statusObject (protected)



212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/libosctl/os_process.rb', line 212

def parse_status
  File.open(File.join(path, 'status'), 'r') do |f|
    f.each_line do |line|
      colon = line.index(':')
      next if colon.nil?

      k = line[0..(colon - 1)].strip
      v = line[(colon + 1)..].strip

      case k
      when 'NSpid'
        @nspid = v.split.map(&:to_i)

      when 'Uid'
        @ruid, @euid, @svuid, @fsuid = v.split.map(&:to_i)

      when 'Gid'
        @rgid, @egid, @svgid, @fsgid = v.split.map(&:to_i)

      when 'Name'
        @name = v
      end
    end
  end
end

#uns_host_to_ns(type, host_id) ⇒ Object (protected)



238
239
240
# File 'lib/libosctl/os_process.rb', line 238

def uns_host_to_ns(type, host_id)
  id_map(type).host_to_ns(host_id)
end

#volatileObject (protected)



260
261
262
263
264
# File 'lib/libosctl/os_process.rb', line 260

def volatile
  yield
rescue Errno::ENOENT, Errno::ESRCH
  raise Exceptions::OsProcessNotFound, pid
end