Module: Zerg::Support::Process

Defined in:
lib/zerg_support/spawn.rb,
lib/zerg_support/spawn.rb,
lib/zerg_support/spawn.rb,
lib/zerg_support/process.rb

Overview

process management

Defined Under Namespace

Modules: Helpers

Constant Summary collapse

@@no_multiple_pids =
false

Class Method Summary collapse

Class Method Details

.kill(pid) ⇒ Object

Kills the process with the given pid.



125
126
127
128
129
130
131
132
133
134
135
# File 'lib/zerg_support/process.rb', line 125

def self.kill(pid)
  begin
    self.kill_primitive pid, false
    Thread.new(pid) do |victim_pid|
      Kernel.sleep 0.2
      self.kill_primitive(pid, true) rescue nil
    end
  rescue
    # we probably don't have the right to kill the process
  end    
end

.kill_primitive(pid, force = false) ⇒ Object

:nodoc: Wrapper around Process.kill that works on all platforms.



114
115
116
# File 'lib/zerg_support/process.rb', line 114

def self.kill_primitive(pid, force = false)
  Process.kill(force ? 9 : 4, pid)
end

.kill_tree(root_pid) ⇒ Object

Kills all the processes descending from the process with the given pid.



138
139
140
141
142
# File 'lib/zerg_support/process.rb', line 138

def self.kill_tree(root_pid)
  self.process_tree(root_pid).each do |pinfo|
    self.kill pinfo[:pid]
  end
end

.process_info(pid) ⇒ Object

Collects information about a single process. Returns nil



56
57
58
59
# File 'lib/zerg_support/process.rb', line 56

def self.process_info(pid)
  pinfo = ps pid
  pinfo ? xlate_process_info(pinfo) : nil 
end

.process_tree(*root_pids) ⇒ Object

Returns information about the descendants of the process with the given pid.



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/zerg_support/process.rb', line 92

def self.process_tree(*root_pids)
  procs_by_ppid = {}
  proc_list = self.processes_by_id
  proc_list.each do |pid, pinfo|
    procs_by_ppid[pinfo[:parent_pid]] ||= []
    procs_by_ppid[pinfo[:parent_pid]] << pinfo
  end
  
  proc_queue = root_pids.map { |pid| proc_list[pid] }.select { |pinfo| pinfo }
  
  index = 0
  while index < proc_queue.length
    pid, index = proc_queue[index][:pid], index + 1
    next unless procs_by_ppid.has_key? pid
    proc_queue += procs_by_ppid[pid]
  end
  return proc_queue
end

.processes(pids = nil) ⇒ Object

Collects information about processes with the given pids. Returns all processes if no list of pids is given.



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/zerg_support/process.rb', line 63

def self.processes(pids = nil)
  if @@no_multiple_pids and !pids.empty
    pids.map { |pid| process_info pid }
  else
    begin
      if pids
        ps_result = ps(pids)
      else
        ps_result = ps
      end
      ps_result.map { |pinfo| xlate_process_info pinfo }
    rescue TypeError
      # we're using the real sys-proctable, and its ps doesn't like multiple
      # arguments
      @@no_multiple_pids = true
      processes pids
    end
  end
end

.processes_by_id(pids = nil) ⇒ Object

Collects information about processes with the given pids. The information is returned indexed by the processes’ pids.



85
86
87
88
89
# File 'lib/zerg_support/process.rb', line 85

def self.processes_by_id(pids = nil)
  retval = {}
  self.processes(pids).each { |pinfo| retval[pinfo[:pid]] = pinfo } 
  return retval
end

.spawn(binary, args = [], options = {}) ⇒ Object

Spawns a new process and returns its pid immediately. Like Kernel.spawn in ruby1.9, except that the environment is passed as an option with the key :env



80
81
82
83
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/zerg_support/spawn.rb', line 80

def self.spawn(binary, args = [], options = {})
  # command line processing
  command_line = '"' + binary + '" "' +
      args.map { |a| a.gsub '"', '""' }.join('" "') + '"'

  # environment processing
  environment_string = nil
  if options[:env]
    if options[:unsetenv_others]
      environment = options[:env]
    else
      environment = Hash.new.merge(ENV).merge(options[:env])
    end
    environment_string = environment.keys.sort.
        map { |k| "#{k}=#{environment[k]}" }.join "\0"
    environment_string << "\0"
  end

  # redirection processing
  startup_info = {}
  files = {}
  stream_files = {}
  deferred_opens = []
  [[STDIN, :stdin], [STDOUT, :stdout], [STDERR, :stderr]].each do |pair|
    next unless options[pair.first]
    if options[pair.first].kind_of? String
      filename = options[pair.first]
      files[filename] ||= File.open(filename,
                                    (pair.last == :stdin) ? 'r' : 'w+')
      startup_info[pair.last] = files[filename]
      stream_files[pair.first] = files[filename]
    else
      deferred_opens << Kernel.proc do
        io = stream_files[options[pair.first]] || pair.first
        startup_info[pair.last] = stream_files[pair.first] = io
      end
    end
  end
  deferred_opens.each { |d| d.call }

  # process leader
  creation_flags = 0
  if options[:pgroup]
    creation_flags |= Process::DETACHED_PROCESS
    if options[:pgroup].kind_of? Numeric and options[:pgroup] > 0
      # TODO: what now?
    else
      creation_flags |= Process::CREATE_NEW_PROCESS_GROUP
    end
  end

  info = Process.create :command_line => command_line,
                        :cwd => options[:chdir] || Dir.pwd,
                        :environment => environment_string,
                        :creation_flags => creation_flags,
                        :startup_info => startup_info
  files.each { |name, io| io.close }

  return info[:process_id]
end

.xlate_process_info(low_info) ⇒ Object

:nodoc:



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/zerg_support/process.rb', line 9

def self.xlate_process_info(low_info)
  {
    :pid => low_info.pid,
    :parent_pid => low_info.ppid,
    :real_uid => low_info.ruid || -1,
    :real_gid => low_info.rgid || -1,
    :start_time => low_info.start,
    :nice => low_info.nice || 0,
    :priority => low_info.priority || 0,
    :syscall_priority => low_info.user_priority || 0,
    :resident_size => (low_info.id_rss || 0) * 1024,
    :code_size => (low_info.ix_rss || 0) * 1024,
    :virtual_size => (low_info.is_rss || 0) * 1024,
    :percent_cpu => low_info.pctcpu,
    :percent_ram => low_info.pctmem,
    :state => low_info.state,
    :command_line => low_info.cmdline
  }
end