#!/usr/bin/env python
# $Id: cget 209 2011-02-02 23:38:27Z tjn $

import c3_config
import c3_version

try:
	import sys, os
	from subprocess import *

	sys.path.append( c3_config.def_path )
	import c3_com_obj, c3_file_obj, socket, re, c3_sock

	######## constants ####################################
	help_info = """Usage: cget [OPTIONS] [MACHINE DEFINTIONS] source [target]

	--help -h	= Display help message.		
	--file -f	= Alternate cluster configuration file.	 If one 
			  is not supplied then /etc/c3.conf will be used.
	-i		= Interactive mode.  Ask once before executing.
	--head		= Execute command on head node.  Does not 
			  execute on compute nodes  if specified.
	--all           = Execute command on all the nodes on all the 
			  clusters in the configuration file, ignores 
			  the [MACHINE_DEFINITONS] block
	--dryrun -n	= Does not send commands to machines.
	--version -v 	= Display the version number

	Machine definitions are in the form of
	clusterName: start-end, node number"""

	backslash = re.compile( r"\\" )
	#######################################################


	######## check for arguments  #########################
	if len(sys.argv) == 1:
		print help_info
		sys.exit()
	#######################################################


	######### et default options  #########################
	to_print = 0			#not used in parallel version
	interactive = 0			#prompt before execution
	head_node = 0			#execute only on head node
	filename = "/etc/c3.conf"	#default config file
	pidlist = []			#list if pid's to wait on
	non_inter = 0			#if called by another program
	all = 0				#flag to execute on all
	defusername = ""		#default user name
	#######################################################


	######### internal variables ##########################
	cluster_from_file = {}
	file_set = 0
	dryrun = 0
	returncode = 0
	#######################################################



	######### parse command line ##########################

	# first create one large string of the command 
	command_line_list = sys.argv[1:]
	command_line_string = ''
	for item in command_line_list:
		command_line_string = '%s %s' % (command_line_string, item)

	# object used to parse command line
	c3_command = c3_com_obj.c3_command_line( command_line_string )
	 
	# get first option
	try:
		option = c3_command.get_opt()
		while option: # while more options
			if (option == '-f') or (option == '--file'): # alternate config file
				if not file_set:
					filename = c3_command.get_opt_string()
					file_set = 1
				else:
					print "only one file name can be specified."
					sys.exit(c3_config.C3_ERRLOCAL)
			elif option == '-h' or option == "--help": # print help info
				print help_info
				sys.exit(c3_config.C3_OKEXIT)
			elif option == '-i': # ask once before executing command
				interactive = 1
			elif option == '--head': # execute only the head node
				head_node = 1
			elif option == '--non_interactive':
				non_inter = 1
			elif option == '--all':
				all = 1
			elif option == '-n' or option == '--dryrun':
				dryrun = 1
			elif option == '--version' or option == '-v':
				# Get C3's version
				print c3_version.c3_version
				sys.exit(c3_config.C3_OKEXIT)
			else: # a catch all, option supplied not valid
				print "option ", option, " is not valid"
				sys.exit(c3_config.C3_ERRLOCAL)
			option = c3_command.get_opt()
	except c3_com_obj.end_of_option: #quit parsing options
		pass

	# create cluster object from command line 
	clusters = c3_com_obj.c3_cluster_list()
	 
	if all == 0:
		clusters = c3_command.get_clusters()
	else:
		c3_command.get_clusters()
	 
	# get command to execute
	try:
		source = c3_command.get_opt_string()
		try:
			target = c3_command.get_opt_string()
		except c3_com_obj.bad_string:
			target = os.getcwd() + '/'
	except c3_com_obj.end_of_opt_string:
		print "Must supply a filename to retrieve"
		sys.exit(c3_config.C3_ERRLOCAL)

	source_file_name = os.path.basename(source)
	if source_file_name == "":
		if non_inter:
			sock=c3_sock.client_sock()
			sock.send ("bad")
			sys.exit(c3_config.C3_ERRLOCAL)
		print "source must be a filename"
		sys.exit(c3_config.C3_ERRLOCAL)
		
	if os.path.basename(target) == "":  	#if target is a directory do nothing
		pass
	elif os.path.isfile( target ):		#if target is a file exit
		print "target must be a directory."
		sys.exit(c3_config.C3_ERRLOCAL)
	else:					#target is a directory but does not end with a '/', add one
		target = target + '/'

	while interactive: # if interactive execution, prompt once before executing
		answer = raw_input( "retrieve " + source + " to " + target + " (y or n): " )
		if re.compile( r".*n(o)?.*", re.IGNORECASE ).match( answer ):
			sys.exit(CE_OKEXIT)
		if re.compile( r".*y(es)?.*", re.IGNORECASE).match( answer ):
                        interactive = 0


	if not os.path.exists( os.path.dirname(target) ):
		print "dir does not exists, creating..."
		try:
			os.mkdir( os.path.dirname(target) )
		except OSError:
			print "Can not make directory ", target
			sys.exit(c3_config.C3_ERRLOCAL)

	#######################################################


	######### test if ssh or rsh ##########################
	try:
		transport = os.environ[ 'C3_RSH' ]
	except KeyError:
		transport = 'ssh'
	#######################################################
	######### set default username ########################
	try:
		defusername = os.environ[ 'C3_USER' ]
	except KeyError:
		defusername = os.environ[ 'USER' ]
	#######################################################

        ######### set filename  ##########################
        if not file_set:
                try:
                        filename = os.environ[ 'C3_CONF' ]
                except KeyError:
                        filename = '/etc/c3.conf'
        #######################################################
	if non_inter and head_node:
		sock = c3_sock.client_sock()
		if os.path.isfile( source ):
			sock.send( 'good' )
		elif os.path.isdir( source ):
			sock.send( 'good' )
		else:
			sock.send( 'bad' )
		sys.exit(c3_config.C3_ERRLOCAL)

	######### make cluster list object from file ##########
	try: # open file & initialize file parser
		file = c3_file_obj.cluster_def( filename )
	except IOError:
		print "error opening file: ", filename
		sys.exit(c3_config.C3_ERRLOCAL)

	try:
		file.get_next_cluster() # set the default cluster (first cluster in list)

		try:
			if clusters.clusters[0] ==  "/default": # if default cluster set correct cluster name
				clusters.clusters[0] = file.get_cluster_name()
				clusters.node[file.get_cluster_name()] = clusters.node["/default"]
				del clusters.node["/default"]
				clusters.username[file.get_cluster_name()] = clusters.username["/default"]
		except IndexError:  #this is thrown if the --all option is used
			pass

		while(1): #throws exception when no more clusters
			c_name = file.get_cluster_name() #name of cluster being parsed
			if all == 1:
				clusters.clusters.append( c_name )
				clusters.node[c_name] = []
				clusters.node[c_name].append( "" )
			if c_name in clusters.clusters:
				cluster_from_file[c_name] = {}
				cluster_from_file[c_name]['external'] = file.get_external_head_node()
				cluster_from_file[c_name]['internal'] = file.get_internal_head_node()
				cluster_from_file[c_name]['nodes'] = [] #list of node names from file
				if cluster_from_file[c_name]['external']: #if a direct cluster
					index = 0
					try:
						while(1): # build list of nodes
							node_obj = file.get_next_node()
							cluster_from_file[c_name]['nodes'].append( c3_file_obj.node_obj() )
							cluster_from_file[c_name]['nodes'][index] = node_obj
							index = index + 1
					except c3_file_obj.end_of_cluster:
						pass
			file.get_next_cluster() #repeat untill no more clusters
	except c3_file_obj.no_more_clusters:
		pass
	except c3_file_obj.parse_error, error:
		print error.description
		print "somewhere around ", error.last
		sys.exit(c3_config.C3_ERRLOCAL)

	#######################################################


	######### execute command on each node in cluster



	# there are two main groups, local and remote clusters
	# in each of those groups there are direct and indirect
	# modes, that is every node specified or a "link". A link
	# on a local cluster is of course invalid.

	# right now the only way I know how to check if a cluster
	# is local is to use a "gethostbyname" and compare it
	# to the head node names (both internel and externel).
	# right now that is acceptable as many tools require 
	# the function to work correctly (ssh being one of them)
	# my want to think about a better way.

	#host_ip = socket.gethostbyname( socket.gethostname() )



		
	pid_list_outer = []
	for cluster in clusters.clusters:
		pid = os.fork()
		if pid == 0:

			############ get machine names #############################
			try: 
				local_ip = socket.gethostbyname( socket.gethostname() )
			except socket.error:
				print "Can not resolve local hostname"
				sys.exit(c3_config.C3_ERRLOCAL)
			try:
				int_ip = socket.gethostbyname( cluster_from_file[cluster]['internal'] )
			except socket.error:
				int_ip = ""
			except KeyError:
				if not non_inter:
					print "Cluster ", cluster, " is not in you configuration file."
					continue
			try:
				ext_ip = socket.gethostbyname( cluster_from_file[cluster]['external'] )
				ext_ip_name = cluster_from_file[cluster]['external']
			except socket.error:
				ext_ip = ""
			except TypeError:
				ext_ip = int_ip
				ext_ip_name = cluster_from_file[cluster]['internal']
			except KeyError:
				pass
			############################################################
			

			try:
				if clusters.username[cluster] == "/default":
					username = defusername
				else:
					username = clusters.username[cluster]
			except KeyError:
				username = defusername
				
				
			if head_node:   #if only execute on head node , do so
					#will execute on local cluster with ssh also
				string_to_execute = transport + " -l " + username + " " + ext_ip + " \'" + c3_config.def_path + "/cget --non_interactive --head " + source + "\'" 
				sock = c3_sock.server_sock( string_to_execute )
				answer = sock.recieve()
				sock.close()
				if answer == 'bad':
					print source, " does not exist on ", cluster, ":", ext_ip
				else:
					local_target = target + source_file_name + "_" + cluster + "_" + ext_ip_name
					string_to_execute = "rsync -avz --rsh=" + transport + " " + username + "@" + ext_ip + ":" + source + " " + local_target
					##ins,out = os.popen4( string_to_execute )
					##out.read()
					#proc = popen2.Popen4(string_to_execute)
					#string_in = proc.fromchild.read()
					#temp = proc.wait()
					p = Popen(string_to_execute, shell=True, 
							  stdout=PIPE, stderr=STDOUT, close_fds=True)
					string_in = p.communicate()[0]
					temp = p.wait()
					if( temp != 0 ):
						returncode = c3_config.C3_ERRREMOTE

			elif cluster_from_file[cluster]['external']: # if a direct cluster
				if non_inter:
					sock = c3_sock.client_sock()
				if ext_ip == local_ip or int_ip == local_ip: # if a local cluster
					if ext_ip == "": # error conditions (just don't execute current cluster)
						print "Can not resolve ", cluster_from_file[cluster]['external']
					elif int_ip == "":
						print "can not resolve ", cluster_from_file[cluster]['internal']

					elif clusters.node[cluster][0] != "" : #range specified on command line
						for node in clusters.node[cluster]: #for each cluster specified on the command line
							try:
								if not cluster_from_file[cluster]['nodes'][node].dead: #if machine is not dead
									pid = os.fork() # execute command on each node in it's own process
									if pid == 0:
										node_name = cluster_from_file[cluster]['nodes'][node].name
										if not non_inter:
											target = target + source_file_name + "_" + cluster + "_" + node_name 
											string_to_execute = "rsync -avz --rsh=" + transport + " " + username + "@" + node_name + ":" + source + " " + target
											if dryrun:
												print string_to_execute
												os._exit(c3_config.C3_OKEXIT)
											##ins, out = os.popen4( string_to_execute )
											##out.read()
											#proc = popen2.Popen4(string_to_execute)
											#string_in = proc.fromchild.read()
											#temp = proc.wait()
											p = Popen(string_to_execute, shell=True, 
							  						  stdout=PIPE, stderr=STDOUT, close_fds=True)
											string_in = p.communicate()[0]
											temp = p.wait()
											if( temp != 0 ):
												returncode = c3_config.C3_ERRREMOTE
											if not os.path.isfile( target ):
												print source, " does not exist on ", node_name
										else:
											target = "/tmp/" + source_file_name + "%d" % os.getuid() + "%d" % os.getpid() + "_" + cluster + "_" + node_name 
											
											while os.path.exists( target ):
												target = target + "1"
											string_to_execute = "rsync -avz --rsh=" + transport + " " + username + "@" + node_name + ":" + source + " " + target + " >& /dev/null"
											if dryrun:
												print string_to_execute
												os._exit(c3_config.C3_OKEXIT)
											##ins,out = os.popen4(string_to_execute)
											##out.read() #block untill rsync finishes
											#proc = popen2.Popen4(string_to_execute)
											#string_in = proc.fromchild.read()
											#temp = proc.wait()
											p = Popen(string_to_execute, shell=True, 
							  						  stdout=PIPE, stderr=STDOUT, close_fds=True)
											string_in = p.communicate()[0]
											temp = p.wait()
											if( temp != 0 ):
												returncode = c3_config.C3_ERRREMOTE
											if os.path.isfile( target ):
												name_string = target + " : " + node_name
												sock.send( name_string )
											else:
												sock.send( "\file\does\not\exist : " + node_name )

										os._exit(returncode)
									pidlist.append(pid)
							except IndexError:
								pass
					else:  # no range specified on command line, do all nodes
						for node in cluster_from_file[cluster]['nodes']:  # for each node in cluster
							if not node.dead: #if node not dead
								pid = os.fork() # execute command in own process
								if pid == 0:
									node_name = node.name

									if not non_inter:
										target = target + source_file_name + "_" + cluster + "_" + node_name 
										string_to_execute = "rsync -avz --rsh=" + transport + " " + username + "@" + node_name + ":" + source + " " + target
										if dryrun:
											print string_to_execute
											os._exit(c3_config.C3_OKEXIT)
										##ins, out  = os.popen4( string_to_execute )
										##out.read()
										#proc = popen2.Popen4(string_to_execute)
										#string_in = proc.fromchild.read()
										#temp = proc.wait()
										p = Popen(string_to_execute, shell=True, 
							  					  stdout=PIPE, stderr=STDOUT, close_fds=True)
										string_in = p.communicate()[0]
										temp = p.wait()
										if( temp != 0 ):
											returncode = c3_config.C3_ERRREMOTE
										if not os.path.isfile( target ):
											print source, " does not exist on ", node_name
									else:
										target = "/tmp/" + source_file_name + "%d" % os.getuid() + "%d" % os.getpid() + "_" + cluster + "_" + node_name 
										
										while os.path.exists( target ):
											target = target + "1"
										string_to_execute = "rsync -avz --rsh=" + transport + " " + username + "@" + node_name + ":" + source + " " + target
										if dryrun:
											print string_to_execute
											os._exit(c3_config.C3_OKEXIT)
										##ins,out= os.popen4(string_to_execute)
										##string = out.read() #block untill rsync finishes
										#proc = popen2.Popen4(string_to_execute)
										#string = proc.fromchild.read()
										#temp = proc.wait()
										p = Popen(string_to_execute, shell=True, 
							  					  stdout=PIPE, stderr=STDOUT, close_fds=True)
										string_in = p.communicate()[0]
										temp = p.wait()
										if( temp != 0 ):
											returncode = c3_config.C3_ERRREMOTE
										if os.path.isfile( target ):
											name_string = target + " : " + node_name
											sock.send( name_string )
										else:
											sock.send( "\file\does\not\exist : " + node_name )
									
									os._exit(returncode)
								pidlist.append(pid)
					for pid in pidlist:
						pid, code = os.waitpid(pid,0)
						code = code>>8
						if( code != 0 ): returncode = code
					if non_inter:
						sock.send( "done\with\cluster : done\with\cluster" )
						sock.close()
					
				else: # remote cluster
					if ext_ip == "": # error condition
						print "Can not resolve ", cluster_from_file[cluster]['external']
						sys.stdout.flush()
					else:
						# generate temprorary config file
						cluster_def_string = "cluster auto_gen {\n"
						cluster_def_string = cluster_def_string + cluster_from_file[cluster]['external'] + ":" + cluster_from_file[cluster]['internal'] + "\n"

			
						if clusters.node[cluster][0] != "" : #range specified on command line
							for node in clusters.node[cluster]: #for each node specified on command line
								try:
									if not cluster_from_file[cluster]['nodes'][node].dead: # if cluster node not dead
										node_name = cluster_from_file[cluster]['nodes'][node].name #add cluster to list
										cluster_def_string = cluster_def_string + node_name + "\n"
								except IndexError:
									print node, " is out of range."
						else:  # no range specified on command line, do all nodes
							for node in cluster_from_file[cluster]['nodes']: # for each node in cluster
								if not node.dead: #if node not dead, add to list
										cluster_def_string = cluster_def_string +  node.name + "\n"
						cluster_def_string = cluster_def_string + "}" # close list
										
						#this is an attempt to generate a unique file name. As there is no easy way
						#for me to see the remote machine I mangle a group of relativly unique
						#identifiers. since i prepend the machine ip address if the file does not reside localy
						#it should not remotely, at the very least it should be safe to rewrite the file
						filename = "/tmp/" + local_ip + "%d" % os.getuid() + "%d" % os.getpid() 
						string_to_execute = transport + " -l " + username + " " + ext_ip + " "+ c3_config.def_path + "/cget --head --non_interactive " + filename
						if dryrun:
							print string_to_execute
							sys.exit()
						sock = c3_sock.server_sock( string_to_execute )
						answer = sock.recieve()
						sock.close()
						while answer == 'good': #make sure file name is unique on local machine
							filename = filename + "1"
							string_to_execute = transport + " -l " + username + " " + ext_ip + " " + c3_config.def_path + "/cget --head --non_interactive " + filename
							if dryrun:
								print string_to_execute
								sys.exit()
							sock.__init__( string_to_execute )
							answer = sock.recieve()
							sock.close()

						FILE = open( filename, 'w' )
						FILE.write( cluster_def_string )
						FILE.close()
						# push file to remote machine
						string_to_execute = "rsync --rsh=" + transport + " " + filename + " " + username + "@" + ext_ip + ":" +  filename
						if dryrun:
							print string_to_execute
							os._exit(c3_config.C3_OKEXIT)
						##ins, out = os.popen4( string_to_execute )
						##out.read()
						#proc = popen2.Popen4(string_to_execute)
						#string_in = proc.fromchild.read()
						#temp = proc.wait()
						p = Popen(string_to_execute, shell=True, 
								  stdout=PIPE, stderr=STDOUT, close_fds=True)
						string_in = p.communicate()[0]
						temp = p.wait()
						if( temp != 0 ):
							returncode = c3_config.C3_ERRREMOTE

						
						string_to_execute = transport + " -l " + username + " " + ext_ip + " \' " + c3_config.def_path + "/cget --non_interactive -f " + filename + " " + source + "\'"
						
						sock = c3_sock.server_sock( string_to_execute )
						file_list = {}
						
						
						temp_filename, temp_nodename = sock.recieve().split( " : " )
						while temp_filename != "done\with\cluster":
							if temp_filename == "\file\does\not\exist":
								print source_file_name, " does not exist on ", temp_nodename, " in cluster ", cluster
								temp_filename, temp_nodename = sock.recieve().split( " : " ) 
							else:
								file_list[temp_filename] = temp_nodename
								temp_filename, temp_nodename = sock.recieve().split( " : " )
							#temp_nodename = sock.recieve()
							#temp_filename = sock.recieve()
							
						for temp_filename in file_list.keys():
							pid = os.fork()
							if pid == 0:
								temp_target = target + source_file_name + "_" + cluster + "_" + file_list[temp_filename]
								string_to_execute = "rsync -avz --rsh=" + transport + " " + username + "@" + ext_ip + ":" + temp_filename + " " + temp_target
								##ins,out = os.popen4(string_to_execute)
								##out.read()
								#proc = popen2.Popen4(string_to_execute)
								#string_in = proc.fromchild.read()
								#temp = proc.wait()
								p = Popen(string_to_execute, shell=True, 
							  			  stdout=PIPE, stderr=STDOUT, close_fds=True)
								string_in = p.communicate()[0]
								temp = p.wait()
								if( temp != 0 ):
									returncode = c3_config.C3_ERRREMOTE
								string_to_execute = transport + " -l " + username + " " + ext_ip + " /bin/rm -f " + temp_filename
								#proc = popen2.Popen4(string_to_execute)
								#string_in = proc.fromchild.read()
								#temp = proc.wait()
								p = Popen(string_to_execute, shell=True, 
							  			  stdout=PIPE, stderr=STDOUT, close_fds=True)
								string_in = p.communicate()[0]
								temp = p.wait()
								if( temp != 0 ):
									returncode = c3_config.C3_ERRREMOTE
								os._exit(returncode)
							pidlist.append(pid)
						sock.close()

						for pid in pidlist:
							pid, code = os.waitpid(pid,0)
							code = code>>8
							if( code != 0 ): returncode = code
						# remove temporary file
						string_to_execute = transport + " -l " + username + " " + ext_ip + " /bin/rm -f " + filename
						if dryrun:
							print string_to_execute
							os._exit(c3_config.C3_OKEXIT)
						#proc = popen2.Popen4(string_to_execute)
						#string_in = proc.fromchild.read()
						#temp = proc.wait()
						p = Popen(string_to_execute, shell=True, 
								  stdout=PIPE, stderr=STDOUT, close_fds=True)
						string_in = p.communicate()[0]
						temp = p.wait()
						if( temp != 0 ):
							returncode = c3_config.C3_ERRREMOTE
						os.unlink( filename ) # remove local temp file
					
			else: # indirect clusters
				if int_ip == local_ip:  # can not have a indirect local cluster since if your default cluster
							# is local you would generate a circular reference
					print "error local cluster"
					sys.stdout.flush()
				else: # remote indirect cluster
					if int_ip == "": # error condition
						print "Can not resolve hostname ", cluster_from_file[cluster]['internal']
						sys.stdout.flush()
					else:
						node_list = "" # generate new node list from the command line
						if clusters.node[cluster][0] != "" : #range specified on command line
							node_list = ":%d" % clusters.node[cluster].pop(0)

							for node in clusters.node[cluster]:
								node_list = node_list + ", %d" % node	
						# execute command on remote machine


						string_to_execute = transport + " -l " + username + " " + int_ip + " \' " + c3_config.def_path + "/cget " + " --non_interactive " + node_list + " " + source + "\'"
						if dryrun:
							print string_to_execute
							os._exit(c3_config.C3_OKEXIT)
						sock = c3_sock.server_sock( string_to_execute )
						file_list = {}
						
						
						temp_filename, temp_nodename = sock.recieve().split( " : " )
						while temp_filename != "done\with\cluster":
							if temp_filename == "\file\does\not\exist":
								print source_file_name, " does not exist on ", temp_nodename, " in cluster ", cluster
								temp_filename, temp_nodename = sock.recieve().split( " : " ) 
							else:
								file_list[temp_filename] = temp_nodename
								temp_filename, temp_nodename = sock.recieve().split( " : " ) 
							#temp_filename = sock.recieve()
							
						for temp_filename in file_list.keys():
							pid = os.fork()
							if pid == 0:
								temp_target = target + source_file_name + "_" + cluster + "_" + file_list[temp_filename]
								string_to_execute = "rsync -avz --rsh=" + transport + " " + username + "@" + int_ip + ":" + temp_filename + " " + temp_target
								if dryrun:
									print string_to_execute
									os._exit(c3_config.C3_OKEXIT)
								##ins, out = os.popen4(string_to_execute)
								##out.read()
								#proc = popen2.Popen4(string_to_execute)
								#string_in = proc.fromchild.read()
								#temp = proc.wait()
								p = Popen(string_to_execute, shell=True, 
							  			  stdout=PIPE, stderr=STDOUT, close_fds=True)
								string_in = p.communicate()[0]
								temp = p.wait()
								if( temp != 0 ):
									returncode = c3_config.C3_ERRREMOTE
								string_to_execute = transport + " -l " + username + " " + int_ip + " /bin/rm -f " + temp_filename
								if dryrun:
									print string_to_execute
									sys.exit()
								##ns,out = os.popen4(string_to_execute)
								##out.read()
								#proc = popen2.Popen4(string_to_execute)
								#string_in = proc.fromchild.read()
								#temp = proc.wait()
								p = Popen(string_to_execute, shell=True, 
							  			  stdout=PIPE, stderr=STDOUT, close_fds=True)
								string_in = p.communicate()[0]
								temp = p.wait()
								if( temp != 0 ):
									returncode = c3_config.C3_ERRREMOTE
								os._exit(returncode)
							pidlist.append(pid)
						sock.close()
					for pid in pidlist:
						pid, code = os.waitpid(pid,0)
						code = code>>8
						if( code != 0 ): returncode = code
			os._exit(returncode)
		pid_list_outer.append( pid )
	for pid in pid_list_outer: # wait for all processes spawned to finish
		pid, code = os.waitpid(pid,0)
		code = code>>8
		if( code != 0 ): returncode = code
except KeyboardInterrupt:
	print "Keyboard interrupt\n"

sys.exit(returncode)

# vim:tabstop=4:shiftwidth=4:noexpandtab:textwidth=76
