Module netstat
[hide private]
[frames] | no frames]

Source Code for Module netstat

  1  #!/usr/bin/env python 
  2  # vim: set fileencoding=UTF-8 : 
  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  # Copyright (c) 2007 Jakub Hrozek (jhrozek@redhat.com) 
 12  # 
 13  # This file is part of Gnome-network-monitor. 
 14  # 
 15  # Gnome-network-monitor is free software; you can redistribute it and/or modify 
 16  # it under the terms of the GNU General Public License as published by 
 17  # the Free Software Foundation, version 2 of the License. 
 18  #  
 19  # Gnome-network-monitor is distributed in the hope that it will be useful, 
 20  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 21  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 22  # GNU General Public License for more details. 
 23  #  
 24  # See the file LICENSE for full text of GPL version 2 
 25  # 
 26  # You should have received a copy of the GNU General Public License 
 27  # along with Gnome-network-monitor; if not, write to the Free Software 
 28  # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA 
 29  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #  
 30   
 31  import os 
 32  import re 
 33  import copy 
 34  #import gtk 
 35  import gettext 
 36  import gobject 
 37  import socket 
 38  from UserDict import UserDict 
 39  _ = gettext.gettext 
 40   
41 -class DesktopIcon(UserDict):
42 """ 43 Use an external dictionary to save space. 44 """
45 - def __init__(self):
46 """ 47 Gets information about the default theme 48 and inits the internal dictionary 49 """ 50 UserDict.__init__(self)
51 #self.__theme = gtk.icon_theme_get_default() 52
53 - def __getitem__(self, key):
54 """ 55 Just calls parent's getitem 56 """ 57 return UserDict.__getitem__(self, key)
58
59 - def __setitem__(self, key, value):
60 """ 61 Just calls parent's setitem 62 """ 63 UserDict.__setitem__(self, key, value)
64
65 - def __delitem__(self, key):
66 """ 67 Just calls parent's delitem 68 """ 69 UserDict.__delitem__(self, key)
70
71 - def get_icon(self, progname):
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 #icon_theme = gtk.icon_theme_get_default() 79 try: # FIXME - try other sizes than 48 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
87 -class NetProcess(UserDict):
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
109 - def __eq__(self, other):
110 """ 111 Comparison operaton. Returns True if the object holds the same data as 112 the other object. 113 """ 114 # idea: the list of items that have differend values for keys in FwRecord.fields must be empty 115 return filter(lambda f: self[f] != other[f], NetProcess.keys) == []
116
117 - def __neq__(self, other):
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
124 - def __repr__(self):
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
132 - def __str__(self):
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
141 - def __resolve_ip(self, address):
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
149 - def __resolve_portnum(self, portnum):
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
157 - def __check_key(self, key):
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
167 - def __getitem__(self, key):
168 """ Return item or N/A if not set. Raises KeyError if the key is 169 not in NetProcess.keys """ 170 171 # a switch-case would come handy here.. 172 if key == "host_local": 173 return self.__resolve_ip(self["addr_local"]) 174 if key == "host_remote": # kind of a phony target 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 # check if we know the key 182 self.__check_key(key) 183 184 # two special cases are handled here 185 if key == "state": 186 return NetProcess.states[self.data["state"]] 187 return self.data.get(key, "N/A")
188
189 - def __setitem__(self, key, value):
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
195 - def __init__(self, line):
196 """ Parses given line and stores in an internal dictionary (see 197 UserDict) """ 198 UserDict.__init__(self) 199 self.__parse_line(line)
200
201 - def __parse_address(self, addr):
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
212 - def __parse_line(self, line):
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
230 -class NetStat(object):
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) # raises IOError when the filename does not exist 241 242 self.__processes = list() 243 self.__inodes = dict() 244 self.__shallow = None
245
246 - def __get_progname_for_pid(self, pid):
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
263 - def __process_one_pid(self, pid):
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: # probably permission denied on the /proc/<fd> directory 279 return
280
281 - def match_inode_pid(self):
282 """ 283 Walk through /proc/<pid>/fd/* and find matching process for these sockets 284 """ 285 # after resolving a pid for inode, we delete it from the list to avoid multiple iteration 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
289 - def read_file(self):
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: # don't even try to resolve root sockets if we're not root 303 continue 304 self.__inodes[new["inode"]] = new
305
306 - def get_processes(self):
307 """ Return all processes the objects holds """ 308 return self.__processes
309