aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--steuermann/rexec.py105
-rw-r--r--steuermann/rexecd.py242
2 files changed, 347 insertions, 0 deletions
diff --git a/steuermann/rexec.py b/steuermann/rexec.py
new file mode 100644
index 0000000..30e58dc
--- /dev/null
+++ b/steuermann/rexec.py
@@ -0,0 +1,105 @@
+import sys
+import urllib
+import urllib2
+import pandokia.helpers.easyargs as easyargs
+import os.path
+
+# http://ss64.com/nt/ - language of cmd.exe
+
+
+# URLs are
+# http://
+# hostname
+# :port
+# /password/
+# directive
+# args
+#
+# method=GET directive=run/
+# args = url encoded command line
+#
+# method=POST directive=upload
+# no args
+# post ...
+
+def urlbase( host, password ) :
+ if not ':' in host :
+ host = host + ':7070'
+ url = "http://%s/%s/"%(host,urllib.quote_plus(password))
+ return url
+
+def run( host, cmd, password, directory ):
+ url = urlbase(host,password) + "run"
+
+ data = urllib.urlencode( { 'password' : password,
+ 'dirname' : directory,
+ 'cmd' : cmd
+ }
+ )
+ req = urllib2.Request(url, data)
+ try :
+ f = urllib2.urlopen(req)
+ except urllib2.HTTPError, e:
+ print "HTTP ERROR",e.code
+ print e.read()
+ return 1
+ print f.read()
+ return 0
+
+def upload( host, filename, password, directory) :
+ url = urlbase(host, password) + "upload"
+
+ f = open(filename,"r")
+
+ data = urllib.urlencode( { 'password' : password,
+ 'dirname' : directory,
+ 'filename' : os.path.basename(filename),
+ 'data' : f.read(),
+ }
+ )
+ req = urllib2.Request(url, data)
+ try :
+ f = urllib2.urlopen(req)
+ except urllib2.HTTPError, e:
+ print "HTTP ERROR",e.code
+ print e.read()
+ return 1
+ print f.read()
+ return 0
+
+if __name__ == '__main__' :
+ opt, args = easyargs.get( {
+ '-p' : '=', # password
+ '-f' : '=', # password from file
+ '-u' : '', # upload
+ '-d' : '=', # upload destination directory (default .)
+ '-h' : '=', # host name
+ } )
+ if '-p' in opt :
+ password = opt['-p']
+ elif '-f' in opt :
+ password = open(opt['-f'],'r').readline().strip()
+
+ if not ( '-h' in opt ) :
+ print "must give host name with -h"
+ sys.exit(2)
+ host = opt['-h']
+ if '-d' in opt :
+ directory = opt['-d']
+ else :
+ directory = "."
+
+ if opt['-u'] :
+ ex = 0
+ for x in args :
+ ex |= upload(host = host , filename=x, directory=directory, password=password)
+ sys.exit(ex)
+ else :
+ x = run( host=host,
+ cmd = ' '.join(args),
+ password = password,
+ directory = directory
+ )
+ sys.exit(x)
+
+
diff --git a/steuermann/rexecd.py b/steuermann/rexecd.py
new file mode 100644
index 0000000..712d48f
--- /dev/null
+++ b/steuermann/rexecd.py
@@ -0,0 +1,242 @@
+#
+# steuermann remote execution daemon - use this when you can't use
+# something better (like ssh) to get your commands running in the
+# other machine.
+#
+# Copyright 2012 Association of Universities for Research in Astronomy (AURA)
+#
+
+import os
+import time
+import CGIHTTPServer
+import BaseHTTPServer
+import SocketServer
+import platform
+import subprocess
+import urllib
+import urlparse
+
+os.chdir('/')
+print os.getcwd()
+
+#####
+#
+
+valid_client_ip = (
+ '130.167.180.22', # arzach
+ '130.167.180.4', # ssb
+ '130.167.237.185', # banana
+ '130.167.237.55', # vxp-dukat
+ )
+
+#####
+#
+# This uses the python stock http server. Here is a request handler
+# that services GET requests by executing the command passed in.
+# All other requests are invalid.
+
+password = "pass"
+
+class my_handler( CGIHTTPServer.CGIHTTPRequestHandler ) :
+
+ def __init__(self, request, client_address, server) :
+ # init the superclass
+ CGIHTTPServer.CGIHTTPRequestHandler.__init__(self, request, client_address, server)
+
+ def reject_client(self) :
+ print self.client_address
+ if not ( self.client_address[0] in valid_client_ip ) :
+ self.bad_client('a')
+ return 1
+
+ if self.path.startswith('/' + password + '/' ) :
+ self.path = self.path[len(password)+2:]
+ else :
+ self.bad_client('p')
+ return 1
+
+ return 0
+
+ def do_GET(self) :
+ # GET /password/command...
+ # run the command
+
+ if self.reject_client() :
+ return
+
+ path = self.path
+
+ print "GET",path
+ self.send_response(200)
+ self.send_header('Content-type', 'text/plain')
+ self.end_headers()
+ self.wfile.write("Hello world: %s\n"%path)
+ self.wfile.flush()
+
+ def bad_client(self, why) :
+ self.send_response(500)
+ self.send_header('Content-type', 'text/plain')
+ self.end_headers()
+ self.wfile.write("bad client: %s\n"%why)
+ self.wfile.flush()
+ return
+
+ def do_INVALID(self) :
+ self.send_response(500)
+ self.send_header('Content-type', 'text/plain')
+ self.end_headers()
+ self.wfile.write("error\n")
+ self.wfile.flush()
+ return
+
+ def do_POST(self) :
+ # POST password=pass&name=filename&data=filedata
+ # upload data to a file
+
+ if self.reject_client() :
+ return
+
+ print self.path
+
+
+ length = int(self.headers['Content-Length'])
+ data = self.rfile.read(length)
+ d = urlparse.parse_qs(data)
+
+ for x in sorted([ x for x in d]) :
+ print x,d[x]
+
+ if d['password'][0] != password :
+ self.bad_client('p')
+ return
+
+ dirname = d['dirname'][0]
+ print "CD",dirname
+ os.chdir(dirname)
+
+ if self.path == 'upload' :
+
+ filename = d['filename'][0]
+
+ mode = 'wb'
+ if 'mode' in d :
+ t = d['mode'][0]
+ if t == 't' or t == 'text' :
+ mode = 'w'
+ elif t == 'b' or t == 'binary' :
+ mode = 'wb'
+ else :
+ return self.bad_client('mode')
+
+ f = open(filename,'wb')
+ f.write(d['data'][0])
+ f.close()
+
+ self.send_response(200)
+ self.send_header('Content-type', 'text/plain')
+ self.end_headers()
+ self.wfile.write("uploaded %s\n"%filename)
+ self.wfile.flush()
+
+ elif self.path == 'run' :
+ cmd = d['cmd'][0]
+ self.send_response(200)
+ self.send_header('Content-type', 'text/plain')
+ self.end_headers()
+ self.wfile.write("Hello world: %s\n"%cmd)
+ run_child(cmd, self.wfile)
+ self.wfile.write("done\n")
+ self.wfile.flush()
+ else :
+ return self.bad_client('c')
+
+
+ def do_HEAD(self) :
+ self.do_invalid()
+
+
+class MultiThreadedHTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
+ pass
+
+####
+
+windows = platform.system() == 'Windows'
+
+def run_child(path, wfile) :
+ env = os.environ
+ cmd = urllib.unquote_plus(path)
+ print "COMMAND",cmd
+
+ # bug: implement timeouts
+ if windows :
+ status = subprocess_windows( cmd, wfile, env )
+ else :
+ status = subprocess_unix( cmd, wfile, env )
+
+ # subprocess gives you weird status values
+ if status > 0 :
+ t_status="exit %d"%(status >> 8)
+ if status != 0 :
+ return_status = 1
+ else :
+ return_status = 1
+ t_status="signal %d" % ( - status )
+ # subprocess does not tell you if there was a core
+ # dump, but there is nothing we can do about it.
+
+ print "COMMAND EXIT:",status,t_status
+
+
+def subprocess_windows(cmd, wfile, env ) :
+ # You might think that subprocess.Popen() would be portable,
+ # but you would be wrong.
+ #
+ # On Windows, sockets are NOT file descriptors. Since we are in a web server, wfile here is a socket.
+
+ # print wfile.fileno()
+ # import msvcrt
+ # print msvcrt.get_osfhandle(wfile.fileno())
+ p = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True, env = env, creationflags = subprocess.CREATE_NEW_PROCESS_GROUP )
+ while 1 :
+ n = p.stdout.read(256)
+ if n == '' :
+ break
+ wfile.write(n)
+ return p.wait()
+
+def subprocess_unix( cmd, wfile, env ) :
+ p = subprocess.Popen( cmd, stdout=wfile, stderr=wfile, shell=True, env = env, preexec_fn=os.setpgrp )
+ return p.wait()
+
+#####
+
+
+def run( args = [ ] ) :
+ # you could parse args here if you wanted to. I don't care to spend
+ # the time. This is just here for people who can't (or don't want to)
+ # install a full featured web server just to try things out.
+ if len(args) > 0 :
+ ip = args[0]
+ else :
+ ip = platform.node()
+
+ port = 7070
+
+ print "http://%s:%s/"%(str(ip),str(port))
+
+ httpd = MultiThreadedHTTPServer( (ip, port) , my_handler)
+
+ sa = httpd.socket.getsockname()
+ print "Serving HTTP on", sa[0], "port", sa[1], "..."
+ while 1 :
+ httpd.handle_request()
+
+if __name__ == '__main__' :
+ run()
+
+
+##
+# in case we need to kill child processes:
+# http://code.activestate.com/recipes/347462-terminating-a-subprocess-on-windows/
+
+