#!/usr/bin/env python

### This program is free software; you can redistribute it and/or modify
### it under the terms of the GNU Library General Public License as published by
### the Free Software Foundation; version 2 only
###
### This program is distributed in the hope that it will be useful,
### but WITHOUT ANY WARRANTY; without even the implied warranty of
### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
### GNU Library General Public License for more details.
###
### You should have received a copy of the GNU Library General Public License
### along with this program; if not, write to the Free Software
### Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
### Copyright 2004 Dag Wieers <dag@wieers.com>

from __future__ import generators

import sys

if sys.version_info < (2, 2):
	sys.exit('error: Python 2.2 or later required, try dstat15 instead')

import os, re, getopt, time, types
import ConfigParser, urlparse, signal, resource
import curses, termios

VERSION = '0.5.10'

enable = ('yes', 'on', 'true', '1')
disable = ('no', 'off', 'false', '0')

class Options:
	def __init__(self, args):
		self.args = args
		self.count = -1
		self.cpulist = None
		self.delay = 1
		self.disklist = None
		self.full = False
		self.integer = False
		self.intlist = None
		self.netlist = None
		self.nolimit = False
		self.color = True
		self.update = True
		self.header = True
		self.output = False

		try:
			opts, args = getopt.getopt (args, 'acdfghilmnopstuvyC:D:I:M:N:V',
				['all', 'cpu', 'disk', 'help', 'int', 'load', 'mem', 'net', 'page', 'proc',
				'swap', 'sys', 'tcp', 'time', 'udp', 'user', 'version', 'vmstat',
				'full', 'integer', 'mods', 'modules', 'nocolor', 'noheaders', 'noupdate', 'output='])
		except getopt.error, exc:
			print 'dstat: %s, try dstat -h for a list of all the options' % str(exc)
			exit(1)

		self.modlist = []

		for opt, arg in opts:
			if opt in ['-c', '--cpu']:
				self.modlist.append('cpu')
			elif opt in ['-C']:
				self.cpulist = arg.split(',')
			elif opt in ['-d', '--disk']:
				self.modlist.append('disk')
			elif opt in ['-D']:
				self.disklist = arg.split(',')
			elif opt in ['-i', '--int']:
				self.modlist.append('int')
			elif opt in ['-g', '--page']:
				self.modlist.append('page')
			elif opt in ['-I']:
				self.intlist = arg.split(',')
			elif opt in ['-m', '--mem']:
				self.modlist.append('mem')
			elif opt in ['-M', '--mods', '--modules']:
				self.modlist = self.modlist + arg.split(',')
			elif opt in ['-l', '--load']:
				self.modlist.append('load')
			elif opt in ['-n', '--net']:
				self.modlist.append('net')
			elif opt in ['-N']:
				self.netlist = arg.split(',')
			elif opt in ['-p', '--proc']:
				self.modlist.append('proc')
			elif opt in ['-s', '--swap']:
				self.modlist.append('swap')
			elif opt in ['--tcp']:
				self.modlist.append('tcp')
			elif opt in ['-t', '--time']:
				self.modlist.append('time')
			elif opt in ['-u', '--user']:
				self.modlist.append('user')
			elif opt in ['--udp']:
				self.modlist.append('udp')
			elif opt in ['-y', '--sys']:
				self.modlist.append('sys')

			elif opt in ['-a', '--all']:
				self.modlist = self.modlist + [ 'cpu', 'disk', 'net', 'page', 'sys', 'load' ]
			elif opt in ['-v', '--vmstat']:
				self.modlist = self.modlist + [ 'proc', 'mem', 'page', 'disk', 'sys', 'cpu' ]
				self.disklist = ('total',)

			elif opt in ['-f', '--full']:
				self.full = True
			elif opt in ['--integer']:
				self.integer = True
			elif opt in ['--nocolor']:
				self.color = False
				self.update = False
			elif opt in ['--noheaders']:
				self.header = False
			elif opt in ['--noupdate']:
				self.update = False
			elif opt in ['-o', '--output']:
				self.output = arg
			elif opt in ['-h', '--help']:
				self.usage()
				self.help()
				exit(0)
			elif opt in ['-V', '--version']:
				self.version()
				exit(0)

		if not self.modlist:
			self.modlist = [ 'cpu', 'disk', 'net', 'page', 'sys' ]

		try:
			if len(args) > 0: self.delay = int(args[0])
			if len(args) > 1: self.count = int(args[1])
		except:
			print 'dstat: incorrect argument, try dstat -h for the correct syntax'
			exit(1)

		if self.delay == 0:
			print 'dstat: delay must be an integer, greater than zero'
			exit(1)

	def version(self):
		print 'Dstat %s' % VERSION
		print 'Written by Dag Wieers <dag@wieers.com>'
		print 'Homepage at http://dag.wieers.com/home-made/dstat/'
		print
		print 'Platform %s/%s' % (os.name, sys.platform)
		print 'Kernel %s' % os.uname()[2]
		print 'Python %s' % sys.version
		print
		print 'Processors: %d' % getcpunr()
		print 'Pagesize: %d' % resource.getpagesize()
		print 'Clock ticks per secs: %d' % os.sysconf('SC_CLK_TCK')

	def usage(self):
		print 'usage: dstat [-afv] [-cdgilmnpsty] [-D..] [-I..] [-N..] [delay [count]]'

	def help(self):
		print '''Versatile tool for generating system resource statistics

Dstat options:
  -c, --cpu                enable cpu stats
     -C 0,3,                  include cpu0, cpu3 and total
  -d, --disk               enable disk stats
     -D total,hda             include hda and total
  -g, --page               enable page stats
  -i, --int                enable interrupt stats
     -I 5,eth2                include int5 and interrupt used by eth2
  -l, --load               enable load stats
  -m, --mem                enable memory stats
  -n, --net                enable network stats
     -N eth1,total            include eth1 and total
  -p, --proc               enable process stats
  -s, --swap               enable swap stats
  -t, --time               enable time counter
  -u, --user               enable user stats
  -y, --sys                enable system stats

  -M stat1,stat2           enable specific stats
     --mods stat1,stat2

    Possible stats are:
      cpu, disk, page, int, load, mem, net, proc,
      swap, sys, tcp, time, udp, user

  -a, --all          equals -cdngyl
  -v, --vmstat       equals -pmgdsc -D total
  -f, --full         expand -D, -I and -N discovery lists

  --integer          show integer values
  --nocolor          disable colors (implies --noupdate)
  --noheaders        disable repetitive headers
  --noupdate         disable intermediate updates when delay > 1
  --output file      write CSV output to file

  delay is the delay in seconds between each update
  count is the number of updates to display before exiting
  The default delay is zero and count is one
'''

class Config:
	def __init__(self):
		self.configfile = op.configfile
		self.cfg = ConfigParser.ConfigParser()

		(s,b,p,q,f,o) = urlparse.urlparse(self.configfile)
		if s in ('http', 'ftp', 'file'):
			configfh = urllib.urlopen(self.configfile)
			try:
				self.cfg.readfp(configfh)
			except ConfigParser.MissingSectionHeaderError, e:
				die(6, 'Error accessing URL: %s' % self.configfile)
		else:
			if os.access(self.configfile, os.R_OK):
				try:
					self.cfg.read(self.configfile)
				except:
					die(7, 'Syntax error reading file: %s' % self.configfile)
			else:
				die(6, 'Error accessing file: %s' % self.configfile)

	def getoption(self, section, option, var):
		"Get an option from a section from configfile"
		try:
			var = self.cfg.get(section, option)
			info(3, 'Setting option %s in section [%s] to: %s' % (option, section, var))
		except ConfigParser.NoSectionError, e:
			info(4, 'Failed to find section [%s] in %s' % (section, op.configfile))
		except ConfigParser.NoOptionError, e:
#			info(4, 'Failed to find option %s in [%s], set to default: %s' % (option, section, var))
			info(4, 'Setting option %s in section [%s] to: %s (default)' % (option, section, var))
		return var

class dstat:
	def init(self):
		### Initialise default variables
		self.val = {}; self.cn1 = {}; self.cn2 = {}
		for i in self.vars: self.val[i] = self.cn1[i] = self.cn2[i] = 0

	def varwidth(self):
		return len(self.vars) * self.width() + len(self.vars) - 1

	def width(self):
		if isinstance(self.name, types.StringType):
			return self.len
		else:
			for name in self.cn2.keys():
				return len(self.cn2[name]) * self.len + len(self.cn2[name]) - 1
			return 0

	def title(self, nr):
		if nr == 1:
			return self.title1()
		else:
			return self.title2()

	def title1(self):
		if isinstance(self.name, types.StringType):
			max = self.varwidth()
			return ansi['darkblue'] + self.name[0:max].center(max).replace(' ', '-') + ansi['default']
		ret = ''
		for i, name in enumerate(self.name):
			max = self.width()
			ret = ret + name[0:max].center(max).replace(' ', '-')
			if i + 1 != len(self.name): ret = ret + '-'
		return ansi['darkblue'] + ret

	def title2(self):
		if isinstance(self.name, types.StringType):
			ret = ''
			for i, nick in enumerate(self.nick):
				ret = ret + nick.center(self.len).replace(' ', '_')
				if i + 1 != len(self.nick): ret = ret + ' '
			return ansi['blue'] + ret
		else:
			ret = ''
			for i, name in enumerate(self.name):
				for j, nick in enumerate(self.nick):
					ret = ret + ansi['blue'] + nick.center(self.len).replace(' ', '_')
					if j + 1 != len(self.nick): ret = ret + ' '
				if i + 1 != len(self.name): ret = ret + ' '
			return ansi['blue'] + ret

	def titlecsv(self, nr):
		if nr == 1:
			return self.titlecsv1()
		else:
			return self.titlecsv2()

	def titlecsv1(self):
		if isinstance(self.name, types.StringType):
			return '"' + self.name + '"' + ',' * (len(self.nick) - 1)
		else:
			ret = ''
			for i, name in enumerate(self.name):
				ret = ret + '"' + name + '"' + ',' * (len(self.nick) - 1)
				if i + 1 != len(self.name): ret = ret + ','
			return ret

	def titlecsv2(self):
		if isinstance(self.name, types.StringType):
			ret = ''
			for i, nick in enumerate(self.nick):
				ret = ret + '"' + nick + '"'
				if i + 1 != len(self.nick): ret = ret + ','
			return ret
		else:
			ret = ''
			for i, name in enumerate(self.name):
				for j, nick in enumerate(self.nick):
					ret = ret + '"' + nick + '"'
					if j + 1 != len(self.nick): ret = ret + ','
				if i + 1 != len(self.name): ret = ret + ','
			return ret

	def check(self):
		if self.discover() and self.width():
			return True
		return False

	def discover(self):
		return True

	def stats(self):
		pass

	def show(self):
		sep = ' '
		for i, name in enumerate(self.vars):
			if self.format in ('%s', '%bs'):
				sys.stdout.write('%s' % conv(self.len, self.val[name], 1024))
			elif self.format == '%ds':
				sys.stdout.write('%s' % conv(self.len, self.val[name], 1000))
			elif self.format == '%ps':
				sys.stdout.write('%s' % conv(self.len, self.val[name], 100))
			elif self.format == '%is':
				sys.stdout.write('%s' % conv(self.len, self.val[name], 10))
			elif self.format == '%f':
				sys.stdout.write('%s' % conv(self.len, self.val[name], 1))
			elif self.format in ('%s %s', '%s:%s', '%s-%s'):
				sys.stdout.write(self.format % convlist(self.len, self.val[name]))
				sep = ansi['default'] + char['colon']
			elif self.format in ('%s %s %s', '%s %s %s %s %s %s'):
				sys.stdout.write(self.format % convlist(self.len, self.val[name], 100))
				sep = ansi['default'] + char['colon']
			else:
				sys.stdout.write(self.format % self.val[name])
			if i + 1 != len(self.vars):
				sys.stdout.write(sep)

	def showend(self, totlist, vislist):
		if self is not vislist[-1]:
			sys.stdout.write(ansi['default'] + char['pipe'])
		elif self is not totlist[-1]:
			sys.stdout.write(ansi['default'] + char['gt'])

	def showcsv(self):
		def printcsv(var):
			if var != round(var):
				outputfile.write('%.2f' % var)
			else:
				outputfile.write('%d' % round(var))

		for i, name in enumerate(self.vars):
			if isinstance(self.val[name], types.ListType) or isinstance(self.val[name], types.TupleType):
				for j, val in enumerate(self.val[name]):
					printcsv(val)
					if j + 1 != len(self.val[name]):
						outputfile.write(',')
			else:
				printcsv(self.val[name])
			if i + 1 != len(self.vars):
				outputfile.write(',')

	def showcsvend(self, totlist, vislist):
		if self is not vislist[-1]:
			outputfile.write(',')
		elif self is not totlist[-1]:
			outputfile.write(',')

class dstat_cpu(dstat):
	def __init__(self):
		self.len = 3
		self.format = '%s %s %s %s %s %s'
		self.nick = ( 'usr', 'sys', 'idl', 'wai', 'hiq', 'siq' )
		self.vars = self.vars()
		self.name = []
		for name in self.vars:
			if name:
				self.name.append('cpu' + name + ' usage')
			else:
				self.name.append('total cpu usage')
		self.init()
		for name in self.vars: self.cn1[name] = (0, 0, 0, 0, 0, 0); self.cn1[''] = (0, 0, 0, 0, 0, 0)
		for name in self.vars: self.cn2[name] = (0, 0, 0, 0, 0, 0); self.cn2[''] = (0, 0, 0, 0, 0, 0)
		for name in self.vars: self.val[name] = [0, 0, 0, 0, 0, 0]; self.val[''] = [0, 0, 0, 0, 0, 0]

	def discover(self, *list):
		ret = []
		if os.path.exists('/proc/stat'):
			for line in open('/proc/stat', 'r').readlines():
				l = line.split()
				if len(l) < 8 or l[0][0:3] != 'cpu': continue
				ret.append(l[0][3:])
			ret.sort()
			for item in list: ret.append(item)
		return ret

	def vars(self):
		ret = []
		if op.cpulist:
			list = op.cpulist
		else:
			if not op.full:
				list = ('', )
			else:
				list = []
				cpu = 0
				while cpu < cpunr:
					list.append(str(cpu))
					cpu = cpu + 1
				if len(list) > 2: list = list[0:2]
		for name in list:
			if name in self.discover(''):
				ret.append(name)
		return ret

	def stats(self):
		for line in open('/proc/stat', 'r').readlines():
			l = line.split()
			for name in self.vars:
				if len(l) < 8 or l[0] != 'cpu' + name: continue
				self.cn2[name] = ( long(l[1]) + long(l[2]), long(l[3]), long(l[4]), long(l[5]), long(l[6]), long(l[7]) )
		for name in self.vars:
			for i in (0, 1, 2, 3, 4, 5):
				self.val[name][i] = 100.0 * (self.cn2[name][i] - self.cn1[name][i]) / (total(self.cn2[name]) - total(self.cn1[name]))
		if step == op.delay:
			self.cn1.update(self.cn2)

class dstat_cpu24(dstat):
	def __init__(self):
		self.len = 3
		self.format = '%s %s %s'
		self.nick = ( 'usr', 'sys', 'idl')
		self.vars = self.vars()
		self.name = []
		for name in self.vars:
			if name:
				self.name.append('cpu' + name)
			else:
				self.name.append('cpu total')
		self.init()
		for name in self.vars: self.cn1[name] = (0, 0, 0); self.cn1[''] = (0, 0, 0)
		for name in self.vars: self.cn2[name] = (0, 0, 0); self.cn2[''] = (0, 0, 0)
		for name in self.vars: self.val[name] = [0, 0, 0]; self.val[''] = [0, 0, 0]

	def discover(self, *list):
		ret = []
		if os.path.exists('/proc/stat'):
			for line in open('/proc/stat', 'r').readlines():
				l = line.split()
				if len(l) != 5 or l[0][0:3] != 'cpu': continue
				ret.append(l[0][3:])
			ret.sort()
			for item in list: ret.append(item)
		return ret

	def vars(self):
		ret = []
		if op.cpulist:
			list = op.cpulist
		else:
			if not op.full:
				list = ('', )
			else:
				list = []
				cpu = 0
				while cpu < cpunr:
					list.append(str(cpu))
					cpu = cpu + 1
				if len(list) > 2: list = list[0:2]
		for name in list:
			if name in self.discover(''):
				ret.append(name)
		return ret

	def stats(self):
		for line in open('/proc/stat', 'r').readlines():
			l = line.split()
			for name in self.vars:
				if len(l) < 5 or l[0] != 'cpu' + name: continue
				self.cn2[name] = ( long(l[1]) + long(l[2]), long(l[3]), long(l[4]) )
		for name in self.vars:
			for i in (0, 1, 2):
				self.val[name][i] = 100.0 * (self.cn2[name][i] - self.cn1[name][i]) / (total(self.cn2[name]) - total(self.cn1[name]))
		if step == op.delay:
			self.cn1.update(self.cn2)

class dstat_disk(dstat):
	def __init__(self):
		self.format = '%s %s'
		self.len = 5
		self.nick = ('read', 'write')
		### Temporary hardcoded for my own project
		self.diskset = {
			'local': ('sda', 'hda', 'hdc'), 
			'lores': ('sdb', 'sdc', 'sdd', 'sde', 'sdf', 'sdg', 'sdh', 'sdi', 'sdj', 'sdk', 'sdv', 'sdw', 'sdx', 'sdy', 'sdz', 'sdaa', 'sdab', 'sdac', 'sdad', 'sdae'),
			'hires': ('sdl', 'sdm', 'sdn', 'sdo', 'sdp', 'sdq', 'sdr', 'sds', 'sdt', 'sdu', 'sdaf', 'sdag', 'sdah', 'sdai', 'sdaj', 'sdak', 'sdal', 'sdam', 'sdan', 'sdao'),
		}
		self.vars = self.vars()
		self.name = []
		for name in self.vars:
			self.name.append('disk/' + name)
		self.init()

		for name in self.vars: self.cn1[name] = (0, 0); self.cn1['total'] = (0, 0)
		for name in self.vars: self.cn2[name] = (0, 0); self.cn2['total'] = (0, 0)
		for name in self.vars: self.val[name] = [0, 0]; self.val['total'] = [0, 0]

	def discover(self, *list):
		ret = []
		if os.path.exists('/proc/diskstats'):
			for line in open('/proc/diskstats', 'r').readlines():
				l = line.split()
				if len(l) < 13 or l[3] == '0': continue
				name = l[2]
				if not re.match('^(ram\d+|loop\d+)$', name):
					ret.append(name)
#			ret.sort()
			for item in list: ret.append(item)
		return ret

	def vars(self):
		ret = []
		if op.disklist:
			list = op.disklist
		else:
			if not op.full:
				list = ('total', )
			else:
				list = self.discover()
				if len(list) > 2: list = list[0:2]
				list.sort()
		for name in list:
			if name in self.discover('total') + self.diskset.keys():
				ret.append(name)
		return ret

	def stats(self):
		for name in self.vars: self.cn2[name] = (0, 0)
		for line in open('/proc/diskstats', 'r').readlines():
			l = line.split()
			if len(l) < 13: continue
			name = l[2]
			if name in self.vars:
				self.cn2[name] = ( self.cn2[name][0] + long(l[5]), self.cn2[name][1] + long(l[9]) )
			for set in self.vars:
				if set in self.diskset.keys() and name in self.diskset[set]:
					self.cn2[set] = ( self.cn2[set][0] + long(l[5]), self.cn2[set][1] + long(l[9]) )
			self.cn2['total'] = ( self.cn2['total'][0] + long(l[5]), self.cn2['total'][1] + long(l[9]) )
		if update:
			for name in self.cn2.keys():
				self.val[name] = ( 
					(self.cn2[name][0] - self.cn1[name][0]) * 512.0 / step,
					(self.cn2[name][1] - self.cn1[name][1]) * 512.0 / step,
				)
		if step == op.delay:
			self.cn1.update(self.cn2)

class dstat_disk24(dstat_disk):
	def discover(self, *list):
		ret = []
		if os.path.exists('/proc/partitions') and not os.path.exists('/proc/diskstats'):
			for line in open('/proc/partitions', 'r').readlines():
				l = line.split()
				if len(l) < 15 or l[0] == 'major' or int(l[1]) % 16 != 0: continue
				name = l[3]
				if not re.match('^(ram\d+|loop\d+|name)$', name):
					ret.append(name)	
#			ret.sort()
			for item in list: ret.append(item)
		return ret

	def vars(self):
		ret = []
		if op.disklist:
			list = op.disklist
		else:
			if not op.full:
				list = ('total', )
			else:
				list = self.discover()
				if len(list) > 2: list = list[0:2]
				list.sort()
		for name in list:
			if name in self.discover('total') + self.diskset.keys():
				ret.append(name)
		return ret

	def stats(self):
		for name in self.vars: self.cn2[name] = (0, 0)
		for line in open('/proc/partitions', 'r').readlines():
			l = line.split()
			if len(l) < 15: continue
			name = l[3]
			if name == 'name': continue
			if name in self.vars:
				self.cn2[name] = ( self.cn2[name][0] + long(l[6]), self.cn2[name][1] + long(l[10]) )
			for set in self.vars:
				if set in self.diskset.keys() and name in self.diskset[set]:
					self.cn2[set] = ( self.cn2[set][0] + long(l[6]), self.cn2[set][1] + long(l[10]) )
			self.cn2['total'] = ( self.cn2['total'][0] + long(l[6]), self.cn2['total'][1] + long(l[10]))
		if update:
			for name in self.cn2.keys():
				self.val[name] = ( 
					(self.cn2[name][0] - self.cn1[name][0]) * 512.0 / step,
					(self.cn2[name][1] - self.cn1[name][1]) * 512.0 / step,
				)
		if step == op.delay:
			self.cn1.update(self.cn2)

### FIXME: Needs rework, does anyone care ?
class dstat_disk24old(dstat_disk24):
	def discover(self, *list):
		ret = []
		if os.path.exists('/proc/stat') and not os.path.exists('/proc/partitions') and not os.path.exists('/proc/diskstats'):
			for line in open('/proc/stat', 'r').readlines():
				l = line.split()
				if len(l) < 15: continue
				ret.append(l[3])
#			ret.sort()
			for item in list: ret.append(item)
		return ret

	def vars(self):
		ret = []
		if op.disklist:
			list = op.disklist
		else:
			list = self.discover()
			if not op.full and len(list) > 2: list = list[0:2]
			list.sort()
		for name in list:
			if name in self.discover('total') + self.diskset.keys():
				ret.append(name)
		return ret

	def stats(self):
		for name in self.vars: self.cn2[name] = (0, 0)
		for line in open('/proc/stat', 'r').readlines():
			l = line.split(':')
			if len(l) < 3: continue
			name = l[0]
			if name == 'disk_io':
				for pair in line.split()[1:]:
					m = re.match('^\((\d+),(\d+)\):\(\d+,\d+,(\d+),\d+,(\d+)\)$', pair)
					if m:
						l = m.groups()
						name = dev(int(l[0]), int(l[1]))
						self.cn2[name] = ( long(l[2]), long(l[3]) )
						for set in self.vars:
							if set in self.diskset.keys() and name in self.diskset[set]:
								self.cn2[set] = ( self.cn2[set][0] + long(l[2]), self.cn2[set][1] + long(l[3]) )
						self.cn2['total'] = ( self.cn2['total'][0] + long(l[2]), self.cn2['total'][1] + long(l[3]) )
		if update:
			for name in self.cn2.keys():
				self.val[name] = (
					(self.cn2[name][0] - self.cn1[name][0]) * 512.0 / step,
					(self.cn2[name][1] - self.cn1[name][1]) * 512.0 / step,
				)
		if step == op.delay:
			self.cn1.update(self.cn2)

class dstat_int(dstat):
	def __init__(self):
		self.len = 5
		self.format = '%ds'
		self.name = 'interrupts'
		self.vars = self.vars()
		self.nick = self.vars
		self.init()

	def discover(self):
		ret = []
		if os.path.exists('/proc/stat'):
			for line in open('/proc/stat', 'r').readlines():
				l = line.split()
				if l[0] != 'intr': continue
				for name, i in enumerate(l[2:]):
					if long(i) > 10: ret.append(str(name))
		return ret

	def vars(self):
		ret = []
		if op.intlist:
			list = op.intlist
		else:
			list = self.discover()
			for name in list:
				if name in ('0', '1', '2', '8', 'NMI', 'LOC', 'MIS', 'CPU0'):
					list.remove(name)
			if not op.full and len(list) > 3: list = list[-3:]
		for name in list:
			if name in self.discover():
				ret.append(name)
		return ret

	def stats(self):
		for line in open('/proc/stat', 'r').readlines():
			l = line.split()
			if l[0] != 'intr': continue
			for name in self.vars:
				self.cn2[name] = long(l[int(name) + 2])
		if update:
			for name in self.vars:
				self.val[name] = (self.cn2[name] - self.cn1[name]) * 1.0 / step
		if step == op.delay:
			self.cn1.update(self.cn2)

class dstat_int24(dstat):
	def __init__(self):
		self.len = 5
		self.format = '%ds'
		self.name = 'interrupts'
		self.vars = self.vars()
		self.nick = self.vars
		self.init()

	def discover(self):
		ret = []
		if os.path.exists('/proc/interrupts'):
			for line in open('/proc/interrupts', 'r').readlines():
				l = line.split()
				if len(l) < cpunr+1: continue
				name = l[0].split(':')[0]
				if long(l[1]) > 10:
					ret.append(name)
		return ret

	def check(self):
		for line in open('/proc/stat', 'r').readlines():
			l = line.split()
			if l[0] != 'intr' or len(l) > 2: continue
			if self.discover():
				return True
		return False

	def vars(self):
		ret = []
		if op.intlist:
			list = op.intlist
		else:
			list = self.discover()
			for name in list:
				if name in ('0', '1', '2', '8', 'NMI', 'LOC', 'MIS', 'CPU0'):
					list.remove(name)
			if not op.full and len(list) > 3: list = list[-3:]
		for name in list:
			if name in self.discover():
				ret.append(name)
		return ret

	def stats(self):
		for line in open('/proc/interrupts', 'r').readlines():
			l = line.split()
			if len(l) < cpunr+1: continue
			name = l[0].split(':')[0]
			if name in self.vars:
				self.cn2[name] = 0
				for i in l[1:1+cpunr]:
					self.cn2[name] = self.cn2[name] + long(i)
#			elif len(l) > 2 + cpunr:
#				for hw in self.vars:
#					for mod in l[2+cpunr:]:
#						self.cn2[mod] = long(l[1])
		if update:
			for name in self.cn2.keys():
				self.val[name] = (self.cn2[name] - self.cn1[name]) * 1.0 / step
		if step == op.delay:
			self.cn1.update(self.cn2)

class dstat_load(dstat):
	def __init__(self):
		self.len = 4
		self.format = '%f'
		self.name = 'load avg'
		self.vars = ('load1', 'load5', 'load15')
		self.nick = ('1m', '5m', '15m')
		self.init()

	def check(self):
		if os.path.exists('/proc/loadavg'):
			return True
		return False

	def stats(self):
		for line in open('/proc/loadavg', 'r').readlines():
			l = line.split()
			if len(l) < 3: continue
			self.val['load1'] = float(l[0])
			self.val['load5'] = float(l[1])
			self.val['load15'] = float(l[2])

class dstat_mem(dstat):
	def __init__(self):
		self.name = 'memory usage'
		self.len = 5
		self.format = '%s'
		self.vars = ('MemUsed', 'Buffers', 'Cached', 'MemFree')
		self.nick = ('used', 'buff', 'cach', 'free')
		self.init()

	def check(self):
		if os.path.exists('/proc/meminfo'):
			return True
		return False

	def stats(self):
		for line in open('/proc/meminfo', 'r').readlines():
			l = line.split()
			if len(l) < 2: continue
			name = l[0].split(':')[0]
			if name in self.vars + ('MemTotal',):
				self.val[name] = long(l[1]) * 1024.0
		self.val['MemUsed'] = self.val['MemTotal'] - self.val['MemFree'] - self.val['Buffers'] - self.val['Cached']

class dstat_net(dstat):
	def __init__(self):
		self.len = 5
		self.format = '%s %s'
		self.vars = self.vars()
		self.name = []
		for name in self.vars:
			self.name.append('net/' + name)
		self.nick = ('recv', 'send')
		self.init()

		for name in self.vars: self.cn1[name] = (0, 0); self.cn1['total'] = (0, 0)
		for name in self.vars: self.cn2[name] = (0, 0); self.cn2['total'] = (0, 0)
		for name in self.vars: self.val[name] = [0, 0]; self.val['total'] = [0, 0]

	def discover(self, *list):
		ret = []
		if os.path.exists('/proc/net/dev'):
			for line in open('/proc/net/dev', 'r').readlines():
				l = line.split()
				if len(l) < 2: continue
				name = l[0].split(':')[0]
				if l[1] == '0': continue
				if not re.match('^(Inter-\||face|lo)$', name):
					ret.append(name)
			ret.sort()
			for item in list: ret.append(item)
		return ret

	def vars(self):
		ret = []
		if op.netlist:
			list = op.netlist
		else:
			if not op.full:
				list = ('total', )
			else:
				list = self.discover()
				if len(list) > 2: list = list[0:2]
				list.sort()
		for name in list:
			if name in self.discover('total'):
				ret.append(name)
		return ret

	def stats(self):
		self.cn2['total'] = [0, 0]
		for line in open('/proc/net/dev', 'r').readlines():
			l = line.split()
			if len(l) < 1: continue
			l2 = l[0].split(':')
			if len(l2) < 1: continue
			name = l2[0]
			if len(l2) > 1 and l2[1].strip():
				l = l2[1:] + l[1:]
			else:
				l = l[1:]
			if len(l) < 9: continue
			if name in (self.vars) :
				self.cn2[name] = ( long(l[0]), long(l[8]) )
			if not re.match('^(Inter-\||face|lo)$', name):
				self.cn2['total'] = ( self.cn2['total'][0] + long(l[0]), self.cn2['total'][1] + long(l[8]))
		if update:
			for name in self.cn2.keys():
				self.val[name] = ( 
					(self.cn2[name][0] - self.cn1[name][0]) * 1.0 / step,
					(self.cn2[name][1] - self.cn1[name][1]) * 1.0 / step,
				 )
		if step == op.delay:
			self.cn1.update(self.cn2)

class dstat_page(dstat):
	def __init__(self):
		self.name = 'paging'
		self.format = '%s'
		self.len = 5
		self.vars = ('pswpin', 'pswpout')
		self.nick = ('in', 'out')
		self.init()

	def check(self):
		if os.path.exists('/proc/vmstat'):
			return True
		return False

	def stats(self):
		for line in open('/proc/vmstat', 'r').readlines():
			l = line.split()
			if len(l) < 2: continue
			name = l[0]
			if name in self.vars:
				self.cn2[name] = long(l[1])
		if update:
			for name in self.vars:
				self.val[name] = (self.cn2[name] - self.cn1[name]) * pagesize * 1.0 / step
		if step == op.delay:
			self.cn1.update(self.cn2)

class dstat_page24(dstat_page):
	def check(self):
		if os.path.exists('/proc/stat') and not os.path.exists('/proc/vmstat'):
			return True
		return False

	def stats(self):
		for line in open('/proc/stat', 'r').readlines():
			l = line.split()
			if len(l) < 3: continue
			name = l[0]
			if name == 'swap':
				self.cn2['pswpin'] = long(l[1])
				self.cn2['pswpout'] = long(l[2])
		if update:
			for name in self.vars:
				self.val[name] = (self.cn2[name] - self.cn1[name]) * pagesize * 1.0 / step
		if step == op.delay:
			self.cn1.update(self.cn2)

class dstat_proc(dstat):
	def __init__(self):
		self.name = 'procs'
		self.format = '%is'
		self.len = 3
		self.vars = ('procs_running', 'procs_blocked', 'processes')
#		self.vars = ('procs_running', 'procs_blocked')
		self.nick = ('run', 'blk', 'new')
#		self.nick = ('ru', 'bl')
		self.init()

	def check(self):
		if os.path.exists('/proc/stat'):
			return True
		return False

	def stats(self):
		for line in open('/proc/stat', 'r').readlines():
			l = line.split()
			if len(l) < 2: continue
			name = l[0]
			if name == 'processes':
				self.val['processes'] = 0
				self.cn2[name] = long(l[1])
			elif name == 'procs_running':
				self.cn2[name] = self.cn2[name] + long(l[1]) - 1
			elif name == 'procs_blocked':
				self.cn2[name] = self.cn2[name] + long(l[1])
		if update:
			self.val['processes'] = (self.cn2['processes'] - self.cn1['processes']) * 1.0 / step
			for name in ('procs_running', 'procs_blocked'):
				self.val[name] = self.cn2[name] * 1.0 / step
		if step == op.delay:
			self.cn1.update(self.cn2)
			for name in ('procs_running', 'procs_blocked'):
				self.cn2[name] = 0

class dstat_swap(dstat):
	def __init__(self):
		self.name = 'swap'
		self.len = 5
		self.format = '%s'
		self.vars = ('SwapUsed', 'SwapFree')
		self.nick = ('used', 'free')
		self.init()

	def check(self):
		if os.path.exists('/proc/meminfo'):
			return True
		return False

	def stats(self):
		for line in open('/proc/meminfo', 'r').readlines():
			l = line.split()
			if len(l) < 2: continue
			name = l[0].split(':')[0]
			if name in self.vars + ('SwapTotal',):
				self.val[name] = long(l[1]) * 1024.0
		self.val['SwapUsed'] = self.val['SwapTotal'] - self.val['SwapFree']

class dstat_sys(dstat):
	def __init__(self):
		self.name = 'system'
		self.format = '%ds'
		self.len = 5
		self.vars = ('intr', 'ctxt')
		self.nick = ('int', 'csw')
		self.init()

	def check(self):
		if os.path.exists('/proc/stat'):
			return True
		return False

	def stats(self):
		for line in open('/proc/stat', 'r').readlines():
			l = line.split()
			if len(l) < 2: continue
			name = l[0]
			if name in self.vars:
				self.cn2[name] = long(l[1])
		if update:
			for name in self.vars:
				self.val[name] = (self.cn2[name] - self.cn1[name]) * 1.0 / step
		if step == op.delay:
			self.cn1.update(self.cn2)

class dstat_tcp(dstat):
	def __init__(self):
		self.name = 'tcp'
		self.format = '%is'
		self.len = 3
		self.vars = ('listen', 'established', 'syn_sent', 'time_wait')
		self.nick = ('lis', 'act', 'syn', 'tim')
		self.init()

	def check(self):
		if os.path.exists('/proc/net/tcp'):
			return True
		return False

	def stats(self):
		self.val['listen'] = self.val['established'] = self.val['syn_sent'] = self.val['time_wait'] = 0
		for line in open('/proc/net/tcp', 'r').readlines():
			l = line.split()
			if len(l) < 12: continue
			if l[3] == '0A': self.val['listen'] = self.val['listen'] + 1
			elif l[3] == '01': self.val['established'] = self.val['established'] + 1
			elif l[3] == '02': self.val['syn_sent'] = self.val['syn_sent'] + 1
			elif l[3] == '06': self.val['time_wait'] = self.val['time_wait'] + 1

class dstat_time(dstat):
	def __init__(self):
		self.name = 'time'
		self.format = '%10d'
		self.len = 10
		### Nice for debugging timer
#		self.format = '%13.3f'
#		self.len = 14
		self.nick = ('epoch',)
		self.vars = ('epoch',)
		self.init()

	def stats(self):
		self.val['epoch'] = time.time()

	def show(self):
		sys.stdout.write(self.format % self.val['epoch'])

class dstat_udp(dstat):
	def __init__(self):
		self.name = 'udp'
		self.format = '%is'
		self.len = 3
		self.nick = ('con', )
		self.vars = ('connections', )
		self.init()

	def check(self):
		if os.path.exists('/proc/net/udp'):
			return True
		return False

	def stats(self):
		self.val['connections'] = 0
		for line in open('/proc/net/udp', 'r').readlines():
			l = line.split()
			if l[3] == '07': self.val['connections'] = self.val['connections'] + 1

class dstat_user(dstat):
	def __init__(self):
		self.name = 'users'
		self.format = '%is'
		self.len = 3
		self.nick = ('usr', 'adm' )
		self.vars = ('users', 'root')
		self.init()

	def check(self):
		try:
			import utmp
			return True
		except:
			print 'The user stat needs the python-utmp module.'
			return False

	def stats(self):
		import utmp
		u = utmp.UtmpRecord()
		self.val['users'] = 0
		self.val['root'] = 0
		for i in u:
			if i.ut_type == utmp.USER_PROCESS:
				self.val['users'] = self.val['users'] + 1
				if i.ut_user == 'root':
					self.val['root'] = self.val['root'] + 1
			

ansi = {
	'black': '\033[0;30m',
	'darkred': '\033[0;31m',
	'darkgreen': '\033[0;32m',
	'darkyellow': '\033[0;33m',
	'darkblue': '\033[0;34m',
	'darkmagenta': '\033[0;35m',
	'darkcyan': '\033[0;36m',
	'silver': '\033[0;37m',

	'gray': '\033[1;30m',
	'red': '\033[1;31m',
	'green': '\033[1;32m',
	'yellow': '\033[1;33m',
	'blue': '\033[1;34m',
	'magenta': '\033[1;35m',
	'cyan': '\033[1;36m',
	'white': '\033[1;37m',

	'blackbg': '\033[40m',
	'redbg': '\033[41m',
	'greenbg': '\033[42m',
	'yellowbg': '\033[43m',
	'bluebg': '\033[44m',
	'magentabg': '\033[45m',
	'cyanbg': '\033[46m',
	'whitebg': '\033[47m',

	'reset': '\033[0;0m',
	'bold': '\033[1m',
	'reverse': '\033[2m',
	'underline': '\033[4m',

	'clear': '\033[2J',
#	'clearline': '\033[K',
	'clearline': '\033[2K',
#	'save': '\033[s',
#	'restore': '\033[u',
	'save': '\0337',
	'restore': '\0338',

	'up': '\033[1A',

	'default': '\033[0;0m',
}

char = {
	'pipe': '|',
	'colon': ':',
	'gt': '>',
	'space': ' ',
}

def total(list):
	ret = 0
	for i in list:
		ret = ret + i
	return ret

def convlist(max, list, base = 1024):
#	max = max / len(list)
	retlist = ()
	for var in list:
		retlist = retlist + (conv(max, var, base), )
	return retlist

#def convlist(max, list):
#	return map(conv, list)

### Use capabilities like: float=true, base=nr, color=true
def conv(max, var, base = 1024):
	if var < 0:
		return ansi['default'] + '- '.rjust(max)

	if step == op.delay:
		color = ('red', 'yellow', 'green', 'blue', 'magenta', 'cyan', 'white', 'darkred', 'darkgreen')
	else:
		color = ('darkred', 'darkyellow', 'darkgreen', 'darkblue', 'darkmagenta', 'darkcyan', 'silver', 'red', 'green')

	if base in (0, 1, 10):
		unit = ('', '', '', '', '', '', '', '', '', '', '')
	elif base == 100:
		if round(var) == 0:
			return ansi['default'] + '0'.rjust(max)
		else:
			return ansi[color[long(round(var)/34)]] + str(long(round(var))).rjust(max)
	elif base == 1000:
		unit = (' ', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
	elif base == 1024:
		### lowercase (b) is better for the eyes than uppercase
		unit = ('B', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
	else:
		return ansi['default'] + '? '.rjust(max)

	c = 0
	while True:
		repr = str(long(round(var))) + unit[c]
		if len(repr) <= max:
			if not op.integer and (base != 1000 or c > 0):
				if var != round(var) and len('%1.2f' % var + unit[c]) <= max:
					repr = '%1.2f' % var + unit[c]
				elif var != round(var) and len('%1.1f' % var + unit[c]) <= max:
					repr = '%1.1f' % var + unit[c]
			else:
				var = long(round(var))
			break
		var = var / base
		c = c + 1

	if var == 0:
		return ansi['default'] + ('0' + ' ' * len(unit[0])).rjust(max)
	return ansi[color[c]] + repr.rjust(max)

def showtitle(nr, totlist, vislist, midchar, endchar):
	for o in vislist:
		sys.stdout.write(o.title(nr))
		if o is not vislist[-1]:
			sys.stdout.write(midchar)
		elif totlist != vislist:
			sys.stdout.write(endchar)
	sys.stdout.write('\n')

def showcsvtitle(nr, totlist):
	for o in totlist:
		outputfile.write(o.titlecsv(nr))
		if o is not totlist[-1]:
			outputfile.write(',')
	outputfile.write('\n')

def info(level, str):
	"Output info message"
#	if level <= op.verbose:
	print str

def die(ret, str):
	"Print error and exit with errorcode"
	info(0, str)
	exit(ret)

def getwinsize():
	if op.nolimit:
		return 1024, 1024
	try:
		curses.setupterm()
		return curses.tigetnum('lines'), curses.tigetnum('cols')
	except:
		try:
			import fcntl, struct, termios
			s = struct.pack('HHHH', 0, 0, 0, 0)
			x = fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, s)
			return struct.unpack('HHHH', x)[:2]
		except:
			try:
				return int(os.environ['LINES']), int(os.environ['COLUMNS'])
			except:
				return 25, 80

def getcpunr():
	cpunr = -1
	for line in open('/proc/stat', 'r').readlines():
		if line[0:3] == 'cpu':
			cpunr = cpunr + 1
	return cpunr

def scsidev(nr):
	if nr < 26:
		return 'sd' + chr(ord('a') + nr)
	else:
		return 'sd' + chr(ord('a') - 1 + nr / 26) + chr(ord('a') + nr % 26)

def dev(maj, min):
	scsi = [8, 65, 66, 67, 68, 69, 70, 71, 128, 129, 130, 131, 132, 133, 134, 135]
	ide = [3, 22, 33, 34, 56, 57, 88, 89, 90, 91]
	if maj in scsi:
		nr = scsi.index(maj) * 16 + min
#		nr = scsi.index(maj) * 16 + min / 16
		return scsidev(nr)
	elif maj in ide:
		nr = ide.index(maj) * 2 + min / 64
		return 'hd' + chr(ord('a') + nr)

#def mountpoint(dev):
#	"Return the mountpoint of a mounted device/file"
#	for entry in open('/etc/mtab', 'r').readlines():
#		if entry:
#			list = entry.split()
#			if dev == list[0]:
#				return list[1]

def signaler(signum, frame):
	signal.alarm(interval)

def exit(ret):
	sys.stdout.write(ansi['reset'])
	signal.signal(signal.SIGALRM, signal.SIG_DFL)
	termios.tcsetattr(fd, termios.TCSADRAIN, old)
	sys.exit(ret)

def main():
	global update, loop, step, pagesize, cpunr, ansi, interval, outputfile

	loop = update = 0
	step = op.delay
	pagesize = resource.getpagesize()
	cpunr = getcpunr()
#	hz = os.sysconf('SC_CLK_TCK')
	interval = 1

	if op.output:
		outputfile = open(op.output, 'a', 0)

	### Build list of requested modules
	totlist = []
	for mod in op.modlist:
		if mod == 'cpu':
			totlist.append(dstat_cpu())
			totlist.append(dstat_cpu24())
		elif mod == 'disk':
			totlist.append(dstat_disk())
			totlist.append(dstat_disk24())
			totlist.append(dstat_disk24old())
		elif mod == 'int':
			totlist.append(dstat_int())
			totlist.append(dstat_int24())
		elif mod == 'load':	totlist.append(dstat_load())
		elif mod == 'mem':	totlist.append(dstat_mem())
		elif mod == 'net':	totlist.append(dstat_net())
		elif mod == 'page':
			totlist.append(dstat_page())
			totlist.append(dstat_page24())
		elif mod == 'proc':	totlist.append(dstat_proc())
		elif mod == 'swap':	totlist.append(dstat_swap())
		elif mod == 'sys':	totlist.append(dstat_sys())
		elif mod == 'tcp':	totlist.append(dstat_tcp())
		elif mod == 'time':	totlist.append(dstat_time())
		elif mod == 'udp':	totlist.append(dstat_udp())
		elif mod == 'user':	totlist.append(dstat_user())
		else:
			info(1, 'Module \'%s\' does not exist or failed to load.' % mod)

	### Check terminal capabilities
	if sys.stdout.isatty():
		if curses.tigetnum('colors') < 0:
			op.color = False
	else:
		op.color = False
		op.nolimit = True
		op.update = False

	rows, cols = getwinsize()

	### Empty ansi database if no colors are requested
	if not op.color:
		op.update = False
		for key in ansi.keys():
			ansi[key] = ''

	if not op.update:
		interval = op.delay

	### Remove defect objects and calculate line length
	linewidth = 0
	for o in totlist + []:
		if o.check():
			linewidth = linewidth + o.varwidth() + 1
		else:
			totlist.remove(o)

	if not totlist:
		die(8, 'None of the stats you selected are available.')

	### FIXME: Get rid of socket()
	if op.output:
		import socket
		outputfile.write('"Dstat %s CSV output"\n' % VERSION)
		outputfile.write('"Author:","Dag Wieers <dag@wieers.com>",,,,"URL:","http://dag.wieers.com/home-made/dstat/"\n')
		outputfile.write('"Host:","%s",,,,"Date:","%s"\n' % (socket.gethostbyaddr(socket.gethostname())[0], time.strftime('%d %b %Y %H:%M:%S %Z', time.localtime())))
		outputfile.write('"Cmd:","dstat %s"\n\n' % ' '.join(op.args))

		if op.output:
			showcsvtitle(1, totlist)
			showcsvtitle(2, totlist)
	oldvislist = []

	### Increase precision if we're root (does not seem to have effect)
#	if os.geteuid() == 0:
#		os.nice(-20)
#	sys.setcheckinterval(op.delay / 10000)

	signal.signal(signal.SIGALRM, signaler)
	signal.alarm(interval)

	### Always show header the first time
	showheader = True

	### Let the games begin
	while update <= op.delay * op.count or op.count == -1:

		### Trim object list to what is visible on screen
		(rows, cols) = getwinsize()
		vislist = []
		curwidth = 0
		for o in totlist:
			if curwidth + o.varwidth() + 1 <= cols:
				vislist.append(o)
				curwidth = curwidth + o.varwidth() + 1
			elif vislist == totlist[:-1] and curwidth + o.varwidth() <= cols:
				vislist.append(o)
				curwidth = curwidth + o.varwidth() + 1

		### Check when to display the header
		if op.header:
			if oldvislist != vislist:
				showheader = True
			elif step == 1 and loop % (rows - 1) == 0:
				showheader = True

		if showheader:
			if loop == 0 and totlist != vislist:
				print 'Screen width too small, trimming output.'
			showheader = False
			showtitle(1, totlist, vislist, ansi['darkblue'] + char['space'], ansi['darkblue'] + char['gt'])
			showtitle(2, totlist, vislist, ansi['silver'] + char['pipe'], ansi['darkblue'] + char['gt'])

		oldvislist = vislist

		### Prepare the colors for intermediate updates, last step in a loop is definitive
		if step == op.delay:
			ansi['default'] = ansi['reset']
		else:
			ansi['default'] = ansi['gray']
		sys.stdout.write(ansi['default'])

		### Debugging info
#		sys.stdout.write('[%d:%d:%d] ' % (loop, step, update))

		### Show the stats, calculate all objects (visible, invisible)
		for o in totlist:
			o.stats()
			if o in vislist:
				o.show()
				o.showend(totlist, vislist)
			if op.output and step == op.delay:
				o.showcsv()
				o.showcsvend(totlist, vislist)
		sys.stdout.write(ansi['default'])
		if op.output and step == op.delay:
			outputfile.write('\n')

		### If intermediate results, update increases with 1 sec (=interval)
		update = update + interval

		### Do not pause when this is the final loop
		if update <= op.delay * op.count or op.count == -1:
			signal.pause()

		### The last step in a loop is to show the definitive line on its own line
		if step == op.delay:
			sys.stdout.write('\n' + ansi['reset'] + ansi['clearline'] + ansi['save'])
		else:
			sys.stdout.write(ansi['clearline'] + ansi['restore'])

		loop = (update + op.delay - 1) / op.delay
		step = ((update - 1) % op.delay) + 1

### Unbuffered sys.stdout
sys.stdout = os.fdopen(1, 'w', 0)

curses.setupterm()

### Prevent keyboard input
fd = sys.stdin.fileno()
old = termios.tcgetattr(fd)
new = termios.tcgetattr(fd)
new[3] = new[3] & ~termios.ECHO
termios.tcsetattr(fd, termios.TCSADRAIN, new)

### Workaround for python > 2.1 and < 2.3
def enumerate(sequence):
    index = 0
    for item in sequence:
        yield index, item
        index = index + 1

### Main entrance
if __name__ == '__main__':
	op=Options(sys.argv[1:])
#	cf=Config()
	try:
		main()
	except KeyboardInterrupt, e:
		print
	except OSError, e:
#		print e.errno
		print
		print 'OSError: %s' % e
		exit(7)
#	except Exception, e:
#		signal.signal(signal.SIGALRM, signal.SIG_DFL)
#		termios.tcsetattr(fd, termios.TCSADRAIN, old)
#		raise e

exit(0)

# vim:ts=4:sw=4
