Skip to main content

nitro_cli/common/
mod.rs

1// Copyright 2020-2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3#![deny(missing_docs)]
4#![deny(warnings)]
5
6/// The module which parses command parameters from command-line arguments.
7pub mod commands_parser;
8/// The module which provides mappings between NitroCliErrors and their corresponding code.
9pub mod document_errors;
10/// The module which provides JSON-ready information structures.
11pub mod json_output;
12/// The module which provides the per-process logger.
13pub mod logger;
14/// The module which provides signal handling.
15pub mod signal_handler;
16
17use chrono::offset::Utc;
18use log::error;
19use serde::de::DeserializeOwned;
20use serde::{Deserialize, Serialize};
21use std::env;
22use std::io::{Read, Write};
23
24#[cfg(test)]
25use std::os::raw::c_char;
26
27use std::os::unix::net::UnixStream;
28use std::path::{Path, PathBuf};
29
30use document_errors::ERROR_CODES;
31use logger::get_log_file_base_path;
32
33/// The most common result type provided by Nitro CLI operations.
34pub type NitroCliResult<T> = Result<T, NitroCliFailure>;
35
36/// The CID for the vsock device of the parent VM.
37pub const VMADDR_CID_PARENT: u32 = 3;
38
39/// The vsock port used to confirm that the enclave has booted.
40pub const ENCLAVE_READY_VSOCK_PORT: u32 = 9000;
41
42/// The amount of time in milliseconds an enclave process will wait for certain operations.
43pub const ENCLAVE_PROC_WAIT_TIMEOUT_MSEC: isize = 3000;
44
45/// The confirmation code sent by an enclave process to a requesting CLI instance
46/// in order to signal that it is alive.
47pub const MSG_ENCLAVE_CONFIRM: u64 = 0xEEC0;
48
49/// The environment variable which holds the path to the Unix sockets directory.
50pub const SOCKETS_DIR_PATH_ENV_VAR: &str = "NITRO_CLI_SOCKETS_PATH";
51
52/// The default path to the Unix sockets directory.
53const SOCKETS_DIR_PATH: &str = "/run/nitro_enclaves";
54
55/// Constant used for identifying the backtrace environment variable.
56const BACKTRACE_VAR: &str = "BACKTRACE";
57
58/// All possible errors which may occur.
59#[derive(Debug, Default, Clone, Copy, Hash, PartialEq)]
60pub enum NitroCliErrorEnum {
61    #[default]
62    /// Unspecified error (should avoid using it thoughout the code).
63    UnspecifiedError = 0,
64    /// Error for handling missing arguments.
65    MissingArgument,
66    /// Error for handling conflicting arguments.
67    ConflictingArgument,
68    /// Invalid type argument.
69    InvalidArgument,
70    /// Failed to create socket pair.
71    SocketPairCreationFailure,
72    /// Failed to spawn a child process.
73    ProcessSpawnFailure,
74    /// Failed to daemonize current process.
75    DaemonizeProcessFailure,
76    /// Failed to read requested content from disk.
77    ReadFromDiskFailure,
78    /// Unusable connection error.
79    UnusableConnectionError,
80    /// Socket close error.
81    SocketCloseError,
82    /// Socket connect timeout error.
83    SocketConnectTimeoutError,
84    /// General error for handling socket-related errors.
85    SocketError,
86    /// General error for handling epoll-related errors.
87    EpollError,
88    /// General error for handling inotify-related errors.
89    InotifyError,
90    /// Invalid command format.
91    InvalidCommand,
92    /// Lock acquire failure.
93    LockAcquireFailure,
94    /// Thread join failure.
95    ThreadJoinFailure,
96    /// General error for handling serde-related errors.
97    SerdeError,
98    /// File permissions error.
99    FilePermissionsError,
100    /// File operation failure.
101    FileOperationFailure,
102    /// Invalid CPU list configuration.
103    InvalidCpuConfiguration,
104    /// Requested CPU not available in the pool.
105    NoSuchCpuAvailableInPool,
106    /// Not enough CPUs available in the pool.
107    InsufficientCpus,
108    /// Malformed CPU ID error.
109    MalformedCpuId,
110    /// General error to catch all other CPU-related errors.
111    CpuError,
112    /// No such hugepage map flag.
113    NoSuchHugepageFlag,
114    /// Insufficient memory requested.
115    InsufficientMemoryRequested,
116    /// Insufficient memory available.
117    InsufficientMemoryAvailable,
118    /// Invalid enclave file descriptor.
119    InvalidEnclaveFd,
120    /// General ioctl failure.
121    IoctlFailure,
122    /// Image load info ioctl failure.
123    IoctlImageLoadInfoFailure,
124    /// Enclave set memory region ioctl failure.
125    IoctlSetMemoryRegionFailure,
126    /// VCPU add ioctl failure.
127    IoctlAddVcpuFailure,
128    /// Enclave start ioctl failure.
129    IoctlEnclaveStartFailure,
130    /// Memory overflow.
131    MemoryOverflow,
132    /// General EIF parsing related error.
133    EifParsingError,
134    /// Error specific to enclave booting issues.
135    EnclaveBootFailure,
136    /// Enclave event wait error.
137    EnclaveEventWaitError,
138    /// Enclave process command was not executed.
139    EnclaveProcessCommandNotExecuted,
140    /// Could not connect to an enclave process.
141    EnclaveProcessConnectionFailure,
142    /// Socket path not found.
143    SocketPathNotFound,
144    /// Enclave process failed to send back reply.
145    EnclaveProcessSendReplyFailure,
146    /// Error when trying to allocate enclave memory regions.
147    EnclaveMmapError,
148    /// Error when trying to release enclave memory regions.
149    EnclaveMunmapError,
150    /// Enclave connection to console failed.
151    EnclaveConsoleConnectionFailure,
152    /// Error when reading from the console.
153    EnclaveConsoleReadError,
154    /// Error when writing console output to stream.
155    EnclaveConsoleWriteOutputError,
156    /// Integer parsing error.
157    IntegerParsingError,
158    /// Could not build EIF file.
159    EifBuildingError,
160    /// Could not build Docker image.
161    DockerImageBuildError,
162    /// Could not pull Docker image.
163    DockerImagePullError,
164    /// Artifacts path environment variable not set.
165    ArtifactsPathNotSet,
166    /// Blobs path environment variable not set.
167    BlobsPathNotSet,
168    /// Clock skew error.
169    ClockSkewError,
170    /// Signal masking error.
171    SignalMaskingError,
172    /// Signal unmasking error.
173    SignalUnmaskingError,
174    /// General error for handling logger-related errors.
175    LoggerError,
176    /// Hasher operation error
177    HasherError,
178    /// Enclave naming error
179    EnclaveNamingError,
180    /// Signature checker error
181    EIFSignatureCheckerError,
182    /// Signing error
183    EIFSigningError,
184}
185
186impl Eq for NitroCliErrorEnum {}
187
188/// The type of commands that can be sent to an enclave process.
189#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
190pub enum EnclaveProcessCommandType {
191    /// Launch (run) an enclave (sent by the CLI).
192    Run = 0,
193    /// Terminate an enclave (sent by the CLI).
194    Terminate,
195    /// Notify that the enclave has terminated (sent by the enclave process to itself).
196    TerminateComplete,
197    /// Describe an enclave (broadcast by the CLI).
198    Describe,
199    /// Request an enclave's CID (sent by the CLI).
200    GetEnclaveCID,
201    /// Request an enclave's flags (sent by the CLI).
202    GetEnclaveFlags,
203    /// Request an enclave's name (sent by the CLI).
204    GetEnclaveName,
205    /// Request the ID of an enclave only if the name matches (sent by the CLI).
206    GetIDbyName,
207    /// Notify the socket connection listener to shut down (sent by the enclave process to itself).
208    ConnectionListenerStop,
209    /// Do not execute a command due to insufficient privileges (sent by the CLI, modified by the enclave process).
210    NotPermitted,
211}
212
213/// The type of replies that an enclave process can send to a CLI instance.
214#[derive(Debug, Serialize, Deserialize)]
215pub enum EnclaveProcessReply {
216    /// A message which must be printed to the CLI's standard output.
217    StdOutMessage(String),
218    /// A messge which must be printed to the CLI's standard error.
219    StdErrMessage(String),
220    /// The status of the operation that the enclave process has performed.
221    Status(i32),
222}
223
224/// Struct that is passed along the backtrace and accumulates error messages.
225#[derive(Debug, Default, PartialEq, Eq)]
226pub struct NitroCliFailure {
227    /// Main action which was attempted and failed.
228    pub action: String,
229    /// (Possibly) more subactions which lead to the root cause of the failure.
230    pub subactions: Vec<String>,
231    /// Computer-readable error code.
232    pub error_code: NitroCliErrorEnum,
233    /// File in which the root error occurred.
234    pub file: String,
235    /// Line at which the root error occurred.
236    pub line: u32,
237    /// Additional info regarding the error, passed as individual components (for easier parsing).
238    pub additional_info: Vec<String>,
239}
240
241impl NitroCliFailure {
242    /// Returns an empty `NitroCliFailure` object.
243    pub fn new() -> Self {
244        NitroCliFailure {
245            action: String::new(),
246            subactions: vec![],
247            error_code: NitroCliErrorEnum::default(),
248            file: String::new(),
249            line: 0,
250            additional_info: vec![],
251        }
252    }
253
254    /// Sets the main action which failed (i.e. RUN_ENCLAVE).
255    pub fn set_action(mut self, action: String) -> Self {
256        self.action = action;
257        self
258    }
259
260    /// Adds a new layer into the backtrace, corresponding to a failing subaction (i.e. NOT_ENOUGH_MEM).
261    pub fn add_subaction(mut self, subaction: String) -> Self {
262        self.subactions.push(subaction);
263        self
264    }
265
266    /// Sets the error code.
267    pub fn set_error_code(mut self, error_code: NitroCliErrorEnum) -> Self {
268        self.error_code = error_code;
269        self
270    }
271
272    /// Sets the name of the file the error occurred in.
273    pub fn set_file(mut self, file: &str) -> Self {
274        self.file = file.to_string();
275        self
276    }
277
278    /// Sets the number of the line the error occurred on.
279    pub fn set_line(mut self, line: u32) -> Self {
280        self.line = line;
281        self
282    }
283
284    /// Sets both error file and error line.
285    pub fn set_file_and_line(mut self, file: &str, line: u32) -> Self {
286        self.file = file.to_string();
287        self.line = line;
288        self
289    }
290
291    /// Include additional error information.
292    pub fn add_info(mut self, info: Vec<&str>) -> Self {
293        for info_ in info {
294            self.additional_info.push(info_.to_string());
295        }
296        self
297    }
298}
299
300/// Macro used for constructing a NitroCliFailure in a more convenient manner.
301#[macro_export]
302macro_rules! new_nitro_cli_failure {
303    ($subaction:expr, $error_code:expr) => {
304        NitroCliFailure::new()
305            .add_subaction(($subaction).to_string())
306            .set_error_code($error_code)
307            .set_file_and_line(file!(), line!())
308    };
309}
310
311/// Logs the given backtrace string to a separate, backtrace-specific file.
312/// Returns a string denoting the path to the corresponding log file.
313fn log_backtrace(backtrace: String) -> Result<String, &'static str> {
314    let log_path_base = get_log_file_base_path();
315
316    // Check if backtrace logs location exists and create it if necessary.
317    if !Path::new(&log_path_base).exists() {
318        let create_logs_dir = std::fs::create_dir_all(&log_path_base);
319        if create_logs_dir.is_err() {
320            return Err("Could not create backtrace logs directory");
321        }
322    }
323
324    let utc_time_now = Utc::now().to_rfc3339();
325    let log_path_str = format!("{}/err{}.log", log_path_base, utc_time_now);
326    let log_path = Path::new(&log_path_str);
327    let log_file = std::fs::File::create(log_path);
328    if log_file.is_err() {
329        return Err("Could not create backtrace log file");
330    }
331
332    let write_result = log_file.unwrap().write_all(backtrace.as_bytes());
333    if write_result.is_err() {
334        return Err("Could not write to backtrace log file");
335    }
336
337    match log_path.to_str() {
338        Some(log_path) => Ok(log_path.to_string()),
339        None => Err("Could not return log file path"),
340    }
341}
342
343/// Assembles the error message which gets displayed to the user.
344pub fn construct_error_message(failure: &NitroCliFailure) -> String {
345    // Suggestive error description comes first.
346    let error_info: String = document_errors::get_detailed_info(
347        (*ERROR_CODES.get(&failure.error_code).unwrap_or(&"E00")).to_string(),
348        &failure.additional_info,
349    );
350
351    // Include a link to the documentation page.
352    let help_link: String = document_errors::construct_help_link(
353        (*ERROR_CODES.get(&failure.error_code).unwrap_or(&"E00")).to_string(),
354    );
355    let backtrace: String = document_errors::construct_backtrace(failure);
356
357    // Write backtrace to a log file.
358    let log_path = log_backtrace(backtrace.clone());
359
360    // Return final output, depending on whether the user requested the backtrace or not.
361    match std::env::var(BACKTRACE_VAR) {
362        Ok(display_backtrace) => match display_backtrace.as_str() {
363            "1" => {
364                if let Ok(log_path) = log_path {
365                    format!(
366                        "{error_info}\n\nFor more details, please visit {help_link}\n\nBacktrace:\n{backtrace}\n\nIf you open a support ticket, please provide the error log found at \"{log_path}\""
367                    )
368                } else {
369                    format!(
370                        "{error_info}\n\nFor more details, please visit {help_link}\n\nBacktrace:\n{backtrace}"
371                    )
372                }
373            }
374            _ => {
375                if let Ok(log_path) = log_path {
376                    format!(
377                        "{error_info}\n\nFor more details, please visit {help_link}\n\nIf you open a support ticket, please provide the error log found at \"{log_path}\""
378                    )
379                } else {
380                    format!("{error_info}\n\nFor more details, please visit {help_link}")
381                }
382            }
383        },
384        _ => {
385            if let Ok(log_path) = log_path {
386                format!(
387                    "{error_info}\n\nFor more details, please visit {help_link}\n\nIf you open a support ticket, please provide the error log found at \"{log_path}\""
388                )
389            } else {
390                format!("{error_info}\n\nFor more details, please visit {help_link}")
391            }
392        }
393    }
394}
395
396/// A trait which allows a more graceful program exit instead of the standard `panic`.
397/// Provides a custom exit code.
398pub trait ExitGracefully<T> {
399    /// Provide the inner value of a `Result` or exit gracefully with a message and custom errno.
400    fn ok_or_exit_with_errno(self, additional_info: Option<&str>) -> T;
401}
402
403impl<T> ExitGracefully<T> for NitroCliResult<T> {
404    /// Provide the inner value of a `Result` or exit gracefully with a message and custom errno.
405    fn ok_or_exit_with_errno(self, additional_info: Option<&str>) -> T {
406        match self {
407            Ok(val) => val,
408            Err(err) => {
409                let err_str = construct_error_message(&err);
410                if let Some(additional_info_str) = additional_info {
411                    notify_error(&format!("{additional_info_str} | {err_str}"));
412                } else {
413                    notify_error(&err_str);
414                }
415                std::process::exit(err.error_code as i32);
416            }
417        }
418    }
419}
420
421/// Notify both the user and the logger of an error.
422pub fn notify_error(err_msg: &str) {
423    eprintln!("{err_msg}");
424    error!("{}", err_msg);
425}
426
427/// Read a LE-encoded 64-bit unsigned value from a socket.
428pub fn read_u64_le(socket: &mut dyn Read) -> NitroCliResult<u64> {
429    let mut bytes = [0u8; std::mem::size_of::<u64>()];
430    socket.read_exact(&mut bytes).map_err(|e| {
431        new_nitro_cli_failure!(
432            &format!(
433                "Failed to read {} bytes from the given socket: {:?}",
434                std::mem::size_of::<u64>(),
435                e
436            ),
437            NitroCliErrorEnum::SocketError
438        )
439    })?;
440
441    Ok(u64::from_le_bytes(bytes))
442}
443
444/// Write a LE-encoded 64-bit unsigned value to a socket.
445pub fn write_u64_le(socket: &mut dyn Write, value: u64) -> NitroCliResult<()> {
446    let bytes = value.to_le_bytes();
447    socket.write_all(&bytes).map_err(|e| {
448        new_nitro_cli_failure!(
449            &format!(
450                "Failed to write {} bytes to the given socket: {:?}",
451                std::mem::size_of::<u64>(),
452                e
453            ),
454            NitroCliErrorEnum::SocketError
455        )
456    })
457}
458
459/// Send a command to a single socket.
460pub fn enclave_proc_command_send_single<T>(
461    cmd: EnclaveProcessCommandType,
462    args: Option<&T>,
463    mut socket: &mut UnixStream,
464) -> NitroCliResult<()>
465where
466    T: Serialize,
467{
468    // Serialize the command type.
469    let mut cmd_bytes = Vec::new();
470    ciborium::ser::into_writer(&cmd, &mut cmd_bytes).map_err(|e| {
471        new_nitro_cli_failure!(
472            &format!("Invalid command format: {e:?}"),
473            NitroCliErrorEnum::InvalidCommand
474        )
475    })?;
476
477    // The command is written twice. The first read is done by the connection listener to check if this is
478    // a shut-down command. The second read is done by the enclave process for all non-shut-down commands.
479    for _ in 0..2 {
480        write_u64_le(&mut socket, cmd_bytes.len() as u64)
481            .map_err(|e| e.add_subaction("Failed to send single command size".to_string()))?;
482        socket.write_all(&cmd_bytes[..]).map_err(|e| {
483            new_nitro_cli_failure!(
484                &format!("Failed to send single command: {e:?}"),
485                NitroCliErrorEnum::SocketError
486            )
487        })?;
488    }
489
490    // Serialize the command arguments.
491    if let Some(args) = args {
492        let mut arg_bytes = Vec::new();
493        ciborium::ser::into_writer(args, &mut arg_bytes).map_err(|e| {
494            new_nitro_cli_failure!(
495                &format!("Invalid single command arguments: {e:?}"),
496                NitroCliErrorEnum::InvalidCommand
497            )
498        })?;
499
500        // Write the serialized command arguments.
501        write_u64_le(&mut socket, arg_bytes.len() as u64)
502            .map_err(|e| e.add_subaction("Failed to send arguments size".to_string()))?;
503        socket.write_all(&arg_bytes).map_err(|e| {
504            new_nitro_cli_failure!(
505                &format!("Failed to send arguments: {e:?}"),
506                NitroCliErrorEnum::SocketError
507            )
508        })?;
509    }
510
511    Ok(())
512}
513
514/// Receive an object of a specified type from an input stream.
515pub fn receive_from_stream<T>(input_stream: &mut dyn Read) -> NitroCliResult<T>
516where
517    T: DeserializeOwned,
518{
519    let size = read_u64_le(input_stream)
520        .map_err(|e| e.add_subaction("Failed to receive data size".to_string()))?
521        as usize;
522    let mut raw_data: Vec<u8> = vec![0; size];
523    let data: T =
524        ciborium::de::from_reader_with_buffer(input_stream, &mut raw_data[..]).map_err(|e| {
525            new_nitro_cli_failure!(
526                &format!("Failed to decode received data: {e:?}"),
527                NitroCliErrorEnum::SerdeError
528            )
529        })?;
530    Ok(data)
531}
532
533/// Get the path to the directory containing the Unix sockets owned by all enclave processes.
534pub fn get_sockets_dir_path() -> PathBuf {
535    let log_path = match env::var(SOCKETS_DIR_PATH_ENV_VAR) {
536        Ok(env_path) => env_path,
537        Err(_) => SOCKETS_DIR_PATH.to_string(),
538    };
539    Path::new(&log_path).to_path_buf()
540}
541
542/// Get the path to the Unix socket owned by an enclave process which also owns the enclave with the given ID.
543pub fn get_socket_path(enclave_id: &str) -> NitroCliResult<PathBuf> {
544    // The full enclave ID is "i-(...)-enc<enc_id>" and we want to extract only <enc_id>.
545    let tokens: Vec<_> = enclave_id.rsplit("-enc").collect();
546    let sockets_path = get_sockets_dir_path();
547    Ok(sockets_path.join(tokens[0]).with_extension("sock"))
548}
549
550#[cfg(test)]
551mod tests {
552    #[allow(unused_imports)]
553    use super::*;
554
555    use crate::common::commands_parser::EmptyArgs;
556
557    const TMP_DIR_STR: &str = "./tmp_sock_dir";
558
559    fn unset_envvar(varname: &str) {
560        unsafe {
561            libc::unsetenv(varname.as_ptr() as *const c_char);
562        };
563    }
564
565    /// Tests that a value wrote by `write_u64_le()` is read
566    /// correctly by `read_u64_le()`.
567    #[test]
568    fn test_read_write_u64() {
569        let (mut sock0, mut sock1) = UnixStream::pair().unwrap();
570
571        let _ = write_u64_le(&mut sock0, 127);
572        let result = read_u64_le(&mut sock1);
573
574        if let Ok(result) = result {
575            assert_eq!(result, 127);
576        }
577    }
578
579    /// Tests that a command sent though a socket by `enclave_proc_command_send_single()`
580    /// is received correctly at the other end, by `receive_command_type()`.
581    #[test]
582    fn test_enclave_proc_command_send_single() {
583        let (mut sock0, mut sock1) = UnixStream::pair().unwrap();
584        let cmd = EnclaveProcessCommandType::Describe;
585        let args: std::option::Option<&EmptyArgs> = None;
586
587        let result0 = enclave_proc_command_send_single::<EmptyArgs>(cmd, args, &mut sock0);
588        assert!(result0.is_ok());
589
590        let result1 = receive_from_stream::<EnclaveProcessCommandType>(&mut sock1);
591        assert!(result1.is_ok());
592        assert_eq!(result1.unwrap(), EnclaveProcessCommandType::Describe);
593    }
594
595    /// Tests that the returned sockets_dir_path matches the expected path,
596    /// as retrieved from the corresponding environment variable.
597    #[test]
598    fn test_get_sockets_dir_path_default() {
599        let sockets_dir = env::var(SOCKETS_DIR_PATH_ENV_VAR);
600        let sockets_dir_path_f = get_sockets_dir_path();
601
602        if let Ok(sockets_dir) = sockets_dir {
603            assert_eq!(sockets_dir, sockets_dir_path_f.as_path().to_str().unwrap());
604        } else {
605            assert_eq!(
606                SOCKETS_DIR_PATH,
607                sockets_dir_path_f.as_path().to_str().unwrap()
608            );
609        }
610    }
611
612    /// Tests that altering the content of the sockets_dir_path environment variable
613    /// changes the sockets_dir_path string returned by `get_sockets_dir_path()`.
614    #[test]
615    fn test_get_sockets_dir_path_custom_envvar() {
616        let old_sockets_dir = env::var(SOCKETS_DIR_PATH_ENV_VAR);
617        env::set_var(SOCKETS_DIR_PATH_ENV_VAR, TMP_DIR_STR);
618
619        let sockets_dir_path_f = get_sockets_dir_path();
620
621        assert_eq!(TMP_DIR_STR, sockets_dir_path_f.as_path().to_str().unwrap());
622
623        // Restore previous environment variable value
624        if let Ok(old_sockets_dir) = old_sockets_dir {
625            env::set_var(SOCKETS_DIR_PATH_ENV_VAR, old_sockets_dir);
626        } else {
627            env::set_var(SOCKETS_DIR_PATH_ENV_VAR, "");
628            unset_envvar(&String::from(SOCKETS_DIR_PATH_ENV_VAR));
629        }
630    }
631
632    /// Tests that `get_socket_path()` returns the expected socket path,
633    /// given a specific enclave id.
634    #[test]
635    fn test_get_socket_path_valid_id() {
636        let enclave_id = "i-0000000000000000-enc0123456789012345";
637        let tokens: Vec<_> = enclave_id.rsplit("-enc").collect();
638        let sockets_path = get_sockets_dir_path();
639        let result = get_socket_path(enclave_id);
640
641        assert!(result.is_ok());
642        assert_eq!(
643            result.unwrap().as_path().to_str().unwrap(),
644            format!(
645                "{}/{}.sock",
646                sockets_path.as_path().to_str().unwrap(),
647                tokens[0]
648            )
649        );
650    }
651
652    /// Tests that `get_socket_path()` returns an invalid socket path,
653    /// given a malformed enclave id.
654    #[test]
655    fn test_get_socket_path_invalid_id() {
656        let enclave_id = "i-0000000000000000_enc0123456789012345";
657        let sockets_path = get_sockets_dir_path();
658        let result = get_socket_path(enclave_id);
659
660        assert!(result.is_ok());
661        assert_eq!(
662            result.unwrap().as_path().to_str().unwrap(),
663            format!(
664                "{}/{}.sock",
665                sockets_path.as_path().to_str().unwrap(),
666                enclave_id
667            )
668        );
669    }
670}