# This holds all the tasks. They are pulled from this directory # with a FIFO execution style. The tasks are identified by the pid of # the requesting process. Unless the tasks really jam up and the server # is rebooted, there's no chance of collision. local_queue_dir = '/tmp/jsonrpc_queue' # This holds all the temporary files for the tasks. It is only used # here as the root of the cleanup process. Each task identifies its own # tmpdir whish is the home directory for the task. This will almost always # be the same as queue filename. But there # is still the option for the # user to identify a specific tmpdir to run from. jsonrpc_tmpdir = '/tmp/jsonrpc' rpc_service = rpc_service_setup() class JSONRPCDaemon(Daemon): "Daemon class for processing JSON RPC requests" def run_an_item(self,qfile_list): "Pulls an item from the queue, and sends it to the rpc_service." # Get the contents of the oldest file. oldest = qfile_list[0] oldest_time = os.stat(local_queue_dir + '/' + oldest).st_mtime for qf in qfile_list[1:]: qf_time = os.stat(local_queue_dir + '/' + qf).st_mtime if qf_time < oldest_time: oldest = qf oldest_time = qf_time
def main(): # This sets up all the json_procs to accessuble for the rpc_service. rpc_service = rpc_service_setup() # QUERY_STRING could have these options # signature Required for any service access. This is an hmak/sha1 # hash againt the request string. # diag ..... Run "run_diagnostics" This allows the user to get a lot # of info on what might be happening on the server. # async .... Send the request to the asynchronous queue. # tmpdir ... User explicitly sets the tmpdir for the task execution. # log ...... Logs the request, response, and environment in # tmpdir/jsonrpc.log option_dict = cgi.parse_qs(os.environ['QUERY_STRING']) # Check that there is a signature. if not 'signature' in option_dict: print json.dumps({ "jsonrpc": "2.0", "error": {"code": -32098, "message": "No signature."}, "id": None}); return pass # Read the request data. form = cgi.FieldStorage() request_str = '' multipart_keys = [] if form.type == 'multipart/form-data': ## Read the "jsonrpc" part, and collect all the names of the file parts. for part_key in form.keys(): if part_key == 'jsonrpc': request_str = form['jsonrpc'].value else: multipart_keys.append(part_key); else: if not form.file: print json.dumps({ "jsonrpc": "2.0", "error": {"code": -32099, "message": "No post data."}, "id": None}); return request_str = form.file.read() # The default hashkey is the local DNS name. This can be changed to grab # the user-data string by uncommenting the code below. # # The main idea is that we want a hashkey that is easily accessible, but # hard to guess. The local DNS fits the bill because an intruder needs to # know both the the public and private DNS names to get access to the # JSONRPC service. Instance tags were considered but they require # the AWS_SECRET_ACCESS_KEY to get. My feeling is that the extra # security provided to this instance by using the AWS secret key, isn't # worth the risk of exposing the key. # ud = None ud = urllib2.urlopen('http://169.254.169.254/latest/meta-data/local-hostname') #try: # ud = urllib2.urlopen('http://169.254.169.254/latest/user-data') #except Exception: # ud = urllib2.urlopen('http://169.254.169.254/latest/meta-data/local-hostname') hashkey = ud.read() h = hmac.new(hashkey,request_str,sha) target_sig = base64.b64encode(h.digest()) if target_sig != option_dict['signature'][0]: data = '' # data += '~'+hashkey+'~'+request_str+'~'+target_sig+'~'+str(option_dict['signature'][0])+'~' print json.dumps({ "jsonrpc": "2.0", "error": { "code": -32097, "message": "Invalid Signature.", "data": data }, "id": None}); return # output all diagnostic data if 'diag' in option_dict and option_dict['diag'][0]: diag_info = diagnostic_data(option_dict) print json.dumps(diag_info, indent=4) return; # Establish a tmpdir location. # A user specified a tmpdir then is relative to tht tmpdir_root. # All diretories are created as needed. global tmpdir_root if 'tmpdir' in option_dict: tmpdir = tmpdir_root + '/' + option_dict['tmpdir'][0] else: tmpdir = tmpdir_root + '/' + str(os.getpid()) if not os.access(tmpdir,os.F_OK): os.makedirs(tmpdir) os.chdir(tmpdir) # Now handle file uploads if necessary for part in multipart_keys: outfd = open(form[part].filename,'w') inbytes = form[part].file.read(1000000) while inbytes: outfd.write(inbytes) inbytes = form[part].file.read(1000000) outfd.close() result = '' if 'async' in option_dict and option_dict['async'][0]: ## Item to be put in the async queue. Currently just need a tmpdir ## and the request. async_item = { 'tmpdir': tmpdir, 'request': json.loads(request_str) } rpc_queue_dir = '/tmp/jsonrpc_queue' queue_path = rpc_queue_dir + '/' + str(os.getpid()) f = open(queue_path,'w') f.write(json.dumps(async_item)) result = json.dumps({'queue_path': queue_path }) print json.dumps({ "jsonrpc": "2.0", "result": json.loads(result), "id": None }); else: # synchronous execution. result = rpc_service.call(request_str) print result if 'log' in option_dict and option_dict['log'][0]: # Logs request, response, and environment to "tmpdir/jsonrpc.log" log_fd = open('jsonrpc.log','w') log_fd.write('{"Request":' + request_str + ', ') log_fd.write('"Result":' + json.dumps(result) + ', ') log_fd.write('"Environment": [') env_keys = sorted(os.environ.keys()) env_out = '' for k in env_keys: env_out += '["'+k+'"' + ', '+ json.dumps(os.environ[k]) + '], ' log_fd.write(env_out[:-2]+']}') log_fd.close()