Index: pkg/fletch_agent/lib/messages.dart |
diff --git a/pkg/fletch_agent/lib/messages.dart b/pkg/fletch_agent/lib/messages.dart |
deleted file mode 100644 |
index e1a1946b8f6cc57bdca99abebbac9aafea9c1355..0000000000000000000000000000000000000000 |
--- a/pkg/fletch_agent/lib/messages.dart |
+++ /dev/null |
@@ -1,622 +0,0 @@ |
-// Copyright (c) 2015, the Dartino project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE.md file. |
- |
-/// This comment describes the Fletch Agent's request and reply message format. |
-/// |
-/// Message requests all start with the following header: |
-/// |
-/// ----------------------------------------- |
-/// | Command (16 bits) | Version (16 bits) | |
-/// ----------------------------------------- |
-/// | Msg ID (16 bits) | Unused (16 bits) | |
-/// ----------------------------------------- |
-/// | Payload Length (32 bits) | |
-/// ----------------------------------------- |
-/// |
-/// Field descriptions: |
-/// |
-/// Command: Identifies the requested action. |
-/// Version: used to determine if the server supports the client's request. |
-/// Msg ID: used to correlate a reply with the sender. |
-/// Unused: Reserved for future uses. |
-/// Payload Length: Length of request payload in bytes. |
-/// |
-/// The request header is immediately followed by the relevant payload data for |
-/// the given command. See details for payloads below. |
-/// |
-/// All requests have a related reply with the following reply header. |
-/// |
-/// --------------------------------------- |
-/// | Msg ID (16 bits) | Result (16 bits) | |
-/// --------------------------------------- |
-/// | Payload Length (32 bits) | |
-/// --------------------------------------- |
-/// |
-/// Field descriptions: |
-/// |
-/// Msg ID: used to correlate a reply with the sender. |
-/// Result: Can be success (0) or failure (a positive number). |
-/// Payload length: Length of reply payload in bytes. |
-/// |
-/// The reply header is immediately followed by the relevant payload data for |
-/// the related request's command. See details for payloads below. |
-/// |
-/// Command descriptions: |
-/// |
-/// Below follows a description of request/reply payloads for a given command. |
-/// Each payload is always preceded by the corresponding header. |
-/// |
-/// START_VM: |
-/// Start a new Fletch VM and return the vm's id and port on which it is |
-/// listening. |
-/// Request Payload: |
-/// None. |
-/// Reply Payload on success: |
-/// --------------------- |
-/// | VM ID (32 bits) | |
-/// --------------------- |
-/// | VM Port (32 bits) | |
-/// --------------------- |
-/// Reply Payload on failure: |
-/// None. |
-/// |
-/// STOP_VM: |
-/// Stop the VM specified by the given vm id. |
-/// Request Payload: |
-/// --------------------- |
-/// | VM ID (32 bits) | |
-/// --------------------- |
-/// Reply Payload on success: |
-/// None. |
-/// Reply Payload on failure: |
-/// None. |
-/// |
-/// LIST_VMS: |
-/// This command lists the currently running Fletch VMs. |
-/// Request Payload: |
-/// None. |
-/// Reply Payload on success: |
-/// --------------------- |
-/// | VM ID (32 bits) | |
-/// --------------------- |
-/// | VM ID (32 bits) | |
-/// --------------------- |
-/// | VM ID (32 bits) | |
-/// --------------------- |
-/// ... |
-/// |
-/// Reply Payload on failure: |
-/// None. |
-/// |
-/// UPGRADE_VM: |
-/// This command is used to update the Fletch VM binary on the device. |
-/// Request Payload: |
-/// ... the vm binary bytes |
-/// |
-/// Reply Payload on success: |
-/// None. |
-/// Reply Payload on failure: |
-/// None. |
- |
-library fletch_agent.messages; |
- |
-import 'dart:convert' show ASCII; |
-import 'dart:typed_data'; |
- |
-/// Current Fletch Agent version |
-const int AGENT_VERSION = 1; |
- |
-/// Default agent port |
-const int AGENT_DEFAULT_PORT = 12121; |
- |
-/// Temporary path for the agent package used during upgrade. |
-const String PACKAGE_FILE_NAME = '/tmp/fletch-agent.deb'; |
- |
-class RequestHeader { |
- static const int START_VM = 0; |
- static const int STOP_VM = 1; |
- static const int LIST_VMS = 2; |
- static const int UPGRADE_AGENT = 3; |
- static const int FLETCH_VERSION = 4; |
- static const int SIGNAL_VM = 5; |
- |
- // Wire size (bytes) of the RequestHeader. |
- static const int HEADER_SIZE = 12; |
- |
- static int _nextMessageId = 1; |
- static int get nextMessageId { |
- if ((_nextMessageId & 0xFFFF) == 0) { |
- _nextMessageId = 1; |
- } |
- return _nextMessageId++; |
- } |
- |
- final int command; |
- final int version; |
- final int id; |
- final int reserved; |
- final int payloadLength; |
- |
- RequestHeader( |
- this.command, |
- {this.version: AGENT_VERSION, |
- int id, |
- this.reserved: 0, |
- this.payloadLength: 0}) |
- : id = id != null ? id : nextMessageId; |
- |
- factory RequestHeader.fromBuffer(ByteBuffer buffer) { |
- if (buffer.lengthInBytes < HEADER_SIZE) { |
- throw new MessageDecodeException( |
- 'Insufficient bytes (${buffer.lengthInBytes}) to decode header: ' |
- '${buffer.asUint8List()})'); |
- } |
- // The network byte order is big endian. |
- int cmd = readUint16(buffer, 0); |
- int version = readUint16(buffer, 2); |
- int id = readUint16(buffer, 4); |
- int reserved = readUint16(buffer, 6); |
- int payloadLength = readUint32(buffer, 8); |
- return new RequestHeader(cmd, version: version, id: id, reserved: reserved, |
- payloadLength: payloadLength); |
- } |
- |
- ByteBuffer toBuffer() { |
- var buffer = new Uint8List(HEADER_SIZE).buffer; |
- _writeHeader(buffer); |
- return buffer; |
- } |
- |
- void _writeHeader(ByteBuffer buffer) { |
- writeUint16(buffer, 0, command); |
- writeUint16(buffer, 2, version); |
- writeUint16(buffer, 4, id); |
- writeUint16(buffer, 6, reserved); |
- writeUint32(buffer, 8, payloadLength); |
- } |
-} |
- |
-class StartVmRequest extends RequestHeader { |
- StartVmRequest() : super(RequestHeader.START_VM); |
- |
- StartVmRequest.withHeader(RequestHeader header) |
- : super( |
- RequestHeader.START_VM, |
- version: header.version, |
- id: header.id, |
- reserved: header.reserved); |
- |
- factory StartVmRequest.fromBuffer(ByteBuffer buffer) { |
- var header = new RequestHeader.fromBuffer(buffer); |
- assert(header != null); |
- if (header.command != RequestHeader.START_VM || header.payloadLength != 0) { |
- throw new MessageDecodeException( |
- "Invalid StartVmRequest: ${buffer.asUint8List()}"); |
- } |
- return new StartVmRequest.withHeader(header); |
- } |
- |
- // A StartVmRequest has no payload so just use parent's toBuffer method. |
-} |
- |
-class StopVmRequest extends RequestHeader { |
- final int vmPid; |
- |
- StopVmRequest(this.vmPid) |
- : super(RequestHeader.STOP_VM, payloadLength: 4); |
- |
- StopVmRequest.withHeader(RequestHeader header, this.vmPid) |
- : super( |
- RequestHeader.STOP_VM, |
- version: header.version, |
- id: header.id, |
- reserved: header.reserved); |
- |
- factory StopVmRequest.fromBuffer(ByteBuffer buffer) { |
- if (buffer.lengthInBytes < RequestHeader.HEADER_SIZE + 4) { |
- throw new MessageDecodeException( |
- 'Insufficient data for a StopVmRequest: ${buffer.asUint8List()}'); |
- } |
- var header = new RequestHeader.fromBuffer(buffer); |
- if (header.command != RequestHeader.STOP_VM || header.payloadLength != 4) { |
- throw new MessageDecodeException( |
- "Invalid StopVmRequest: ${buffer.asUint8List()}"); |
- } |
- var vmPid = readUint32(buffer, RequestHeader.HEADER_SIZE); |
- return new StopVmRequest.withHeader(header, vmPid); |
- } |
- |
- ByteBuffer toBuffer() { |
- var buffer = new Uint8List(RequestHeader.HEADER_SIZE + 4).buffer; |
- _writeHeader(buffer); |
- writeUint32(buffer, RequestHeader.HEADER_SIZE, vmPid); |
- return buffer; |
- } |
-} |
- |
-class ListVmsRequest extends RequestHeader { |
- ListVmsRequest() : super(RequestHeader.LIST_VMS); |
- |
- ListVmsRequest.withHeader(RequestHeader header) |
- : super( |
- RequestHeader.LIST_VMS, |
- version: header.version, |
- id: header.id, |
- reserved: header.reserved); |
- |
- factory ListVmsRequest.fromBuffer(ByteBuffer buffer) { |
- var header = new RequestHeader.fromBuffer(buffer); |
- if (header.command != RequestHeader.LIST_VMS || header.payloadLength != 0) { |
- throw new MessageDecodeException( |
- "Invalid ListVmsRequest: ${buffer.asUint8List()}"); |
- } |
- return new ListVmsRequest.withHeader(header); |
- } |
- |
- // A ListVmsRequest has no payload so just use parent's toBuffer method. |
-} |
- |
-class UpgradeAgentRequest extends RequestHeader { |
- final List<int> binary; |
- |
- UpgradeAgentRequest(List<int> binary) |
- : super(RequestHeader.UPGRADE_AGENT, payloadLength: binary.length), |
- binary = binary; |
- |
- UpgradeAgentRequest.withHeader(RequestHeader header, List<int> binary) |
- : super( |
- RequestHeader.UPGRADE_AGENT, |
- version: header.version, |
- id: header.id, |
- reserved: header.reserved, |
- payloadLength: binary.length), |
- binary = binary; |
- |
- factory UpgradeAgentRequest.fromBuffer(ByteBuffer buffer) { |
- var header = new RequestHeader.fromBuffer(buffer); |
- if (header.command != RequestHeader.UPGRADE_AGENT) { |
- throw new MessageDecodeException( |
- 'Invalid UpgradeVmRequest: ${buffer.asUint8List()}'); |
- } |
- // TODO(wibling): figure out how to best represent the vm binary data. |
- // The below has issues since the list view is offset and hence using |
- // the underlying buffer requires the user to know the buffer is not the |
- // same length as the list. |
- var binary = buffer.asUint8List(RequestHeader.HEADER_SIZE); |
- return new UpgradeAgentRequest.withHeader(header, binary); |
- } |
- |
- ByteBuffer toBuffer() { |
- var bytes = |
- new Uint8List(RequestHeader.HEADER_SIZE + binary.length); |
- _writeHeader(bytes.buffer); |
- // TODO(wibling): This does a copy of the vm binary. Try to avoid that. |
- for (int i = 0; i < binary.length; ++i) { |
- bytes[RequestHeader.HEADER_SIZE + i] = binary[i]; |
- } |
- return bytes.buffer; |
- } |
-} |
- |
-class FletchVersionRequest extends RequestHeader { |
- FletchVersionRequest() |
- : super(RequestHeader.FLETCH_VERSION); |
- |
- FletchVersionRequest.withHeader(RequestHeader header) |
- : super( |
- RequestHeader.FLETCH_VERSION, |
- version: header.version, |
- id: header.id, |
- reserved: header.reserved); |
- |
- factory FletchVersionRequest.fromBuffer(ByteBuffer buffer) { |
- var header = new RequestHeader.fromBuffer(buffer); |
- if (header.command != RequestHeader.FLETCH_VERSION || |
- header.payloadLength != 0) { |
- throw new MessageDecodeException( |
- 'Invalid FletchVersionRequest: ${buffer.asUint8List()}'); |
- } |
- return new FletchVersionRequest.withHeader(header); |
- } |
- |
- // A FletchVersionRequest has no payload so just use parent's toBuffer method. |
-} |
- |
-class SignalVmRequest extends RequestHeader { |
- final int vmPid; |
- final int signal; |
- |
- SignalVmRequest(this.vmPid, this.signal) |
- : super(RequestHeader.SIGNAL_VM, payloadLength: 8); |
- |
- SignalVmRequest.withHeader(RequestHeader header, this.vmPid, this.signal) |
- : super( |
- RequestHeader.SIGNAL_VM, |
- version: header.version, |
- id: header.id, |
- reserved: header.reserved); |
- |
- factory SignalVmRequest.fromBuffer(ByteBuffer buffer) { |
- if (buffer.lengthInBytes < RequestHeader.HEADER_SIZE + 8) { |
- throw new MessageDecodeException( |
- 'Insufficient data for a SignalVmRequest: ${buffer.asUint8List()}'); |
- } |
- RequestHeader header = new RequestHeader.fromBuffer(buffer); |
- if (header.command != RequestHeader.SIGNAL_VM || |
- header.payloadLength != 8) { |
- throw new MessageDecodeException( |
- "Invalid SignalVmRequest: ${buffer.asUint8List()}"); |
- } |
- int vmPid = readUint32(buffer, RequestHeader.HEADER_SIZE); |
- int signal = readUint32(buffer, RequestHeader.HEADER_SIZE + 4); |
- return new SignalVmRequest.withHeader(header, vmPid, signal); |
- } |
- |
- ByteBuffer toBuffer() { |
- var buffer = new Uint8List(RequestHeader.HEADER_SIZE + 8).buffer; |
- _writeHeader(buffer); |
- writeUint32(buffer, RequestHeader.HEADER_SIZE, vmPid); |
- writeUint32(buffer, RequestHeader.HEADER_SIZE + 4, signal); |
- return buffer; |
- } |
-} |
- |
-class ReplyHeader { |
- /// Error codes. |
- static const int SUCCESS = 0; |
- static const int UNKNOWN_COMMAND = 1; |
- static const int INVALID_PAYLOAD = 2; |
- static const int UNSUPPORTED_VERSION = 3; |
- static const int START_VM_FAILED = 4; |
- static const int UNKNOWN_VM_ID = 5; |
- static const int UPGRADE_FAILED = 6; |
- |
- // Wire size (bytes) of the ReplyHeader. |
- static const int HEADER_SIZE = 8; |
- |
- final int id; |
- final int result; |
- final int payloadLength; |
- |
- ReplyHeader(this.id, this.result, {this.payloadLength: 0}); |
- |
- factory ReplyHeader.fromBuffer(ByteBuffer buffer) { |
- if (buffer == null || buffer.lengthInBytes < HEADER_SIZE) { |
- throw new MessageDecodeException( |
- 'Insufficient data for a ReplyHeader: ${buffer.asUint8List()}'); |
- } |
- var id = readUint16(buffer, 0); |
- var result = readUint16(buffer, 2); |
- var payloadLength = readUint32(buffer, 4); |
- return new ReplyHeader(id, result, payloadLength: payloadLength); |
- } |
- |
- ByteBuffer toBuffer() { |
- var buffer = new Uint8List(HEADER_SIZE).buffer; |
- _writeHeader(buffer); |
- return buffer; |
- } |
- |
- void _writeHeader(ByteBuffer buffer) { |
- writeUint16(buffer, 0, id); |
- writeUint16(buffer, 2, result); |
- writeUint32(buffer, 4, payloadLength); |
- } |
-} |
- |
-class StartVmReply extends ReplyHeader { |
- final int vmId; |
- final int vmPort; |
- |
- StartVmReply(int id, int result, {this.vmId, this.vmPort}) |
- : super( |
- id, |
- result, |
- payloadLength: result == ReplyHeader.SUCCESS ? 8 : 0); |
- |
- factory StartVmReply.fromBuffer(ByteBuffer buffer) { |
- var header = new ReplyHeader.fromBuffer(buffer); |
- int vmId; |
- int vmPort; |
- if (header.result == ReplyHeader.SUCCESS) { |
- // There must be 8 bytes of payload. |
- if (buffer.lengthInBytes < ReplyHeader.HEADER_SIZE + 8 || |
- header.payloadLength != 8) { |
- throw new MessageDecodeException( |
- "Invalid StartVmReply: ${buffer.asUint8List()}"); |
- } |
- vmId = readUint32(buffer, ReplyHeader.HEADER_SIZE); |
- vmPort = readUint32(buffer, ReplyHeader.HEADER_SIZE + 4); |
- } |
- return new StartVmReply( |
- header.id, header.result, vmId: vmId, vmPort: vmPort); |
- } |
- |
- ByteBuffer toBuffer() { |
- ByteBuffer buffer; |
- if (result == ReplyHeader.SUCCESS) { |
- buffer = new Uint8List(ReplyHeader.HEADER_SIZE + 8).buffer; |
- _writeHeader(buffer); |
- writeUint32(buffer, ReplyHeader.HEADER_SIZE, vmId); |
- writeUint32(buffer, ReplyHeader.HEADER_SIZE + 4, vmPort); |
- } else { |
- buffer = new Uint8List(ReplyHeader.HEADER_SIZE).buffer; |
- _writeHeader(buffer); |
- } |
- return buffer; |
- } |
-} |
- |
-class StopVmReply extends ReplyHeader { |
- StopVmReply(int id, int result) : super(id, result); |
- |
- factory StopVmReply.fromBuffer(ByteBuffer buffer) { |
- ReplyHeader header = new ReplyHeader.fromBuffer(buffer); |
- if (header.payloadLength != 0) { |
- throw new MessageDecodeException( |
- "Invalid payload length in StopVmReply: ${buffer.asUint8List()}"); |
- } |
- return new StopVmReply(header.id, header.result); |
- } |
- |
- // The STOP_VM reply has no payload, so leverage parent's toBuffer method. |
-} |
- |
-class ListVmsReply extends ReplyHeader { |
- final List<int> vmIds; |
- |
- ListVmsReply(int id, int result, {List<int> vmIds}) |
- : super(id, result, payloadLength: vmIds != null ? vmIds.length * 4 : 0), |
- vmIds = vmIds; |
- |
- factory ListVmsReply.fromBuffer(ByteBuffer buffer) { |
- var header = new ReplyHeader.fromBuffer(buffer); |
- List<int> vmIds = []; |
- if (header.result == ReplyHeader.SUCCESS) { |
- for (int i = 0; i < header.payloadLength ~/ 4; ++i) { |
- vmIds.add(readUint32(buffer, ReplyHeader.HEADER_SIZE + (i * 4))); |
- } |
- } |
- return new ListVmsReply(header.id, header.result, vmIds: vmIds); |
- } |
- |
- ByteBuffer toBuffer() { |
- ByteBuffer buffer; |
- if (result == ReplyHeader.SUCCESS) { |
- int numVms = vmIds != null ? vmIds.length : 0; |
- buffer = new Uint8List(ReplyHeader.HEADER_SIZE + (numVms * 4)).buffer; |
- _writeHeader(buffer); |
- for (int i = 0; i < numVms; ++i) { |
- writeUint32(buffer, ReplyHeader.HEADER_SIZE + (i * 4), vmIds[i]); |
- } |
- } else { |
- buffer = new Uint8List(ReplyHeader.HEADER_SIZE).buffer; |
- _writeHeader(buffer); |
- } |
- return buffer; |
- } |
-} |
- |
-class UpgradeAgentReply extends ReplyHeader { |
- |
- UpgradeAgentReply(int id, int result) : super(id, result); |
- |
- factory UpgradeAgentReply.fromBuffer(ByteBuffer buffer) { |
- ReplyHeader header = new ReplyHeader.fromBuffer(buffer); |
- if (header.payloadLength != 0) { |
- throw new MessageDecodeException( |
- "Invalid payload length in UpgradeAgentReply: ${buffer.asUint8List()}"); |
- } |
- return new UpgradeAgentReply(header.id, header.result); |
- } |
- |
- // The UPGRADE_AGENT reply has no payload, so leverage parent's toBuffer method. |
-} |
- |
-class FletchVersionReply extends ReplyHeader { |
- final String fletchVersion; |
- |
- FletchVersionReply(int id, int result, {String version}) |
- : super(id, result, payloadLength: version != null ? version.length : 0), |
- fletchVersion = version { |
- if (result == ReplyHeader.SUCCESS && version == null) { |
- throw new MessageEncodeException( |
- "Missing version for FletchVersionReply."); |
- } |
- } |
- |
- factory FletchVersionReply.fromBuffer(ByteBuffer buffer) { |
- var header = new ReplyHeader.fromBuffer(buffer); |
- String version; |
- if (header.result == ReplyHeader.SUCCESS) { |
- int payloadLength = header.payloadLength; |
- if (payloadLength == 0 || |
- buffer.lengthInBytes < ReplyHeader.HEADER_SIZE + payloadLength) { |
- throw new MessageDecodeException( |
- "Invalid FletchVersionReply: ${buffer.asUint8List()}"); |
- } |
- version = ASCII.decode(buffer.asUint8List(ReplyHeader.HEADER_SIZE)); |
- } |
- return new FletchVersionReply(header.id, header.result, version: version); |
- } |
- |
- ByteBuffer toBuffer() { |
- ByteBuffer buffer; |
- if (result == ReplyHeader.SUCCESS) { |
- Uint8List message = new Uint8List( |
- ReplyHeader.HEADER_SIZE + fletchVersion.length); |
- _writeHeader(message.buffer); |
- List<int> encodedVersion = ASCII.encode(fletchVersion); |
- assert(encodedVersion != null); |
- assert(fletchVersion.length == encodedVersion.length); |
- message.setRange( |
- ReplyHeader.HEADER_SIZE, |
- ReplyHeader.HEADER_SIZE + encodedVersion.length, |
- encodedVersion); |
- buffer = message.buffer; |
- } else { |
- buffer = new Uint8List(ReplyHeader.HEADER_SIZE).buffer; |
- _writeHeader(buffer); |
- } |
- return buffer; |
- } |
-} |
- |
-class SignalVmReply extends ReplyHeader { |
- SignalVmReply(int id, int result) |
- : super(id, result); |
- |
- factory SignalVmReply.fromBuffer(ByteBuffer buffer) { |
- ReplyHeader header = new ReplyHeader.fromBuffer(buffer); |
- if (header.payloadLength != 0) { |
- throw new MessageDecodeException( |
- "Invalid payload length in SignalVmReply: ${buffer.asUint8List()}"); |
- } |
- return new SignalVmReply(header.id, header.result); |
- } |
- |
- // The SIGNAL_VM reply has no payload, so leverage parent's toBuffer method. |
- // TODO(wibling): Figure out how to return exitcode from vm on exit. |
-} |
- |
-// Utility methods to read and write 16 and 32 bit entities from/to big endian. |
-int readUint16(ByteBuffer buffer, int offset) { |
- // Goto right offset |
- var b = buffer.asUint8List(offset, 2); |
- return b[0] << 8 | b[1]; |
-} |
- |
-void writeUint16(ByteBuffer buffer, int offset, int value) { |
- // Goto right offset |
- var b = buffer.asUint8List(offset, 2); |
- b[0] = value >> 8 & 0xff; |
- b[1] = value & 0xff; |
-} |
- |
-int readUint32(ByteBuffer buffer, int offset) { |
- // Goto right offset |
- var b = buffer.asUint8List(offset, 4); |
- return b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3]; |
-} |
- |
-void writeUint32(ByteBuffer buffer, int offset, int value) { |
- // Goto right offset |
- var b = buffer.asUint8List(offset, 4); |
- b[0] = value >> 24 & 0xff; |
- b[1] = value >> 16 & 0xff; |
- b[2] = value >> 8 & 0xff; |
- b[3] = value & 0xff; |
-} |
- |
-class MessageDecodeException implements Exception { |
- final String message; |
- MessageDecodeException(this.message); |
- String toString() => 'MessageDecodeException($message)'; |
-} |
- |
-class MessageEncodeException implements Exception { |
- final String message; |
- MessageEncodeException(this.message); |
- String toString() => 'MessageEncodeException($message)'; |
-} |