Misc s/func/certmaster/ replacements
[certmaster.git] / certmaster / minion / modules / process.py.orig
1 ## -*- coding: utf-8 -*-
2 ##
3 ## Process lister (control TBA)
4 ##
5 ## Copyright 2007, Red Hat, Inc
6 ## Michael DeHaan <mdehaan@redhat.com>
7 ##
8 ## This software may be freely redistributed under the terms of the GNU
9 ## general public license.
10 ##
11 ## You should have received a copy of the GNU General Public License
12 ## along with this program; if not, write to the Free Software
13 ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
14 ##
15
16 # other modules
17 import sub_process
18 import codes
19
20 # our modules
21 from modules import func_module
22
23 # =================================
24
25 class ProcessModule(func_module.FuncModule):
26 def __init__(self):
27 self.methods = {
28 "info" : self.info,
29 "kill" : self.kill,
30 "pkill" : self.pkill,
31 "mem" : self.mem
32 }
33 func_module.FuncModule.__init__(self)
34
35 def info(self, flags="-auxh"):
36 """
37 Returns a struct of hardware information. By default, this pulls down
38 all of the devices. If you don't care about them, set with_devices to
39 False.
40 """
41
42 flags.replace(";", "") # prevent stupidity
43
44 cmd = sub_process.Popen(["/bin/ps", flags], executable="/bin/ps",
45 stdout=sub_process.PIPE,
46 stderr=sub_process.PIPE,
47 shell=False)
48
49 data, error = cmd.communicate()
50
51 # We can get warnings for odd formatting. warnings != errors.
52 if error and error[:7] != "Warning":
53 raise codes.FuncException(error.split('\n')[0])
54
55 results = []
56 for x in data.split("\n"):
57 tokens = x.split()
58 results.append(tokens)
59
60 return results
61
62 def mem(self):
63 """
64 Returns a list of per-program memory usage.
65
66 Private + Shared = RAM used Program
67
68 [["39.4 MiB", "10.3 MiB", "49.8 MiB", "Xorg"],
69 ["42.2 MiB", "12.4 MiB", "54.6 MiB", "nautilus"],
70 ["52.3 MiB", "10.8 MiB", "63.0 MiB", "liferea-bin"]
71 ["171.6 MiB", "11.9 MiB", "183.5 MiB", "firefox-bin"]]
72
73 Taken from the ps_mem.py script written by Pádraig Brady.
74 http://www.pixelbeat.org/scripts/ps_mem.py
75 """
76 import os
77 our_pid=os.getpid()
78 results = []
79 have_smaps=0
80 have_pss=0
81
82 def kernel_ver():
83 """ (major,minor,release) """
84 kv=open("/proc/sys/kernel/osrelease").readline().split(".")[:3]
85 for char in "-_":
86 kv[2]=kv[2].split(char)[0]
87 return (int(kv[0]), int(kv[1]), int(kv[2]))
88
89 kv=kernel_ver()
90
91 def getMemStats(pid):
92 """ return Rss,Pss,Shared (note Private = Rss-Shared) """
93 Shared_lines=[]
94 Pss_lines=[]
95 pagesize=os.sysconf("SC_PAGE_SIZE")/1024 #KiB
96 Rss=int(open("/proc/"+str(pid)+"/statm").readline().split()[1])*pagesize
97 if os.path.exists("/proc/"+str(pid)+"/smaps"): #stat
98 global have_smaps
99 have_smaps=1
100 for line in open("/proc/"+str(pid)+"/smaps").readlines(): #open
101 #Note in smaps Shared+Private = Rss above
102 #The Rss in smaps includes video card mem etc.
103 if line.startswith("Shared"):
104 Shared_lines.append(line)
105 elif line.startswith("Pss"):
106 global have_pss
107 have_pss=1
108 Pss_lines.append(line)
109 Shared=sum([int(line.split()[1]) for line in Shared_lines])
110 Pss=sum([int(line.split()[1]) for line in Pss_lines])
111 elif (2,6,1) <= kv <= (2,6,9):
112 Pss=0
113 Shared=0 #lots of overestimation, but what can we do?
114 else:
115 Pss=0
116 Shared=int(open("/proc/"+str(pid)+"/statm").readline().split()[2])*pagesize
117 return (Rss, Pss, Shared)
118
119 cmds={}
120 shareds={}
121 count={}
122 for pid in os.listdir("/proc/"):
123 try:
124 pid = int(pid) #note Thread IDs not listed in /proc/
125 if pid ==our_pid: continue
126 except:
127 continue
128 cmd = file("/proc/%d/status" % pid).readline()[6:-1]
129 try:
130 exe = os.path.basename(os.path.realpath("/proc/%d/exe" % pid))
131 if exe.startswith(cmd):
132 cmd=exe #show non truncated version
133 #Note because we show the non truncated name
134 #one can have separated programs as follows:
135 #584.0 KiB + 1.0 MiB = 1.6 MiB mozilla-thunder (exe -> bash)
136 #56.0 MiB + 22.2 MiB = 78.2 MiB mozilla-thunderbird-bin
137 except:
138 #permission denied or
139 #kernel threads don't have exe links or
140 #process gone
141 continue
142 try:
143 rss, pss, shared = getMemStats(pid)
144 private = rss-shared
145 #Note shared is always a subset of rss (trs is not always)
146 except:
147 continue #process gone
148 if shareds.get(cmd):
149 if pss: #add shared portion of PSS together
150 shareds[cmd]+=pss-private
151 elif shareds[cmd] < shared: #just take largest shared val
152 shareds[cmd]=shared
153 else:
154 if pss:
155 shareds[cmd]=pss-private
156 else:
157 shareds[cmd]=shared
158 cmds[cmd]=cmds.setdefault(cmd,0)+private
159 if count.has_key(cmd):
160 count[cmd] += 1
161 else:
162 count[cmd] = 1
163
164 #Add max shared mem for each program
165 total=0
166 for cmd in cmds.keys():
167 cmds[cmd]=cmds[cmd]+shareds[cmd]
168 total+=cmds[cmd] #valid if PSS available
169
170 sort_list = cmds.items()
171 sort_list.sort(lambda x,y:cmp(x[1],y[1]))
172 sort_list=filter(lambda x:x[1],sort_list) #get rid of zero sized processes
173
174 #The following matches "du -h" output
175 def human(num, power="Ki"):
176 powers=["Ki","Mi","Gi","Ti"]
177 while num >= 1000: #4 digits
178 num /= 1024.0
179 power=powers[powers.index(power)+1]
180 return "%.1f %s" % (num,power)
181
182 def cmd_with_count(cmd, count):
183 if count>1:
184 return "%s (%u)" % (cmd, count)
185 else:
186 return cmd
187
188 for cmd in sort_list:
189 results.append([
190 "%sB" % human(cmd[1]-shareds[cmd[0]]),
191 "%sB" % human(shareds[cmd[0]]),
192 "%sB" % human(cmd[1]),
193 "%s" % cmd_with_count(cmd[0], count[cmd[0]])
194 ])
195 if have_pss:
196 results.append(["", "", "", "%sB" % human(total)])
197
198 return results
199
200 def kill(self,pid,signal="TERM"):
201 if pid == "0":
202 raise codes.FuncException("Killing pid group 0 not permitted")
203 if signal == "":
204 # this is default /bin/kill behaviour,
205 # it claims, but enfore it anyway
206 signal = "-TERM"
207 if signal[0] != "-":
208 signal = "-%s" % signal
209 rc = sub_process.call(["/bin/kill",signal, pid],
210 executable="/bin/kill", shell=False)
211 print rc
212 return rc
213
214 def pkill(self,name,level=""):
215 # example killall("thunderbird","-9")
216 rc = sub_process.call(["/usr/bin/pkill", name, level],
217 executable="/usr/bin/pkill", shell=False)
218 return rc
219
220 methods = ProcessModule()
221 register_rpc = methods.register_rpc