├── .gitignore ├── src ├── rust_criu_protobuf │ └── mod.rs ├── bin.rs └── lib.rs ├── .github ├── dependabot.yml └── workflows │ └── test.yml ├── Cargo.toml ├── README.md ├── LICENSE ├── test └── piggie.c └── proto └── rpc.proto /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | test/piggie 4 | -------------------------------------------------------------------------------- /src/rust_criu_protobuf/mod.rs: -------------------------------------------------------------------------------- 1 | // @generated 2 | 3 | pub mod rpc; 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-criu" 3 | version = "0.4.0" 4 | edition = "2021" 5 | description = "Rust bindings for CRIU" 6 | repository = "https://github.com/checkpoint-restore/rust-criu" 7 | readme = "README.md" 8 | license-file = "LICENSE" 9 | 10 | [dependencies] 11 | libc = "0.2" 12 | anyhow = "1.0.56" 13 | protobuf = "= 3.2.0" 14 | 15 | [build-dependencies] 16 | protobuf-codegen = "= 3.2.0" 17 | 18 | [lib] 19 | name = "rust_criu" 20 | path = "src/lib.rs" 21 | 22 | [[bin]] 23 | name = "rust-criu-test" 24 | path = "src/bin.rs" 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![crates.io](https://img.shields.io/crates/v/rust-criu.svg)](https://crates.io/crates/rust-criu) 2 | [![ci](https://github.com/checkpoint-restore/rust-criu/actions/workflows/test.yml/badge.svg)](https://github.com/checkpoint-restore/rust-criu/actions) 3 | 4 | # rust-criu 5 | 6 | `rust-criu` provides an interface to use [CRIU](https://criu.org/) in the 7 | same way as [go-criu](https://github.com/checkpoint-restore/go-criu) does. 8 | 9 | ## Generate protobuf bindings 10 | 11 | The CRIU RPC protobuf bindings are pre-generated and part of the rust-criu 12 | repository. The bindings can be re-generated with 13 | ```shell 14 | $ GENERATE_PROTOBUF=1 cargo build 15 | ``` 16 | 17 | ## Run tests 18 | 19 | To run the included tests please use the following command to build `rust-criu`: 20 | ``` 21 | $ GENERATE_TEST_PROCESS=1 cargo build 22 | $ sudo target/debug/rust-criu-test /path/to/criu/binary 23 | ``` 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 Adrian Reber 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-20.04 8 | 9 | steps: 10 | 11 | - name: checkout 12 | uses: actions/checkout@v2 13 | 14 | - name: install criu 15 | run: | 16 | # Latest development CRIU 17 | sudo apt-get install -y libprotobuf-dev libprotobuf-c-dev protobuf-c-compiler protobuf-compiler python-protobuf libnl-3-dev libnet-dev libcap-dev 18 | git clone --single-branch -b criu-dev https://github.com/checkpoint-restore/criu.git 19 | make -C criu -j $(nproc) 20 | # Latest CRIU release is already part of the github actions image 21 | 22 | - name: Build 23 | run: cargo build --verbose 24 | 25 | - name: Run clippy 26 | run: cargo clippy --verbose --all-targets --all-features -- -D warnings 27 | 28 | - name: Run fmt 29 | run: cargo fmt --all -- --check 30 | 31 | - name: Build with generate enabled 32 | run: GENERATE_PROTOBUF=1 GENERATE_TEST_PROCESS=1 cargo build --verbose 33 | 34 | - name: Run tests 35 | run: sudo target/debug/rust-criu-test criu/criu/criu 36 | -------------------------------------------------------------------------------- /test/piggie.c: -------------------------------------------------------------------------------- 1 | /* Taken from https://raw.githubusercontent.com/checkpoint-restore/go-criu/master/test/piggie/piggie.c */ 2 | 3 | #define _GNU_SOURCE 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define STKS (4*4096) 12 | 13 | #ifndef CLONE_NEWPID 14 | #define CLONE_NEWPID 0x20000000 15 | #endif 16 | 17 | static int do_test(void *logf) 18 | { 19 | int fd, i = 0; 20 | 21 | setsid(); 22 | 23 | close(0); 24 | close(1); 25 | close(2); 26 | 27 | fd = open("/dev/null", O_RDONLY); 28 | if (fd != 0) { 29 | dup2(fd, 0); 30 | close(fd); 31 | } 32 | 33 | fd = open(logf, O_WRONLY | O_TRUNC | O_CREAT, 0600); 34 | dup2(fd, 1); 35 | dup2(fd, 2); 36 | if (fd != 1 && fd != 2) 37 | close(fd); 38 | 39 | while (1) { 40 | sleep(1); 41 | printf("%d\n", i++); 42 | fflush(stdout); 43 | } 44 | 45 | return 0; 46 | } 47 | 48 | int main(int argc, char **argv) 49 | { 50 | int pid; 51 | void *stk; 52 | 53 | stk = mmap(NULL, STKS, PROT_READ | PROT_WRITE, 54 | MAP_PRIVATE | MAP_ANON | MAP_GROWSDOWN, 0, 0); 55 | pid = clone(do_test, stk + STKS, SIGCHLD | CLONE_NEWPID, argv[1]); 56 | if (pid < 0) { 57 | fprintf(stderr, "clone() failed: %m\n"); 58 | return 1; 59 | } 60 | printf("%d", pid); 61 | 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /src/bin.rs: -------------------------------------------------------------------------------- 1 | #![deny(warnings)] 2 | 3 | use std::os::unix::io::AsRawFd; 4 | use std::path::Path; 5 | 6 | fn main() { 7 | let args: Vec = std::env::args().collect(); 8 | 9 | if args.len() != 2 { 10 | println!("Need exactly one parameter: path to a criu binary"); 11 | std::process::exit(1); 12 | } 13 | 14 | let criu_bin_path = args[1].clone(); 15 | if !Path::new(&criu_bin_path).is_file() { 16 | println!("Invalid path to a criu binary"); 17 | std::process::exit(1); 18 | } 19 | 20 | let mut criu = rust_criu::Criu::new().unwrap(); 21 | match criu.get_criu_version() { 22 | Ok(version) => println!("Version from CRIU found in $PATH: {}", version), 23 | Err(e) => println!("{:#?}", e), 24 | }; 25 | 26 | criu = rust_criu::Criu::new_with_criu_path(criu_bin_path).unwrap(); 27 | match criu.get_criu_version() { 28 | Ok(version) => println!("Version from {}: {}", args[1], version), 29 | Err(e) => println!("{:#?}", e), 30 | }; 31 | 32 | let pid = match std::process::Command::new("test/piggie").output() { 33 | Ok(p) => String::from_utf8_lossy(&p.stdout).parse().unwrap_or(0), 34 | Err(e) => panic!("Starting test process failed ({:#?})", e), 35 | }; 36 | 37 | criu.set_pid(pid); 38 | 39 | if let Err(e) = std::fs::create_dir("test/images") { 40 | if e.kind() != std::io::ErrorKind::AlreadyExists { 41 | panic!( 42 | "Creating image directory 'test/images' failed with {:#?}", 43 | e 44 | ); 45 | } 46 | } 47 | 48 | let directory = std::fs::File::open(String::from("test/images")).unwrap(); 49 | criu.set_images_dir_fd(directory.as_raw_fd()); 50 | // Using a non-default log_file name to be able to check if it has been created. 51 | criu.set_log_file("dumppp.log".to_string()); 52 | criu.set_log_level(4); 53 | println!("Dumping PID {}", pid); 54 | if let Err(e) = criu.dump() { 55 | panic!("Dumping process failed with {:#?}", e); 56 | } 57 | 58 | if !std::path::Path::new("test/images/dumppp.log").exists() { 59 | panic!("Error: Expected log file 'test/images/dumppp.log' missing."); 60 | } 61 | 62 | // Need to set all values again as everything is being cleared after success. 63 | criu.set_images_dir_fd(directory.as_raw_fd()); 64 | criu.set_log_level(4); 65 | criu.set_log_file("restoreee.log".to_string()); 66 | println!("Restoring PID {}", pid); 67 | if let Err(e) = criu.restore() { 68 | panic!("Restoring process failed with {:#?}", e); 69 | } 70 | if !std::path::Path::new("test/images/restoreee.log").exists() { 71 | panic!("Error: Expected log file 'test/images/restoreee.log' missing."); 72 | } 73 | 74 | println!("Cleaning up"); 75 | if let Err(e) = std::fs::remove_dir_all("test/images") { 76 | panic!( 77 | "Removing image directory 'test/images' failed with {:#?}", 78 | e 79 | ); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /proto/rpc.proto: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | syntax = "proto2"; 4 | 5 | message criu_page_server_info { 6 | optional string address = 1; 7 | optional int32 port = 2; 8 | optional int32 pid = 3; 9 | optional int32 fd = 4; 10 | } 11 | 12 | message criu_veth_pair { 13 | required string if_in = 1; 14 | required string if_out = 2; 15 | }; 16 | 17 | message ext_mount_map { 18 | required string key = 1; 19 | required string val = 2; 20 | }; 21 | 22 | message join_namespace { 23 | required string ns = 1; 24 | required string ns_file = 2; 25 | optional string extra_opt = 3; 26 | } 27 | 28 | message inherit_fd { 29 | required string key = 1; 30 | required int32 fd = 2; 31 | }; 32 | 33 | message cgroup_root { 34 | optional string ctrl = 1; 35 | required string path = 2; 36 | }; 37 | 38 | message unix_sk { 39 | required uint32 inode = 1; 40 | }; 41 | 42 | enum criu_cg_mode { 43 | IGNORE = 0; 44 | CG_NONE = 1; 45 | PROPS = 2; 46 | SOFT = 3; 47 | FULL = 4; 48 | STRICT = 5; 49 | DEFAULT = 6; 50 | }; 51 | 52 | enum criu_pre_dump_mode { 53 | SPLICE = 1; 54 | VM_READ = 2; 55 | }; 56 | 57 | message criu_opts { 58 | required int32 images_dir_fd = 1; 59 | optional int32 pid = 2; /* if not set on dump, will dump requesting process */ 60 | 61 | optional bool leave_running = 3; 62 | optional bool ext_unix_sk = 4; 63 | optional bool tcp_established = 5; 64 | optional bool evasive_devices = 6; 65 | optional bool shell_job = 7; 66 | optional bool file_locks = 8; 67 | optional int32 log_level = 9 [default = 2]; 68 | optional string log_file = 10; /* No subdirs are allowed. Consider using work-dir */ 69 | 70 | optional criu_page_server_info ps = 11; 71 | 72 | optional bool notify_scripts = 12; 73 | 74 | optional string root = 13; 75 | optional string parent_img = 14; 76 | optional bool track_mem = 15; 77 | optional bool auto_dedup = 16; 78 | 79 | optional int32 work_dir_fd = 17; 80 | optional bool link_remap = 18; 81 | repeated criu_veth_pair veths = 19; /* DEPRECATED, use external instead */ 82 | 83 | optional uint32 cpu_cap = 20 [default = 0xffffffff]; 84 | optional bool force_irmap = 21; 85 | repeated string exec_cmd = 22; 86 | 87 | repeated ext_mount_map ext_mnt = 23; /* DEPRECATED, use external instead */ 88 | optional bool manage_cgroups = 24; /* backward compatibility */ 89 | repeated cgroup_root cg_root = 25; 90 | 91 | optional bool rst_sibling = 26; /* swrk only */ 92 | repeated inherit_fd inherit_fd = 27; /* swrk only */ 93 | 94 | optional bool auto_ext_mnt = 28; 95 | optional bool ext_sharing = 29; 96 | optional bool ext_masters = 30; 97 | 98 | repeated string skip_mnt = 31; 99 | repeated string enable_fs = 32; 100 | 101 | repeated unix_sk unix_sk_ino = 33; /* DEPRECATED, use external instead */ 102 | 103 | optional criu_cg_mode manage_cgroups_mode = 34; 104 | optional uint32 ghost_limit = 35 [default = 0x100000]; 105 | repeated string irmap_scan_paths = 36; 106 | repeated string external = 37; 107 | optional uint32 empty_ns = 38; 108 | repeated join_namespace join_ns = 39; 109 | 110 | optional string cgroup_props = 41; 111 | optional string cgroup_props_file = 42; 112 | repeated string cgroup_dump_controller = 43; 113 | 114 | optional string freeze_cgroup = 44; 115 | optional uint32 timeout = 45; 116 | optional bool tcp_skip_in_flight = 46; 117 | optional bool weak_sysctls = 47; 118 | optional bool lazy_pages = 48; 119 | optional int32 status_fd = 49; 120 | optional bool orphan_pts_master = 50; 121 | optional string config_file = 51; 122 | optional bool tcp_close = 52; 123 | optional string lsm_profile = 53; 124 | optional string tls_cacert = 54; 125 | optional string tls_cacrl = 55; 126 | optional string tls_cert = 56; 127 | optional string tls_key = 57; 128 | optional bool tls = 58; 129 | optional bool tls_no_cn_verify = 59; 130 | optional string cgroup_yard = 60; 131 | optional criu_pre_dump_mode pre_dump_mode = 61 [default = SPLICE]; 132 | optional int32 pidfd_store_sk = 62; 133 | optional string lsm_mount_context = 63; 134 | /* optional bool check_mounts = 128; */ 135 | } 136 | 137 | message criu_dump_resp { 138 | optional bool restored = 1; 139 | } 140 | 141 | message criu_restore_resp { 142 | required int32 pid = 1; 143 | } 144 | 145 | message criu_notify { 146 | optional string script = 1; 147 | optional int32 pid = 2; 148 | } 149 | 150 | enum criu_req_type { 151 | EMPTY = 0; 152 | DUMP = 1; 153 | RESTORE = 2; 154 | CHECK = 3; 155 | PRE_DUMP = 4; 156 | PAGE_SERVER = 5; 157 | 158 | NOTIFY = 6; 159 | 160 | CPUINFO_DUMP = 7; 161 | CPUINFO_CHECK = 8; 162 | 163 | FEATURE_CHECK = 9; 164 | 165 | VERSION = 10; 166 | 167 | WAIT_PID = 11; 168 | PAGE_SERVER_CHLD = 12; 169 | } 170 | 171 | /* 172 | * List of features which can queried via 173 | * CRIU_REQ_TYPE__FEATURE_CHECK 174 | */ 175 | message criu_features { 176 | optional bool mem_track = 1; 177 | optional bool lazy_pages = 2; 178 | optional bool pidfd_store = 3; 179 | } 180 | 181 | /* 182 | * Request -- each type corresponds to must-be-there 183 | * request arguments of respective type 184 | */ 185 | 186 | message criu_req { 187 | required criu_req_type type = 1; 188 | 189 | optional criu_opts opts = 2; 190 | optional bool notify_success = 3; 191 | 192 | /* 193 | * When set service won't close the connection but 194 | * will wait for more req-s to appear. Works not 195 | * for all request types. 196 | */ 197 | optional bool keep_open = 4; 198 | /* 199 | * 'features' can be used to query which features 200 | * are supported by the installed criu/kernel 201 | * via RPC. 202 | */ 203 | optional criu_features features = 5; 204 | 205 | /* 'pid' is used for WAIT_PID */ 206 | optional uint32 pid = 6; 207 | } 208 | 209 | /* 210 | * Response -- it states whether the request was served 211 | * and additional request-specific information 212 | */ 213 | 214 | message criu_resp { 215 | required criu_req_type type = 1; 216 | required bool success = 2; 217 | 218 | optional criu_dump_resp dump = 3; 219 | optional criu_restore_resp restore = 4; 220 | optional criu_notify notify = 5; 221 | optional criu_page_server_info ps = 6; 222 | 223 | optional int32 cr_errno = 7; 224 | optional criu_features features = 8; 225 | optional string cr_errmsg = 9; 226 | optional criu_version version = 10; 227 | 228 | optional int32 status = 11; 229 | } 230 | 231 | /* Answer for criu_req_type.VERSION requests */ 232 | message criu_version { 233 | required int32 major_number = 1; 234 | required int32 minor_number = 2; 235 | optional string gitid = 3; 236 | optional int32 sublevel = 4; 237 | optional int32 extra = 5; 238 | optional string name = 6; 239 | } 240 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | mod rust_criu_protobuf; 2 | 3 | use anyhow::{Context, Result}; 4 | use protobuf::Message; 5 | use rust_criu_protobuf::rpc; 6 | use std::error::Error; 7 | use std::fs::File; 8 | use std::io::{Read, Write}; 9 | use std::os::unix::io::FromRawFd; 10 | use std::process::Command; 11 | 12 | #[derive(Clone)] 13 | pub enum CgMode { 14 | IGNORE = 0, 15 | NONE = 1, 16 | PROPS = 2, 17 | SOFT = 3, 18 | FULL = 4, 19 | STRICT = 5, 20 | DEFAULT = 6, 21 | } 22 | 23 | impl CgMode { 24 | pub fn from(value: i32) -> CgMode { 25 | match value { 26 | 0 => Self::IGNORE, 27 | 1 => Self::NONE, 28 | 2 => Self::PROPS, 29 | 3 => Self::SOFT, 30 | 4 => Self::FULL, 31 | 5 => Self::STRICT, 32 | 6 => Self::DEFAULT, 33 | _ => Self::DEFAULT, 34 | } 35 | } 36 | } 37 | 38 | #[derive(Clone)] 39 | pub struct Criu { 40 | criu_path: String, 41 | sv: [i32; 2], 42 | pid: i32, 43 | images_dir_fd: i32, 44 | log_level: i32, 45 | log_file: Option, 46 | external_mounts: Vec<(String, String)>, 47 | orphan_pts_master: Option, 48 | root: Option, 49 | leave_running: Option, 50 | ext_unix_sk: Option, 51 | shell_job: Option, 52 | tcp_established: Option, 53 | file_locks: Option, 54 | manage_cgroups: Option, 55 | work_dir_fd: i32, 56 | freeze_cgroup: Option, 57 | cgroups_mode: Option, 58 | cgroup_props: Option, 59 | } 60 | 61 | impl Criu { 62 | pub fn new() -> Result> { 63 | Criu::new_with_criu_path(String::from("criu")) 64 | } 65 | 66 | pub fn new_with_criu_path(path_to_criu: String) -> Result> { 67 | Ok(Self { 68 | criu_path: path_to_criu, 69 | sv: [-1, -1], 70 | pid: -1, 71 | images_dir_fd: -1, 72 | log_level: -1, 73 | log_file: None, 74 | external_mounts: Vec::new(), 75 | orphan_pts_master: None, 76 | root: None, 77 | leave_running: None, 78 | ext_unix_sk: None, 79 | shell_job: None, 80 | tcp_established: None, 81 | file_locks: None, 82 | manage_cgroups: None, 83 | work_dir_fd: -1, 84 | freeze_cgroup: None, 85 | cgroups_mode: None, 86 | cgroup_props: None, 87 | }) 88 | } 89 | 90 | pub fn get_criu_version(&mut self) -> Result> { 91 | let response = self.do_swrk_with_response(rpc::Criu_req_type::VERSION, None)?; 92 | 93 | let mut version: u32 = (response.version.major_number() * 10000) 94 | .try_into() 95 | .context("parsing criu version failed")?; 96 | version += (response.version.minor_number() * 100) as u32; 97 | version += response.version.sublevel() as u32; 98 | 99 | if response.version.has_gitid() { 100 | // taken from runc: if it is a git release -> increase minor by 1 101 | version -= version % 100; 102 | version += 100; 103 | } 104 | 105 | Ok(version) 106 | } 107 | 108 | fn do_swrk_with_response( 109 | &mut self, 110 | request_type: rpc::Criu_req_type, 111 | criu_opts: Option, 112 | ) -> Result> { 113 | if unsafe { 114 | libc::socketpair( 115 | libc::AF_LOCAL, 116 | libc::SOCK_SEQPACKET, 117 | 0, 118 | self.sv.as_mut_ptr(), 119 | ) != 0 120 | } { 121 | return Err("libc::socketpair failed".into()); 122 | } 123 | 124 | let mut criu = Command::new(self.criu_path.clone()) 125 | .arg("swrk") 126 | .arg(format!("{}", self.sv[1])) 127 | .spawn() 128 | .with_context(|| { 129 | format!( 130 | "executing criu binary for swrk using path {:?} failed", 131 | self.criu_path 132 | ) 133 | })?; 134 | 135 | let mut req = rpc::Criu_req::new(); 136 | req.set_type(request_type); 137 | 138 | if let Some(co) = criu_opts { 139 | req.opts = protobuf::MessageField::some(co); 140 | } 141 | 142 | let mut f = unsafe { File::from_raw_fd(self.sv[0]) }; 143 | 144 | f.write_all( 145 | &req.write_to_bytes() 146 | .context("writing protobuf request to byte vec failed")?, 147 | ) 148 | .with_context(|| { 149 | format!( 150 | "writing protobuf request to file (fd : {}) failed", 151 | self.sv[0] 152 | ) 153 | })?; 154 | 155 | // 2*4096 taken from go-criu 156 | let mut buffer = [0; 2 * 4096]; 157 | 158 | let read = f.read(&mut buffer[..]).with_context(|| { 159 | format!( 160 | "reading criu response from file (fd :{}) failed", 161 | self.sv[0] 162 | ) 163 | })?; 164 | 165 | let response: rpc::Criu_resp = 166 | Message::parse_from_bytes(&buffer[..read]).context("parsing criu response failed")?; 167 | 168 | if !response.success() { 169 | criu.kill() 170 | .context("killing criu process (due to failed request) failed")?; 171 | return Result::Err( 172 | format!( 173 | "CRIU RPC request failed with message:{} error:{}", 174 | response.cr_errmsg(), 175 | response.cr_errno() 176 | ) 177 | .into(), 178 | ); 179 | } 180 | 181 | if response.type_() != request_type { 182 | criu.kill() 183 | .context("killing criu process (due to incorrect response) failed")?; 184 | return Result::Err( 185 | format!("Unexpected CRIU RPC response ({:?})", response.type_()).into(), 186 | ); 187 | } 188 | 189 | criu.kill().context("killing criu process failed")?; 190 | Result::Ok(response) 191 | } 192 | 193 | pub fn set_pid(&mut self, pid: i32) { 194 | self.pid = pid; 195 | } 196 | 197 | pub fn set_images_dir_fd(&mut self, fd: i32) { 198 | self.images_dir_fd = fd; 199 | } 200 | 201 | pub fn set_log_level(&mut self, log_level: i32) { 202 | self.log_level = log_level; 203 | } 204 | 205 | pub fn set_log_file(&mut self, log_file: String) { 206 | self.log_file = Some(log_file); 207 | } 208 | 209 | pub fn set_external_mount(&mut self, key: String, value: String) { 210 | self.external_mounts.push((key, value)); 211 | } 212 | 213 | pub fn set_orphan_pts_master(&mut self, orphan_pts_master: bool) { 214 | self.orphan_pts_master = Some(orphan_pts_master); 215 | } 216 | 217 | pub fn set_root(&mut self, root: String) { 218 | self.root = Some(root); 219 | } 220 | 221 | pub fn set_leave_running(&mut self, leave_running: bool) { 222 | self.leave_running = Some(leave_running); 223 | } 224 | 225 | pub fn set_ext_unix_sk(&mut self, ext_unix_sk: bool) { 226 | self.ext_unix_sk = Some(ext_unix_sk); 227 | } 228 | 229 | pub fn set_shell_job(&mut self, shell_job: bool) { 230 | self.shell_job = Some(shell_job); 231 | } 232 | 233 | pub fn set_tcp_established(&mut self, tcp_established: bool) { 234 | self.tcp_established = Some(tcp_established); 235 | } 236 | 237 | pub fn set_file_locks(&mut self, file_locks: bool) { 238 | self.file_locks = Some(file_locks); 239 | } 240 | 241 | pub fn set_manage_cgroups(&mut self, manage_cgroups: bool) { 242 | self.manage_cgroups = Some(manage_cgroups); 243 | } 244 | 245 | pub fn set_work_dir_fd(&mut self, fd: i32) { 246 | self.work_dir_fd = fd; 247 | } 248 | 249 | pub fn set_freeze_cgroup(&mut self, freeze_cgroup: String) { 250 | self.freeze_cgroup = Some(freeze_cgroup); 251 | } 252 | 253 | pub fn cgroups_mode(&mut self, mode: CgMode) { 254 | self.cgroups_mode = Some(mode); 255 | } 256 | 257 | pub fn set_cgroup_props(&mut self, props: String) { 258 | self.cgroup_props = Some(props); 259 | } 260 | 261 | fn fill_criu_opts(&mut self, criu_opts: &mut rpc::Criu_opts) { 262 | if self.pid != -1 { 263 | criu_opts.set_pid(self.pid); 264 | } 265 | 266 | if self.images_dir_fd != -1 { 267 | criu_opts.set_images_dir_fd(self.images_dir_fd); 268 | } 269 | 270 | if self.log_level != -1 { 271 | criu_opts.set_log_level(self.log_level); 272 | } 273 | 274 | if self.log_file.is_some() { 275 | criu_opts.set_log_file(self.log_file.clone().unwrap()); 276 | } 277 | 278 | if !self.external_mounts.is_empty() { 279 | let mut external_mounts = Vec::new(); 280 | for e in &self.external_mounts { 281 | let mut external_mount = rpc::Ext_mount_map::new(); 282 | external_mount.set_key(e.0.clone()); 283 | external_mount.set_val(e.1.clone()); 284 | external_mounts.push(external_mount); 285 | } 286 | self.external_mounts.clear(); 287 | criu_opts.ext_mnt = external_mounts; 288 | } 289 | 290 | if self.orphan_pts_master.is_some() { 291 | criu_opts.set_orphan_pts_master(self.orphan_pts_master.unwrap()); 292 | } 293 | 294 | if self.root.is_some() { 295 | criu_opts.set_root(self.root.clone().unwrap()); 296 | } 297 | 298 | if self.leave_running.is_some() { 299 | criu_opts.set_leave_running(self.leave_running.unwrap()); 300 | } 301 | 302 | if self.ext_unix_sk.is_some() { 303 | criu_opts.set_ext_unix_sk(self.ext_unix_sk.unwrap()); 304 | } 305 | 306 | if self.shell_job.is_some() { 307 | criu_opts.set_shell_job(self.shell_job.unwrap()); 308 | } 309 | 310 | if self.tcp_established.is_some() { 311 | criu_opts.set_tcp_established(self.tcp_established.unwrap()); 312 | } 313 | 314 | if self.file_locks.is_some() { 315 | criu_opts.set_file_locks(self.file_locks.unwrap()); 316 | } 317 | 318 | if self.manage_cgroups.is_some() { 319 | criu_opts.set_manage_cgroups(self.manage_cgroups.unwrap()); 320 | } 321 | 322 | if self.work_dir_fd != -1 { 323 | criu_opts.set_work_dir_fd(self.work_dir_fd); 324 | } 325 | 326 | if self.freeze_cgroup.is_some() { 327 | criu_opts.set_freeze_cgroup(self.freeze_cgroup.clone().unwrap()); 328 | } 329 | 330 | if self.cgroups_mode.is_some() { 331 | let mode = match self.cgroups_mode.as_ref().unwrap() { 332 | CgMode::IGNORE => rpc::Criu_cg_mode::IGNORE, 333 | CgMode::NONE => rpc::Criu_cg_mode::CG_NONE, 334 | CgMode::PROPS => rpc::Criu_cg_mode::PROPS, 335 | CgMode::SOFT => rpc::Criu_cg_mode::SOFT, 336 | CgMode::FULL => rpc::Criu_cg_mode::FULL, 337 | CgMode::STRICT => rpc::Criu_cg_mode::STRICT, 338 | CgMode::DEFAULT => rpc::Criu_cg_mode::DEFAULT, 339 | }; 340 | criu_opts.set_manage_cgroups_mode(mode); 341 | } 342 | 343 | if self.cgroup_props.is_some() { 344 | criu_opts.set_cgroup_props(self.cgroup_props.clone().unwrap()); 345 | } 346 | } 347 | 348 | fn clear(&mut self) { 349 | self.pid = -1; 350 | self.images_dir_fd = -1; 351 | self.log_level = -1; 352 | self.log_file = None; 353 | self.external_mounts = Vec::new(); 354 | self.orphan_pts_master = None; 355 | self.root = None; 356 | self.leave_running = None; 357 | self.ext_unix_sk = None; 358 | self.shell_job = None; 359 | self.tcp_established = None; 360 | self.file_locks = None; 361 | self.manage_cgroups = None; 362 | self.work_dir_fd = -1; 363 | self.freeze_cgroup = None; 364 | self.cgroups_mode = None; 365 | self.cgroup_props = None; 366 | } 367 | 368 | pub fn dump(&mut self) -> Result<(), Box> { 369 | let mut criu_opts = rpc::Criu_opts::default(); 370 | self.fill_criu_opts(&mut criu_opts); 371 | self.do_swrk_with_response(rpc::Criu_req_type::DUMP, Some(criu_opts))?; 372 | self.clear(); 373 | 374 | Ok(()) 375 | } 376 | 377 | pub fn restore(&mut self) -> Result<(), Box> { 378 | let mut criu_opts = rpc::Criu_opts::default(); 379 | self.fill_criu_opts(&mut criu_opts); 380 | self.do_swrk_with_response(rpc::Criu_req_type::RESTORE, Some(criu_opts))?; 381 | self.clear(); 382 | 383 | Ok(()) 384 | } 385 | } 386 | 387 | impl Drop for Criu { 388 | fn drop(&mut self) { 389 | unsafe { libc::close(self.sv[0]) }; 390 | unsafe { libc::close(self.sv[1]) }; 391 | } 392 | } 393 | --------------------------------------------------------------------------------