private byte[] core_open_stream_channel(byte[] args) { List <byte> request = new List <byte>(args); int stream_id = Struct.extractInt32(request); byte passthrough_byte = Struct.extractByte(request); bool passthrough = false; if (passthrough_byte == 1) { passthrough = true; } // check if stream is present bool exists = this.hasStream(stream_id); if (!exists) { throw new Exception(String.Format("Stream with ID '{0}' doesn't exist", stream_id)); } // create stream channel Stream stream = this.getStream(stream_id); StreamChannel sc = new StreamChannel(stream, this.callbackChannelOutputPresent, this.triggerProcessingNeeded, passthrough); //add stream channel to transport layer channels this.AddChannel(sc); //return stream id UInt32 result = sc.ID; List <byte> response = Struct.packUInt32(result); return(response.ToArray()); }
private byte[] core_create_proc(byte[] args) { List <byte> data = new List <byte>(args); // first byte indicates if STDIN, STDOUT and STDERR should be streamed to channels bool use_channels = (Struct.extractByte(data) != 0); string proc_filename = Struct.extractNullTerminatedString(data); string proc_args = Struct.extractNullTerminatedString(data); ClientProcess proc = new ClientProcess(proc_filename, proc_args, use_channels, this.callbackChannelOutputPresent, this.triggerProcessingNeeded); //starts the process already proc.registerOnExitCallback(this.onProcessExit); if (use_channels) { this.AddChannel(proc.Ch_stdin); this.AddChannel(proc.Ch_stdout); this.AddChannel(proc.Ch_stderr); /* * proc.Ch_stdin = this.tl.CreateChannel(Channel.Types.IN, Channel.Encodings.UTF8); * proc.Ch_stdout = this.tl.CreateChannel(Channel.Types.OUT, Channel.Encodings.UTF8); * proc.Ch_stderr = this.tl.CreateChannel(Channel.Types.OUT, Channel.Encodings.UTF8); */ } //generate method response List <byte> resp = Struct.packUInt32((UInt32)proc.Id); if (use_channels) { resp = Struct.packByte(1, resp); resp = Struct.packUInt32(proc.Ch_stdin.ID, resp); resp = Struct.packUInt32(proc.Ch_stdout.ID, resp); resp = Struct.packUInt32(proc.Ch_stderr.ID, resp); } else { resp = Struct.packByte(0, resp); resp = Struct.packUInt32(0, resp); resp = Struct.packUInt32(0, resp); resp = Struct.packUInt32(0, resp); } Monitor.Enter(this.pendingClientProcessesLock); this.pending_client_processes.Add(proc.Id, proc); Monitor.Exit(this.pendingClientProcessesLock); //throw new ClientMethodException(String.Format("Not implemented: Trying to start proc '{0}' with args: {1}", proc_filename, proc_args)); return(resp.ToArray()); }
public void SendControlMessage(UInt32 msg_type, byte[] data) { List <byte> msg = Struct.packUInt32(msg_type); if (data != null) { msg = Struct.packByteArray(data, msg); } this.control_channel.write(msg.ToArray()); }
private byte[] core_kill_proc(byte[] args) { UInt32 proc_id = Struct.extractUInt32(new List <byte>(args)); //check if proc ID exists (for now only managed procs) if (this.pending_client_processes.Contains((int)proc_id)) { ((ClientProcess)this.pending_client_processes[(int)proc_id]).kill(); //return Struct.packNullTerminatedString(String.Format("Sent kill signal to process with ID {0}", proc_id)).ToArray(); return(Struct.packUInt32(proc_id).ToArray()); // return process id on success } else { throw new ClientMethodException(String.Format("Process with ID {0} not known. Kill signal hasn't been sent", proc_id)); //return Struct.packNullTerminatedString(String.Format("Process with ID {0} not known. Kill signal hasn't been sent", proc_id)).ToArray(); } }
public byte[] createResponse() { // this function should only be called when the method has finished (finished member == $true), but anyway, this isn't checked here // first response field is uint32 method id List <byte> response = Struct.packUInt32(this.id); // next field is a ubyte indicating success or error (0 success, everything else error) if (this.error) { response = Struct.packByte((byte)1, response); // indicating an error response = Struct.packNullTerminatedString(this.error_message, response); //append error message return(response.ToArray()); // hand back error response } response = Struct.packByte((byte)0, response); // add success field response = Struct.packByteArray(this.result, response); return(response.ToArray()); // return result }
private void dispatchControlMessage(List <byte> msg) { /* * This method is called from the input thread (not the processing thread), so time consuming or * blocking tasks mustn't be run here */ CH_MSG_TYPE ch_msg_type = (CH_MSG_TYPE)Struct.extractUInt32(msg); switch (ch_msg_type) { case CH_MSG_TYPE.CHANNEL_CONTROL_REQUEST_STATE: Console.WriteLine(String.Format("Received STATE request for StreamChannel {0}", this.ID)); break; case CH_MSG_TYPE.CHANNEL_CONTROL_REQUEST_FLUSH: Console.WriteLine(String.Format("Received FLUSH request for StreamChannel {0}", this.ID)); stream.Flush(); //return message, stating that everything has been written (should be handed by output thread) List <byte> flush_response = Struct.packUInt32((UInt32)StreamChannel.CH_MSG_TYPE.CHANNEL_CONTROL_INFORM_FLUSH_SUCCEEDED); this.writeControlMessage(flush_response); break; case CH_MSG_TYPE.CHANNEL_CONTROL_REQUEST_CLOSE: Console.WriteLine(String.Format("Received CLOSE request for StreamChannel {0}", this.ID)); this.shouldBeClosed = true; //no processing overhead break; case CH_MSG_TYPE.CHANNEL_CONTROL_REQUEST_POSITION: Console.WriteLine(String.Format("Received POSITION request for StreamChannel {0}", this.ID)); break; case CH_MSG_TYPE.CHANNEL_CONTROL_REQUEST_LENGTH: Console.WriteLine(String.Format("Received LENGTH request for StreamChannel {0}", this.ID)); break; case CH_MSG_TYPE.CHANNEL_CONTROL_REQUEST_READ_TIMEOUT: Console.WriteLine(String.Format("Received READ_TIMEOUT request for StreamChannel {0}", this.ID)); break; case CH_MSG_TYPE.CHANNEL_CONTROL_REQUEST_WRITE_TIMEOUT: Console.WriteLine(String.Format("Received WRITE_TIMEOUT request for StreamChannel {0}", this.ID)); break; case CH_MSG_TYPE.CHANNEL_CONTROL_REQUEST_SEEK: int offset = Struct.extractInt32(msg); int origin = Struct.extractInt32(msg); Console.WriteLine(String.Format("Received SEEK request for StreamChannel {0}, count {1}, timeout {2}", this.ID, offset, origin)); break; case CH_MSG_TYPE.CHANNEL_CONTROL_REQUEST_WRITE: int size = Struct.extractInt32(msg); byte[] data_to_write = msg.ToArray(); //the data shouldn't be written from this thread, so the operation has to be moved to processing thread this.stream.Write(data_to_write, 0, data_to_write.Length); //return message, stating that everything has been written (should be handed by output thread) List <byte> write_response = Struct.packUInt32((UInt32)StreamChannel.CH_MSG_TYPE.CHANNEL_CONTROL_INFORM_WRITE_SUCCEEDED); write_response = Struct.packInt32(data_to_write.Length, write_response); this.writeControlMessage(write_response); break; case CH_MSG_TYPE.CHANNEL_CONTROL_REQUEST_READ: int count = Struct.extractInt32(msg); int timeout = Struct.extractInt32(msg); Console.WriteLine(String.Format("Received READ request for StreamChannel {0}, count {1}, timeout {2}", this.ID, count, timeout)); //the data shouldn't be readen from this thread, blocking would stop the input thread (has to be done from processing thread if (this.readbuf.Length < count) { this.readbuf = new byte[count]; //resize read buffer if needed } int read_size = this.stream.Read(this.readbuf, 0, count); List <Byte> read_data = new List <byte>(this.readbuf); read_data.RemoveRange(read_size, this.readbuf.Length - read_size); //return message, stating legngth of read data and the data itself (should be handed by output thread) List <byte> read_response = Struct.packUInt32((UInt32)StreamChannel.CH_MSG_TYPE.CHANNEL_CONTROL_INFORM_READ_SUCCEEDED); read_response = Struct.packInt32(read_size, read_response); read_response.AddRange(read_data); this.writeControlMessage(read_response); break; default: Console.WriteLine(String.Format("Received unknown channel message for StreamChannel {0}", this.ID)); break; } }
public void run() { List <UInt32> method_remove_list = new List <UInt32>(); //start input thread this.inputProcessingThread = new Thread(new ThreadStart(this.__processTransportLayerInput)); this.inputProcessingThread.Start(); this.outputProcessingThread = new Thread(new ThreadStart(this.__processTransportLayerOutput)); this.outputProcessingThread.Start(); while (this.running) { //this.tl.ProcessInSingle(false); //stop processing until sgnal is received while (true) { if (this.eventDataNeedsToBeProcessed.WaitOne(100) || (!running)) { break; } } //re-check if we are still running (eraly out) if (!running) { break; } /* * process channels (removing + heavy tasks) * */ //check for closed channels Monitor.Enter(this.lockChannels); ICollection keys = this.outChannels.Keys; foreach (Object key in keys) { Channel channel = (Channel)this.outChannels[key]; if (channel.shouldBeClosed && !channel.CloseRequestedForRemote) { Console.WriteLine(String.Format("OUT channel {0}, requesting close from server", channel.ID)); this.SendControlMessage(Client.CTRL_MSG_FROM_CLIENT_CHANNEL_SHOULD_CLOSE, Struct.packUInt32(channel.ID).ToArray()); channel.CloseRequestedForRemote = true; } if (channel.CloseRequestedForLocal) { this.channelsToRemove.Add(channel); } //processing for out channel in else branch } keys = this.inChannels.Keys; foreach (Object key in keys) { Channel channel = (Channel)this.inChannels[key]; if (channel.shouldBeClosed && !channel.CloseRequestedForRemote) { Console.WriteLine(String.Format("IN channel {0}, requesting close from server", channel.ID)); this.SendControlMessage(Client.CTRL_MSG_FROM_CLIENT_CHANNEL_SHOULD_CLOSE, Struct.packUInt32(channel.ID).ToArray()); channel.CloseRequestedForRemote = true; } if (channel.CloseRequestedForLocal) { //check if not already in remove list, because handled as outChannel if (!this.channelsToRemove.Contains(channel)) { this.channelsToRemove.Add(channel); } } //processing for in channel in else branch } //remove closed channels foreach (Channel channel in this.channelsToRemove) { if (this.inChannels.Contains(channel.ID)) { this.inChannels.Remove(channel.ID); } if (this.outChannels.Contains(channel.ID)) { this.outChannels.Remove(channel.ID); } channel.onClose(); Console.WriteLine(String.Format("Channel {0} closed", channel.ID)); this.SendControlMessage(Client.CTRL_MSG_FROM_CLIENT_CHANNEL_CLOSED, Struct.packUInt32(channel.ID).ToArray()); } channelsToRemove.Clear(); //if the channel itself needs processing (not input or output) do it here Monitor.Exit(this.lockChannels); /* * remove exited processes */ Monitor.Enter(this.exitedProcessesLock); foreach (ClientProcess cproc in this.exitedProcesses) { Monitor.Enter(this.pendingClientProcessesLock); this.pending_client_processes.Remove(cproc.Id); cproc.Dispose(); Monitor.Exit(this.pendingClientProcessesLock); //ToDo: inform client about process removement this.SendControlMessage(Client.CTRL_MSG_FROM_CLIENT_PROCESS_EXITED, (Struct.packUInt32((UInt32)cproc.Id)).ToArray()); //ToDo: destroy channels and inform client } this.exitedProcesses.Clear(); Monitor.Exit(this.exitedProcessesLock); /* * Process running methods */ Monitor.Enter(this.pendingMethodCallsLock); ICollection method_ids = this.pending_method_calls.Keys; foreach (UInt32 method_id in method_ids) { if (this.pending_method_calls.ContainsKey(method_id)) //we have to recheck if the method still exists in every iteration { ClientMethod method = (ClientMethod)this.pending_method_calls[method_id]; //check if method has been started, do it if not if (!method.started) { //find method implementation MethodInfo method_implementation = this.GetType().GetMethod(method.name, BindingFlags.NonPublic | BindingFlags.Instance); if (method_implementation != null) { try { byte[] method_result = (byte[])method_implementation.Invoke(this, new Object[] { method.args }); method.setResult(method_result); } catch (ClientMethodException e) { method.setError(String.Format("Method '{0}' throwed error:\n{1}", method.name, e.Message)); } catch (Exception e) { method.setError(String.Format("'{0}' exception:\n{1}", method.name, e.InnerException.Message)); Console.WriteLine("Catch block of Method invocation"); } } else { method.setError(String.Format("Method '{0}' not found!", method.name)); } } if (method.finished) { //Enqueue response and remove method from pending ones byte[] response = method.createResponse(); this.SendControlMessage(Client.CTRL_MSG_FROM_CLIENT_RUN_METHOD_RESPONSE, response); //this.pending_method_calls.Remove(method_id); //add method to remove list method_remove_list.Add(method_id); } } } Monitor.Exit(this.pendingMethodCallsLock); //remove finished methods Monitor.Enter(this.pendingMethodCallsLock); foreach (UInt32 method_id in method_remove_list) { this.pending_method_calls.Remove(method_id); } Monitor.Exit(this.pendingMethodCallsLock); } }
private void __processTransportLayerOutput() { while (this.running) { //stop processing until signal is received while (true) { if (this.eventChannelOutputNeedsToBeProcessed.WaitOne(100) || !this.running) { break; } } //abort if we aren't in run state anymore if (!this.running) { return; } Monitor.Enter(this.lockChannels); ICollection keys = this.outChannels.Keys; Console.WriteLine(String.Format("Out channel count {0}", keys.Count)); foreach (Object key in keys) { Channel channel = (Channel)this.outChannels[key]; //while (channel.hasPendingOutData()) if (channel.hasPendingOutData()) //we only process a single chunk per channel (load balancing) and we only deliver data if the channel is linked { UInt32 ch_id = (UInt32)channel.ID; if ((ch_id == 0) || channel.isLinked) // send output only if channel is linked (P4wnP1 knows about it) or it is the control channel (id 0) { byte[] data = channel.DequeueOutput(); List <byte> stream = Struct.packUInt32(ch_id); stream = Struct.packByteArray(data, stream); //Console.WriteLine("TransportLayer: trying to push channel data"); if (ch_id == 0) { this.tl.writeOutputStream(stream.ToArray(), false); } else { this.tl.writeOutputStream(stream.ToArray(), true); } } } if (channel.hasPendingOutData()) { this.eventChannelOutputNeedsToBeProcessed.Set(); //reenable event, if there's still data to process } } Monitor.Exit(this.lockChannels); } }