syntax = "proto3";

package com.apple.containerization.sandbox.v3;

import "google/protobuf/timestamp.proto";

// Context for interacting with a container's runtime environment.
service SandboxContext {
  // Mount a filesystem.
  rpc Mount(MountRequest) returns (MountResponse);
  // Unmount a filesystem.
  rpc Umount(UmountRequest) returns (UmountResponse);
  // Set an environment variable on the init process.
  rpc Setenv(SetenvRequest) returns (SetenvResponse);
  // Get an environment variable from the init process.
  rpc Getenv(GetenvRequest) returns (GetenvResponse);
  // Create a new directory inside the sandbox.
  rpc Mkdir(MkdirRequest) returns (MkdirResponse);
  // Set sysctls in the context of the sandbox.
  rpc Sysctl(SysctlRequest) returns (SysctlResponse);
  // Set time in the guest.
  rpc SetTime(SetTimeRequest) returns (SetTimeResponse);
  // Set up an emulator in the guest for a specific binary format.
  rpc SetupEmulator(SetupEmulatorRequest) returns (SetupEmulatorResponse);
  // Write data to an existing or new file.
  rpc WriteFile(WriteFileRequest) returns (WriteFileResponse);
  // Copy a file or directory between the host and guest.
  // Data transfer happens over a dedicated vsock connection;
  // the gRPC stream is used only for control/metadata.
  rpc Copy(CopyRequest) returns (stream CopyResponse);
  // Stat a path in the guest filesystem.
  rpc Stat(StatRequest) returns (StatResponse);
  // Perform a filesystem operation on a mounted filesystem.
  rpc FilesystemOperation(FilesystemOperationRequest) returns (FilesystemOperationResponse);

  // Create a new process inside the container.
  rpc CreateProcess(CreateProcessRequest) returns (CreateProcessResponse);
  // Delete an existing process inside the container.
  rpc DeleteProcess(DeleteProcessRequest) returns (DeleteProcessResponse);
  // Start the provided process.
  rpc StartProcess(StartProcessRequest) returns (StartProcessResponse);
  // Send a signal to the provided process.
  rpc KillProcess(KillProcessRequest) returns (KillProcessResponse);
  // Wait for a process to exit and return the exit code.
  rpc WaitProcess(WaitProcessRequest) returns (WaitProcessResponse);
  // Resize the tty of a given process. This will error if the process does
  // not have a pty allocated.
  rpc ResizeProcess(ResizeProcessRequest) returns (ResizeProcessResponse);
  // Close IO for a given process.
  rpc CloseProcessStdin(CloseProcessStdinRequest) returns (CloseProcessStdinResponse);

  // Get statistics for containers.
  rpc ContainerStatistics(ContainerStatisticsRequest) returns (ContainerStatisticsResponse);

  // Proxy a vsock port to a unix domain socket in the guest, or vice versa.
  rpc ProxyVsock(ProxyVsockRequest) returns (ProxyVsockResponse);
  // Stop a vsock proxy to a unix domain socket.
  rpc StopVsockProxy(StopVsockProxyRequest) returns (StopVsockProxyResponse);

  // Set the link state of a network interface.
  rpc IpLinkSet(IpLinkSetRequest) returns (IpLinkSetResponse);
  // Add an IPv4 address to a network interface.
  rpc IpAddrAdd(IpAddrAddRequest) returns (IpAddrAddResponse);
  // Add an IP route for a network interface.
  rpc IpRouteAddLink(IpRouteAddLinkRequest) returns (IpRouteAddLinkResponse);
  // Add an IP route for a network interface.
  rpc IpRouteAddDefault(IpRouteAddDefaultRequest) returns (IpRouteAddDefaultResponse);
  // Configure DNS resolver.
  rpc ConfigureDns(ConfigureDnsRequest) returns (ConfigureDnsResponse);
  // Configure /etc/hosts.
  rpc ConfigureHosts(ConfigureHostsRequest) returns (ConfigureHostsResponse);

  // Perform the sync syscall.
  rpc Sync(SyncRequest) returns (SyncResponse);
  // Send a signal to a process via the PID.
  rpc Kill(KillRequest) returns (KillResponse);
}

message Stdio {
  optional int32 stdinPort = 1;
  optional int32 stdoutPort = 2;
  optional int32 stderrPort = 3;
}

message SetupEmulatorRequest {
  string binary_path = 1;
  string name = 2;
  string type = 3;
  string offset = 4;
  string magic = 5;
  string mask = 6;
  string flags = 7;
}

message SetupEmulatorResponse {}

message SetTimeRequest {
  int64 sec = 1;
  int32 usec = 2;
}

message SetTimeResponse {}

message SysctlRequest { map<string, string> settings = 1; }

message SysctlResponse {}

message ProxyVsockRequest {
  enum Action {
    INTO = 0;
    OUT_OF = 1;
  }
  string id = 1;
  uint32 vsock_port = 2;
  string guestPath = 3;
  optional uint32 guestSocketPermissions = 4;
  Action action = 5;
}

message ProxyVsockResponse {}

message StopVsockProxyRequest { string id = 1; }

message StopVsockProxyResponse {}

message MountRequest {
  string type = 1;
  string source = 2;
  string destination = 3;
  repeated string options = 4;
}

message MountResponse {}

message UmountRequest {
  string path = 1;
  int32 flags = 2;
}

message UmountResponse {}

message SetenvRequest {
  string key = 1;
  optional string value = 2;
}

message SetenvResponse {}

message GetenvRequest { string key = 1; }

message GetenvResponse { optional string value = 1; }

message CreateProcessRequest {
  string id = 1;
  optional string containerID = 2;
  optional uint32 stdin = 3;
  optional uint32 stdout = 4;
  optional uint32 stderr = 5;
  optional string ociRuntimePath = 6;
  bytes configuration = 7;
  optional bytes options = 8;
}

message CreateProcessResponse {}

message WaitProcessRequest {
  string id = 1;
  optional string containerID = 2;
}

message WaitProcessResponse {
  int32 exitCode = 1;
  google.protobuf.Timestamp exited_at = 2;
}

message ResizeProcessRequest {
  string id = 1;
  optional string containerID = 2;
  uint32 rows = 3;
  uint32 columns = 4;
}

message ResizeProcessResponse {}

message DeleteProcessRequest {
  string id = 1;
  optional string containerID = 2;
}

message DeleteProcessResponse {}

message StartProcessRequest {
  string id = 1;
  optional string containerID = 2;
}

message StartProcessResponse { int32 pid = 1; }

message KillProcessRequest {
  string id = 1;
  optional string containerID = 2;
  int32 signal = 3;
}

message KillProcessResponse { int32 result = 1; }

message CloseProcessStdinRequest {
  string id = 1;
  optional string containerID = 2;
}

message CloseProcessStdinResponse {}

message MkdirRequest {
  string path = 1;
  bool all = 2;
  uint32 perms = 3;
}

message MkdirResponse {}

message WriteFileRequest {
  message WriteFileFlags {
    bool create_parent_dirs = 1;
    bool append = 2;
    bool create_if_missing = 3;
  }
  string path = 1;
  bytes data = 2;
  uint32 mode = 3;
  WriteFileFlags flags = 4;
}

message WriteFileResponse {}

message CopyRequest {
  enum Direction {
    // Copy from host into guest.
    COPY_IN = 0;
    // Copy from guest to host.
    COPY_OUT = 1;
  }
  // Direction of the copy operation.
  Direction direction = 1;
  // Path in the guest (destination for COPY_IN, source for COPY_OUT).
  string path = 2;
  // File mode for single-file COPY_IN (defaults to 0644 if not set).
  uint32 mode = 3;
  // Create parent directories if they don't exist.
  bool create_parents = 4;
  // Vsock port the host is listening on for data transfer.
  uint32 vsock_port = 5;
  // For COPY_IN: indicates the data arriving on vsock is a tar+gzip archive.
  bool is_archive = 6;
}

message CopyResponse {
  enum Status {
    // Transfer metadata (first message for COPY_OUT: is_archive, total_size).
    METADATA = 0;
    // Data transfer completed successfully.
    COMPLETE = 1;
  }
  // What this response represents.
  Status status = 1;
  // For COPY_OUT METADATA: indicates the data on vsock will be a tar+gzip archive.
  bool is_archive = 2;
  // For COPY_OUT METADATA: total size in bytes (0 if unknown, e.g. for archives).
  uint64 total_size = 3;
  // Non-empty if an error occurred.
  string error = 4;
}

message StatRequest { string path = 1; }

message Stat {
  uint64 dev = 1;   // st_dev:     ID of device containing file
  uint64 ino = 2;   // st_ino:     inode number
  uint32 mode = 3;  // st_mode:    file type and mode (permissions)
  uint64 nlink = 4; // st_nlink:   number of hard links
  uint32 uid = 5;   // st_uid:     user ID of owner
  uint32 gid = 6;   // st_gid:     group ID of owner
  uint64 rdev = 7;  // st_rdev:    device ID (if special file)
  int64 size = 8;   // st_size:    total size in bytes
  int64 blksize = 9;  // st_blksize: preferred block size for filesystem I/O
  int64 blocks = 10;  // st_blocks:  number of 512-byte blocks allocated
  google.protobuf.Timestamp atime = 11; // st_atim: time of last access
  google.protobuf.Timestamp mtime = 12; // st_mtim: time of last modification
  google.protobuf.Timestamp ctime = 13; // st_ctim: time of last status change
}

message StatResponse {
  Stat stat = 1;
  string error = 2; // Non-empty if stat failed.
}

message FiTrimParams {
  oneof schedule {
    OneShot one_shot = 1;
  }

  message OneShot {}
}
message FiFreezeParams {}
message FiThawParams {}

message FiTrimResult {
  uint64 trimmed_bytes = 1;
}

message FilesystemOperationRequest {
  string path = 1;
  oneof operation {
    FiTrimParams trim = 2;
    FiFreezeParams freeze = 3;
    FiThawParams thaw = 4;
  }
}

message FilesystemOperationResponse {
  oneof result {
    FiTrimResult trim = 1;
  }
}

message IpLinkSetRequest {
  string interface = 1;
  bool up = 2;
  optional uint32 mtu = 3;
}

message IpLinkSetResponse {}

message IpAddrAddRequest {
  string interface = 1;
  string ipv4Address = 2;
  optional string ipv6Address = 3;
}

message IpAddrAddResponse {}

message IpRouteAddLinkRequest {
  string interface = 1;
  string dstIpv4Addr = 2;
  string srcIpv4Addr = 3;
  optional string dstIpv6Addr = 4;
  optional string srcIpv6Addr = 5;
}

message IpRouteAddLinkResponse {}

message IpRouteAddDefaultRequest {
  string interface = 1;
  string ipv4Gateway = 2;
  optional string ipv6Gateway = 3;
}

message IpRouteAddDefaultResponse {}

message ConfigureDnsRequest {
  string location = 1;
  repeated string nameservers = 2;
  optional string domain = 3;
  repeated string searchDomains = 4;
  repeated string options = 5;
}

message ConfigureDnsResponse {}

message ConfigureHostsRequest {
  message HostsEntry {
    string ipAddress = 1;
    repeated string hostnames = 2;
    optional string comment = 3;
  }
  string location = 1;
  repeated HostsEntry entries = 2;
  optional string comment = 3;
}

message ConfigureHostsResponse {}

message SyncRequest {}
message SyncResponse {}

message KillRequest {
  int32 pid = 1;
  int32 signal = 3;
}

message KillResponse { int32 result = 1; }

// Categories of statistics that can be requested.
enum StatCategory {
  STAT_CATEGORY_UNSPECIFIED = 0;
  STAT_CATEGORY_PROCESS = 1;
  STAT_CATEGORY_MEMORY = 2;
  STAT_CATEGORY_CPU = 3;
  STAT_CATEGORY_BLOCK_IO = 4;
  STAT_CATEGORY_NETWORK = 5;
  STAT_CATEGORY_MEMORY_EVENTS = 6;
}

message ContainerStatisticsRequest {
  repeated string container_ids = 1;  // Empty = all containers
  repeated StatCategory categories = 2;  // Empty = all categories
}

message ContainerStatisticsResponse {
  repeated ContainerStats containers = 1;
}

message ContainerStats {
  string container_id = 1;
  ProcessStats process = 2;
  MemoryStats memory = 3;
  CPUStats cpu = 4;
  BlockIOStats block_io = 5;
  repeated NetworkStats networks = 6;
  MemoryEventStats memory_events = 7;
}

message ProcessStats {
  uint64 current = 1;
  uint64 limit = 2;  // 0 or max value = unlimited
}

message MemoryStats {
  uint64 usage_bytes = 1;
  uint64 limit_bytes = 2;
  uint64 swap_usage_bytes = 3;
  uint64 swap_limit_bytes = 4;
  uint64 cache_bytes = 5;
  uint64 kernel_stack_bytes = 6;
  uint64 slab_bytes = 7;
  uint64 page_faults = 8;
  uint64 major_page_faults = 9;
  uint64 inactive_file = 10;
  uint64 anon = 11;
  uint64 workingset_refault_anon = 12;
  uint64 workingset_refault_file = 13;
  uint64 pgsteal_kswapd = 14;
  uint64 pgsteal_direct = 15;
  uint64 pgsteal_khugepaged = 16;
}

message CPUStats {
  uint64 usage_usec = 1;
  uint64 user_usec = 2;
  uint64 system_usec = 3;
  uint64 throttling_periods = 4;
  uint64 throttled_periods = 5;
  uint64 throttled_time_usec = 6;
}

message BlockIOStats {
  repeated BlockIOEntry devices = 1;
}

message BlockIOEntry {
  uint64 major = 1;
  uint64 minor = 2;
  uint64 read_bytes = 3;
  uint64 write_bytes = 4;
  uint64 read_operations = 5;
  uint64 write_operations = 6;
}

message NetworkStats {
  string interface = 1;
  uint64 receivedPackets = 2;
  uint64 transmittedPackets = 3;
  uint64 receivedBytes = 4;
  uint64 transmittedBytes = 5;
  uint64 receivedErrors = 6;
  uint64 transmittedErrors = 7;
}

// Memory event counters from cgroup2's memory.events file.
message MemoryEventStats {
  // Number of times the cgroup was reclaimed due to low memory.
  uint64 low = 1;
  // Number of times the cgroup exceeded its high memory limit.
  uint64 high = 2;
  // Number of times the cgroup hit its max memory limit.
  uint64 max = 3;
  // Number of times the cgroup triggered OOM.
  uint64 oom = 4;
  // Number of processes killed by OOM killer.
  uint64 oom_kill = 5;
  // Number of times charge for memory failed because of limit.
  uint64 oom_group_kill = 6;
}
