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


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

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)


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

def egid
  @egid
end

#euidInteger (readonly)

Returns:

  • (Integer)


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

def euid
  @euid
end

#nameString (readonly)

Returns:

  • (String)


48
49
50
# File 'lib/libosctl/os_process.rb', line 48

def name
  @name
end

#niceInteger (readonly)

Returns:

  • (Integer)


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

def nice
  @nice
end

#nspidArray (readonly)

Returns:

  • (Array)


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

def nspid
  @nspid
end

#num_threadsInteger (readonly)

Returns:

  • (Integer)


62
63
64
# File 'lib/libosctl/os_process.rb', line 62

def num_threads
  @num_threads
end

#optsObject (readonly, protected)

Returns the value of attribute opts.



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

def opts
  @opts
end

#pathObject (readonly, protected)

Returns the value of attribute path.



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

def path
  @path
end

#pgrpInteger (readonly)

Process group ID

Returns:

  • (Integer)


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

def pgrp
  @pgrp
end

#pidInteger (readonly)

Process ID

Returns:

  • (Integer)


19
20
21
# File 'lib/libosctl/os_process.rb', line 19

def pid
  @pid
end

#ppidInteger (readonly)

Parent process ID

Returns:

  • (Integer)


23
24
25
# File 'lib/libosctl/os_process.rb', line 23

def ppid
  @ppid
end

#rgidInteger (readonly)

Returns:

  • (Integer)


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

def rgid
  @rgid
end

#rssInteger (readonly)

Resident set size in bytes

Returns:

  • (Integer)


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

def rss
  @rss
end

#ruidInteger (readonly)

Returns:

  • (Integer)


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

def ruid
  @ruid
end

#start_timeTime (readonly)

Time when the process was started

Returns:

  • (Time)


74
75
76
# File 'lib/libosctl/os_process.rb', line 74

def start_time
  @start_time
end

#stateString (readonly)

Returns:

  • (String)


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

def state
  @state
end

#sys_timeInteger (readonly)

Number of seconds this process was scheduled in kernel mode

Returns:

  • (Integer)


70
71
72
# File 'lib/libosctl/os_process.rb', line 70

def sys_time
  @sys_time
end

#user_timeInteger (readonly)

Number of seconds this process was scheduled in user mode

Returns:

  • (Integer)


66
67
68
# File 'lib/libosctl/os_process.rb', line 66

def user_time
  @user_time
end

#vmsizeInteger (readonly)

Virtual memory size in bytes

Returns:

  • (Integer)


52
53
54
# File 'lib/libosctl/os_process.rb', line 52

def vmsize
  @vmsize
end

Class Method Details

.system_start_timeObject



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

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

  @system_start_time
end

Instance Method Details

#cache(key) ⇒ Object (protected)



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

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)


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 119

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 /^\/osctl\/pool\.([^\/]+)/ !~ path
          pool = $1

          next if /ct\.([^\/]+)\/user\-owned(\/|$)/ !~ path
          ctid = $1

          return [pool, ctid]
        end
      end

      nil
    end
  end
end

#ct_pidInteger

Return PID as seen in the container's PID namespace

Returns:

  • (Integer)


113
114
115
# File 'lib/libosctl/os_process.rb', line 113

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:



107
108
109
# File 'lib/libosctl/os_process.rb', line 107

def grandparent
  parent.parent
end

#id_map(type) ⇒ Object (protected)



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

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

#parentOsProcess

Returns:



102
103
104
# File 'lib/libosctl/os_process.rb', line 102

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

#parseObject

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



94
95
96
97
98
99
# File 'lib/libosctl/os_process.rb', line 94

def parse
  volatile do
    parse_stat
    parse_status
  end
end

#parse_id_map(type) ⇒ Object (protected)



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

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)



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

def parse_stat
  File.open(File.join(path, 'stat'), 'r') do |f|
    line = f.readline
    fields = line[line.rindex(')') + 1..-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)



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

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)..-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)



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

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

#volatileObject (protected)



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

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