| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/env python | |
| 2 # | |
| 3 # $Id: _pslinux.py 1142 2011-10-05 18:45:49Z g.rodola $ | |
| 4 # | |
| 5 # Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. | |
| 6 # Use of this source code is governed by a BSD-style license that can be | |
| 7 # found in the LICENSE file. | |
| 8 | |
| 9 """Linux platform implementation.""" | |
| 10 | |
| 11 import os | |
| 12 import errno | |
| 13 import socket | |
| 14 import struct | |
| 15 import sys | |
| 16 import base64 | |
| 17 import re | |
| 18 | |
| 19 import _psutil_posix | |
| 20 import _psutil_linux | |
| 21 from psutil import _psposix | |
| 22 from psutil.error import AccessDenied, NoSuchProcess, TimeoutExpired | |
| 23 from psutil._common import * | |
| 24 | |
| 25 __extra__all__ = [ | |
| 26 "IOPRIO_CLASS_NONE", "IOPRIO_CLASS_RT", "IOPRIO_CLASS_BE", | |
| 27 "IOPRIO_CLASS_IDLE", | |
| 28 "phymem_buffers", "cached_phymem"] | |
| 29 | |
| 30 | |
| 31 def _get_boot_time(): | |
| 32 """Return system boot time (epoch in seconds)""" | |
| 33 f = open('/proc/stat', 'r') | |
| 34 try: | |
| 35 for line in f: | |
| 36 if line.startswith('btime'): | |
| 37 return float(line.strip().split()[1]) | |
| 38 raise RuntimeError("line not found") | |
| 39 finally: | |
| 40 f.close() | |
| 41 | |
| 42 def _get_num_cpus(): | |
| 43 """Return the number of CPUs on the system""" | |
| 44 num = 0 | |
| 45 f = open('/proc/cpuinfo', 'r') | |
| 46 try: | |
| 47 lines = f.readlines() | |
| 48 finally: | |
| 49 f.close() | |
| 50 for line in lines: | |
| 51 if line.lower().startswith('processor'): | |
| 52 num += 1 | |
| 53 | |
| 54 # unknown format (e.g. amrel/sparc architectures), see: | |
| 55 # http://code.google.com/p/psutil/issues/detail?id=200 | |
| 56 if num == 0: | |
| 57 f = open('/proc/stat', 'r') | |
| 58 try: | |
| 59 lines = f.readlines() | |
| 60 finally: | |
| 61 f.close() | |
| 62 search = re.compile('cpu\d') | |
| 63 for line in lines: | |
| 64 line = line.split(' ')[0] | |
| 65 if search.match(line): | |
| 66 num += 1 | |
| 67 | |
| 68 if num == 0: | |
| 69 raise RuntimeError("can't determine number of CPUs") | |
| 70 return num | |
| 71 | |
| 72 | |
| 73 # Number of clock ticks per second | |
| 74 _CLOCK_TICKS = os.sysconf(os.sysconf_names["SC_CLK_TCK"]) | |
| 75 _TERMINAL_MAP = _psposix._get_terminal_map() | |
| 76 BOOT_TIME = _get_boot_time() | |
| 77 NUM_CPUS = _get_num_cpus() | |
| 78 # ioprio_* constants http://linux.die.net/man/2/ioprio_get | |
| 79 IOPRIO_CLASS_NONE = 0 | |
| 80 IOPRIO_CLASS_RT = 1 | |
| 81 IOPRIO_CLASS_BE = 2 | |
| 82 IOPRIO_CLASS_IDLE = 3 | |
| 83 | |
| 84 # http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h | |
| 85 _TCP_STATES_TABLE = {"01" : "ESTABLISHED", | |
| 86 "02" : "SYN_SENT", | |
| 87 "03" : "SYN_RECV", | |
| 88 "04" : "FIN_WAIT1", | |
| 89 "05" : "FIN_WAIT2", | |
| 90 "06" : "TIME_WAIT", | |
| 91 "07" : "CLOSE", | |
| 92 "08" : "CLOSE_WAIT", | |
| 93 "09" : "LAST_ACK", | |
| 94 "0A" : "LISTEN", | |
| 95 "0B" : "CLOSING" | |
| 96 } | |
| 97 | |
| 98 # --- system memory functions | |
| 99 | |
| 100 def cached_phymem(): | |
| 101 """Return the amount of cached memory on the system, in bytes. | |
| 102 This reflects the "cached" column of free command line utility. | |
| 103 """ | |
| 104 f = open('/proc/meminfo', 'r') | |
| 105 try: | |
| 106 for line in f: | |
| 107 if line.startswith('Cached:'): | |
| 108 return int(line.split()[1]) * 1024 | |
| 109 raise RuntimeError("line not found") | |
| 110 finally: | |
| 111 f.close() | |
| 112 | |
| 113 def phymem_buffers(): | |
| 114 """Return the amount of physical memory buffers used by the | |
| 115 kernel in bytes. | |
| 116 This reflects the "buffers" column of free command line utility. | |
| 117 """ | |
| 118 f = open('/proc/meminfo', 'r') | |
| 119 try: | |
| 120 for line in f: | |
| 121 if line.startswith('Buffers:'): | |
| 122 return int(line.split()[1]) * 1024 | |
| 123 raise RuntimeError("line not found") | |
| 124 finally: | |
| 125 f.close() | |
| 126 | |
| 127 def phymem_usage(): | |
| 128 # total, used and free values are matched against free cmdline utility | |
| 129 # the percentage matches top/htop and gnome-system-monitor | |
| 130 f = open('/proc/meminfo', 'r') | |
| 131 try: | |
| 132 total = free = buffers = cached = None | |
| 133 for line in f: | |
| 134 if line.startswith('MemTotal:'): | |
| 135 total = int(line.split()[1]) * 1024 | |
| 136 elif line.startswith('MemFree:'): | |
| 137 free = int(line.split()[1]) * 1024 | |
| 138 elif line.startswith('Buffers:'): | |
| 139 buffers = int(line.split()[1]) * 1024 | |
| 140 elif line.startswith('Cached:'): | |
| 141 cached = int(line.split()[1]) * 1024 | |
| 142 break | |
| 143 used = total - free | |
| 144 percent = usage_percent(total - (free + buffers + cached), total, | |
| 145 _round=1) | |
| 146 return ntuple_sysmeminfo(total, used, free, percent) | |
| 147 finally: | |
| 148 f.close() | |
| 149 | |
| 150 | |
| 151 def virtmem_usage(): | |
| 152 f = open('/proc/meminfo', 'r') | |
| 153 try: | |
| 154 total = free = None | |
| 155 for line in f: | |
| 156 if line.startswith('SwapTotal:'): | |
| 157 total = int(line.split()[1]) * 1024 | |
| 158 elif line.startswith('SwapFree:'): | |
| 159 free = int(line.split()[1]) * 1024 | |
| 160 if total is not None and free is not None: | |
| 161 break | |
| 162 assert total is not None and free is not None | |
| 163 used = total - free | |
| 164 percent = usage_percent(used, total, _round=1) | |
| 165 return ntuple_sysmeminfo(total, used, free, percent) | |
| 166 finally: | |
| 167 f.close() | |
| 168 | |
| 169 | |
| 170 # --- system CPU functions | |
| 171 | |
| 172 def get_system_cpu_times(): | |
| 173 """Return a named tuple representing the following CPU times: | |
| 174 user, nice, system, idle, iowait, irq, softirq. | |
| 175 """ | |
| 176 f = open('/proc/stat', 'r') | |
| 177 try: | |
| 178 values = f.readline().split() | |
| 179 finally: | |
| 180 f.close() | |
| 181 | |
| 182 values = values[1:8] | |
| 183 values = tuple([float(x) / _CLOCK_TICKS for x in values]) | |
| 184 return ntuple_sys_cputimes(*values[:7]) | |
| 185 | |
| 186 def get_system_per_cpu_times(): | |
| 187 """Return a list of namedtuple representing the CPU times | |
| 188 for every CPU available on the system. | |
| 189 """ | |
| 190 cpus = [] | |
| 191 f = open('/proc/stat', 'r') | |
| 192 # get rid of the first line who refers to system wide CPU stats | |
| 193 try: | |
| 194 f.readline() | |
| 195 for line in f.readlines(): | |
| 196 if line.startswith('cpu'): | |
| 197 values = line.split()[1:8] | |
| 198 values = tuple([float(x) / _CLOCK_TICKS for x in values]) | |
| 199 entry = ntuple_sys_cputimes(*values[:7]) | |
| 200 cpus.append(entry) | |
| 201 return cpus | |
| 202 finally: | |
| 203 f.close() | |
| 204 | |
| 205 | |
| 206 # --- system disk functions | |
| 207 | |
| 208 def disk_partitions(all=False): | |
| 209 """Return mounted disk partitions as a list of nameduples""" | |
| 210 phydevs = [] | |
| 211 f = open("/proc/filesystems", "r") | |
| 212 try: | |
| 213 for line in f: | |
| 214 if not line.startswith("nodev"): | |
| 215 phydevs.append(line.strip()) | |
| 216 finally: | |
| 217 f.close() | |
| 218 | |
| 219 retlist = [] | |
| 220 partitions = _psutil_linux.get_disk_partitions() | |
| 221 for partition in partitions: | |
| 222 device, mountpoint, fstype = partition | |
| 223 if device == 'none': | |
| 224 device = '' | |
| 225 if not all: | |
| 226 if device == '' or fstype not in phydevs: | |
| 227 continue | |
| 228 ntuple = ntuple_partition(device, mountpoint, fstype) | |
| 229 retlist.append(ntuple) | |
| 230 return retlist | |
| 231 | |
| 232 get_disk_usage = _psposix.get_disk_usage | |
| 233 | |
| 234 # --- process functions | |
| 235 | |
| 236 def get_pid_list(): | |
| 237 """Returns a list of PIDs currently running on the system.""" | |
| 238 pids = [int(x) for x in os.listdir('/proc') if x.isdigit()] | |
| 239 return pids | |
| 240 | |
| 241 def pid_exists(pid): | |
| 242 """Check For the existence of a unix pid.""" | |
| 243 return _psposix.pid_exists(pid) | |
| 244 | |
| 245 def network_io_counters(): | |
| 246 """Return network I/O statistics for every network interface | |
| 247 installed on the system as a dict of raw tuples. | |
| 248 """ | |
| 249 f = open("/proc/net/dev", "r") | |
| 250 try: | |
| 251 lines = f.readlines() | |
| 252 finally: | |
| 253 f.close() | |
| 254 | |
| 255 retdict = {} | |
| 256 for line in lines[2:]: | |
| 257 fields = line.split() | |
| 258 name = fields[0][:-1] | |
| 259 bytes_recv = int(fields[1]) | |
| 260 packets_recv = int(fields[2]) | |
| 261 bytes_sent = int(fields[9]) | |
| 262 packets_sent = int(fields[10]) | |
| 263 retdict[name] = (bytes_sent, bytes_recv, packets_sent, packets_recv) | |
| 264 return retdict | |
| 265 | |
| 266 def disk_io_counters(): | |
| 267 """Return disk I/O statistics for every disk installed on the | |
| 268 system as a dict of raw tuples. | |
| 269 """ | |
| 270 # man iostat states that sectors are equivalent with blocks and | |
| 271 # have a size of 512 bytes since 2.4 kernels. This value is | |
| 272 # needed to calculate the amount of disk I/O in bytes. | |
| 273 SECTOR_SIZE = 512 | |
| 274 | |
| 275 # determine partitions we want to look for | |
| 276 partitions = [] | |
| 277 f = open("/proc/partitions", "r") | |
| 278 try: | |
| 279 lines = f.readlines()[2:] | |
| 280 finally: | |
| 281 f.close() | |
| 282 for line in lines: | |
| 283 _, _, _, name = line.split() | |
| 284 if name[-1].isdigit(): | |
| 285 partitions.append(name) | |
| 286 # | |
| 287 retdict = {} | |
| 288 f = open("/proc/diskstats", "r") | |
| 289 try: | |
| 290 lines = f.readlines() | |
| 291 finally: | |
| 292 f.close() | |
| 293 for line in lines: | |
| 294 _, _, name, reads, _, rbytes, rtime, writes, _, wbytes, wtime = \ | |
| 295 line.split()[:11] | |
| 296 if name in partitions: | |
| 297 rbytes = int(rbytes) * SECTOR_SIZE | |
| 298 wbytes = int(wbytes) * SECTOR_SIZE | |
| 299 reads = int(reads) | |
| 300 writes = int(writes) | |
| 301 # TODO: times are expressed in milliseconds while OSX/BSD has | |
| 302 # these expressed in nanoseconds; figure this out. | |
| 303 rtime = int(rtime) | |
| 304 wtime = int(wtime) | |
| 305 retdict[name] = (reads, writes, rbytes, wbytes, rtime, wtime) | |
| 306 return retdict | |
| 307 | |
| 308 | |
| 309 # taken from /fs/proc/array.c | |
| 310 _status_map = {"R" : STATUS_RUNNING, | |
| 311 "S" : STATUS_SLEEPING, | |
| 312 "D" : STATUS_DISK_SLEEP, | |
| 313 "T" : STATUS_STOPPED, | |
| 314 "t" : STATUS_TRACING_STOP, | |
| 315 "Z" : STATUS_ZOMBIE, | |
| 316 "X" : STATUS_DEAD, | |
| 317 "x" : STATUS_DEAD, | |
| 318 "K" : STATUS_WAKE_KILL, | |
| 319 "W" : STATUS_WAKING} | |
| 320 | |
| 321 # --- decorators | |
| 322 | |
| 323 def wrap_exceptions(callable): | |
| 324 """Call callable into a try/except clause and translate ENOENT, | |
| 325 EACCES and EPERM in NoSuchProcess or AccessDenied exceptions. | |
| 326 """ | |
| 327 def wrapper(self, *args, **kwargs): | |
| 328 try: | |
| 329 return callable(self, *args, **kwargs) | |
| 330 except (OSError, IOError), err: | |
| 331 # ENOENT (no such file or directory) gets raised on open(). | |
| 332 # ESRCH (no such process) can get raised on read() if | |
| 333 # process is gone in meantime. | |
| 334 if err.errno in (errno.ENOENT, errno.ESRCH): | |
| 335 raise NoSuchProcess(self.pid, self._process_name) | |
| 336 if err.errno in (errno.EPERM, errno.EACCES): | |
| 337 raise AccessDenied(self.pid, self._process_name) | |
| 338 raise | |
| 339 return wrapper | |
| 340 | |
| 341 | |
| 342 class Process(object): | |
| 343 """Linux process implementation.""" | |
| 344 | |
| 345 __slots__ = ["pid", "_process_name"] | |
| 346 | |
| 347 def __init__(self, pid): | |
| 348 self.pid = pid | |
| 349 self._process_name = None | |
| 350 | |
| 351 @wrap_exceptions | |
| 352 def get_process_name(self): | |
| 353 f = open("/proc/%s/stat" % self.pid) | |
| 354 try: | |
| 355 name = f.read().split(' ')[1].replace('(', '').replace(')', '') | |
| 356 finally: | |
| 357 f.close() | |
| 358 # XXX - gets changed later and probably needs refactoring | |
| 359 return name | |
| 360 | |
| 361 def get_process_exe(self): | |
| 362 if self.pid in (0, 2): | |
| 363 raise AccessDenied(self.pid, self._process_name) | |
| 364 try: | |
| 365 exe = os.readlink("/proc/%s/exe" % self.pid) | |
| 366 except (OSError, IOError), err: | |
| 367 if err.errno == errno.ENOENT: | |
| 368 # no such file error; might be raised also if the | |
| 369 # path actually exists for system processes with | |
| 370 # low pids (about 0-20) | |
| 371 if os.path.lexists("/proc/%s/exe" % self.pid): | |
| 372 return "" | |
| 373 else: | |
| 374 # ok, it is a process which has gone away | |
| 375 raise NoSuchProcess(self.pid, self._process_name) | |
| 376 if err.errno in (errno.EPERM, errno.EACCES): | |
| 377 raise AccessDenied(self.pid, self._process_name) | |
| 378 raise | |
| 379 | |
| 380 # readlink() might return paths containing null bytes causing | |
| 381 # problems when used with other fs-related functions (os.*, | |
| 382 # open(), ...) | |
| 383 exe = exe.replace('\x00', '') | |
| 384 # It seems symlinks can point to a deleted/invalid location | |
| 385 # (this usually happens with "pulseaudio" process). | |
| 386 # However, if we had permissions to execute readlink() it's | |
| 387 # likely that we'll be able to figure out exe from argv[0] | |
| 388 # later on. | |
| 389 if exe.endswith(" (deleted)") and not os.path.isfile(exe): | |
| 390 return "" | |
| 391 return exe | |
| 392 | |
| 393 @wrap_exceptions | |
| 394 def get_process_cmdline(self): | |
| 395 f = open("/proc/%s/cmdline" % self.pid) | |
| 396 try: | |
| 397 # return the args as a list | |
| 398 return [x for x in f.read().split('\x00') if x] | |
| 399 finally: | |
| 400 f.close() | |
| 401 | |
| 402 @wrap_exceptions | |
| 403 def get_process_terminal(self): | |
| 404 f = open("/proc/%s/stat" % self.pid) | |
| 405 try: | |
| 406 tty_nr = int(f.read().split(' ')[6]) | |
| 407 finally: | |
| 408 f.close() | |
| 409 try: | |
| 410 return _TERMINAL_MAP[tty_nr] | |
| 411 except KeyError: | |
| 412 return None | |
| 413 | |
| 414 @wrap_exceptions | |
| 415 def get_process_io_counters(self): | |
| 416 f = open("/proc/%s/io" % self.pid) | |
| 417 try: | |
| 418 for line in f: | |
| 419 if line.startswith("rchar"): | |
| 420 read_count = int(line.split()[1]) | |
| 421 elif line.startswith("wchar"): | |
| 422 write_count = int(line.split()[1]) | |
| 423 elif line.startswith("read_bytes"): | |
| 424 read_bytes = int(line.split()[1]) | |
| 425 elif line.startswith("write_bytes"): | |
| 426 write_bytes = int(line.split()[1]) | |
| 427 return ntuple_io(read_count, write_count, read_bytes, write_bytes) | |
| 428 finally: | |
| 429 f.close() | |
| 430 | |
| 431 @wrap_exceptions | |
| 432 def get_cpu_times(self): | |
| 433 f = open("/proc/%s/stat" % self.pid) | |
| 434 try: | |
| 435 st = f.read().strip() | |
| 436 finally: | |
| 437 f.close() | |
| 438 # ignore the first two values ("pid (exe)") | |
| 439 st = st[st.find(')') + 2:] | |
| 440 values = st.split(' ') | |
| 441 utime = float(values[11]) / _CLOCK_TICKS | |
| 442 stime = float(values[12]) / _CLOCK_TICKS | |
| 443 return ntuple_cputimes(utime, stime) | |
| 444 | |
| 445 @wrap_exceptions | |
| 446 def process_wait(self, timeout=None): | |
| 447 try: | |
| 448 return _psposix.wait_pid(self.pid, timeout) | |
| 449 except TimeoutExpired: | |
| 450 raise TimeoutExpired(self.pid, self._process_name) | |
| 451 | |
| 452 @wrap_exceptions | |
| 453 def get_process_create_time(self): | |
| 454 f = open("/proc/%s/stat" % self.pid) | |
| 455 try: | |
| 456 st = f.read().strip() | |
| 457 finally: | |
| 458 f.close() | |
| 459 # ignore the first two values ("pid (exe)") | |
| 460 st = st[st.find(')') + 2:] | |
| 461 values = st.split(' ') | |
| 462 # According to documentation, starttime is in field 21 and the | |
| 463 # unit is jiffies (clock ticks). | |
| 464 # We first divide it for clock ticks and then add uptime returning | |
| 465 # seconds since the epoch, in UTC. | |
| 466 starttime = (float(values[19]) / _CLOCK_TICKS) + BOOT_TIME | |
| 467 return starttime | |
| 468 | |
| 469 @wrap_exceptions | |
| 470 def get_memory_info(self): | |
| 471 f = open("/proc/%s/status" % self.pid) | |
| 472 try: | |
| 473 virtual_size = 0 | |
| 474 resident_size = 0 | |
| 475 _flag = False | |
| 476 for line in f: | |
| 477 if (not _flag) and line.startswith("VmSize:"): | |
| 478 virtual_size = int(line.split()[1]) * 1024 | |
| 479 _flag = True | |
| 480 elif line.startswith("VmRSS"): | |
| 481 resident_size = int(line.split()[1]) * 1024 | |
| 482 break | |
| 483 return ntuple_meminfo(resident_size, virtual_size) | |
| 484 finally: | |
| 485 f.close() | |
| 486 | |
| 487 @wrap_exceptions | |
| 488 def get_process_cwd(self): | |
| 489 # readlink() might return paths containing null bytes causing | |
| 490 # problems when used with other fs-related functions (os.*, | |
| 491 # open(), ...) | |
| 492 path = os.readlink("/proc/%s/cwd" % self.pid) | |
| 493 return path.replace('\x00', '') | |
| 494 | |
| 495 @wrap_exceptions | |
| 496 def get_process_num_threads(self): | |
| 497 f = open("/proc/%s/status" % self.pid) | |
| 498 try: | |
| 499 for line in f: | |
| 500 if line.startswith("Threads:"): | |
| 501 return int(line.split()[1]) | |
| 502 raise RuntimeError("line not found") | |
| 503 finally: | |
| 504 f.close() | |
| 505 | |
| 506 @wrap_exceptions | |
| 507 def get_process_threads(self): | |
| 508 thread_ids = os.listdir("/proc/%s/task" % self.pid) | |
| 509 thread_ids.sort() | |
| 510 retlist = [] | |
| 511 for thread_id in thread_ids: | |
| 512 try: | |
| 513 f = open("/proc/%s/task/%s/stat" % (self.pid, thread_id)) | |
| 514 except (OSError, IOError), err: | |
| 515 if err.errno == errno.ENOENT: | |
| 516 # no such file or directory; it means thread | |
| 517 # disappeared on us | |
| 518 continue | |
| 519 raise | |
| 520 try: | |
| 521 st = f.read().strip() | |
| 522 finally: | |
| 523 f.close() | |
| 524 # ignore the first two values ("pid (exe)") | |
| 525 st = st[st.find(')') + 2:] | |
| 526 values = st.split(' ') | |
| 527 utime = float(values[11]) / _CLOCK_TICKS | |
| 528 stime = float(values[12]) / _CLOCK_TICKS | |
| 529 ntuple = ntuple_thread(int(thread_id), utime, stime) | |
| 530 retlist.append(ntuple) | |
| 531 return retlist | |
| 532 | |
| 533 @wrap_exceptions | |
| 534 def get_process_nice(self): | |
| 535 #f = open('/proc/%s/stat' % self.pid, 'r') | |
| 536 #try: | |
| 537 # data = f.read() | |
| 538 # return int(data.split()[18]) | |
| 539 #finally: | |
| 540 # f.close() | |
| 541 | |
| 542 # Use C implementation | |
| 543 return _psutil_posix.getpriority(self.pid) | |
| 544 | |
| 545 @wrap_exceptions | |
| 546 def set_process_nice(self, value): | |
| 547 return _psutil_posix.setpriority(self.pid, value) | |
| 548 | |
| 549 # only starting from kernel 2.6.13 | |
| 550 if hasattr(_psutil_linux, "ioprio_get"): | |
| 551 | |
| 552 @wrap_exceptions | |
| 553 def get_process_ionice(self): | |
| 554 ioclass, value = _psutil_linux.ioprio_get(self.pid) | |
| 555 return ntuple_ionice(ioclass, value) | |
| 556 | |
| 557 @wrap_exceptions | |
| 558 def set_process_ionice(self, ioclass, value): | |
| 559 if ioclass in (IOPRIO_CLASS_NONE, None): | |
| 560 if value: | |
| 561 raise ValueError("can't specify value with IOPRIO_CLASS_NONE
") | |
| 562 ioclass = IOPRIO_CLASS_NONE | |
| 563 value = 0 | |
| 564 if ioclass in (IOPRIO_CLASS_RT, IOPRIO_CLASS_BE): | |
| 565 if value is None: | |
| 566 value = 4 | |
| 567 elif ioclass == IOPRIO_CLASS_IDLE: | |
| 568 if value: | |
| 569 raise ValueError("can't specify value with IOPRIO_CLASS_IDLE
") | |
| 570 value = 0 | |
| 571 else: | |
| 572 value = 0 | |
| 573 if not 0 <= value <= 8: | |
| 574 raise ValueError("value argument range expected is between 0 and
8") | |
| 575 return _psutil_linux.ioprio_set(self.pid, ioclass, value) | |
| 576 | |
| 577 @wrap_exceptions | |
| 578 def get_process_status(self): | |
| 579 f = open("/proc/%s/status" % self.pid) | |
| 580 try: | |
| 581 for line in f: | |
| 582 if line.startswith("State:"): | |
| 583 letter = line.split()[1] | |
| 584 if letter in _status_map: | |
| 585 return _status_map[letter] | |
| 586 return constant(-1, '?') | |
| 587 finally: | |
| 588 f.close() | |
| 589 | |
| 590 @wrap_exceptions | |
| 591 def get_open_files(self): | |
| 592 retlist = [] | |
| 593 files = os.listdir("/proc/%s/fd" % self.pid) | |
| 594 for fd in files: | |
| 595 file = "/proc/%s/fd/%s" % (self.pid, fd) | |
| 596 if os.path.islink(file): | |
| 597 file = os.readlink(file) | |
| 598 if file.startswith("socket:["): | |
| 599 continue | |
| 600 if file.startswith("pipe:["): | |
| 601 continue | |
| 602 if file == "[]": | |
| 603 continue | |
| 604 if os.path.isfile(file) and not file in retlist: | |
| 605 ntuple = ntuple_openfile(file, int(fd)) | |
| 606 retlist.append(ntuple) | |
| 607 return retlist | |
| 608 | |
| 609 @wrap_exceptions | |
| 610 def get_connections(self): | |
| 611 inodes = {} | |
| 612 # os.listdir() is gonna raise a lot of access denied | |
| 613 # exceptions in case of unprivileged user; that's fine: | |
| 614 # lsof does the same so it's unlikely that we can to better. | |
| 615 for fd in os.listdir("/proc/%s/fd" % self.pid): | |
| 616 try: | |
| 617 inode = os.readlink("/proc/%s/fd/%s" % (self.pid, fd)) | |
| 618 except OSError: | |
| 619 continue | |
| 620 if inode.startswith('socket:['): | |
| 621 # the process is using a socket | |
| 622 inode = inode[8:][:-1] | |
| 623 inodes[inode] = fd | |
| 624 | |
| 625 if not inodes: | |
| 626 # no connections for this process | |
| 627 return [] | |
| 628 | |
| 629 def process(file, family, _type): | |
| 630 retlist = [] | |
| 631 f = open(file) | |
| 632 try: | |
| 633 f.readline() # skip the first line | |
| 634 for line in f: | |
| 635 _, laddr, raddr, status, _, _, _, _, _, inode = \ | |
| 636 line.split()[:10] | |
| 637 if inode in inodes: | |
| 638 laddr = self._decode_address(laddr, family) | |
| 639 raddr = self._decode_address(raddr, family) | |
| 640 if _type == socket.SOCK_STREAM: | |
| 641 status = _TCP_STATES_TABLE[status] | |
| 642 else: | |
| 643 status = "" | |
| 644 fd = int(inodes[inode]) | |
| 645 conn = ntuple_connection(fd, family, _type, laddr, | |
| 646 raddr, status) | |
| 647 retlist.append(conn) | |
| 648 return retlist | |
| 649 finally: | |
| 650 f.close() | |
| 651 | |
| 652 tcp4 = process("/proc/net/tcp", socket.AF_INET, socket.SOCK_STREAM) | |
| 653 udp4 = process("/proc/net/udp", socket.AF_INET, socket.SOCK_DGRAM) | |
| 654 try: | |
| 655 tcp6 = process("/proc/net/tcp6", socket.AF_INET6, socket.SOCK_STREAM
) | |
| 656 udp6 = process("/proc/net/udp6", socket.AF_INET6, socket.SOCK_DGRAM) | |
| 657 except IOError, err: | |
| 658 if err.errno == errno.ENOENT: | |
| 659 # IPv6 is not supported on this platform | |
| 660 tcp6 = udp6 = [] | |
| 661 else: | |
| 662 raise | |
| 663 return tcp4 + tcp6 + udp4 + udp6 | |
| 664 | |
| 665 # --- lsof implementation | |
| 666 # | |
| 667 # def get_connections(self): | |
| 668 # lsof = _psposix.LsofParser(self.pid, self._process_name) | |
| 669 # return lsof.get_process_connections() | |
| 670 | |
| 671 @wrap_exceptions | |
| 672 def get_process_ppid(self): | |
| 673 f = open("/proc/%s/status" % self.pid) | |
| 674 try: | |
| 675 for line in f: | |
| 676 if line.startswith("PPid:"): | |
| 677 # PPid: nnnn | |
| 678 return int(line.split()[1]) | |
| 679 raise RuntimeError("line not found") | |
| 680 finally: | |
| 681 f.close() | |
| 682 | |
| 683 @wrap_exceptions | |
| 684 def get_process_uids(self): | |
| 685 f = open("/proc/%s/status" % self.pid) | |
| 686 try: | |
| 687 for line in f: | |
| 688 if line.startswith('Uid:'): | |
| 689 _, real, effective, saved, fs = line.split() | |
| 690 return ntuple_uids(int(real), int(effective), int(saved)) | |
| 691 raise RuntimeError("line not found") | |
| 692 finally: | |
| 693 f.close() | |
| 694 | |
| 695 @wrap_exceptions | |
| 696 def get_process_gids(self): | |
| 697 f = open("/proc/%s/status" % self.pid) | |
| 698 try: | |
| 699 for line in f: | |
| 700 if line.startswith('Gid:'): | |
| 701 _, real, effective, saved, fs = line.split() | |
| 702 return ntuple_gids(int(real), int(effective), int(saved)) | |
| 703 raise RuntimeError("line not found") | |
| 704 finally: | |
| 705 f.close() | |
| 706 | |
| 707 @staticmethod | |
| 708 def _decode_address(addr, family): | |
| 709 """Accept an "ip:port" address as displayed in /proc/net/* | |
| 710 and convert it into a human readable form, like: | |
| 711 | |
| 712 "0500000A:0016" -> ("10.0.0.5", 22) | |
| 713 "0000000000000000FFFF00000100007F:9E49" -> ("::ffff:127.0.0.1", 40521) | |
| 714 | |
| 715 The IP address portion is a little or big endian four-byte | |
| 716 hexadecimal number; that is, the least significant byte is listed | |
| 717 first, so we need to reverse the order of the bytes to convert it | |
| 718 to an IP address. | |
| 719 The port is represented as a two-byte hexadecimal number. | |
| 720 | |
| 721 Reference: | |
| 722 http://linuxdevcenter.com/pub/a/linux/2000/11/16/LinuxAdmin.html | |
| 723 """ | |
| 724 ip, port = addr.split(':') | |
| 725 port = int(port, 16) | |
| 726 if sys.version_info >= (3,): | |
| 727 ip = ip.encode('ascii') | |
| 728 # this usually refers to a local socket in listen mode with | |
| 729 # no end-points connected | |
| 730 if not port: | |
| 731 return () | |
| 732 if family == socket.AF_INET: | |
| 733 # see: http://code.google.com/p/psutil/issues/detail?id=201 | |
| 734 if sys.byteorder == 'little': | |
| 735 ip = socket.inet_ntop(family, base64.b16decode(ip)[::-1]) | |
| 736 else: | |
| 737 ip = socket.inet_ntop(family, base64.b16decode(ip)) | |
| 738 else: # IPv6 | |
| 739 # old version - let's keep it, just in case... | |
| 740 #ip = ip.decode('hex') | |
| 741 #return socket.inet_ntop(socket.AF_INET6, | |
| 742 # ''.join(ip[i:i+4][::-1] for i in xrange(0, 16, 4))) | |
| 743 ip = base64.b16decode(ip) | |
| 744 # see: http://code.google.com/p/psutil/issues/detail?id=201 | |
| 745 if sys.byteorder == 'little': | |
| 746 ip = socket.inet_ntop(socket.AF_INET6, | |
| 747 struct.pack('>4I', *struct.unpack('<4I', ip))) | |
| 748 else: | |
| 749 ip = socket.inet_ntop(socket.AF_INET6, | |
| 750 struct.pack('<4I', *struct.unpack('<4I', ip))) | |
| 751 return (ip, port) | |
| OLD | NEW |