// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved.
//
// StratoVirt is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan
// PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//         http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.

pub mod aio;
pub mod arg_parser;
pub mod bitmap;
pub mod byte_code;
pub mod checksum;
pub mod daemonize;
#[cfg(target_arch = "aarch64")]
pub mod device_tree;
pub mod leak_bucket;
mod link_list;
pub mod logger;
pub mod loop_context;
pub mod num_ops;
pub mod offsetof;
pub mod reader;
pub mod seccomp;
pub mod syscall;
pub mod tap;
pub mod trace;
pub mod unix;

pub mod errors {
    use error_chain::error_chain;

    error_chain! {
        foreign_links {
            KvmIoctl(kvm_ioctls::Error);
            Io(std::io::Error);
            Nul(std::ffi::NulError);
        }
        errors {
            // arg_parser submodule error
            MissingArgument(t: String) {
                description("The required argument was not provided.")
                display("Argument '{}' required, but not found. Use \'-h\' or \'-help\' to get usage.", t)
            }
            MissingValue(t: String) {
                description("A value for args was not provided.")
                display("The argument '{}' requires a value, but none was supplied. Use \'-h\' or \'-help\' to get usage.", t)
            }
            IllegelValue(t1: String, t2: String) {
                description("A value is illegel for args.")
                display("The value '{}' is illegel for argument '{}'. Use \'-h\' or \'-help\' to get usage.", t1, t2)
            }
            ValueOutOfPossible(t1: String, t2: String) {
                description("A value for args is out of possile values.")
                display("The value of argument '{}' must be in '{}'. Use \'-h\' or \'-help\' to get usage.", t1, t2)
            }
            UnexpectedArguments(t: String) {
                description("The provided argument was not expected.")
                display("Found argument '{}' which wasn't expected, or isn't valid in the context. Use \'-h\' or \'-help\' to get usage.", t)
            }
            DuplicateArgument(t: String) {
                description("The argument was provided more than once.")
                display("The argument '{}' was provided more than once. Use \'-h\' or \'-help\' to get usage.", t)
            }
            DuplicateValue(t: String) {
                description("The argument value was provided more than once.")
                display("The argument '{}' only need one value. Use \'-h\' or \'-help\' to get usage.", t)
            }
            // daemonize submodule error
            DaemonFork {
                description("Unable to fork.")
                display("Unable to fork.")
            }
            DaemonSetsid {
                description("Unable to create new session.")
                display("Unable to create new session.")
            }
            DaemonRedirectStdio {
                description("Unable to redirect standard streams to /dev/null.")
                display("Unable to redirect standard streams to /dev/null.")
            }
            PidFileExist {
                description("Pidfile path is existed yet.")
                display("Pidfile path is existed yet.")
            }
            // epoll_context error
            BadSyscall(err: std::io::Error) {
                description("Return a bad syscall.")
                display("Found bad syscall, error is {} .", err)
            }
            UnExpectedOperationType {
                description("Unsupported notifier operation type.")
                display("Unsupported Epoll notifier operation type.")
            }
            EpollWait(err: std::io::Error) {
                description("Failed to execute epoll_wait syscall.")
                display("Failed to execute epoll_wait syscall: {} .", err)
            }
            NoRegisterFd(t: i32) {
                description("The fd is not registered in epoll.")
                display("The fd {} is not registered in epoll.", t)
            }
            NoParkedFd(t: i32) {
                description("Found no parked fd in registered.")
                display("Found no parked fd {}.", t)
            }
            BadNotifierOperation {
                description("Bad Notifier Operation.")
                display("Notifier Operation non allowed.")
            }
            ChmodFailed(e: i32) {
                description("Chmod command failed.")
                display("Chmod command failed, os error {}", e)
            }
            OutOfBound(index: u64, bound: u64) {
                description("Index out of bound of array")
                display("Index :{} out of bound :{}", index, bound)
            }
            NodeDepthMismatch(target_dep: u32, real_dep: u32) {
                description("Fdt structure nested node depth mismatch")
                display("Desired node depth :{}, current node depth :{}", target_dep, real_dep)
            }
            NodeUnclosed(unclose: u32) {
                description("Fdt structure block node unclose")
                display("Still have {} node open when terminating the fdt", unclose)
            }
            IllegelPropertyPos {
                description("Cann't add property outside the node")
                display("Failed to add property because there is no open node")
            }
            IllegalString(s: String) {
                description("The string for fdt should not contain null")
                display("Failed to add string to fdt because of null character inside \"{}\"", s)
            }
            MemReserveOverlap {
                description("The mem reserve entry should not overlap")
                display("Failed to add overlapped mem reserve entries to fdt")
            }
            SetPropertyErr(s: String) {
                description("Cann't set property for fdt node")
                display("Failed to set {} property", s)
            }
        }
    }
}

use libc::{tcgetattr, tcsetattr, termios, OPOST, TCSANOW};
use log::debug;
use once_cell::sync::Lazy;
use std::sync::Mutex;
use vmm_sys_util::terminal::Terminal;

pub static TERMINAL_MODE: Lazy<Mutex<Option<termios>>> = Lazy::new(|| Mutex::new(None));

pub fn set_termi_raw_mode() -> std::io::Result<()> {
    let tty_fd = std::io::stdin().lock().tty_fd();

    // Safe because this only set the `old_term_mode` struct to zero.
    let mut old_term_mode: termios = unsafe { std::mem::zeroed() };
    // Safe because this only get stdin's current mode and save it.
    let ret = unsafe { tcgetattr(tty_fd, &mut old_term_mode as *mut _) };
    if ret < 0 {
        return Err(std::io::Error::last_os_error());
    }
    *TERMINAL_MODE.lock().unwrap() = Some(old_term_mode);

    let mut new_term_mode: termios = old_term_mode;
    // Safe because this function only change the `new_term_mode` argument.
    unsafe { libc::cfmakeraw(&mut new_term_mode as *mut _) };
    new_term_mode.c_oflag |= OPOST;
    // Safe because this function only set the stdin to raw mode.
    let ret = unsafe { tcsetattr(tty_fd, TCSANOW, &new_term_mode as *const _) };
    if ret < 0 {
        return Err(std::io::Error::last_os_error());
    }

    Ok(())
}

pub fn set_termi_canon_mode() -> std::io::Result<()> {
    let tty_fd = std::io::stdin().lock().tty_fd();
    if let Some(old_term_mode) = TERMINAL_MODE.lock().unwrap().as_ref() {
        // Safe because this only recover the stdin's mode.
        let ret = unsafe { tcsetattr(tty_fd, TCSANOW, old_term_mode as *const _) };
        if ret < 0 {
            return Err(std::io::Error::last_os_error());
        }
    } else {
        debug!("stdin's mode is not initialized: please check the config");
    }

    Ok(())
}