1
2
3 """
4 This module contains classes NetStat, NetProcess and DesktopIcon.
5
6 It is able to collect per-process statistcs about TCP, UDP and raw UNIX
7 sockets. Its information are displayed in class TabNetStat
8 """
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 import os
32 import re
33 import copy
34
35 import gettext
36 import gobject
37 import socket
38 from UserDict import UserDict
39 _ = gettext.gettext
40
42 """
43 Use an external dictionary to save space.
44 """
46 """
47 Gets information about the default theme
48 and inits the internal dictionary
49 """
50 UserDict.__init__(self)
51
52
54 """
55 Just calls parent's getitem
56 """
57 return UserDict.__getitem__(self, key)
58
60 """
61 Just calls parent's setitem
62 """
63 UserDict.__setitem__(self, key, value)
64
66 """
67 Just calls parent's delitem
68 """
69 UserDict.__delitem__(self, key)
70
72 """
73 Finds an icon for given progname in the current theme.
74 If successfull, returns the icon as a pixbuf and adds it to
75 the self dictionary (key is progname). If not successfull,
76 returns None
77 """
78
79 try:
80 pixbuf = icon_theme.load_icon(progname, 48, 0)
81 except gobject.GError, exc:
82 print "Can't load icon", exc
83 return None
84 self[progname] = pixbuf
85 return pixbuf
86
88 """
89 Class NetProcess represents one network connection. Most of the properties
90 are read from /proc/net/{tcp,udp,raw} lines. The class uses dictionary-like
91 mapping.
92
93 The fields we remember about a process are described in
94 NetProcess.keys. Mapping of someof them to fields in /proc/net file are
95 in NetProcess.keys. In order to save some space, TCP states are stored
96 in NetProcess.states and every process has only an integer pointing into
97 this list.
98 """
99
100 keys = [ "addr_local", "port_local", "addr_remote", "port_remote", "pid",
101 "inode", "uid", "state", "progname", "icon" ]
102
103 fields = { "addr_local" : 1, "addr_remote" : 2, "state" : 3,
104 "uid" : 7, "inode" : 9 }
105
106 states = [ "none", "Established", "Syn-Sent", "Syn-Recv", "Fin-Wait1", "Fin-Wait2",
107 "Time-Wait", "Close", "Close-Wait", "Last-Ack", "Listening", "Closing"]
108
110 """
111 Comparison operaton. Returns True if the object holds the same data as
112 the other object.
113 """
114
115 return filter(lambda f: self[f] != other[f], NetProcess.keys) == []
116
118 """
119 Negative comparison operaton Returns True if the object holds the same data as
120 the other object.
121 """
122 return not (self == other)
123
125 """ Prints out a human readable representation of the structure that
126 can be easily used to recreate the same object """
127 ret = ""
128 for k in NetProcess.keys[:-1]:
129 ret += "%s %s " % (k, self.data.get(k, "N/A"))
130 return ret
131
133 """ Returns human-readable representation of the object """
134 ret = ""
135 ret += "%s %s:%s \n" % ("Local".ljust(20), self["addr_local"], self["port_local"])
136 ret += "%s %s:%s \n" % ("Remote".ljust(20), self["addr_remote"], self["port_remote"])
137 ret += "%s %s/%s (UID %s) \n" % ("Program info: ".ljust(20), self["progname"], self["pid"], self["uid"])
138 ret += "%s %s \n" % ("Connection state: ".ljust(20), self["state"] )
139 return ret
140
142 """ Tries to resolve a hostname for a given IP. If not successfull,
143 returns the IP adress back """
144 try:
145 hostname = socket.gethostbyaddr(address)[0]
146 except socket.herror: return address
147 return hostname
148
150 """ Tries to resolve a service name for a given portnum. If not
151 successfull, returns the port number back """
152 try:
153 servname = socket.getservbyport(portnum)
154 except socket.error: return portnum
155 return servname
156
158 """
159 The strategy is: if we are asked for a key we should not know,
160 raise exception. If we are asked for a key we should know, but
161 for some reason we don't (such as could not find icon, or could not
162 resolve pid for a root program) we return N/A
163 """
164 if key not in NetProcess.keys:
165 raise KeyError(_("NetProcess does not have any field %s\n") % (key))
166
168 """ Return item or N/A if not set. Raises KeyError if the key is
169 not in NetProcess.keys """
170
171
172 if key == "host_local":
173 return self.__resolve_ip(self["addr_local"])
174 if key == "host_remote":
175 return self.__resolve_ip(self["addr_remote"])
176 if key == "serv_local":
177 return self.__resolve_portnum(self["port_local"])
178 if key == "serv_remote":
179 return self.__resolve_portnum(self["port_remote"])
180
181
182 self.__check_key(key)
183
184
185 if key == "state":
186 return NetProcess.states[self.data["state"]]
187 return self.data.get(key, "N/A")
188
190 """ Adjust value of a key. Raises KeyError if the key is not in
191 NetProcess.keys. """
192 self.__check_key(key)
193 UserDict.__setitem__(self, key, value)
194
196 """ Parses given line and stores in an internal dictionary (see
197 UserDict) """
198 UserDict.__init__(self)
199 self.__parse_line(line)
200
202 """ Adresses in the /proc/net/{tcp,udp} file are in reverse
203 byte-order. This file converts it to human-readable notation and
204 returns a tuple of: ('address', port) """
205 ip_rev, port = addr.split(":")
206
207 port = int(port, 16)
208 address = '.'.join([ str(int(ip_rev[i-2:i], 16)) for i in xrange(8, 0, -2) ])
209
210 return (address, port)
211
213 """
214 Splits fields of a given line and stores them in self-dictionary (see
215 UserDict). Raises IndexError if the line does not have correct number
216 of fields or ValueError if a field does not contain the info it should.
217 """
218 sp = line.split()
219
220 try:
221 self["addr_local"], self["port_local"] = self.__parse_address(sp[NetProcess.fields["addr_local"]])
222 self["addr_remote"], self["port_remote"] = self.__parse_address(sp[NetProcess.fields["addr_remote"]])
223 self["uid"] = sp[NetProcess.fields["uid"]]
224 self["inode"] = sp[NetProcess.fields["inode"]]
225 self["state"] = int(sp[NetProcess.fields["state"]], 16)
226 except IndexError:
227 raise IndexError("""Could not parse line %s, probably a bug in
228 the application, please report""" % (line))
229
231 """
232 Parses /proc/net/{tcp,udp} and returns a list of NetProcesses. Is
233 also able to find a matching pid/progname for the process.
234 """
235 - def __init__(self, filename, proc_mount):
236 assert(proc_mount != None)
237 self.__proc_mount = proc_mount
238
239 self.__pat = re.compile("^socket:\[(?P<sock>\d+)\]")
240 self.__fd = open(filename)
241
242 self.__processes = list()
243 self.__inodes = dict()
244 self.__shallow = None
245
247 """
248 Walks through the /proc/<pid>/ hierarchy and tries to match
249 an open socket with a pid or progname
250 """
251 path = "%s/%s/status" % (self.__proc_mount, pid)
252 bad = "N/A"
253 fd = None
254 try:
255 fd = open(path)
256 except IOError: return bad
257 progname = [ l.split(":")[1].strip() for l in fd if "Name:" in l ][0]
258 if progname == None or progname == "":
259 return bad
260 return progname
261
262
264 """
265 Returns True if the pid has any open socket from self.__processes
266 and False if not
267 """
268 path = "%s/%s/fd/" % (self.__proc_mount, pid)
269 try:
270 for link in [ os.readlink(path + fdesc) for fdesc in os.listdir(path) if os.path.islink(path + fdesc) ]:
271 has_socket = self.__pat.match(link)
272 if has_socket:
273 sock = has_socket.groups('sock')[0]
274 while sock in self.__shallow.keys():
275 self.__shallow[sock]["pid"] = pid
276 self.__shallow[sock]["progname"] = self.__get_progname_for_pid(pid)
277 del self.__shallow[sock]
278 except OSError:
279 return
280
282 """
283 Walk through /proc/<pid>/fd/* and find matching process for these sockets
284 """
285
286 self.__shallow = copy.copy(self.__inodes)
287 map(self.__process_one_pid, [ pid for pid in os.listdir(self.__proc_mount) if pid.isdigit() ])
288
290 """
291 Parses the file given to contructor.
292 """
293
294 self.__processes = list()
295 self.__inodes.clear()
296 self.__fd.seek(0)
297
298 mypid = os.getpid()
299 for line in self.__fd.readlines()[1:]:
300 new = NetProcess(line)
301 self.__processes.append(new)
302 if mypid != 0 and new["pid"] == 0:
303 continue
304 self.__inodes[new["inode"]] = new
305
307 """ Return all processes the objects holds """
308 return self.__processes
309