<commit_before>extern crate futures;
extern crate text_ui;
extern crate tokio;
extern crate tokio_serde_msgpack;
extern crate tokio_uds;
extern crate weaver;

use text_ui::app::App;
use text_ui::backend::Backend;
use text_ui::widget::Widget;
use text_ui::pane::Pane;
use text_ui::widget::{shared, DbgDump, Line, Linear, Shared, Text, TextInput};
use text_ui::{text_to_lines, Event, Input, Key, Position, Size};

use std::collections::BTreeMap;
use std::fmt;
use std::sync::mpsc::Sender;
use std::sync::{Arc, RwLock};
use std::thread;

use futures::sync::mpsc::SendError as FutureSendError;
use futures::sync::mpsc::{unbounded, UnboundedReceiver, UnboundedSender};
use futures::AsyncSink;

use tokio::prelude::{task, Async, Future, Sink, Stream};
use tokio_serde_msgpack::{from_io, MsgPackReader, MsgPackWriter};
use tokio_uds::UnixStream;

use weaver::{weaver_socket_path, ClientMessage, ClientRequest, ServerMessage, WeaverCommand};

#[derive(Debug, PartialEq)]
pub enum WeaverNotification {
    Updated,
    Server(ServerMessage),
}

pub struct WeaverState {
    pub command_history: BTreeMap<usize, WeaverCommand>,
    pub commands_tx: UnboundedSender<ClientMessage>,
    msgcounter: u32,
}

impl fmt::Debug for WeaverState {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("WeaverState")
            .field("command_history", &self.command_history)
            .finish()
    }
}

impl WeaverState {
    pub fn new(commands_tx: UnboundedSender<ClientMessage>) -> Self {
        let command_history = BTreeMap::new();
        let msgcounter = 0;
        WeaverState {
            commands_tx,
            command_history,
            msgcounter,
        }
    }
    pub fn run_command(
        &mut self,
        cmd: String,
    ) -> Result<(), FutureSendError<weaver::ClientMessage>> {
        let request = ClientRequest::RunCommand(cmd);
        self.send_request(request)
    }

    pub fn send_request(
        &mut self,
        request: ClientRequest,
    ) -> Result<(), FutureSendError<weaver::ClientMessage>> {
        self.msgcounter += 1;
        let id = self.msgcounter;
        let msg = ClientMessage { id, request };
        self.commands_tx.unbounded_send(msg)
    }
}

pub struct WeaverClient<'a> {
    commands_rx: UnboundedReceiver<ClientMessage>,
    socket_rx: MsgPackReader<'a, UnixStream, ServerMessage>,
    socket_tx: MsgPackWriter<UnixStream, ClientMessage>,
    pub state: Arc<RwLock<WeaverState>>,
    pub notifications: Sender<WeaverNotification>,
    overflow: Option<ClientMessage>,
}

impl<'a> WeaverClient<'a> {
    pub fn new(notifications: Sender<WeaverNotification>) -> Self {
        let (commands_tx, commands_rx): (
            UnboundedSender<ClientMessage>,
            UnboundedReceiver<ClientMessage>,
        ) = unbounded();

        let state = Arc::new(RwLock::new(WeaverState::new(commands_tx)));
        let overflow = None;

        let socketpath = weaver_socket_path();
        let socket = UnixStream::connect(socketpath);
        let (socket_rx, socket_tx): (
            MsgPackReader<UnixStream, ServerMessage>,
            MsgPackWriter<UnixStream, ClientMessage>,
        ) = from_io(socket.expect("Could not connect to weaver daemon"));
        //let socket_tx = socket_tx.sink_map_err(|e| println!("Send Err: {:#?}", e));
        //let socket_rx = socket_rx.map_err(|e| panic!("Decode Error: {:#?}", e));

        WeaverClient {
            commands_rx,
            socket_rx,
            socket_tx,
            notifications,
            state,
            overflow,
        }
    }

    fn do_update(&mut self, msg: ServerMessage) -> Option<WeaverNotification> {
        use weaver::ServerNotice::*;
        let command_history = &mut self.state.write().unwrap().command_history;
        match msg.notice {
            CommandStarted(i, cmd) => {
                let _ = command_history.insert(i, WeaverCommand::new(cmd));
            }
            CommandOutput(i, text) => command_history.get_mut(&i).unwrap().stdout.push_str(&text),
            CommandErr(i, text) => command_history.get_mut(&i).unwrap().stderr.push_str(&text),
            CommandCompleted(i, rv) => command_history.get_mut(&i).unwrap().status = Some(rv),
        };
        Some(WeaverNotification::Updated)
    }
}

impl<'a> Future for WeaverClient<'a> {
    type Item = ();
    type Error = tokio_serde_msgpack::DecodeError;
    fn poll(&mut self) -> Result<Async<Self::Item>, Self::Error> {
        if let Some(msg) = self.overflow.take() {
            if let Ok(AsyncSink::NotReady(msg)) = self.socket_tx.start_send(msg) {
                self.overflow = Some(msg);
            }
        }

        if self.overflow == None {
            const LINES_PER_TICK: usize = 10;
            for i in 0..LINES_PER_TICK {
                match self.commands_rx.poll().unwrap() {
                    Async::Ready(Some(msg)) => {
                        if let Ok(AsyncSink::NotReady(msg)) = self.socket_tx.start_send(msg) {
                            self.overflow = Some(msg);
                        }
                        if i + 1 == LINES_PER_TICK {
                            task::current().notify();
                        }
                    }
                    _ => break,
                }
            }
        }

        let _ = self.socket_tx.poll_complete();

        while let Async::Ready(msg) = self.socket_rx.poll()? {
            if let Some(msg) = msg {
                self.notifications
                    .send(WeaverNotification::Server(msg.clone())).unwrap();
                match self.do_update(msg) {
                    Some(notification) => self.notifications.send(notification).unwrap(),
                    None => {},
                };
            } else {
                return Ok(Async::Ready(()));
            }
        }
        Ok(Async::NotReady)
    }
}

struct WeaverStateWidget {
    state: Shared<WeaverState>,
}

impl Widget for WeaverStateWidget {
    fn render_children(&self, size: Size) -> Option<Vec<Pane>> {
        let mut rv = vec![];
        let height = size.height as usize;
        let mut ctr: usize = 0;
        for (i, cmd) in self.state.read().unwrap().command_history.iter().rev() {
            let status = match cmd.status {
                None => '…',
                Some(0) => '✔',
                _ => '❌',
            };
            let mut content = vec![];
            let command_line = format!("{} {}", status, cmd.cmd.clone());
            let command_line = text_to_lines(command_line, size.width as usize);
            ctr += command_line.len();
            content.extend(command_line);
            if cmd.stdout.len() > 0 {
                let stdout = text_to_lines(cmd.stdout.clone(), size.width as usize);
                ctr += stdout.len();
                content.extend(stdout);
            }
            let contentlen = content.len();
            if ctr >= height {
                let top_spill = ctr - height;
                content = content.split_off(top_spill);
                ctr = height;
            }
            let pos = Position::new(0,size.height - ctr as u16);
            rv.push(Pane::new(pos, content));
            if ctr == height {
                break;
            }
        }
        Some(rv)
    }
}

struct WeaverTui {
    log: Shared<Text>,
    input: Shared<TextInput>,
    vbox: Shared<Linear>,
    content: Shared<Linear>,
    state: Shared<WeaverState>,
    statew: Shared<WeaverStateWidget>,
    show_debug: bool,
}

impl WeaverTui {
    fn new(state: Arc<RwLock<WeaverState>>) -> WeaverTui {
        let log = shared(Text::new(vec![]));
        let input = shared(TextInput::new(""));
        let state: Shared<WeaverState> = state.into();
        let statew = shared(WeaverStateWidget{ state: state.clone() });
        let dbgdump = shared(DbgDump::new(&state));
        let show_debug = false;
        // XXX Ugly hack D:
        let mut contentbox = Linear::hbox();
        //contentbox.push(&log);
        //contentbox.push(&dbgdump);
        contentbox.push(&statew);
        let content = shared(contentbox);
        let mut mainbox = Linear::vbox();
        mainbox.push(&content);
        mainbox.push(&input);
        let vbox = shared(mainbox);
        WeaverTui {
            log,
            input,
            vbox,
            content,
            state,
            statew,
            show_debug,
        }
    }

    fn toggle_debug(&mut self) {
        let mut content = self.content.write().unwrap();
        match self.show_debug {
            true => {
                self.show_debug = false;
                content.contents.truncate(0);
                content.push(&self.statew);
            }
            false => {
                self.show_debug = true;
                content.contents.truncate(0);
                content.push(&self.statew);
                content.push(&shared(Line::vertical()));
                content.push(&self.log);
            }
        }
    }

    fn submit_input(&mut self) {
        let text = self.input.write().unwrap().submit();
        self.state
            .write()
            .unwrap()
            .run_command(text.clone())
            .unwrap();
        /*let lines = text.lines();
        let mut log = self.log.write().unwrap();
        for line in lines {
            log.push(line.to_owned());
        }*/
    }

    fn log_msg(&mut self, msg: &str) {
        let lines: Vec<String> = msg.lines().map(|l| l.to_owned()).collect();
        self.log.write().unwrap().lines.extend(lines);
    }

    fn input(&mut self, key: Key) {
        match key {
            Key::Char('\n') => self.submit_input(),
            k => self.input.write().unwrap().keypress(k),
        }
    }
}

impl App for WeaverTui {
    type UI = Shared<Linear>;
    type MyEvent = WeaverNotification;
    fn widget(&self) -> Self::UI {
        self.vbox.clone()
    }
    fn handle_event(&mut self, event: Event<Self::MyEvent>) -> Result<(), Option<String>> {
        match event {
            Event::InputEvent(i) => match i {
                Input::Key(Key::Esc) => Err(None),
                Input::Key(Key::Alt('d')) => {
                    self.toggle_debug();
                    Ok(())
                }
                Input::Key(k) => {
                    self.input(k);
                    Ok(())
                }
                _ => Ok(()),
            },
            Event::AppEvent(_) => {
                self.log_msg(&format!("{:?}", event));
                Ok(())
            }
        }
    }
}

fn main() {
    let be = Backend::new();
    let sender = be.sender.clone();
    let weaver = WeaverClient::new(sender);
    let mut app = WeaverTui::new(weaver.state.clone());
    thread::spawn(move || {
        tokio::run(weaver.map_err(|e| panic!("Client Error: {:#?}", e)));
    });
    app.log_msg("Esc to exit");
    be.run_app(&mut app);
}
<commit_msg>Use readline, handle widget overflow<commit_after>extern crate futures;
extern crate text_ui;
extern crate tokio;
extern crate tokio_serde_msgpack;
extern crate tokio_uds;
extern crate weaver;

use text_ui::app::App;
use text_ui::backend::Backend;
use text_ui::pane::Pane;
//use text_ui::widget::DbgDump;
use text_ui::widget::Widget;
use text_ui::widget::{shared, Line, Linear, Readline, Shared, Text};
use text_ui::{text_to_lines, Event, Input, Key, Position, Size};

use std::collections::BTreeMap;
use std::fmt;
use std::sync::mpsc::Sender;
use std::sync::{Arc, RwLock};
use std::thread;

use futures::sync::mpsc::SendError as FutureSendError;
use futures::sync::mpsc::{unbounded, UnboundedReceiver, UnboundedSender};
use futures::AsyncSink;

use tokio::prelude::{task, Async, Future, Sink, Stream};
use tokio_serde_msgpack::{from_io, MsgPackReader, MsgPackWriter};
use tokio_uds::UnixStream;

use weaver::{weaver_socket_path, ClientMessage, ClientRequest, ServerMessage, WeaverCommand};

#[derive(Debug, PartialEq)]
pub enum WeaverNotification {
    Updated,
    Server(ServerMessage),
}

pub struct WeaverState {
    pub command_history: BTreeMap<usize, WeaverCommand>,
    pub commands_tx: UnboundedSender<ClientMessage>,
    msgcounter: u32,
}

impl fmt::Debug for WeaverState {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("WeaverState")
            .field("command_history", &self.command_history)
            .finish()
    }
}

impl WeaverState {
    pub fn new(commands_tx: UnboundedSender<ClientMessage>) -> Self {
        let command_history = BTreeMap::new();
        let msgcounter = 0;
        WeaverState {
            commands_tx,
            command_history,
            msgcounter,
        }
    }
    pub fn run_command(
        &mut self,
        cmd: String,
    ) -> Result<(), FutureSendError<weaver::ClientMessage>> {
        let request = ClientRequest::RunCommand(cmd);
        self.send_request(request)
    }

    pub fn send_request(
        &mut self,
        request: ClientRequest,
    ) -> Result<(), FutureSendError<weaver::ClientMessage>> {
        self.msgcounter += 1;
        let id = self.msgcounter;
        let msg = ClientMessage { id, request };
        self.commands_tx.unbounded_send(msg)
    }
}

pub struct WeaverClient<'a> {
    commands_rx: UnboundedReceiver<ClientMessage>,
    socket_rx: MsgPackReader<'a, UnixStream, ServerMessage>,
    socket_tx: MsgPackWriter<UnixStream, ClientMessage>,
    pub state: Arc<RwLock<WeaverState>>,
    pub notifications: Sender<WeaverNotification>,
    overflow: Option<ClientMessage>,
}

impl<'a> WeaverClient<'a> {
    pub fn new(notifications: Sender<WeaverNotification>) -> Self {
        let (commands_tx, commands_rx): (
            UnboundedSender<ClientMessage>,
            UnboundedReceiver<ClientMessage>,
        ) = unbounded();

        let state = Arc::new(RwLock::new(WeaverState::new(commands_tx)));
        let overflow = None;

        let socketpath = weaver_socket_path();
        let socket = UnixStream::connect(socketpath);
        let (socket_rx, socket_tx): (
            MsgPackReader<UnixStream, ServerMessage>,
            MsgPackWriter<UnixStream, ClientMessage>,
        ) = from_io(socket.expect("Could not connect to weaver daemon"));
        //let socket_tx = socket_tx.sink_map_err(|e| println!("Send Err: {:#?}", e));
        //let socket_rx = socket_rx.map_err(|e| panic!("Decode Error: {:#?}", e));

        WeaverClient {
            commands_rx,
            socket_rx,
            socket_tx,
            notifications,
            state,
            overflow,
        }
    }

    fn do_update(&mut self, msg: ServerMessage) -> Option<WeaverNotification> {
        use weaver::ServerNotice::*;
        let command_history = &mut self.state.write().unwrap().command_history;
        match msg.notice {
            CommandStarted(i, cmd) => {
                let _ = command_history.insert(i, WeaverCommand::new(cmd));
            }
            CommandOutput(i, text) => command_history.get_mut(&i).unwrap().stdout.push_str(&text),
            CommandErr(i, text) => command_history.get_mut(&i).unwrap().stderr.push_str(&text),
            CommandCompleted(i, rv) => command_history.get_mut(&i).unwrap().status = Some(rv),
        };
        Some(WeaverNotification::Updated)
    }
}

impl<'a> Future for WeaverClient<'a> {
    type Item = ();
    type Error = tokio_serde_msgpack::DecodeError;
    fn poll(&mut self) -> Result<Async<Self::Item>, Self::Error> {
        if let Some(msg) = self.overflow.take() {
            if let Ok(AsyncSink::NotReady(msg)) = self.socket_tx.start_send(msg) {
                self.overflow = Some(msg);
            }
        }

        if self.overflow == None {
            const LINES_PER_TICK: usize = 10;
            for i in 0..LINES_PER_TICK {
                match self.commands_rx.poll().unwrap() {
                    Async::Ready(Some(msg)) => {
                        if let Ok(AsyncSink::NotReady(msg)) = self.socket_tx.start_send(msg) {
                            self.overflow = Some(msg);
                        }
                        if i + 1 == LINES_PER_TICK {
                            task::current().notify();
                        }
                    }
                    _ => break,
                }
            }
        }

        let _ = self.socket_tx.poll_complete();

        while let Async::Ready(msg) = self.socket_rx.poll()? {
            if let Some(msg) = msg {
                self.notifications
                    .send(WeaverNotification::Server(msg.clone()))
                    .unwrap();
                match self.do_update(msg) {
                    Some(notification) => self.notifications.send(notification).unwrap(),
                    None => {}
                };
            } else {
                return Ok(Async::Ready(()));
            }
        }
        Ok(Async::NotReady)
    }
}

struct WeaverStateWidget {
    state: Shared<WeaverState>,
}

impl Widget for WeaverStateWidget {
    fn render_children(&self, size: Size) -> Option<Vec<Pane>> {
        let mut rv = vec![];
        let height = size.height as usize;
        let mut ctr: usize = 0;
        for (_i, cmd) in self.state.read().unwrap().command_history.iter().rev() {
            let status = match cmd.status {
                None => '…',
                Some(0) => '✔',
                _ => '❌',
            };
            let mut content = vec![];
            let command_line = format!("{} {}", status, cmd.cmd.clone());
            let command_line = text_to_lines(command_line, size.width as usize);
            ctr += command_line.len();
            content.extend(command_line);
            if cmd.stdout.len() > 0 {
                let stdout = text_to_lines(cmd.stdout.clone(), size.width as usize);
                ctr += stdout.len();
                content.extend(stdout);
            }
            if ctr >= height {
                let top_spill = ctr - height;
                content = content.split_off(top_spill);
                ctr = height;
            }
            let pos = Position::new(0, size.height - ctr as u16);
            rv.push(Pane::new(pos, content));
            if ctr == height {
                break;
            }
        }
        Some(rv)
    }
}

struct WeaverTui {
    log: Shared<Text>,
    input: Shared<Readline>,
    vbox: Shared<Linear>,
    content: Shared<Linear>,
    state: Shared<WeaverState>,
    statew: Shared<WeaverStateWidget>,
    show_debug: bool,
}

impl WeaverTui {
    fn new(state: Arc<RwLock<WeaverState>>) -> WeaverTui {
        let log = shared(Text::new(vec![]));
        let input = shared(Readline::new());
        let state: Shared<WeaverState> = state.into();
        let statew = shared(WeaverStateWidget {
            state: state.clone(),
        });
        //let dbgdump = shared(DbgDump::new(&state));
        let show_debug = false;
        let mut contentbox = Linear::hbox();
        //contentbox.push(&dbgdump);
        contentbox.push(&statew);
        let content = shared(contentbox);
        let mut mainbox = Linear::vbox();
        mainbox.push(&content);
        mainbox.push(&input);
        let vbox = shared(mainbox);
        WeaverTui {
            log,
            input,
            vbox,
            content,
            state,
            statew,
            show_debug,
        }
    }

    fn toggle_debug(&mut self) {
        let mut content = self.content.write().unwrap();
        match self.show_debug {
            true => {
                self.show_debug = false;
                content.contents.truncate(0);
                content.push(&self.statew);
            }
            false => {
                self.show_debug = true;
                content.contents.truncate(0);
                content.push(&self.statew);
                content.push(&shared(Line::vertical()));
                content.push(&self.log);
            }
        }
    }

    fn submit_input(&mut self) {
        let text = self.input.write().unwrap().finalize();
        self.state
            .write()
            .unwrap()
            .run_command(text.clone())
            .unwrap();
    }

    fn log_msg(&mut self, msg: &str) {
        let lines: Vec<String> = msg.lines().map(|l| l.to_owned()).collect();
        self.log.write().unwrap().lines.extend(lines);
    }

    fn input(&mut self, key: Key) {
        match key {
            Key::Char('\n') => self.submit_input(),
            k => self.input.write().unwrap().process_key(k),
        }
    }
}

impl App for WeaverTui {
    type UI = Shared<Linear>;
    type MyEvent = WeaverNotification;
    fn widget(&self) -> Self::UI {
        self.vbox.clone()
    }
    fn handle_event(&mut self, event: Event<Self::MyEvent>) -> Result<(), Option<String>> {
        match event {
            Event::InputEvent(i) => match i {
                Input::Key(Key::Esc) => Err(None),
                Input::Key(Key::Alt('d')) => {
                    self.toggle_debug();
                    Ok(())
                }
                Input::Key(k) => {
                    self.input(k);
                    Ok(())
                }
                _ => Ok(()),
            },
            Event::AppEvent(_) => {
                self.log_msg(&format!("{:?}", event));
                Ok(())
            }
        }
    }
}

fn main() {
    let be = Backend::new();
    let sender = be.sender.clone();
    let weaver = WeaverClient::new(sender);
    let mut app = WeaverTui::new(weaver.state.clone());
    thread::spawn(move || {
        tokio::run(weaver.map_err(|e| panic!("Client Error: {:#?}", e)));
    });
    app.log_msg("Esc to exit");
    be.run_app(&mut app);
}
<|endoftext|>


<commit_before>//! Helpers to program the task state segment.
//! See Intel 3a, Chapter 7, Section 7

/// Although hardware task-switching is not supported in 64-bit mode,
/// a 64-bit task state segment (TSS) must exist.
///
/// The TSS holds information important to 64-bit mode and that is not
/// directly related to the task-switch mechanism. This information includes:
///
/// # RSPn
/// The full 64-bit canonical forms of the stack pointers (RSP) for privilege levels 0-2.
/// RSPx is loaded in whenever an interrupt causes the CPU to change RPL to x.
/// Note on a syscall entry this field is not used to load a stack, setting the stack there
/// is the handler's responsibility (however when using the int instruction in user-space,
/// we load the stack from RSPn).
///
/// # ISTn
/// The full 64-bit canonical forms of the interrupt stack table (IST) pointers.
/// You can set an interrupt vector to use an IST entry in the Interrupt Descriptor
/// Table by giving it a number from 0 - 7. If 0 is selected, then the IST mechanism
/// is not used. If any other number is selected then when that interrupt vector is
/// called the CPU will load RSP from the corresponding IST entry. This is useful for
/// handling things like double faults, since you don't have to worry about switching
/// stacks; the CPU will do it for you.
///
/// # I/O map base address
/// The 16-bit offset to the I/O permission bit map from the 64-bit TSS base.
///
/// The operating system must create at least one 64-bit TSS after activating IA-32e mode.
/// It must execute the LTR instruction (in 64-bit mode) to load the TR register with a
/// pointer to the 64-bit TSS responsible for both 64-bitmode programs and
/// compatibility-mode programs ([load_tr](crate::task::load_tr)).
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct TaskStateSegment {
    pub reserved: u32,
    /// The full 64-bit canonical forms of the stack pointers (RSP) for privilege levels 0-2.
    pub rsp: [u64; 3],
    pub reserved2: u64,
    /// The full 64-bit canonical forms of the interrupt stack table (IST) pointers.
    pub ist: [u64; 7],
    pub reserved3: u64,
    pub reserved4: u16,
    /// The 16-bit offset to the I/O permission bit map from the 64-bit TSS base.
    pub iomap_base: u16,
}

impl TaskStateSegment {
    pub const fn new() -> TaskStateSegment {
        TaskStateSegment {
            reserved: 0,
            rsp: [0; 3],
            reserved2: 0,
            ist: [0; 7],
            reserved3: 0,
            reserved4: 0,
            iomap_base: 0,
        }
    }
}
<commit_msg>Add set_rsp, set_ist operations for task segment.<commit_after>//! Helpers to program the task state segment.
//! See Intel 3a, Chapter 7, Section 7

use crate::Ring;

/// Although hardware task-switching is not supported in 64-bit mode,
/// a 64-bit task state segment (TSS) must exist.
///
/// The TSS holds information important to 64-bit mode and that is not
/// directly related to the task-switch mechanism. This information includes:
///
/// # RSPn
/// The full 64-bit canonical forms of the stack pointers (RSP) for privilege levels 0-2.
/// RSPx is loaded in whenever an interrupt causes the CPU to change RPL to x.
/// Note on a syscall entry this field is not used to load a stack, setting the stack there
/// is the handler's responsibility (however when using the int instruction in user-space,
/// we load the stack from RSPn).
///
/// # ISTn
/// The full 64-bit canonical forms of the interrupt stack table (IST) pointers.
/// You can set an interrupt vector to use an IST entry in the Interrupt Descriptor
/// Table by giving it a number from 0 - 7. If 0 is selected, then the IST mechanism
/// is not used. If any other number is selected then when that interrupt vector is
/// called the CPU will load RSP from the corresponding IST entry. This is useful for
/// handling things like double faults, since you don't have to worry about switching
/// stacks; the CPU will do it for you.
///
/// # I/O map base address
/// The 16-bit offset to the I/O permission bit map from the 64-bit TSS base.
///
/// The operating system must create at least one 64-bit TSS after activating IA-32e mode.
/// It must execute the LTR instruction (in 64-bit mode) to load the TR register with a
/// pointer to the 64-bit TSS responsible for both 64-bitmode programs and
/// compatibility-mode programs ([load_tr](crate::task::load_tr)).
#[derive(Clone, Copy, Debug, Default)]
#[repr(C, packed)]
pub struct TaskStateSegment {
    pub reserved: u32,
    /// The full 64-bit canonical forms of the stack pointers (RSP) for privilege levels 0-2.
    pub rsp: [u64; 3],
    pub reserved2: u64,
    /// The full 64-bit canonical forms of the interrupt stack table (IST) pointers.
    pub ist: [u64; 7],
    pub reserved3: u64,
    pub reserved4: u16,
    /// The 16-bit offset to the I/O permission bit map from the 64-bit TSS base.
    pub iomap_base: u16,
}

impl TaskStateSegment {
    /// Creates a new empty TSS.
    pub const fn new() -> TaskStateSegment {
        TaskStateSegment {
            reserved: 0,
            rsp: [0; 3],
            reserved2: 0,
            ist: [0; 7],
            reserved3: 0,
            reserved4: 0,
            iomap_base: 0,
        }
    }

    /// Sets the stack pointer (`stack_ptr`) to be used for when
    /// an interrupt causes the CPU to change RPL to `pl`.
    pub fn set_rsp(&mut self, pl: Ring, stack_ptr: u64) {
        match pl {
            Ring::Ring0 => self.rsp[0] = stack_ptr,
            Ring::Ring1 => self.rsp[1] = stack_ptr,
            Ring::Ring2 => self.rsp[2] = stack_ptr,
            Ring::Ring3 => unreachable!("Can't set stack for PL3"),
        }
    }

    /// Sets the stack pointer (`stack_ptr`) to be used when
    /// an interrupt with a corresponding IST entry in the Interrupt
    /// Descriptor table pointing to the given `index` is raised.
    pub fn set_ist(&mut self, index: usize, stack_ptr: u64) {
        match index {
            0 => self.ist[0] = stack_ptr,
            1 => self.ist[1] = stack_ptr,
            2 => self.ist[2] = stack_ptr,
            3 => self.ist[3] = stack_ptr,
            4 => self.ist[4] = stack_ptr,
            5 => self.ist[5] = stack_ptr,
            6 => self.ist[6] = stack_ptr,
            _ => unreachable!("Can't set IST for this index (out of bounds)."),
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2017-2020 Aron Heinecke
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::config::Config;
use crate::config::TSConfig;
use crate::*;
use ::std::time::{Duration, SystemTime, UNIX_EPOCH};
use ts3_query::*;

pub const ERR_NAME_TAKEN: usize = 513;
const MAX_LEN_NAME: usize = 20;

// Safety: see module tick interval
const TIMEOUT_CONN: Duration = Duration::from_millis(1500);
const TIMEOUT_CMD: Duration = Duration::from_millis(1500);
/// Same as super::CLIENT_CONN_ID, but TS returns a different one on whoami
const KEY_CLIENT_ID_SELF: &str = "client_id";

/// QueryClient wrapper with connection-check on access
pub struct Connection {
    conn: QueryClient,
    cfg: Config,
    last_ping: Instant,
    conn_id: Option<TsConID>,
    name: Option<&'static str>,
}

impl Connection {
    fn connect(cfg: &TSConfig, name: Option<&'static str>) -> Result<QueryClient> {
        // let mut conn = QueryClient::new((cfg.ip.as_ref(), cfg.port))?;
        let mut conn = QueryClient::with_timeout(
            (cfg.ip.as_ref(), cfg.port),
            Some(TIMEOUT_CONN),
            Some(TIMEOUT_CMD),
        )?;
        conn.login(&cfg.user, &cfg.password)?;
        conn.select_server_by_port(cfg.server_port)?;
        if let Some(n) = name {
            // prevent underflow in name fallback
            if n.len() > MAX_LEN_NAME {
                panic!("Invalid name length: {} max: {}!", n.len(), MAX_LEN_NAME);
            }
            Self::set_name_fallback(&mut conn, n)?;
        }
        Ok(conn)
    }

    /// Set name of client, fallback to name+last unix timestamp MS to make it unique
    fn set_name_fallback(conn: &mut QueryClient, name: &str) -> Result<()> {
        if let Err(e) = conn.rename(name) {
            if e.error_response().map_or(true, |r| r.id != ERR_NAME_TAKEN) {
                return Err(e.into());
            } else {
                let time = SystemTime::now()
                    .duration_since(UNIX_EPOCH)
                    .unwrap()
                    .as_millis()
                    .to_string();
                let remaining = MAX_LEN_NAME - name.len();
                let offset = time.len() - remaining;
                conn.rename(&format!("{}{}", name, &time.as_str()[offset..]))?;
            }
        }
        Ok(())
    }

    /// Returns the current connection id (clid)
    pub fn conn_id(&mut self) -> Result<TsConID> {
        Ok(match self.conn_id {
            Some(v) => v,
            None => {
                let res = self.get()?.whoami(false)?;
                let clid = res
                    .get(KEY_CLIENT_ID_SELF)
                    .ok_or_else(|| Error::NoValue("No client id!"))?;
                let clid = clid.parse()?;
                self.conn_id = Some(clid);
                clid
            }
        })
    }

    /// Try creating a second connection
    pub fn clone(&self, name: Option<&'static str>) -> Result<Self> {
        let name = name.or(self.name);
        Self::new(self.cfg.clone(), name)
    }

    pub fn config(&self) -> &Config {
        &self.cfg
    }

    pub fn new(cfg: Config, name: Option<&'static str>) -> Result<Connection> {
        let conn = Self::connect(&cfg.ts, name)?;
        Ok(Self {
            conn,
            cfg,
            last_ping: Instant::now(),
            conn_id: None,
            name,
        })
    }

    /// Returns the active connection or tries to create a new one
    pub fn get(&mut self) -> Result<&mut QueryClient> {
        if self.last_ping.elapsed() < Duration::from_secs(0) {
            return Ok(&mut self.conn);
        }
        let conn = match self.conn.ping() {
            Ok(_) => &mut self.conn,
            Err(e) => {
                debug!("Previous connection died: {}", e);
                self.conn = Self::connect(&self.cfg.ts, self.name)?;
                self.conn_id = None;
                &mut self.conn
            }
        };
        self.last_ping = Instant::now();
        Ok(conn)
    }
}
<commit_msg>Fix substraction underflow with a quick hack<commit_after>// Copyright 2017-2020 Aron Heinecke
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::config::Config;
use crate::config::TSConfig;
use crate::*;
use ::std::time::{Duration, SystemTime, UNIX_EPOCH};
use ts3_query::*;

pub const ERR_NAME_TAKEN: usize = 513;
const MAX_LEN_NAME: usize = 20;

// Safety: see module tick interval
const TIMEOUT_CONN: Duration = Duration::from_millis(1500);
const TIMEOUT_CMD: Duration = Duration::from_millis(1500);
/// Same as super::CLIENT_CONN_ID, but TS returns a different one on whoami
const KEY_CLIENT_ID_SELF: &str = "client_id";

/// QueryClient wrapper with connection-check on access
pub struct Connection {
    conn: QueryClient,
    cfg: Config,
    last_ping: Instant,
    conn_id: Option<TsConID>,
    name: Option<&'static str>,
}

impl Connection {
    fn connect(cfg: &TSConfig, name: Option<&'static str>) -> Result<QueryClient> {
        // let mut conn = QueryClient::new((cfg.ip.as_ref(), cfg.port))?;
        let mut conn = QueryClient::with_timeout(
            (cfg.ip.as_ref(), cfg.port),
            Some(TIMEOUT_CONN),
            Some(TIMEOUT_CMD),
        )?;
        conn.login(&cfg.user, &cfg.password)?;
        conn.select_server_by_port(cfg.server_port)?;
        if let Some(n) = name {
            // prevent underflow in name fallback
            if n.len() > MAX_LEN_NAME {
                panic!("Invalid name length: {} max: {}!", n.len(), MAX_LEN_NAME);
            }
            Self::set_name_fallback(&mut conn, n)?;
        }
        Ok(conn)
    }

    /// Set name of client, fallback to name+last unix timestamp MS to make it unique
    fn set_name_fallback(conn: &mut QueryClient, name: &str) -> Result<()> {
        if let Err(e) = conn.rename(name) {
            if e.error_response().map_or(true, |r| r.id != ERR_NAME_TAKEN) {
                return Err(e.into());
            } else {
                let time = SystemTime::now()
                    .duration_since(UNIX_EPOCH)
                    .unwrap()
                    .as_millis()
                    .to_string();
                let remaining = MAX_LEN_NAME - name.len();
                let offset = if remaining > time.len() {
                    warn!("Name > time len");
                    warn!("name: {} time: {}, remaining: {}",name,time,remaining);
                    time.len()
                } else {
                    time.len() - remaining
                };
                conn.rename(&format!("{}{}", name, &time.as_str()[offset..]))?;
            }
        }
        Ok(())
    }

    /// Returns the current connection id (clid)
    pub fn conn_id(&mut self) -> Result<TsConID> {
        Ok(match self.conn_id {
            Some(v) => v,
            None => {
                let res = self.get()?.whoami(false)?;
                let clid = res
                    .get(KEY_CLIENT_ID_SELF)
                    .ok_or_else(|| Error::NoValue("No client id!"))?;
                let clid = clid.parse()?;
                self.conn_id = Some(clid);
                clid
            }
        })
    }

    /// Try creating a second connection
    pub fn clone(&self, name: Option<&'static str>) -> Result<Self> {
        let name = name.or(self.name);
        Self::new(self.cfg.clone(), name)
    }

    pub fn config(&self) -> &Config {
        &self.cfg
    }

    pub fn new(cfg: Config, name: Option<&'static str>) -> Result<Connection> {
        let conn = Self::connect(&cfg.ts, name)?;
        Ok(Self {
            conn,
            cfg,
            last_ping: Instant::now(),
            conn_id: None,
            name,
        })
    }

    /// Returns the active connection or tries to create a new one
    pub fn get(&mut self) -> Result<&mut QueryClient> {
        if self.last_ping.elapsed() < Duration::from_secs(0) {
            return Ok(&mut self.conn);
        }
        let conn = match self.conn.ping() {
            Ok(_) => &mut self.conn,
            Err(e) => {
                debug!("Previous connection died: {}", e);
                self.conn = Self::connect(&self.cfg.ts, self.name)?;
                self.conn_id = None;
                &mut self.conn
            }
        };
        self.last_ping = Instant::now();
        Ok(conn)
    }
}
<|endoftext|>


<commit_before>
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub enum ListingOrderBy {
    #[serde(rename = "id")]
    Id,
    #[serde(rename = "name")]
    Name,
    #[serde(rename = "path")]
    Path,
    #[serde(rename = "created_at")]
    CreatedAt,
    #[serde(rename = "updated_at")]
    UpdatedAt,
    #[serde(rename = "last_activity_at")]
    LastActivityAt,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
struct ProjectListerInternal {
    /// Limit by archived status
    archived: Option<bool>,
    /// Limit by visibility.
    visibility: Option<::ListingVisibility>,
    /// Return requests ordered by. Default is `ListingOrderBy::CreatedAt`.
    order_by: Option<ListingOrderBy>,
    /// Return requests sorted. Default is `::ListingSort::Desc`.
    sort: Option<::ListingSort>,
    /// Return list of authorized projects matching the search criteria.
    search: Option<String>,
    /// Return only the ID, URL, name, and path of each project
    simple: Option<bool>,
}
<commit_msg>Add `OwnedProjectListerInternal` struct<commit_after>
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub enum ListingOrderBy {
    #[serde(rename = "id")]
    Id,
    #[serde(rename = "name")]
    Name,
    #[serde(rename = "path")]
    Path,
    #[serde(rename = "created_at")]
    CreatedAt,
    #[serde(rename = "updated_at")]
    UpdatedAt,
    #[serde(rename = "last_activity_at")]
    LastActivityAt,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
struct ProjectListerInternal {
    /// Limit by archived status
    archived: Option<bool>,
    /// Limit by visibility.
    visibility: Option<::ListingVisibility>,
    /// Return requests ordered by. Default is `ListingOrderBy::CreatedAt`.
    order_by: Option<ListingOrderBy>,
    /// Return requests sorted. Default is `::ListingSort::Desc`.
    sort: Option<::ListingSort>,
    /// Return list of authorized projects matching the search criteria.
    search: Option<String>,
    /// Return only the ID, URL, name, and path of each project
    simple: Option<bool>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
struct OwnedProjectListerInternal {

}
<|endoftext|>


<commit_before><commit_msg>rust implementation for crontab generator<commit_after><|endoftext|>


<commit_before>#![crate_name = "uu_chmod"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Arcterus <arcterus@mail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

#[cfg(unix)]
extern crate libc;
extern crate walker;

#[macro_use]
extern crate uucore;

use std::error::Error;
use std::fs;
use std::io::Write;
use std::os::unix::fs::{MetadataExt, PermissionsExt};
use std::path::Path;
use walker::Walker;

const NAME: &'static str = "chmod";
static SUMMARY: &'static str = "Change the mode of each FILE to MODE.
 With --reference, change the mode of each FILE to that of RFILE.";
static LONG_HELP: &'static str = "
 Each MODE is of the form '[ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=]?[0-7]+'.
";

pub fn uumain(mut args: Vec<String>) -> i32 {
    let syntax = format!("[OPTION]... MODE[,MODE]... FILE...
 {0} [OPTION]... OCTAL-MODE FILE...
 {0} [OPTION]... --reference=RFILE FILE...", NAME);
    let mut opts = new_coreopts!(&syntax, SUMMARY, LONG_HELP);
    opts.optflag("c", "changes", "like verbose but report only when a change is made (unimplemented)")
        .optflag("f", "quiet", "suppress most error messages (unimplemented)") // TODO: support --silent
        .optflag("v", "verbose", "output a diagnostic for every file processed (unimplemented)")
        .optflag("", "no-preserve-root", "do not treat '/' specially (the default)")
        .optflag("", "preserve-root", "fail to operate recursively on '/'")
        .optopt("", "reference", "use RFILE's mode instead of MODE values", "RFILE")
        .optflag("R", "recursive", "change files and directories recursively");

    // sanitize input for - at beginning (e.g. chmod -x testfile). Remove
    // the option and save it for later, after parsing is finished.
    let mut negative_option = None;
    for i in 0..args.len() {
        if let Some(first) = args[i].chars().nth(0) {
            if first != '-' {
                continue;
            }

            if let Some(second) = args[i].chars().nth(1) {
                match second {
                    'r' | 'w' | 'x' | 'X' | 's' | 't' | 'u' | 'g' | 'o' | '0' ... '7' => {
                        negative_option = Some(args.remove(i));
                        break;
                    },
                    _ => {}
                }
            }
        }
    }

    let mut matches = opts.parse(args);
    if matches.free.is_empty() {
        show_error!("missing an argument");
        show_error!("for help, try '{} --help'", NAME);
        return 1;
    } else {
        let changes = matches.opt_present("changes");
        let quiet = matches.opt_present("quiet");
        let verbose = matches.opt_present("verbose");
        let preserve_root = matches.opt_present("preserve-root");
        let recursive = matches.opt_present("recursive");
        let fmode = matches.opt_str("reference").and_then(|ref fref| {
            match fs::metadata(fref) {
                Ok(meta) => Some(meta.mode()),
                Err(err) => crash!(1, "cannot stat attribues of '{}': {}", fref, err)
            }
        });
        let cmode =
            if fmode.is_none() {
                // If there was a negative option, now it's a good time to
                // use it.
                if negative_option.is_some() {
                    negative_option
                } else {
                    Some(matches.free.remove(0))
                }
            } else {
                None
            };
        match chmod(matches.free, changes, quiet, verbose, preserve_root,
                    recursive, fmode, cmode.as_ref()) {
            Ok(()) => {}
            Err(e) => return e
        }
    }

    0
}

fn chmod(files: Vec<String>, changes: bool, quiet: bool, verbose: bool, preserve_root: bool, recursive: bool, fmode: Option<u32>, cmode: Option<&String>) -> Result<(), i32> {
    let mut r = Ok(());

    for filename in &files {
        let filename = &filename[..];
        let file = Path::new(filename);
        if file.exists() {
            if file.is_dir() {
                if !preserve_root || filename != "/" {
                    if recursive {
                        let walk_dir = match Walker::new(&file) {
                            Ok(m) => m,
                            Err(f) => {
                                crash!(1, "{}", f.to_string());
                            }
                        };
                        // XXX: here (and elsewhere) we see that this impl will have issues
                        // with non-UTF-8 filenames. Using OsString won't fix this because
                        // on Windows OsStrings cannot be built out of non-UTF-8 chars. One
                        // possible fix is to use CStrings rather than Strings in the args
                        // to chmod() and chmod_file().
                        r = chmod(walk_dir.filter_map(|x| match x {
                                                            Ok(o) => match o.path().into_os_string().to_str() {
                                                                Some(s) => Some(s.to_owned()),
                                                                None => None,
                                                            },
                                                            Err(_) => None,
                                                          }).collect(),
                                  changes, quiet, verbose, preserve_root, recursive, fmode, cmode).and(r);
                        r = chmod_file(&file, filename, changes, quiet, verbose, fmode, cmode).and(r);
                    }
                } else {
                    show_error!("could not change permissions of directory '{}'",
                                filename);
                    r = Err(1);
                }
            } else {
                r = chmod_file(&file, filename, changes, quiet, verbose, fmode, cmode).and(r);
            }
        } else {
            show_error!("no such file or directory '{}'", filename);
            r = Err(1);
        }
    }

    r
}

#[cfg(windows)]
fn chmod_file(file: &Path, name: &str, changes: bool, quiet: bool, verbose: bool, fmode: Option<u32>, cmode: Option<&String>) -> Result<(), i32> {
    // chmod is useless on Windows
    // it doesn't set any permissions at all
    // instead it just sets the readonly attribute on the file
    Err(0)
}
#[cfg(any(unix, target_os = "redox"))]
fn chmod_file(file: &Path, name: &str, changes: bool, quiet: bool, verbose: bool, fmode: Option<u32>, cmode: Option<&String>) -> Result<(), i32> {
    let mut fperm = match fs::metadata(name) {
        Ok(meta) => meta.mode() & 0o7777,
        Err(err) => {
            if !quiet {
                show_error!("{}", err);
            }
            return Err(1);
        }
    };
    match fmode {
        Some(mode) => try!(change_file(fperm, mode, file, name, verbose, changes, quiet)),
        None => {
            for mode in cmode.unwrap().split(',') {  // cmode is guaranteed to be Some in this case
                let arr: &[char] = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
                let result =
                    if mode.contains(arr) {
                        parse_numeric(fperm, mode)
                    } else {
                        parse_symbolic(fperm, mode, file)
                    };
                match result {
                    Ok(mode) => {
                        try!(change_file(fperm, mode, file, name, verbose, changes, quiet));
                        fperm = mode;
                    }
                    Err(f) => {
                        if !quiet {
                            show_error!("{}", f);
                        }
                        return Err(1);
                    }
                }
            }
        }
    }

    Ok(())
}

fn parse_numeric(fperm: u32, mut mode: &str) -> Result<u32, String> {
    let (op, pos) = try!(parse_op(mode, Some('=')));
    mode = mode[pos..].trim_left_matches('0');
    if mode.len() > 4 {
        Err(format!("mode is too large ({} > 7777)", mode))
    } else {
        match u32::from_str_radix(mode, 8) {
            Ok(change) => {
                Ok(match op {
                    '+' => fperm | change,
                    '-' => fperm & !change,
                    '=' => change,
                    _ => unreachable!()
                })
            }
            Err(err) => Err(err.description().to_owned())
        }
    }
}

fn parse_symbolic(mut fperm: u32, mut mode: &str, file: &Path) -> Result<u32, String> {
    #[cfg(unix)]
    use libc::umask;

    #[cfg(target_os = "redox")]
    unsafe fn umask(_mask: u32) -> u32 {
        // XXX Redox does not currently have umask
        0
    }

    let (mask, pos) = parse_levels(mode);
    if pos == mode.len() {
        return Err(format!("invalid mode ({})", mode));
    }
    let respect_umask = pos == 0;
    let last_umask = unsafe {
        umask(0)
    };
    mode = &mode[pos..];
    while mode.len() > 0 {
        let (op, pos) = try!(parse_op(mode, None));
        mode = &mode[pos..];
        let (mut srwx, pos) = parse_change(mode, fperm, file);
        if respect_umask {
            srwx &= !(last_umask as u32);
        }
        mode = &mode[pos..];
        match op {
            '+' => fperm |= srwx & mask,
            '-' => fperm &= !(srwx & mask),
            '=' => fperm = (fperm & !mask) | (srwx & mask),
            _ => unreachable!()
        }
    }
    unsafe {
        umask(last_umask);
    }
    Ok(fperm)
}

fn parse_levels(mode: &str) -> (u32, usize) {
    let mut mask = 0;
    let mut pos = 0;
    for ch in mode.chars() {
        mask |= match ch {
            'u' => 0o7700,
            'g' => 0o7070,
            'o' => 0o7007,
            'a' => 0o7777,
            _ => break
        };
        pos += 1;
    }
    if pos == 0 {
        mask = 0o7777;  // default to 'a'
    }
    (mask, pos)
}

fn parse_op(mode: &str, default: Option<char>) -> Result<(char, usize), String> {
    match mode.chars().next() {
        Some(ch) => match ch {
            '+' | '-' | '=' => Ok((ch, 1)),
            _ => match default {
                Some(ch) => Ok((ch, 0)),
                None => Err(format!("invalid operator (expected +, -, or =, but found {})", ch))
            }
        },
        None => Err("unexpected end of mode".to_owned())
    }
}

fn parse_change(mode: &str, fperm: u32, file: &Path) -> (u32, usize) {
    let mut srwx = fperm & 0o7000;
    let mut pos = 0;
    for ch in mode.chars() {
        match ch {
            'r' => srwx |= 0o444,
            'w' => srwx |= 0o222,
            'x' => srwx |= 0o111,
            'X' => {
                if file.is_dir() || (fperm & 0o0111) != 0 {
                    srwx |= 0o111
                }
            }
            's' => srwx |= 0o4000 | 0o2000,
            't' => srwx |= 0o1000,
            'u' => srwx = (fperm & 0o700) | ((fperm >> 3) & 0o070) | ((fperm >> 6) & 0o007),
            'g' => srwx = ((fperm << 3) & 0o700) | (fperm & 0o070) | ((fperm >> 3) & 0o007),
            'o' => srwx = ((fperm << 6) & 0o700) | ((fperm << 3) & 0o070) | (fperm & 0o007),
            _ => break
        };
        pos += 1;
    }
    if pos == 0 {
        srwx = 0;
    }
    (srwx, pos)
}

fn change_file(fperm: u32, mode: u32, file: &Path, path: &str, verbose: bool, changes: bool, quiet: bool) -> Result<(), i32> {
    if fperm == mode {
        if verbose && !changes {
            show_info!("mode of '{}' retained as {:o}", file.display(), fperm);
        }
        Ok(())
    } else if let Err(err) = fs::set_permissions(Path::new(path), fs::Permissions::from_mode(mode)) {
        if !quiet {
            show_error!("{}", err);
        }
        if verbose {
            show_info!("failed to change mode of file '{}' from {:o} to {:o}", file.display(), fperm, mode);
        }
        Err(1)
    } else {
        if verbose || changes {
            show_info!("mode of '{}' changed from {:o} to {:o}", file.display(), fperm, mode);
        }
        Ok(())
    }
}
<commit_msg>chmod: avoid passing all args through function hierarchy<commit_after>#![crate_name = "uu_chmod"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Arcterus <arcterus@mail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

#[cfg(unix)]
extern crate libc;
extern crate walker;

#[macro_use]
extern crate uucore;

use std::error::Error;
use std::fs;
use std::io::Write;
use std::os::unix::fs::{MetadataExt, PermissionsExt};
use std::path::Path;
use walker::Walker;

const NAME: &'static str = "chmod";
static SUMMARY: &'static str = "Change the mode of each FILE to MODE.
 With --reference, change the mode of each FILE to that of RFILE.";
static LONG_HELP: &'static str = "
 Each MODE is of the form '[ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=]?[0-7]+'.
";

pub fn uumain(mut args: Vec<String>) -> i32 {
    let syntax = format!("[OPTION]... MODE[,MODE]... FILE...
 {0} [OPTION]... OCTAL-MODE FILE...
 {0} [OPTION]... --reference=RFILE FILE...", NAME);
    let mut opts = new_coreopts!(&syntax, SUMMARY, LONG_HELP);
    opts.optflag("c", "changes", "like verbose but report only when a change is made (unimplemented)")
        .optflag("f", "quiet", "suppress most error messages (unimplemented)") // TODO: support --silent
        .optflag("v", "verbose", "output a diagnostic for every file processed (unimplemented)")
        .optflag("", "no-preserve-root", "do not treat '/' specially (the default)")
        .optflag("", "preserve-root", "fail to operate recursively on '/'")
        .optopt("", "reference", "use RFILE's mode instead of MODE values", "RFILE")
        .optflag("R", "recursive", "change files and directories recursively");

    // sanitize input for - at beginning (e.g. chmod -x testfile). Remove
    // the option and save it for later, after parsing is finished.
    let negative_option = sanitize_input(&mut args);

    let mut matches = opts.parse(args);
    if matches.free.is_empty() {
        show_error!("missing an argument");
        show_error!("for help, try '{} --help'", NAME);
        return 1;
    } else {
        let changes = matches.opt_present("changes");
        let quiet = matches.opt_present("quiet");
        let verbose = matches.opt_present("verbose");
        let preserve_root = matches.opt_present("preserve-root");
        let recursive = matches.opt_present("recursive");
        let fmode = matches.opt_str("reference").and_then(|ref fref| {
            match fs::metadata(fref) {
                Ok(meta) => Some(meta.mode()),
                Err(err) => crash!(1, "cannot stat attribues of '{}': {}", fref, err)
            }
        });
        let cmode =
            if fmode.is_none() {
                // If there was a negative option, now it's a good time to
                // use it.
                if negative_option.is_some() {
                    negative_option
                } else {
                    Some(matches.free.remove(0))
                }
            } else {
                None
            };
        let chmoder = Chmoder{
            changes: changes,
            quiet: quiet,
            verbose: verbose,
            preserve_root: preserve_root,
            recursive: recursive,
            fmode: fmode,
            cmode: cmode,
        };
        match chmoder.chmod(matches.free) {
            Ok(()) => {}
            Err(e) => return e
        }
    }

    0
}

fn sanitize_input(args: &mut Vec<String>) -> Option<String> {
    for i in 0..args.len() {
        let first = args[i].chars().nth(0).unwrap();
        if first != '-' {
            continue;
        }
        if let Some(second) = args[i].chars().nth(1) {
            match second {
                'r' | 'w' | 'x' | 'X' | 's' | 't' | 'u' | 'g' | 'o' | '0' ... '7' => {
                    return Some(args.remove(i));
                },
                _ => {}
            }
        }
    }
    None
}

struct Chmoder {
    changes: bool,
    quiet: bool,
    verbose: bool,
    preserve_root: bool,
    recursive: bool,
    fmode: Option<u32>,
    cmode: Option<String>,
}

impl Chmoder {

    fn chmod(&self, files: Vec<String>) -> Result<(), i32> {
        let mut r = Ok(());

        for filename in &files {
            let filename = &filename[..];
            let file = Path::new(filename);
            if file.exists() {
                if file.is_dir() {
                    if !self.preserve_root || filename != "/" {
                        if self.recursive {
                            let walk_dir = match Walker::new(&file) {
                                Ok(m) => m,
                                Err(f) => {
                                    crash!(1, "{}", f.to_string());
                                }
                            };
                            // XXX: here (and elsewhere) we see that this impl will have issues
                            // with non-UTF-8 filenames. Using OsString won't fix this because
                            // on Windows OsStrings cannot be built out of non-UTF-8 chars. One
                            // possible fix is to use CStrings rather than Strings in the args
                            // to chmod() and chmod_file().
                            r = self.chmod(walk_dir.filter_map(|x| match x {
                                                                Ok(o) => match o.path().into_os_string().to_str() {
                                                                    Some(s) => Some(s.to_owned()),
                                                                    None => None,
                                                                },
                                                                Err(_) => None,
                                                              }).collect()).and(r);
                            r = self.chmod_file(&file, filename).and(r);
                        }
                    } else {
                        show_error!("could not change permissions of directory '{}'",
                                    filename);
                        r = Err(1);
                    }
                } else {
                    r = self.chmod_file(&file, filename).and(r);
                }
            } else {
                show_error!("no such file or directory '{}'", filename);
                r = Err(1);
            }
        }

        r
    }

    #[cfg(windows)]
    fn chmod_file(&self, file: &Path, name: &str) -> Result<(), i32> {
        // chmod is useless on Windows
        // it doesn't set any permissions at all
        // instead it just sets the readonly attribute on the file
        Err(0)
    }
    #[cfg(any(unix, target_os = "redox"))]
    fn chmod_file(&self, file: &Path, name: &str) -> Result<(), i32> {
        let mut fperm = match fs::metadata(name) {
            Ok(meta) => meta.mode() & 0o7777,
            Err(err) => {
                if !self.quiet {
                    show_error!("{}", err);
                }
                return Err(1);
            }
        };
        match self.fmode {
            Some(mode) => try!(self.change_file(fperm, mode, file, name)),
            None => {
                let cmode_unwrapped = self.cmode.clone().unwrap();
                for mode in cmode_unwrapped.split(',') {  // cmode is guaranteed to be Some in this case
                    let arr: &[char] = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
                    let result =
                        if mode.contains(arr) {
                            self.parse_numeric(fperm, mode)
                        } else {
                            self.parse_symbolic(fperm, mode, file)
                        };
                    match result {
                        Ok(mode) => {
                            try!(self.change_file(fperm, mode, file, name));
                            fperm = mode;
                        }
                        Err(f) => {
                            if !self.quiet {
                                show_error!("{}", f);
                            }
                            return Err(1);
                        }
                    }
                }
            }
        }

        Ok(())
    }

    fn parse_numeric(&self, fperm: u32, mut mode: &str) -> Result<u32, String> {
        let (op, pos) = try!(self.parse_op(mode, Some('=')));
        mode = mode[pos..].trim_left_matches('0');
        if mode.len() > 4 {
            Err(format!("mode is too large ({} > 7777)", mode))
        } else {
            match u32::from_str_radix(mode, 8) {
                Ok(change) => {
                    Ok(match op {
                        '+' => fperm | change,
                        '-' => fperm & !change,
                        '=' => change,
                        _ => unreachable!()
                    })
                }
                Err(err) => Err(err.description().to_owned())
            }
        }
    }

    fn parse_symbolic(&self, mut fperm: u32, mut mode: &str, file: &Path) -> Result<u32, String> {
        #[cfg(unix)]
        use libc::umask;

        #[cfg(target_os = "redox")]
        unsafe fn umask(_mask: u32) -> u32 {
            // XXX Redox does not currently have umask
            0
        }

        let (mask, pos) = self.parse_levels(mode);
        if pos == mode.len() {
            return Err(format!("invalid mode ({})", mode));
        }
        let respect_umask = pos == 0;
        let last_umask = unsafe {
            umask(0)
        };
        mode = &mode[pos..];
        while mode.len() > 0 {
            let (op, pos) = try!(self.parse_op(mode, None));
            mode = &mode[pos..];
            let (mut srwx, pos) = self.parse_change(mode, fperm, file);
            if respect_umask {
                srwx &= !(last_umask as u32);
            }
            mode = &mode[pos..];
            match op {
                '+' => fperm |= srwx & mask,
                '-' => fperm &= !(srwx & mask),
                '=' => fperm = (fperm & !mask) | (srwx & mask),
                _ => unreachable!()
            }
        }
        unsafe {
            umask(last_umask);
        }
        Ok(fperm)
    }

    fn parse_levels(&self, mode: &str) -> (u32, usize) {
        let mut mask = 0;
        let mut pos = 0;
        for ch in mode.chars() {
            mask |= match ch {
                'u' => 0o7700,
                'g' => 0o7070,
                'o' => 0o7007,
                'a' => 0o7777,
                _ => break
            };
            pos += 1;
        }
        if pos == 0 {
            mask = 0o7777;  // default to 'a'
        }
        (mask, pos)
    }

    fn parse_op(&self, mode: &str, default: Option<char>) -> Result<(char, usize), String> {
        match mode.chars().next() {
            Some(ch) => match ch {
                '+' | '-' | '=' => Ok((ch, 1)),
                _ => match default {
                    Some(ch) => Ok((ch, 0)),
                    None => Err(format!("invalid operator (expected +, -, or =, but found {})", ch))
                }
            },
            None => Err("unexpected end of mode".to_owned())
        }
    }

    fn parse_change(&self, mode: &str, fperm: u32, file: &Path) -> (u32, usize) {
        let mut srwx = fperm & 0o7000;
        let mut pos = 0;
        for ch in mode.chars() {
            match ch {
                'r' => srwx |= 0o444,
                'w' => srwx |= 0o222,
                'x' => srwx |= 0o111,
                'X' => {
                    if file.is_dir() || (fperm & 0o0111) != 0 {
                        srwx |= 0o111
                    }
                }
                's' => srwx |= 0o4000 | 0o2000,
                't' => srwx |= 0o1000,
                'u' => srwx = (fperm & 0o700) | ((fperm >> 3) & 0o070) | ((fperm >> 6) & 0o007),
                'g' => srwx = ((fperm << 3) & 0o700) | (fperm & 0o070) | ((fperm >> 3) & 0o007),
                'o' => srwx = ((fperm << 6) & 0o700) | ((fperm << 3) & 0o070) | (fperm & 0o007),
                _ => break
            };
            pos += 1;
        }
        if pos == 0 {
            srwx = 0;
        }
        (srwx, pos)
    }

    fn change_file(&self, fperm: u32, mode: u32, file: &Path, path: &str) -> Result<(), i32> {
        if fperm == mode {
            if self.verbose && !self.changes {
                show_info!("mode of '{}' retained as {:o}", file.display(), fperm);
            }
            Ok(())
        } else if let Err(err) = fs::set_permissions(Path::new(path), fs::Permissions::from_mode(mode)) {
            if !self.quiet {
                show_error!("{}", err);
            }
            if self.verbose {
                show_info!("failed to change mode of file '{}' from {:o} to {:o}", file.display(), fperm, mode);
            }
            Err(1)
        } else {
            if self.verbose || self.changes {
                show_info!("mode of '{}' changed from {:o} to {:o}", file.display(), fperm, mode);
            }
            Ok(())
        }
    }
}<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::collections::HashSet;
use std::collections::hash_map::RandomState;
use std::iter::FromIterator;
use std::path::Path;
use std::path::PathBuf;
use std::vec::Vec;

use rand::random;
use serde_json;
use time::now;
use uuid::Uuid;

use devicemapper::consts::SECTOR_SIZE;
use devicemapper::DM;
use devicemapper::{DataBlocks, Sectors, Segment};
use devicemapper::LinearDev;
use devicemapper::{ThinDevId, ThinPoolDev, ThinPoolStatus, ThinPoolWorkingStatus};

use super::super::consts::IEC::Mi;
use super::super::engine::{Filesystem, HasName, HasUuid, Pool};
use super::super::errors::{EngineError, EngineResult, ErrorEnum};
use super::super::structures::Table;
use super::super::types::{FilesystemUuid, PoolUuid, RenameAction, Redundancy};

use super::blockdevmgr::BlockDevMgr;
use super::device::wipe_sectors;
use super::dmdevice::{FlexRole, ThinPoolRole, format_flex_name, format_thinpool_name};
use super::filesystem::{StratFilesystem, FilesystemStatus};
use super::mdv::MetadataVol;
use super::metadata::MIN_MDA_SECTORS;
use super::serde_structs::{FlexDevsSave, PoolSave, Recordable, ThinPoolDevSave};

const DATA_BLOCK_SIZE: Sectors = Sectors(2048);
const META_LOWATER: u64 = 512;
const DATA_LOWATER: DataBlocks = DataBlocks(512);

const INITIAL_META_SIZE: Sectors = Sectors(16 * Mi / SECTOR_SIZE as u64);
const INITIAL_DATA_SIZE: Sectors = Sectors(768 * Mi / SECTOR_SIZE as u64);
const INITIAL_MDV_SIZE: Sectors = Sectors(16 * Mi / SECTOR_SIZE as u64);

#[derive(Debug)]
pub struct StratPool {
    name: String,
    pool_uuid: PoolUuid,
    pub block_devs: BlockDevMgr,
    pub filesystems: Table<StratFilesystem>,
    redundancy: Redundancy,
    thin_pool: ThinPoolDev,
    mdv: MetadataVol,
}

impl StratPool {
    /// Initialize a Stratis Pool.
    /// 1. Initialize the block devices specified by paths.
    /// 2. Set up thinpool device to back filesystems.
    pub fn initialize(name: &str,
                      dm: &DM,
                      paths: &[&Path],
                      redundancy: Redundancy,
                      force: bool)
                      -> EngineResult<StratPool> {
        let pool_uuid = Uuid::new_v4();

        let mut block_mgr =
            try!(BlockDevMgr::initialize(&pool_uuid, paths, MIN_MDA_SECTORS, force));

        if block_mgr.avail_space() < StratPool::min_initial_size() {
            let avail_size = block_mgr.avail_space().bytes();
            try!(block_mgr.destroy_all());
            return Err(EngineError::Engine(ErrorEnum::Invalid,
                                           format!("Space on pool must be at least {} bytes, \
                                                   available space is only {} bytes",
                                                   StratPool::min_initial_size().bytes(),
                                                   avail_size)));

        }

        let meta_regions = block_mgr
            .alloc_space(INITIAL_META_SIZE)
            .expect("blockmgr must not fail, already checked for space");

        let data_regions = block_mgr
            .alloc_space(INITIAL_DATA_SIZE)
            .expect("blockmgr must not fail, already checked for space");

        let thinpool_dev =
            try!(StratPool::setup_thinpooldev(dm, &pool_uuid, meta_regions, data_regions));

        let mdv_regions = block_mgr
            .alloc_space(INITIAL_MDV_SIZE)
            .expect("blockmgr must not fail, already checked for space");

        let mdv = try!(StratPool::setup_mdv(dm, &pool_uuid, mdv_regions));

        let mut pool = StratPool {
            name: name.to_owned(),
            pool_uuid: pool_uuid,
            block_devs: block_mgr,
            filesystems: Table::new(),
            redundancy: redundancy,
            thin_pool: thinpool_dev,
            mdv: mdv,
        };

        try!(pool.write_metadata());

        Ok(pool)
    }

    /// Setup a thin pool device for this pool from the designated segments.
    // TODO: complicate this method by optionally checking whether the device
    // already exists.
    pub fn setup_thinpooldev(dm: &DM,
                             pool_uuid: &PoolUuid,
                             meta_segs: Vec<Segment>,
                             data_segs: Vec<Segment>)
                             -> EngineResult<ThinPoolDev> {
        let device_name = format_flex_name(pool_uuid, FlexRole::ThinMeta);
        let meta_dev = try!(LinearDev::new(&device_name, dm, meta_segs));

        // When constructing a thin-pool, Stratis reserves the first N
        // sectors on a block device by creating a linear device with a
        // starting offset. DM writes the super block in the first block.
        // DM requires this first block to be zeros when the meta data for
        // the thin-pool is initially created. If we don't zero the
        // superblock DM issue error messages because it triggers code paths
        // that are trying to re-adopt the device with the attributes that
        // have been passed.
        try!(wipe_sectors(&try!(meta_dev.devnode()), Sectors(0), INITIAL_META_SIZE));

        let device_name = format_flex_name(pool_uuid, FlexRole::ThinData);
        let data_dev = try!(LinearDev::new(&device_name, dm, data_segs));

        let device_name = format_thinpool_name(&pool_uuid, ThinPoolRole::Pool);
        // TODO Fix hard coded data blocksize and low water mark.
        Ok(try!(ThinPoolDev::new(&device_name,
                                 dm,
                                 try!(data_dev.size()),
                                 DATA_BLOCK_SIZE,
                                 DATA_LOWATER,
                                 meta_dev,
                                 data_dev)))
    }

    pub fn setup_mdv(dm: &DM,
                     pool_uuid: &PoolUuid,
                     segs: Vec<Segment>)
                     -> EngineResult<MetadataVol> {
        let device_name = format_flex_name(pool_uuid, FlexRole::MetadataVolume);
        let mdv_dev = try!(LinearDev::new(&device_name, dm, segs));
        let mdv = try!(MetadataVol::initialize(&pool_uuid, mdv_dev));
        Ok(mdv)
    }

    /// Minimum initial size for a pool.
    pub fn min_initial_size() -> Sectors {
        INITIAL_META_SIZE + INITIAL_DATA_SIZE + INITIAL_MDV_SIZE
    }

    // TODO: Check current time against global last updated, and use
    // alternate time value if earlier, as described in SWDD
    fn write_metadata(&mut self) -> EngineResult<()> {
        let data = try!(serde_json::to_string(&try!(self.record())));
        self.block_devs
            .save_state(&now().to_timespec(), data.as_bytes())
    }

    pub fn check(&mut self) -> () {
        let dm = DM::new().expect("Could not get DM handle");

        if let Err(e) = self.mdv.check() {
            error!("MDV error: {}", e);
            return;
        }

        let result = match self.thin_pool.status(&dm) {
            Ok(r) => r,
            Err(_) => {
                error!("Could not get thinpool status");
                // TODO: Take pool offline?
                return;
            }
        };

        match result {
            ThinPoolStatus::Good(wstatus, usage) => {
                match wstatus {
                    ThinPoolWorkingStatus::Good => {}
                    ThinPoolWorkingStatus::ReadOnly => {
                        // TODO: why is pool r/o and how do we get it
                        // rw again?
                    }
                    ThinPoolWorkingStatus::OutOfSpace => {
                        // TODO: Add more space if possible, or
                        // prevent further usage
                        // Should never happen -- we should be extending first!
                    }
                    ThinPoolWorkingStatus::NeedsCheck => {
                        // TODO: Take pool offline?
                        // TODO: run thin_check
                    }
                }

                if usage.used_meta > usage.total_meta - META_LOWATER {
                    // TODO: Extend meta device
                }

                if usage.used_data > usage.total_data - DATA_LOWATER {
                    // TODO: Extend data device
                }
            }
            ThinPoolStatus::Fail => {
                // TODO: Take pool offline?
                // TODO: Run thin_check
            }
        }

        for fs in &mut self.filesystems {
            match fs.check(&dm) {
                Ok(f_status) => {
                    if let FilesystemStatus::Failed = f_status {
                        // TODO: recover fs? (how?)
                    }
                }
                Err(_e) => error!("fs.check() failed"),
            }
        }
    }

    /// Teardown a pool.
    /// Take down the device mapper devices belonging to the pool.
    /// This method and destroy() must keep their DM teardown operations
    /// in sync.
    pub fn teardown(self) -> EngineResult<()> {
        // TODO: any necessary clean up of filesystems
        if !self.filesystems.is_empty() {
            return Err(EngineError::Engine(ErrorEnum::Busy,
                                           format!("May be unsynced files on device.")));
        }
        let dm = try!(DM::new());
        try!(self.thin_pool.teardown(&dm));
        try!(self.mdv.teardown(&dm));
        Ok(())
    }
}

impl Pool for StratPool {
    fn create_filesystems<'a, 'b>(&'a mut self,
                                  specs: &[&'b str])
                                  -> EngineResult<Vec<(&'b str, FilesystemUuid)>> {
        let names: HashSet<_, RandomState> = HashSet::from_iter(specs);
        for name in names.iter() {
            if self.filesystems.contains_name(name) {
                return Err(EngineError::Engine(ErrorEnum::AlreadyExists, name.to_string()));
            }
        }

        // TODO: Roll back on filesystem initialization failure.
        let dm = try!(DM::new());
        let mut result = Vec::new();
        for name in names.iter() {
            let uuid = Uuid::new_v4();
            // FIXME: Start managing thin ids in pool.
            let thin_id =
                ThinDevId::new_u64((random::<u32>() >> 8) as u64)
                    .expect("must require only 24 bits");
            let new_filesystem = try!(StratFilesystem::initialize(&self.pool_uuid,
                                                                  uuid,
                                                                  thin_id,
                                                                  name,
                                                                  &dm,
                                                                  &mut self.thin_pool));
            try!(self.mdv.save_fs(&new_filesystem));
            self.filesystems.insert(new_filesystem);
            result.push((**name, uuid));
        }

        Ok(result)
    }

    fn add_blockdevs(&mut self, paths: &[&Path], force: bool) -> EngineResult<Vec<PathBuf>> {
        let bdev_paths = try!(self.block_devs.add(&self.pool_uuid, paths, force));
        try!(self.write_metadata());
        Ok(bdev_paths)
    }

    fn destroy(self) -> EngineResult<()> {
        // Ensure that DM teardown operations in this method are in sync
        // with operations in teardown().
        let dm = try!(DM::new());
        try!(self.thin_pool.teardown(&dm));
        try!(self.mdv.teardown(&dm));
        try!(self.block_devs.destroy_all());

        Ok(())
    }

    fn destroy_filesystems<'a, 'b>(&'a mut self,
                                   fs_uuids: &[&'b FilesystemUuid])
                                   -> EngineResult<Vec<&'b FilesystemUuid>> {
        for fsid in fs_uuids {
            try!(self.mdv.rm_fs(fsid));
        }
        destroy_filesystems!{self; fs_uuids}
    }

    fn rename_filesystem(&mut self,
                         uuid: &FilesystemUuid,
                         new_name: &str)
                         -> EngineResult<RenameAction> {
        rename_filesystem!{self; uuid; new_name}
    }

    fn rename(&mut self, name: &str) {
        self.name = name.to_owned();
    }

    fn get_filesystem(&mut self, uuid: &FilesystemUuid) -> Option<&mut Filesystem> {
        get_filesystem!(self; uuid)
    }
}

impl HasUuid for StratPool {
    fn uuid(&self) -> &PoolUuid {
        &self.pool_uuid
    }
}

impl HasName for StratPool {
    fn name(&self) -> &str {
        &self.name
    }
}

impl Recordable<PoolSave> for StratPool {
    fn record(&self) -> EngineResult<PoolSave> {

        let mapper = |seg: &Segment| -> EngineResult<(Uuid, Sectors, Sectors)> {
            let bd = try!(self.block_devs
                     .get_by_device(seg.device)
                     .ok_or(EngineError::Engine(ErrorEnum::NotFound,
                                                format!("no block device found for device {:?}",
                                                        seg.device))));
            Ok((*bd.uuid(), seg.start, seg.length))
        };

        let meta_dev = try!(self.mdv.segments().iter().map(&mapper).collect());

        let thin_meta_dev = try!(self.thin_pool
                                     .meta_dev()
                                     .segments()
                                     .iter()
                                     .map(&mapper)
                                     .collect());

        let thin_data_dev = try!(self.thin_pool
                                     .data_dev()
                                     .segments()
                                     .iter()
                                     .map(&mapper)
                                     .collect());

        Ok(PoolSave {
               name: self.name.clone(),
               block_devs: try!(self.block_devs.record()),
               flex_devs: FlexDevsSave {
                   meta_dev: meta_dev,
                   thin_meta_dev: thin_meta_dev,
                   thin_data_dev: thin_data_dev,
               },
               thinpool_dev: ThinPoolDevSave { data_block_size: self.thin_pool.data_block_size() },
           })
    }
}
<commit_msg>Get rid of a TODO which is certainly in the wrong place<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::collections::HashSet;
use std::collections::hash_map::RandomState;
use std::iter::FromIterator;
use std::path::Path;
use std::path::PathBuf;
use std::vec::Vec;

use rand::random;
use serde_json;
use time::now;
use uuid::Uuid;

use devicemapper::consts::SECTOR_SIZE;
use devicemapper::DM;
use devicemapper::{DataBlocks, Sectors, Segment};
use devicemapper::LinearDev;
use devicemapper::{ThinDevId, ThinPoolDev, ThinPoolStatus, ThinPoolWorkingStatus};

use super::super::consts::IEC::Mi;
use super::super::engine::{Filesystem, HasName, HasUuid, Pool};
use super::super::errors::{EngineError, EngineResult, ErrorEnum};
use super::super::structures::Table;
use super::super::types::{FilesystemUuid, PoolUuid, RenameAction, Redundancy};

use super::blockdevmgr::BlockDevMgr;
use super::device::wipe_sectors;
use super::dmdevice::{FlexRole, ThinPoolRole, format_flex_name, format_thinpool_name};
use super::filesystem::{StratFilesystem, FilesystemStatus};
use super::mdv::MetadataVol;
use super::metadata::MIN_MDA_SECTORS;
use super::serde_structs::{FlexDevsSave, PoolSave, Recordable, ThinPoolDevSave};

const DATA_BLOCK_SIZE: Sectors = Sectors(2048);
const META_LOWATER: u64 = 512;
const DATA_LOWATER: DataBlocks = DataBlocks(512);

const INITIAL_META_SIZE: Sectors = Sectors(16 * Mi / SECTOR_SIZE as u64);
const INITIAL_DATA_SIZE: Sectors = Sectors(768 * Mi / SECTOR_SIZE as u64);
const INITIAL_MDV_SIZE: Sectors = Sectors(16 * Mi / SECTOR_SIZE as u64);

#[derive(Debug)]
pub struct StratPool {
    name: String,
    pool_uuid: PoolUuid,
    pub block_devs: BlockDevMgr,
    pub filesystems: Table<StratFilesystem>,
    redundancy: Redundancy,
    thin_pool: ThinPoolDev,
    mdv: MetadataVol,
}

impl StratPool {
    /// Initialize a Stratis Pool.
    /// 1. Initialize the block devices specified by paths.
    /// 2. Set up thinpool device to back filesystems.
    pub fn initialize(name: &str,
                      dm: &DM,
                      paths: &[&Path],
                      redundancy: Redundancy,
                      force: bool)
                      -> EngineResult<StratPool> {
        let pool_uuid = Uuid::new_v4();

        let mut block_mgr =
            try!(BlockDevMgr::initialize(&pool_uuid, paths, MIN_MDA_SECTORS, force));

        if block_mgr.avail_space() < StratPool::min_initial_size() {
            let avail_size = block_mgr.avail_space().bytes();
            try!(block_mgr.destroy_all());
            return Err(EngineError::Engine(ErrorEnum::Invalid,
                                           format!("Space on pool must be at least {} bytes, \
                                                   available space is only {} bytes",
                                                   StratPool::min_initial_size().bytes(),
                                                   avail_size)));

        }

        let meta_regions = block_mgr
            .alloc_space(INITIAL_META_SIZE)
            .expect("blockmgr must not fail, already checked for space");

        let data_regions = block_mgr
            .alloc_space(INITIAL_DATA_SIZE)
            .expect("blockmgr must not fail, already checked for space");

        let thinpool_dev =
            try!(StratPool::setup_thinpooldev(dm, &pool_uuid, meta_regions, data_regions));

        let mdv_regions = block_mgr
            .alloc_space(INITIAL_MDV_SIZE)
            .expect("blockmgr must not fail, already checked for space");

        let mdv = try!(StratPool::setup_mdv(dm, &pool_uuid, mdv_regions));

        let mut pool = StratPool {
            name: name.to_owned(),
            pool_uuid: pool_uuid,
            block_devs: block_mgr,
            filesystems: Table::new(),
            redundancy: redundancy,
            thin_pool: thinpool_dev,
            mdv: mdv,
        };

        try!(pool.write_metadata());

        Ok(pool)
    }

    /// Setup a thin pool device for this pool from the designated segments.
    // TODO: complicate this method by optionally checking whether the device
    // already exists.
    pub fn setup_thinpooldev(dm: &DM,
                             pool_uuid: &PoolUuid,
                             meta_segs: Vec<Segment>,
                             data_segs: Vec<Segment>)
                             -> EngineResult<ThinPoolDev> {
        let device_name = format_flex_name(pool_uuid, FlexRole::ThinMeta);
        let meta_dev = try!(LinearDev::new(&device_name, dm, meta_segs));

        // When constructing a thin-pool, Stratis reserves the first N
        // sectors on a block device by creating a linear device with a
        // starting offset. DM writes the super block in the first block.
        // DM requires this first block to be zeros when the meta data for
        // the thin-pool is initially created. If we don't zero the
        // superblock DM issue error messages because it triggers code paths
        // that are trying to re-adopt the device with the attributes that
        // have been passed.
        try!(wipe_sectors(&try!(meta_dev.devnode()), Sectors(0), INITIAL_META_SIZE));

        let device_name = format_flex_name(pool_uuid, FlexRole::ThinData);
        let data_dev = try!(LinearDev::new(&device_name, dm, data_segs));

        let device_name = format_thinpool_name(&pool_uuid, ThinPoolRole::Pool);
        Ok(try!(ThinPoolDev::new(&device_name,
                                 dm,
                                 try!(data_dev.size()),
                                 DATA_BLOCK_SIZE,
                                 DATA_LOWATER,
                                 meta_dev,
                                 data_dev)))
    }

    pub fn setup_mdv(dm: &DM,
                     pool_uuid: &PoolUuid,
                     segs: Vec<Segment>)
                     -> EngineResult<MetadataVol> {
        let device_name = format_flex_name(pool_uuid, FlexRole::MetadataVolume);
        let mdv_dev = try!(LinearDev::new(&device_name, dm, segs));
        let mdv = try!(MetadataVol::initialize(&pool_uuid, mdv_dev));
        Ok(mdv)
    }

    /// Minimum initial size for a pool.
    pub fn min_initial_size() -> Sectors {
        INITIAL_META_SIZE + INITIAL_DATA_SIZE + INITIAL_MDV_SIZE
    }

    // TODO: Check current time against global last updated, and use
    // alternate time value if earlier, as described in SWDD
    fn write_metadata(&mut self) -> EngineResult<()> {
        let data = try!(serde_json::to_string(&try!(self.record())));
        self.block_devs
            .save_state(&now().to_timespec(), data.as_bytes())
    }

    pub fn check(&mut self) -> () {
        let dm = DM::new().expect("Could not get DM handle");

        if let Err(e) = self.mdv.check() {
            error!("MDV error: {}", e);
            return;
        }

        let result = match self.thin_pool.status(&dm) {
            Ok(r) => r,
            Err(_) => {
                error!("Could not get thinpool status");
                // TODO: Take pool offline?
                return;
            }
        };

        match result {
            ThinPoolStatus::Good(wstatus, usage) => {
                match wstatus {
                    ThinPoolWorkingStatus::Good => {}
                    ThinPoolWorkingStatus::ReadOnly => {
                        // TODO: why is pool r/o and how do we get it
                        // rw again?
                    }
                    ThinPoolWorkingStatus::OutOfSpace => {
                        // TODO: Add more space if possible, or
                        // prevent further usage
                        // Should never happen -- we should be extending first!
                    }
                    ThinPoolWorkingStatus::NeedsCheck => {
                        // TODO: Take pool offline?
                        // TODO: run thin_check
                    }
                }

                if usage.used_meta > usage.total_meta - META_LOWATER {
                    // TODO: Extend meta device
                }

                if usage.used_data > usage.total_data - DATA_LOWATER {
                    // TODO: Extend data device
                }
            }
            ThinPoolStatus::Fail => {
                // TODO: Take pool offline?
                // TODO: Run thin_check
            }
        }

        for fs in &mut self.filesystems {
            match fs.check(&dm) {
                Ok(f_status) => {
                    if let FilesystemStatus::Failed = f_status {
                        // TODO: recover fs? (how?)
                    }
                }
                Err(_e) => error!("fs.check() failed"),
            }
        }
    }

    /// Teardown a pool.
    /// Take down the device mapper devices belonging to the pool.
    /// This method and destroy() must keep their DM teardown operations
    /// in sync.
    pub fn teardown(self) -> EngineResult<()> {
        // TODO: any necessary clean up of filesystems
        if !self.filesystems.is_empty() {
            return Err(EngineError::Engine(ErrorEnum::Busy,
                                           format!("May be unsynced files on device.")));
        }
        let dm = try!(DM::new());
        try!(self.thin_pool.teardown(&dm));
        try!(self.mdv.teardown(&dm));
        Ok(())
    }
}

impl Pool for StratPool {
    fn create_filesystems<'a, 'b>(&'a mut self,
                                  specs: &[&'b str])
                                  -> EngineResult<Vec<(&'b str, FilesystemUuid)>> {
        let names: HashSet<_, RandomState> = HashSet::from_iter(specs);
        for name in names.iter() {
            if self.filesystems.contains_name(name) {
                return Err(EngineError::Engine(ErrorEnum::AlreadyExists, name.to_string()));
            }
        }

        // TODO: Roll back on filesystem initialization failure.
        let dm = try!(DM::new());
        let mut result = Vec::new();
        for name in names.iter() {
            let uuid = Uuid::new_v4();
            // FIXME: Start managing thin ids in pool.
            let thin_id =
                ThinDevId::new_u64((random::<u32>() >> 8) as u64)
                    .expect("must require only 24 bits");
            let new_filesystem = try!(StratFilesystem::initialize(&self.pool_uuid,
                                                                  uuid,
                                                                  thin_id,
                                                                  name,
                                                                  &dm,
                                                                  &mut self.thin_pool));
            try!(self.mdv.save_fs(&new_filesystem));
            self.filesystems.insert(new_filesystem);
            result.push((**name, uuid));
        }

        Ok(result)
    }

    fn add_blockdevs(&mut self, paths: &[&Path], force: bool) -> EngineResult<Vec<PathBuf>> {
        let bdev_paths = try!(self.block_devs.add(&self.pool_uuid, paths, force));
        try!(self.write_metadata());
        Ok(bdev_paths)
    }

    fn destroy(self) -> EngineResult<()> {
        // Ensure that DM teardown operations in this method are in sync
        // with operations in teardown().
        let dm = try!(DM::new());
        try!(self.thin_pool.teardown(&dm));
        try!(self.mdv.teardown(&dm));
        try!(self.block_devs.destroy_all());

        Ok(())
    }

    fn destroy_filesystems<'a, 'b>(&'a mut self,
                                   fs_uuids: &[&'b FilesystemUuid])
                                   -> EngineResult<Vec<&'b FilesystemUuid>> {
        for fsid in fs_uuids {
            try!(self.mdv.rm_fs(fsid));
        }
        destroy_filesystems!{self; fs_uuids}
    }

    fn rename_filesystem(&mut self,
                         uuid: &FilesystemUuid,
                         new_name: &str)
                         -> EngineResult<RenameAction> {
        rename_filesystem!{self; uuid; new_name}
    }

    fn rename(&mut self, name: &str) {
        self.name = name.to_owned();
    }

    fn get_filesystem(&mut self, uuid: &FilesystemUuid) -> Option<&mut Filesystem> {
        get_filesystem!(self; uuid)
    }
}

impl HasUuid for StratPool {
    fn uuid(&self) -> &PoolUuid {
        &self.pool_uuid
    }
}

impl HasName for StratPool {
    fn name(&self) -> &str {
        &self.name
    }
}

impl Recordable<PoolSave> for StratPool {
    fn record(&self) -> EngineResult<PoolSave> {

        let mapper = |seg: &Segment| -> EngineResult<(Uuid, Sectors, Sectors)> {
            let bd = try!(self.block_devs
                     .get_by_device(seg.device)
                     .ok_or(EngineError::Engine(ErrorEnum::NotFound,
                                                format!("no block device found for device {:?}",
                                                        seg.device))));
            Ok((*bd.uuid(), seg.start, seg.length))
        };

        let meta_dev = try!(self.mdv.segments().iter().map(&mapper).collect());

        let thin_meta_dev = try!(self.thin_pool
                                     .meta_dev()
                                     .segments()
                                     .iter()
                                     .map(&mapper)
                                     .collect());

        let thin_data_dev = try!(self.thin_pool
                                     .data_dev()
                                     .segments()
                                     .iter()
                                     .map(&mapper)
                                     .collect());

        Ok(PoolSave {
               name: self.name.clone(),
               block_devs: try!(self.block_devs.record()),
               flex_devs: FlexDevsSave {
                   meta_dev: meta_dev,
                   thin_meta_dev: thin_meta_dev,
                   thin_data_dev: thin_data_dev,
               },
               thinpool_dev: ThinPoolDevSave { data_block_size: self.thin_pool.data_block_size() },
           })
    }
}
<|endoftext|>


<commit_before>use libindy::ErrorCode;
use utils::sequence;

use libc::c_char;
use std::collections::HashMap;
use std::ffi::CStr;
use std::sync::Mutex;

type EcClosure = Box<FnMut(ErrorCode) + Send>;
type EcCallback = Option<extern fn(command_handle: i32, err: ErrorCode)>;
type EcStringClosure = Box<FnMut(ErrorCode, String) + Send>;
type EcStringCallback = Option<extern fn(command_handle: i32, err: ErrorCode, c_str: *const c_char)>;

pub fn closure_to_cb_ec(closure: EcClosure) -> (i32, EcCallback) {
    lazy_static! {
       static ref CALLBACKS: Mutex<HashMap<i32, Box<EcClosure>>> = Default::default();
    }

    extern "C" fn _callback(command_handle: i32, err: ErrorCode) {
        let mut callbacks = CALLBACKS.lock().unwrap();
        let mut cb = callbacks.remove(&command_handle).unwrap();
        cb(err)
    }

    let command_handle = sequence::get_next_id();
    let mut callbacks = CALLBACKS.lock().unwrap();
    callbacks.insert(command_handle, Box::new(closure));

    (command_handle, Some(_callback))
}

pub fn closure_to_cb_ec_string(closure: EcStringClosure) -> (i32, EcStringCallback) {
    lazy_static! {
       static ref CALLBACKS: Mutex<HashMap<i32, Box<EcStringClosure>>> = Default::default();
    }

    extern "C" fn _callback(command_handle: i32, err: ErrorCode, c_str: *const c_char) {
        let mut callbacks = CALLBACKS.lock().unwrap();
        let mut cb = callbacks.remove(&command_handle).unwrap();
        let metadata = unsafe { CStr::from_ptr(c_str).to_str().unwrap().to_string() };
        cb(err, metadata)
    }

    let mut callbacks = CALLBACKS.lock().unwrap();
    let command_handle = sequence::get_next_id();
    callbacks.insert(command_handle, Box::new(closure));

    (command_handle, Some(_callback))
}<commit_msg>Fix misprint with multiple sequential boxings in callbacks utils<commit_after>use libindy::ErrorCode;
use utils::sequence;

use libc::c_char;
use std::collections::HashMap;
use std::ffi::CStr;
use std::sync::Mutex;

type EcClosure = Box<FnMut(ErrorCode) + Send>;
type EcCallback = Option<extern fn(command_handle: i32, err: ErrorCode)>;
type EcStringClosure = Box<FnMut(ErrorCode, String) + Send>;
type EcStringCallback = Option<extern fn(command_handle: i32, err: ErrorCode, c_str: *const c_char)>;

pub fn closure_to_cb_ec(closure: EcClosure) -> (i32, EcCallback) {
    lazy_static! {
       static ref CALLBACKS: Mutex<HashMap<i32, EcClosure>> = Default::default();
    }

    extern "C" fn _callback(command_handle: i32, err: ErrorCode) {
        let mut callbacks = CALLBACKS.lock().unwrap();
        let mut cb = callbacks.remove(&command_handle).unwrap();
        cb(err)
    }

    let command_handle = sequence::get_next_id();
    let mut callbacks = CALLBACKS.lock().unwrap();
    callbacks.insert(command_handle, closure);

    (command_handle, Some(_callback))
}

pub fn closure_to_cb_ec_string(closure: EcStringClosure) -> (i32, EcStringCallback) {
    lazy_static! {
       static ref CALLBACKS: Mutex<HashMap<i32, EcStringClosure>> = Default::default();
    }

    extern "C" fn _callback(command_handle: i32, err: ErrorCode, c_str: *const c_char) {
        let mut callbacks = CALLBACKS.lock().unwrap();
        let mut cb = callbacks.remove(&command_handle).unwrap();
        let metadata = unsafe { CStr::from_ptr(c_str).to_str().unwrap().to_string() };
        cb(err, metadata)
    }

    let mut callbacks = CALLBACKS.lock().unwrap();
    let command_handle = sequence::get_next_id();
    callbacks.insert(command_handle, closure);

    (command_handle, Some(_callback))
}<|endoftext|>