<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/.

// Discover or identify devices that may belong to Stratis.

use std::{
    collections::HashMap,
    fs::OpenOptions,
    path::{Path, PathBuf},
};

use libudev;

use devicemapper::Device;

use crate::engine::{
    strat_engine::{
        backstore::metadata::device_identifiers,
        udev::{block_enumerator, decide_ownership, is_multipath_member, UdevOwnership},
    },
    types::{DevUuid, PoolUuid},
};

// A wrapper for obtaining the device number as a devicemapper Device
// which interprets absence of the value as an error, which it is in this
// context.
fn device_to_devno_wrapper(device: &libudev::Device) -> Result<Device, String> {
    device
        .devnum()
        .ok_or_else(|| "udev entry did not contain a device number".into())
        .map(Device::from)
}

// A wrapper around the metadata module's device_identifers method
// which also handles failure to open a device for reading.
// Returns an error if the device could not be opened for reading.
// Returns Ok(Err(...)) if there was an error while reading the
// Stratis identifiers from the device.
// Returns Ok(Ok(None)) if the identifers did not appear to be on
// the device.
fn device_identifiers_wrapper(
    devnode: &Path,
) -> Result<Result<Option<(PoolUuid, DevUuid)>, String>, String> {
    OpenOptions::new()
        .read(true)
        .open(devnode)
        .as_mut()
        .map_err(|err| {
            format!(
                "device {} could not be opened for reading: {}",
                devnode.display(),
                err
            )
        })
        .map(|f| {
            device_identifiers(f).map_err(|err| {
                format!(
                    "encountered an error while reading Stratis header for device {}: {}",
                    devnode.display(),
                    err
                )
            })
        })
}

// Use udev to identify all block devices and return the subset of those
// that have Stratis signatures.
fn find_all_block_devices_with_stratis_signatures(
) -> libudev::Result<HashMap<PoolUuid, HashMap<Device, PathBuf>>> {
    let context = libudev::Context::new()?;
    let mut enumerator = block_enumerator(&context)?;

    let pool_map = enumerator.scan_devices()?
        .filter(|dev| {
            let initialized = dev.is_initialized();
            if !initialized {
                debug!("Found a udev entry for a device identified as a block device, but udev also identified it as uninitialized, omitting the device from the set of devices to process, for safety");
            };
            initialized
        })
        .filter(|dev| {
            decide_ownership(dev)
                .map_err(|err| {
                    warn!("Could not determine ownership of a udev block device because of an error processing udev information, omitting the device from the set of devices to process, for safety: {}",
                          err);
                })
                .map(|decision| match decision {
                    UdevOwnership::Stratis | UdevOwnership::Unowned => true,
                    _ => false,
                })
                .unwrap_or(false)
        })
        .filter_map(|dev| match dev.devnode() {
            Some(devnode) => {
                match (device_to_devno_wrapper(&dev), device_identifiers_wrapper(devnode)) {
                    (Err(err), _) | (_, Err(err)) => {
                        warn!("udev identified device {} as a block device but {}, omitting the device from the set of devices to process",
                              devnode.display(),
                              err);
                        None
                    }
                    // FIXME: Refine error return in StaticHeader::setup(),
                    // so it can be used to distinguish between signficant
                    // and insignficant errors and then use that ability to
                    // distinguish here between different levels of
                    // severity.
                    (_, Ok(Err(err))) => {
                        debug!("udev identified device {} as a block device but {}, omitting the device from the set of devices to process",
                               devnode.display(),
                               err);
                        None
                    }
                    (_, Ok(Ok(None))) => None,
                    (Ok(devno), Ok(Ok(Some((pool_uuid, _))))) => Some((pool_uuid, devno, devnode.to_path_buf())),
                }
            }
            None => {
                warn!("udev identified a device as a block device, but the udev entry for the device had no device node, omitting the device from the set of devices to process");
                None
            }
        })
        .fold(HashMap::new(), |mut acc, (pool_uuid, device, devnode)| {
            acc.entry(pool_uuid).or_insert_with(HashMap::new).insert(device, devnode);
            acc
        });

    Ok(pool_map)
}

// Find all devices identified by udev as Stratis devices.
fn find_all_stratis_devices() -> libudev::Result<HashMap<PoolUuid, HashMap<Device, PathBuf>>> {
    let context = libudev::Context::new()?;
    let mut enumerator = block_enumerator(&context)?;
    enumerator.match_property("ID_FS_TYPE", "stratis")?;

    let pool_map = enumerator.scan_devices()?
        .filter(|dev| {
            let initialized = dev.is_initialized();
            if !initialized {
                warn!("Found a udev entry for a device identified as a Stratis device, but udev also identified it as uninitialized, omitting the device from the set of devices to process, for safety");
            };
            initialized
        })
        .filter(|dev| !is_multipath_member(dev)
                .map_err(|err| {
                    warn!("Could not certainly determine whether a device was a multipath member because of an error processing udev information, omitting the device from the set of devices to process, for safety: {}",
                          err);
                })
                .unwrap_or(true))
        .filter_map(|dev| match dev.devnode() {
            Some(devnode) => {
                match (device_to_devno_wrapper(&dev), device_identifiers_wrapper(devnode)) {
                    (Err(err), _) | (_, Err(err)) | (_, Ok(Err(err)))=> {
                        warn!("udev identified device {} as a Stratis device but {}, omitting the device from the set of devices to process",
                              devnode.display(),
                              err);
                        None
                    }
                    (_, Ok(Ok(None))) => {
                            warn!("udev identified device {} as a Stratis device but there appeared to be no Stratis metadata on the device, omitting the device from the set of devices to process",
                                  devnode.display());
                            None
                    }
                    (Ok(devno), Ok(Ok(Some((pool_uuid, _))))) => Some((pool_uuid, devno, devnode.to_path_buf())),
                }
            }
            None => {
                warn!("udev identified a device as a Stratis device, but the udev entry for the device had no device node, omitting the the device from the set of devices to process");
                None
            }
        })
        .fold(HashMap::new(), |mut acc, (pool_uuid, device, devnode)| {
            acc.entry(pool_uuid).or_insert_with(HashMap::new).insert(device, devnode);
            acc
        });
    Ok(pool_map)
}

/// Identify a block device in the context where a udev event has been
/// captured for some block device. Return None if the device does not
/// appear to be a Stratis device. Log at an appropriate level on all errors.
pub fn identify_block_device(
    dev: &libudev::Device,
) -> Option<(PoolUuid, DevUuid, Device, PathBuf)> {
    let initialized = dev.is_initialized();
    if !initialized {
        debug!("Found a udev entry for a device identified as a block device, but udev also identified it as uninitialized, disregarding the device");
        return None;
    };

    match decide_ownership(dev) {
        Err(err) => {
            warn!("Could not determine ownership of a udev block device because of an error processing udev information, disregarding the device: {}",
                  err);
            None
        }
        Ok(ownership) => match ownership {
            UdevOwnership::Stratis => match dev.devnode() {
                Some(devnode) => {
                    match (
                        device_to_devno_wrapper(dev),
                        device_identifiers_wrapper(devnode),
                    ) {
                        (Err(err), _) | (_, Err(err)) | (_, Ok(Err(err))) => {
                            warn!("udev identified device {} as a Stratis device but {}, disregarding the device",
                              devnode.display(),
                              err);
                            None
                        }
                        (_, Ok(Ok(None))) => {
                            warn!("udev identified device {} as a Stratis device but there appeared to be no Stratis metadata on the device, disregarding the device",
                                  devnode.display());
                            None
                        }
                        (Ok(devno), Ok(Ok(Some((pool_uuid, device_uuid))))) => {
                            Some((pool_uuid, device_uuid, devno, devnode.to_path_buf()))
                        }
                    }
                }
                None => {
                    warn!("udev identified a device as a Stratis device, but the udev entry for the device had no device node, disregarding device");
                    None
                }
            },
            UdevOwnership::Unowned => match dev.devnode() {
                Some(devnode) => {
                    match (
                        device_to_devno_wrapper(dev),
                        device_identifiers_wrapper(devnode),
                    ) {
                        (Err(err), _) | (_, Err(err)) => {
                            warn!("udev identified device {} as a block device but {}, disregarding device",
                              devnode.display(),
                              err);
                            None
                        }
                        // FIXME: Refine error return in StaticHeader::setup(),
                        // so it can be used to distinguish between signficant
                        // and insignficant errors and then use that ability to
                        // distinguish here between different levels of
                        // severity.
                        (_, Ok(Err(err))) => {
                            debug!("udev identified device {} as a block device but {}, disregarding device",
                               devnode.display(),
                               err);
                            None
                        }
                        (_, Ok(Ok(None))) => None,
                        (Ok(devno), Ok(Ok(Some((pool_uuid, device_uuid))))) => {
                            Some((pool_uuid, device_uuid, devno, devnode.to_path_buf()))
                        }
                    }
                }
                None => {
                    warn!("udev identified a device as a block device, but the udev entry for the device had no device node, omitting the device from the set of devices to process");
                    None
                }
            },
            _ => None,
        },
    }
}

/// Retrieve all block devices that should be made use of by the
/// Stratis engine. This excludes Stratis block devices that appear to be
/// multipath members.
///
/// Includes a fallback path, which is used if no Stratis block devices are
/// found using the obvious udev property- and enumerator-based approach.
/// This fallback path is more expensive, because it must search all block
/// devices via udev rather than just all Stratis block devices.
///
/// Omits any device that appears problematic in some way.
///
/// Return an error only on a failure to construct or scan with a udev
/// enumerator.
///
/// Returns a map of pool uuids to a map of devices to devnodes for each pool.
pub fn find_all() -> libudev::Result<HashMap<PoolUuid, HashMap<Device, PathBuf>>> {
    info!("Beginning initial search for Stratis block devices");
    let pool_map = find_all_stratis_devices()?;

    if pool_map.is_empty() {
        // No Stratis devices have been found, possible reasons are:
        // 1. There are none
        // 2. There are some but libblkid version is less than 2.32, so
        // Stratis devices are not recognized by udev.
        // 3. There are many incomplete udev entries, because this code is
        // being run before the udev database is populated.
        //
        // Try again to find Stratis block devices, but this time enumerate
        // all block devices, not just all the ones that can be identified
        // as Stratis blockdevs by udev, and process only those that appear
        // unclaimed or appear to be claimed by Stratis (and not
        // multipath members).

        info!("Could not identify any Stratis devices by a udev search for devices with ID_FS_TYPE=\"stratis\"; using fallback search mechanism");

        find_all_block_devices_with_stratis_signatures()
    } else {
        Ok(pool_map)
    }
}
<commit_msg>Abstract the action of checking a device after udev processing<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/.

// Discover or identify devices that may belong to Stratis.

use std::{
    collections::HashMap,
    fs::OpenOptions,
    path::{Path, PathBuf},
};

use libudev;

use devicemapper::Device;

use crate::engine::{
    strat_engine::{
        backstore::metadata::device_identifiers,
        udev::{block_enumerator, decide_ownership, is_multipath_member, UdevOwnership},
    },
    types::{DevUuid, PoolUuid},
};

// A wrapper for obtaining the device number as a devicemapper Device
// which interprets absence of the value as an error, which it is in this
// context.
fn device_to_devno_wrapper(device: &libudev::Device) -> Result<Device, String> {
    device
        .devnum()
        .ok_or_else(|| "udev entry did not contain a device number".into())
        .map(Device::from)
}

// A wrapper around the metadata module's device_identifers method
// which also handles failure to open a device for reading.
// Returns an error if the device could not be opened for reading.
// Returns Ok(Err(...)) if there was an error while reading the
// Stratis identifiers from the device.
// Returns Ok(Ok(None)) if the identifers did not appear to be on
// the device.
fn device_identifiers_wrapper(
    devnode: &Path,
) -> Result<Result<Option<(PoolUuid, DevUuid)>, String>, String> {
    OpenOptions::new()
        .read(true)
        .open(devnode)
        .as_mut()
        .map_err(|err| {
            format!(
                "device {} could not be opened for reading: {}",
                devnode.display(),
                err
            )
        })
        .map(|f| {
            device_identifiers(f).map_err(|err| {
                format!(
                    "encountered an error while reading Stratis header for device {}: {}",
                    devnode.display(),
                    err
                )
            })
        })
}

/// Process a device which udev information indicates is a Stratis device.
fn process_stratis_device(dev: &libudev::Device) -> Option<(PoolUuid, DevUuid, Device, PathBuf)> {
    match dev.devnode() {
        Some(devnode) => {
            match (
                device_to_devno_wrapper(dev),
                device_identifiers_wrapper(devnode),
            ) {
                (Err(err), _) | (_, Err(err)) | (_, Ok(Err(err))) => {
                    warn!("udev identified device {} as a Stratis device but {}, disregarding the device",
                          devnode.display(),
                          err);
                    None
                }
                (_, Ok(Ok(None))) => {
                    warn!("udev identified device {} as a Stratis device but there appeared to be no Stratis metadata on the device, disregarding the device",
                          devnode.display());
                    None
                }
                (Ok(devno), Ok(Ok(Some((pool_uuid, device_uuid))))) => {
                    Some((pool_uuid, device_uuid, devno, devnode.to_path_buf()))
                }
            }
        }
        None => {
            warn!("udev identified a device as a Stratis device, but the udev entry for the device had no device node, disregarding device");
            None
        }
    }
}

/// Process a device which udev information indicates is unowned.
fn process_unowned_device(dev: &libudev::Device) -> Option<(PoolUuid, DevUuid, Device, PathBuf)> {
    match dev.devnode() {
        Some(devnode) => {
            match (
                device_to_devno_wrapper(dev),
                device_identifiers_wrapper(devnode),
            ) {
                (Err(err), _) | (_, Err(err)) => {
                    warn!("udev identified device {} as a block device but {}, disregarding the device",
                          devnode.display(),
                          err);
                    None
                }
                // FIXME: Refine error return in StaticHeader::setup(),
                // so it can be used to distinguish between signficant
                // and insignficant errors and then use that ability to
                // distinguish here between different levels of
                // severity.
                (_, Ok(Err(err))) => {
                    debug!("udev identified device {} as a block device but {}, disregarding the device",
                           devnode.display(),
                           err);
                    None
                }
                (_, Ok(Ok(None))) => None,
                (Ok(devno), Ok(Ok(Some((pool_uuid, device_uuid))))) => {
                    Some((pool_uuid, device_uuid, devno, devnode.to_path_buf()))
                }
            }
        }
        None => {
            warn!("udev identified a device as a block device, but the udev entry for the device had no device node, disregarding the device");
            None
        }
    }
}

// Use udev to identify all block devices and return the subset of those
// that have Stratis signatures.
fn find_all_block_devices_with_stratis_signatures(
) -> libudev::Result<HashMap<PoolUuid, HashMap<Device, PathBuf>>> {
    let context = libudev::Context::new()?;
    let mut enumerator = block_enumerator(&context)?;

    let pool_map = enumerator.scan_devices()?
        .filter(|dev| {
            let initialized = dev.is_initialized();
            if !initialized {
                debug!("Found a udev entry for a device identified as a block device, but udev also identified it as uninitialized, omitting the device from the set of devices to process, for safety");
            };
            initialized
        })
        .filter_map(|dev| {
            decide_ownership(&dev)
                .map_err(|err| {
                    warn!("Could not determine ownership of a udev block device because of an error processing udev information, omitting the device from the set of devices to process, for safety: {}",
                          err);
                })
            .map(|decision| match decision {
                UdevOwnership::Stratis => process_stratis_device(&dev),
                UdevOwnership::Unowned => process_unowned_device(&dev),
                _ => None,
            })
            .unwrap_or(None)
        })
        .fold(HashMap::new(), |mut acc, (pool_uuid, _, device, devnode)| {
            acc.entry(pool_uuid).or_insert_with(HashMap::new).insert(device, devnode);
            acc
        });

    Ok(pool_map)
}

// Find all devices identified by udev as Stratis devices.
fn find_all_stratis_devices() -> libudev::Result<HashMap<PoolUuid, HashMap<Device, PathBuf>>> {
    let context = libudev::Context::new()?;
    let mut enumerator = block_enumerator(&context)?;
    enumerator.match_property("ID_FS_TYPE", "stratis")?;

    let pool_map = enumerator.scan_devices()?
        .filter(|dev| {
            let initialized = dev.is_initialized();
            if !initialized {
                warn!("Found a udev entry for a device identified as a Stratis device, but udev also identified it as uninitialized, omitting the device from the set of devices to process, for safety");
            };
            initialized
        })
        .filter(|dev| !is_multipath_member(dev)
                .map_err(|err| {
                    warn!("Could not certainly determine whether a device was a multipath member because of an error processing udev information, omitting the device from the set of devices to process, for safety: {}",
                          err);
                })
                .unwrap_or(true))
        .filter_map(|dev| process_stratis_device(&dev))
        .fold(HashMap::new(), |mut acc, (pool_uuid, _, device, devnode)| {
            acc.entry(pool_uuid).or_insert_with(HashMap::new).insert(device, devnode);
            acc
        });
    Ok(pool_map)
}

/// Identify a block device in the context where a udev event has been
/// captured for some block device. Return None if the device does not
/// appear to be a Stratis device. Log at an appropriate level on all errors.
pub fn identify_block_device(
    dev: &libudev::Device,
) -> Option<(PoolUuid, DevUuid, Device, PathBuf)> {
    let initialized = dev.is_initialized();
    if !initialized {
        debug!("Found a udev entry for a device identified as a block device, but udev also identified it as uninitialized, disregarding the device");
        return None;
    };

    match decide_ownership(dev) {
        Err(err) => {
            warn!("Could not determine ownership of a udev block device because of an error processing udev information, disregarding the device: {}",
                  err);
            None
        }
        Ok(ownership) => match ownership {
            UdevOwnership::Stratis => process_stratis_device(dev),
            UdevOwnership::Unowned => process_unowned_device(dev),
            _ => None,
        },
    }
}

/// Retrieve all block devices that should be made use of by the
/// Stratis engine. This excludes Stratis block devices that appear to be
/// multipath members.
///
/// Includes a fallback path, which is used if no Stratis block devices are
/// found using the obvious udev property- and enumerator-based approach.
/// This fallback path is more expensive, because it must search all block
/// devices via udev rather than just all Stratis block devices.
///
/// Omits any device that appears problematic in some way.
///
/// Return an error only on a failure to construct or scan with a udev
/// enumerator.
///
/// Returns a map of pool uuids to a map of devices to devnodes for each pool.
pub fn find_all() -> libudev::Result<HashMap<PoolUuid, HashMap<Device, PathBuf>>> {
    info!("Beginning initial search for Stratis block devices");
    let pool_map = find_all_stratis_devices()?;

    if pool_map.is_empty() {
        // No Stratis devices have been found, possible reasons are:
        // 1. There are none
        // 2. There are some but libblkid version is less than 2.32, so
        // Stratis devices are not recognized by udev.
        // 3. There are many incomplete udev entries, because this code is
        // being run before the udev database is populated.
        //
        // Try again to find Stratis block devices, but this time enumerate
        // all block devices, not just all the ones that can be identified
        // as Stratis blockdevs by udev, and process only those that appear
        // unclaimed or appear to be claimed by Stratis (and not
        // multipath members).

        info!("Could not identify any Stratis devices by a udev search for devices with ID_FS_TYPE=\"stratis\"; using fallback search mechanism");

        find_all_block_devices_with_stratis_signatures()
    } else {
        Ok(pool_map)
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2014 The 6502-rs Developers
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 3. Neither the names of the copyright holders nor the names of any
//    contributors may be used to endorse or promote products derived from this
//    software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

use log;

use std;

use address::{AddressDiff};
use instruction;
use instruction::{DecodedInstr};
use memory::Memory;
use registers::{ Registers, Status, StatusArgs };
use registers::{ PS_NEGATIVE, PS_OVERFLOW, PS_ZERO, PS_CARRY };

pub struct Machine {
    pub registers: Registers,
    pub memory:    Memory
}

impl Machine {
    pub fn new() -> Machine {
    	Machine{
    	    registers: Registers::new(),
    	    memory:    Memory::new()
    	}
    }

    pub fn reset(&mut self) {
    	*self = Machine::new();
    }

    pub fn fetch_next_and_decode(&mut self) -> Option<DecodedInstr> {
        let x: u8 = self.memory.get_byte(self.registers.program_counter);

        match instruction::OPCODES[x as uint] {
            Some((instr, am)) => {
                let extra_bytes = am.extra_bytes();
                let num_bytes = AddressDiff(1) + extra_bytes;

                let data_start = self.registers.program_counter
                               + AddressDiff(1);

                let slice = self.memory.get_slice(data_start, extra_bytes);
                let am_out = am.process(self, slice);

                // Increment program counter
                self.registers.program_counter =
                    self.registers.program_counter + num_bytes;

                Some((instr, am_out))
            }
            _ => None
        }
    }

    pub fn execute_instruction(&mut self, decoded_instr: DecodedInstr) {
        match decoded_instr {
            (instruction::ADC, instruction::UseImmediate(val)) => {
                log!(log::DEBUG, "add with carry immediate: {}", val);
                self.add_with_carry(val as i8);
            },
            (instruction::ADC, instruction::UseAddress(addr)) => {
                let val = self.memory.get_byte(addr) as i8;
                log!(log::DEBUG, "add with carry. address: {}. value: {}",
                                 addr, val);
                self.add_with_carry(val);
            },

            (instruction::BMI, instruction::UseRelative(rel)) => {
                let addr = self.registers.program_counter + rel;
                log!(log::DEBUG, "branch if minus relative. address: {}", addr);
                self.branch_if_minus(addr);
            },

            (instruction::LDA, instruction::UseImmediate(val)) => {
                log!(log::DEBUG, "load A immediate: {}", val);
                self.load_accumulator(val as i8);
            },
            (instruction::LDA, instruction::UseAddress(addr)) => {
                let val = self.memory.get_byte(addr);
                log!(log::DEBUG, "load A. address: {}. value: {}", addr, val);
                self.load_accumulator(val as i8);
            },

            (instruction::LDX, instruction::UseImmediate(val)) => {
                log!(log::DEBUG, "load X immediate: {}", val);
                self.load_x_register(val as i8);
            },
            (instruction::LDX, instruction::UseAddress(addr)) => {
                let val = self.memory.get_byte(addr);
                log!(log::DEBUG, "load X. address: {}. value: {}", addr, val);
                self.load_x_register(val as i8);
            },

            (instruction::LDY, instruction::UseImmediate(val)) => {
                log!(log::DEBUG, "load Y immediate: {}", val);
                self.load_y_register(val as i8);
            },
            (instruction::LDY, instruction::UseAddress(addr)) => {
                let val = self.memory.get_byte(addr);
                log!(log::DEBUG, "load Y. address: {}. value: {}", addr, val);
                self.load_y_register(val as i8);
            },

            (instruction::NOP, _) => {
                log!(log::DEBUG, "nop instr");
            },
            (_, _) => {
                log!(log::DEBUG, "attempting to execute unimplemented \
                                  instruction");
            },
        };
    }

    pub fn run(&mut self) {
        loop {
            if let Some(decoded_instr) = self.fetch_next_and_decode() {
                self.execute_instruction(decoded_instr);
            } else {
                break
            }
        }
    }

    fn load_register_with_flags(register: &mut i8,
                                status: &mut Status,
                                value: i8) {
        *register = value;

        let is_zero = value == 0;
        let is_negative = value < 0;

        status.set_with_mask(
            PS_ZERO | PS_NEGATIVE,
            Status::new(StatusArgs { zero: is_zero,
                                     negative: is_negative,
                                     ..StatusArgs::none() } ));
    }

    pub fn load_x_register(&mut self, value: i8) {
        Machine::load_register_with_flags(&mut self.registers.index_x,
                                          &mut self.registers.status,
                                          value);
    }

    pub fn load_y_register(&mut self, value: i8) {
        Machine::load_register_with_flags(&mut self.registers.index_y,
                                          &mut self.registers.status,
                                          value);
    }

    pub fn load_accumulator(&mut self, value: i8) {
        Machine::load_register_with_flags(&mut self.registers.accumulator,
                                          &mut self.registers.status,
                                          value);
    }

    // TODO akeeton: Implement binary-coded decimal.
    pub fn add_with_carry(&mut self, value: i8) {
        let a_before: i8 = self.registers.accumulator;
        let c_before: i8 = self.registers.status.get_carry();
        let a_after: i8 = a_before + c_before + value;

        debug_assert_eq!(a_after as u8, a_before as u8 + c_before as u8
                                        + value as u8);

        let did_carry = (a_after as u8) < (a_before as u8);

        let did_overflow   =
        	   (a_before < 0 && value < 0 && a_after >= 0)
        	|| (a_before > 0 && value > 0 && a_after <= 0);

        let mask = PS_CARRY | PS_OVERFLOW;

        self.registers.status.set_with_mask(mask,
            Status::new(StatusArgs { carry: did_carry,
                                     overflow: did_overflow,
                                     ..StatusArgs::none() } ));

        self.load_accumulator(a_after);

        log!(log::DEBUG, "accumulator: {}", self.registers.accumulator);
    }

    pub fn branch_if_minus(&mut self, addr: Address) {
        if (self.registers.status.contains(PS_NEGATIVE)) {
            self.registers.program_counter = addr;
        }
    }
}

impl std::fmt::Show for Machine {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "Machine Dump:\n\nAccumulator: {}",
               self.registers.accumulator)
    }
}

#[test]
fn add_with_carry_test() {
    let mut machine = Machine::new();

    machine.add_with_carry(1);
    assert_eq!(machine.registers.accumulator, 1);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.add_with_carry(-1);
    assert_eq!(machine.registers.accumulator, 0);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    true);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     true);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.add_with_carry(1);
    assert_eq!(machine.registers.accumulator, 2);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    let mut machine = Machine::new();

    machine.add_with_carry(127);
    assert_eq!(machine.registers.accumulator, 127);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.add_with_carry(-127);
    assert_eq!(machine.registers.accumulator, 0);
    assert_eq!(machine.registers.status.contains(PS_CARRY),     true);
    assert_eq!(machine.registers.status.contains(PS_ZERO),      true);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.registers.status.remove(PS_CARRY);
    machine.add_with_carry(-128);
    assert_eq!(machine.registers.accumulator, -128);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE),  true);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.add_with_carry(127);
    assert_eq!(machine.registers.accumulator, -1);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE),  true);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    let mut machine = Machine::new();

    machine.add_with_carry(127);
    assert_eq!(machine.registers.accumulator, 127);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.add_with_carry(1);
    assert_eq!(machine.registers.accumulator, -128);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE),  true);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW),  true);
}

#[test]
fn branch_if_minus_test() {
    {
        let mut machine      = Machine::new();
        let registers_before = machine.registers;

        machine.branch_if_minus(Address(0xABCD));
        assert_eq!(machine.registers, registers_before);
        assert_eq!(machine.registers.program_counter, Address(0));
    }

    {
        let mut machine = Machine::new();

        machine.registers.status.set_with_mask(PS_NEGATIVE, PS_NEGATIVE);
        let registers_before = machine.registers;

        machine.branch_if_minus(Address(0xABCD));
        assert_eq!(machine.registers.status, registers_before.status);
        assert_eq!(machine.registers.program_counter, Address(0xABCD));
    }
}
<commit_msg>Fix build.<commit_after>// Copyright (C) 2014 The 6502-rs Developers
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 3. Neither the names of the copyright holders nor the names of any
//    contributors may be used to endorse or promote products derived from this
//    software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

use log;

use std;

use address::{AddressDiff};
use instruction;
use instruction::{DecodedInstr};
use memory::Memory;
use registers::{ Registers, Status, StatusArgs };
use registers::{ PS_NEGATIVE, PS_OVERFLOW, PS_ZERO, PS_CARRY };

pub struct Machine {
    pub registers: Registers,
    pub memory:    Memory
}

impl Machine {
    pub fn new() -> Machine {
    	Machine{
    	    registers: Registers::new(),
    	    memory:    Memory::new()
    	}
    }

    pub fn reset(&mut self) {
    	*self = Machine::new();
    }

    pub fn fetch_next_and_decode(&mut self) -> Option<DecodedInstr> {
        let x: u8 = self.memory.get_byte(self.registers.program_counter);

        match instruction::OPCODES[x as uint] {
            Some((instr, am)) => {
                let extra_bytes = am.extra_bytes();
                let num_bytes = AddressDiff(1) + extra_bytes;

                let data_start = self.registers.program_counter
                               + AddressDiff(1);

                let slice = self.memory.get_slice(data_start, extra_bytes);
                let am_out = am.process(self, slice);

                // Increment program counter
                self.registers.program_counter =
                    self.registers.program_counter + num_bytes;

                Some((instr, am_out))
            }
            _ => None
        }
    }

    pub fn execute_instruction(&mut self, decoded_instr: DecodedInstr) {
        match decoded_instr {
            (instruction::ADC, instruction::UseImmediate(val)) => {
                log!(log::DEBUG, "add with carry immediate: {}", val);
                self.add_with_carry(val as i8);
            },
            (instruction::ADC, instruction::UseAddress(addr)) => {
                let val = self.memory.get_byte(addr) as i8;
                log!(log::DEBUG, "add with carry. address: {}. value: {}",
                                 addr, val);
                self.add_with_carry(val);
            },

            (instruction::BMI, instruction::UseRelative(rel)) => {
                let addr = self.registers.program_counter + AddressDiff(rel as i32);
                log!(log::DEBUG, "branch if minus relative. address: {}", addr);
                self.branch_if_minus(addr);
            },

            (instruction::LDA, instruction::UseImmediate(val)) => {
                log!(log::DEBUG, "load A immediate: {}", val);
                self.load_accumulator(val as i8);
            },
            (instruction::LDA, instruction::UseAddress(addr)) => {
                let val = self.memory.get_byte(addr);
                log!(log::DEBUG, "load A. address: {}. value: {}", addr, val);
                self.load_accumulator(val as i8);
            },

            (instruction::LDX, instruction::UseImmediate(val)) => {
                log!(log::DEBUG, "load X immediate: {}", val);
                self.load_x_register(val as i8);
            },
            (instruction::LDX, instruction::UseAddress(addr)) => {
                let val = self.memory.get_byte(addr);
                log!(log::DEBUG, "load X. address: {}. value: {}", addr, val);
                self.load_x_register(val as i8);
            },

            (instruction::LDY, instruction::UseImmediate(val)) => {
                log!(log::DEBUG, "load Y immediate: {}", val);
                self.load_y_register(val as i8);
            },
            (instruction::LDY, instruction::UseAddress(addr)) => {
                let val = self.memory.get_byte(addr);
                log!(log::DEBUG, "load Y. address: {}. value: {}", addr, val);
                self.load_y_register(val as i8);
            },

            (instruction::NOP, _) => {
                log!(log::DEBUG, "nop instr");
            },
            (_, _) => {
                log!(log::DEBUG, "attempting to execute unimplemented \
                                  instruction");
            },
        };
    }

    pub fn run(&mut self) {
        loop {
            if let Some(decoded_instr) = self.fetch_next_and_decode() {
                self.execute_instruction(decoded_instr);
            } else {
                break
            }
        }
    }

    fn load_register_with_flags(register: &mut i8,
                                status: &mut Status,
                                value: i8) {
        *register = value;

        let is_zero = value == 0;
        let is_negative = value < 0;

        status.set_with_mask(
            PS_ZERO | PS_NEGATIVE,
            Status::new(StatusArgs { zero: is_zero,
                                     negative: is_negative,
                                     ..StatusArgs::none() } ));
    }

    pub fn load_x_register(&mut self, value: i8) {
        Machine::load_register_with_flags(&mut self.registers.index_x,
                                          &mut self.registers.status,
                                          value);
    }

    pub fn load_y_register(&mut self, value: i8) {
        Machine::load_register_with_flags(&mut self.registers.index_y,
                                          &mut self.registers.status,
                                          value);
    }

    pub fn load_accumulator(&mut self, value: i8) {
        Machine::load_register_with_flags(&mut self.registers.accumulator,
                                          &mut self.registers.status,
                                          value);
    }

    // TODO akeeton: Implement binary-coded decimal.
    pub fn add_with_carry(&mut self, value: i8) {
        let a_before: i8 = self.registers.accumulator;
        let c_before: i8 = self.registers.status.get_carry();
        let a_after: i8 = a_before + c_before + value;

        debug_assert_eq!(a_after as u8, a_before as u8 + c_before as u8
                                        + value as u8);

        let did_carry = (a_after as u8) < (a_before as u8);

        let did_overflow   =
        	   (a_before < 0 && value < 0 && a_after >= 0)
        	|| (a_before > 0 && value > 0 && a_after <= 0);

        let mask = PS_CARRY | PS_OVERFLOW;

        self.registers.status.set_with_mask(mask,
            Status::new(StatusArgs { carry: did_carry,
                                     overflow: did_overflow,
                                     ..StatusArgs::none() } ));

        self.load_accumulator(a_after);

        log!(log::DEBUG, "accumulator: {}", self.registers.accumulator);
    }

    pub fn branch_if_minus(&mut self, addr: Address) {
        if self.registers.status.contains(PS_NEGATIVE) {
            self.registers.program_counter = addr;
        }
    }
}

impl std::fmt::Show for Machine {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "Machine Dump:\n\nAccumulator: {}",
               self.registers.accumulator)
    }
}

#[test]
fn add_with_carry_test() {
    let mut machine = Machine::new();

    machine.add_with_carry(1);
    assert_eq!(machine.registers.accumulator, 1);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.add_with_carry(-1);
    assert_eq!(machine.registers.accumulator, 0);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    true);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     true);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.add_with_carry(1);
    assert_eq!(machine.registers.accumulator, 2);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    let mut machine = Machine::new();

    machine.add_with_carry(127);
    assert_eq!(machine.registers.accumulator, 127);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.add_with_carry(-127);
    assert_eq!(machine.registers.accumulator, 0);
    assert_eq!(machine.registers.status.contains(PS_CARRY),     true);
    assert_eq!(machine.registers.status.contains(PS_ZERO),      true);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.registers.status.remove(PS_CARRY);
    machine.add_with_carry(-128);
    assert_eq!(machine.registers.accumulator, -128);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE),  true);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.add_with_carry(127);
    assert_eq!(machine.registers.accumulator, -1);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE),  true);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    let mut machine = Machine::new();

    machine.add_with_carry(127);
    assert_eq!(machine.registers.accumulator, 127);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.add_with_carry(1);
    assert_eq!(machine.registers.accumulator, -128);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE),  true);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW),  true);
}

#[test]
fn branch_if_minus_test() {
    {
        let mut machine      = Machine::new();
        let registers_before = machine.registers;

        machine.branch_if_minus(Address(0xABCD));
        assert_eq!(machine.registers, registers_before);
        assert_eq!(machine.registers.program_counter, Address(0));
    }

    {
        let mut machine = Machine::new();

        machine.registers.status.set_with_mask(PS_NEGATIVE, PS_NEGATIVE);
        let registers_before = machine.registers;

        machine.branch_if_minus(Address(0xABCD));
        assert_eq!(machine.registers.status, registers_before.status);
        assert_eq!(machine.registers.program_counter, Address(0xABCD));
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

// TODO: get rid of this when splitting dumbo into public and internal parts.
#![allow(missing_docs)]

use std::convert::From;
use std::net::Ipv4Addr;
use std::num::NonZeroUsize;
use std::result::Result;

use fc_util::timestamp_cycles;
use logger::{Metric, METRICS};
use net_util::MacAddr;
use pdu::arp::{test_speculative_tpa, Error as ArpFrameError, EthIPv4ArpFrame, ETH_IPV4_FRAME_LEN};
use pdu::ethernet::{Error as EthernetFrameError, EthernetFrame, ETHERTYPE_ARP, ETHERTYPE_IPV4};
use pdu::ipv4::{test_speculative_dst_addr, Error as IPv4PacketError, IPv4Packet, PROTOCOL_TCP};
use pdu::tcp::Error as TcpSegmentError;
use tcp::handler::{self, RecvEvent, TcpIPv4Handler, WriteEvent};
use tcp::NextSegmentStatus;

const DEFAULT_MAC_ADDR: &str = "06:01:23:45:67:01";
const DEFAULT_IPV4_ADDR: [u8; 4] = [169, 254, 169, 254];
const DEFAULT_TCP_PORT: u16 = 80;
const DEFAULT_MAX_CONNECTIONS: usize = 30;
const DEFAULT_MAX_PENDING_RESETS: usize = 100;

#[cfg_attr(test, derive(Debug, PartialEq))]
enum WriteArpReplyError {
    Arp(ArpFrameError),
    Ethernet(EthernetFrameError),
}

#[cfg_attr(test, derive(Debug, PartialEq))]
enum WritePacketError {
    IPv4Packet(IPv4PacketError),
    Ethernet(EthernetFrameError),
    TcpSegment(TcpSegmentError),
}

impl From<handler::WriteNextError> for WritePacketError {
    fn from(error: handler::WriteNextError) -> Self {
        match error {
            handler::WriteNextError::IPv4Packet(inner) => WritePacketError::IPv4Packet(inner),
            handler::WriteNextError::TcpSegment(inner) => WritePacketError::TcpSegment(inner),
        }
    }
}

pub struct MmdsNetworkStack {
    // The Ethernet MAC address of the MMDS server.
    mac_addr: MacAddr,
    // Whenever we detour a frame, we update the value of the remote MAC address. We need this
    // because we don't send ARP request ourselves.
    remote_mac_addr: MacAddr,
    // The IPv4 address of the MMDS server.
    ipv4_addr: Ipv4Addr,
    // We only remember the most recently received ARP request, and store the remote IPv4 address
    // here (we keep the remote MAC address in self.remote_mac_addr), to be used when the next
    // opportunity to send a frame presents itself.
    pending_arp_reply: Option<Ipv4Addr>,
    // This handles MMDS<->guest interaction at the TCP level.
    tcp_handler: TcpIPv4Handler,
}

impl MmdsNetworkStack {
    pub fn new(
        mac_addr: MacAddr,
        ipv4_addr: Ipv4Addr,
        tcp_port: u16,
        max_connections: NonZeroUsize,
        max_pending_resets: NonZeroUsize,
    ) -> Self {
        MmdsNetworkStack {
            mac_addr,
            remote_mac_addr: mac_addr,
            ipv4_addr,
            pending_arp_reply: None,
            tcp_handler: TcpIPv4Handler::new(
                ipv4_addr,
                tcp_port,
                max_connections,
                max_pending_resets,
            ),
        }
    }

    pub fn new_with_defaults() -> Self {
        // The unwrap is safe if parse_str() is implemented properly.
        let mac_addr = MacAddr::parse_str(DEFAULT_MAC_ADDR).unwrap();
        let ipv4_addr = Ipv4Addr::from(DEFAULT_IPV4_ADDR);

        // The unwrap()s are safe because the given literals are greater than 0.
        Self::new(
            mac_addr,
            ipv4_addr,
            DEFAULT_TCP_PORT,
            NonZeroUsize::new(DEFAULT_MAX_CONNECTIONS).unwrap(),
            NonZeroUsize::new(DEFAULT_MAX_PENDING_RESETS).unwrap(),
        )
    }

    // This is the entry point into the MMDS network stack. The src slice should hold the contents
    // of an Ethernet frame (of that exact size, without the CRC).
    pub fn detour_frame(&mut self, src: &[u8]) -> bool {
        // The frame cannot possibly contain an ARP request or IPv4 packet for the MMDS.
        if !test_speculative_tpa(src, self.ipv4_addr)
            && !test_speculative_dst_addr(src, self.ipv4_addr)
        {
            return false;
        }

        if let Ok(eth) = EthernetFrame::from_bytes(src) {
            match eth.ethertype() {
                ETHERTYPE_ARP => return self.detour_arp(eth),
                ETHERTYPE_IPV4 => return self.detour_ipv4(eth),
                _ => (),
            };
        } else {
            METRICS.mmds.rx_bad_eth.inc();
        }
        return false;
    }

    fn detour_arp(&mut self, eth: EthernetFrame<&[u8]>) -> bool {
        if let Ok(arp) = EthIPv4ArpFrame::request_from_bytes(eth.payload()) {
            if arp.tpa() == self.ipv4_addr {
                self.remote_mac_addr = arp.sha();
                self.pending_arp_reply = Some(arp.spa());
                return true;
            }
        }
        false
    }

    fn detour_ipv4(&mut self, eth: EthernetFrame<&[u8]>) -> bool {
        // TODO: We skip verifying the checksum, just in case the device model relies on offloading
        // checksum computation from the guest driver to some other entity. Clear up this entire
        // context at some point!
        if let Ok(ip) = IPv4Packet::from_bytes(eth.payload(), false) {
            if ip.destination_address() == self.ipv4_addr {
                if ip.protocol() == PROTOCOL_TCP {
                    self.remote_mac_addr = eth.src_mac();
                    match self.tcp_handler.receive_packet(&ip) {
                        Ok(event) => match event {
                            RecvEvent::NewConnectionSuccessful => {
                                METRICS.mmds.connections_created.inc()
                            }
                            RecvEvent::NewConnectionReplacing => {
                                METRICS.mmds.connections_created.inc();
                                METRICS.mmds.connections_destroyed.inc();
                            }
                            RecvEvent::EndpointDone => {
                                METRICS.mmds.connections_destroyed.inc();
                            }
                            _ => (),
                        },
                        Err(_) => METRICS.mmds.rx_accepted_err.inc(),
                    }
                } else {
                    // A non-TCP IPv4 packet heading towards the MMDS; we consider it unusual.
                    METRICS.mmds.rx_accepted_unusual.inc();
                }
                return true;
            }
        }
        false
    }

    // Allows the MMDS network stack to write a frame to the specified buffer. Will return:
    // - None, if the MMDS network stack has no frame to send at this point. The buffer can be
    // used for something else by the device model.
    // - Some(len), if a frame of the given length has been written to the specified buffer.
    pub fn write_next_frame(&mut self, buf: &mut [u8]) -> Option<NonZeroUsize> {
        // We try to send ARP replies first.
        if let Some(spa) = self.pending_arp_reply {
            return match self.write_arp_reply(buf, spa) {
                Ok(something) => {
                    self.pending_arp_reply = None;
                    something
                }
                Err(_) => {
                    METRICS.mmds.tx_errors.inc();
                    None
                }
            };
        } else {
            let call_write = match self.tcp_handler.next_segment_status() {
                NextSegmentStatus::Available => true,
                NextSegmentStatus::Timeout(value) => timestamp_cycles() >= value,
                NextSegmentStatus::Nothing => false,
            };

            if call_write {
                return match self.write_packet(buf) {
                    Ok(something) => something,
                    Err(_) => {
                        METRICS.mmds.tx_errors.inc();
                        None
                    }
                };
            }
        }
        None
    }

    fn write_arp_reply(
        &mut self,
        buf: &mut [u8],
        dst_ipv4: Ipv4Addr,
    ) -> Result<Option<NonZeroUsize>, WriteArpReplyError> {
        let mut eth_unsized = EthernetFrame::write_incomplete(
            buf,
            self.remote_mac_addr,
            self.mac_addr,
            ETHERTYPE_ARP,
        )
        .map_err(WriteArpReplyError::Ethernet)?;

        let arp_len = EthIPv4ArpFrame::write_reply(
            eth_unsized
                .inner_mut()
                .payload_mut()
                .split_at_mut(ETH_IPV4_FRAME_LEN)
                .0,
            self.mac_addr,
            self.ipv4_addr,
            self.remote_mac_addr,
            dst_ipv4,
        )
        .map_err(WriteArpReplyError::Arp)?
        .len();

        Ok(Some(
            // The unwrap() is safe because arp_len > 0.
            NonZeroUsize::new(eth_unsized.with_payload_len_unchecked(arp_len).len()).unwrap(),
        ))
    }

    fn write_packet(&mut self, buf: &mut [u8]) -> Result<Option<NonZeroUsize>, WritePacketError> {
        let mut eth_unsized = EthernetFrame::write_incomplete(
            buf,
            self.remote_mac_addr,
            self.mac_addr,
            ETHERTYPE_IPV4,
        )
        .map_err(WritePacketError::Ethernet)?;

        let (maybe_len, event) = self
            .tcp_handler
            .write_next_packet(eth_unsized.inner_mut().payload_mut())?;

        match event {
            WriteEvent::EndpointDone => METRICS.mmds.connections_destroyed.inc(),
            _ => (),
        }

        if let Some(packet_len) = maybe_len {
            return Ok(Some(
                // The unwrap() is safe because packet_len > 0.
                NonZeroUsize::new(
                    eth_unsized
                        .with_payload_len_unchecked(packet_len.get())
                        .len(),
                )
                .unwrap(),
            ));
        }
        Ok(None)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_new() {
        let ns = MmdsNetworkStack::new_with_defaults();
        assert_eq!(ns.mac_addr, MacAddr::parse_str(DEFAULT_MAC_ADDR).unwrap());
        assert_eq!(ns.ipv4_addr, Ipv4Addr::from(DEFAULT_IPV4_ADDR));
    }
}
<commit_msg>dumbo: new unit tests for ns.rs<commit_after>// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

// TODO: get rid of this when splitting dumbo into public and internal parts.
#![allow(missing_docs)]

use std::convert::From;
use std::net::Ipv4Addr;
use std::num::NonZeroUsize;
use std::result::Result;

use fc_util::timestamp_cycles;
use logger::{Metric, METRICS};
use net_util::MacAddr;
use pdu::arp::{test_speculative_tpa, Error as ArpFrameError, EthIPv4ArpFrame, ETH_IPV4_FRAME_LEN};
use pdu::ethernet::{Error as EthernetFrameError, EthernetFrame, ETHERTYPE_ARP, ETHERTYPE_IPV4};
use pdu::ipv4::{test_speculative_dst_addr, Error as IPv4PacketError, IPv4Packet, PROTOCOL_TCP};
use pdu::tcp::Error as TcpSegmentError;
use pdu::Incomplete;
use tcp::handler::{self, RecvEvent, TcpIPv4Handler, WriteEvent};
use tcp::NextSegmentStatus;

const DEFAULT_MAC_ADDR: &str = "06:01:23:45:67:01";
const DEFAULT_IPV4_ADDR: [u8; 4] = [169, 254, 169, 254];
const DEFAULT_TCP_PORT: u16 = 80;
const DEFAULT_MAX_CONNECTIONS: usize = 30;
const DEFAULT_MAX_PENDING_RESETS: usize = 100;

#[cfg_attr(test, derive(Debug, PartialEq))]
enum WriteArpFrameError {
    Arp(ArpFrameError),
    Ethernet(EthernetFrameError),
}

#[cfg_attr(test, derive(Debug, PartialEq))]
enum WritePacketError {
    IPv4Packet(IPv4PacketError),
    Ethernet(EthernetFrameError),
    TcpSegment(TcpSegmentError),
}

impl From<handler::WriteNextError> for WritePacketError {
    fn from(error: handler::WriteNextError) -> Self {
        match error {
            handler::WriteNextError::IPv4Packet(inner) => WritePacketError::IPv4Packet(inner),
            handler::WriteNextError::TcpSegment(inner) => WritePacketError::TcpSegment(inner),
        }
    }
}

pub struct MmdsNetworkStack {
    // The Ethernet MAC address of the MMDS server.
    mac_addr: MacAddr,
    // Whenever we detour a frame, we update the value of the remote MAC address. We need this
    // because we don't send ARP request ourselves.
    remote_mac_addr: MacAddr,
    // The IPv4 address of the MMDS server.
    ipv4_addr: Ipv4Addr,
    // We only remember the most recently received ARP request, and store the remote IPv4 address
    // here (we keep the remote MAC address in self.remote_mac_addr), to be used when the next
    // opportunity to send a frame presents itself.
    pending_arp_reply: Option<Ipv4Addr>,
    // This handles MMDS<->guest interaction at the TCP level.
    tcp_handler: TcpIPv4Handler,
}

impl MmdsNetworkStack {
    pub fn new(
        mac_addr: MacAddr,
        ipv4_addr: Ipv4Addr,
        tcp_port: u16,
        max_connections: NonZeroUsize,
        max_pending_resets: NonZeroUsize,
    ) -> Self {
        MmdsNetworkStack {
            mac_addr,
            remote_mac_addr: mac_addr,
            ipv4_addr,
            pending_arp_reply: None,
            tcp_handler: TcpIPv4Handler::new(
                ipv4_addr,
                tcp_port,
                max_connections,
                max_pending_resets,
            ),
        }
    }

    pub fn new_with_defaults() -> Self {
        // The unwrap is safe if parse_str() is implemented properly.
        let mac_addr = MacAddr::parse_str(DEFAULT_MAC_ADDR).unwrap();
        let ipv4_addr = Ipv4Addr::from(DEFAULT_IPV4_ADDR);

        // The unwrap()s are safe because the given literals are greater than 0.
        Self::new(
            mac_addr,
            ipv4_addr,
            DEFAULT_TCP_PORT,
            NonZeroUsize::new(DEFAULT_MAX_CONNECTIONS).unwrap(),
            NonZeroUsize::new(DEFAULT_MAX_PENDING_RESETS).unwrap(),
        )
    }

    // This is the entry point into the MMDS network stack. The src slice should hold the contents
    // of an Ethernet frame (of that exact size, without the CRC).
    pub fn detour_frame(&mut self, src: &[u8]) -> bool {
        // The frame cannot possibly contain an ARP request or IPv4 packet for the MMDS.
        if !test_speculative_tpa(src, self.ipv4_addr)
            && !test_speculative_dst_addr(src, self.ipv4_addr)
        {
            return false;
        }

        if let Ok(eth) = EthernetFrame::from_bytes(src) {
            match eth.ethertype() {
                ETHERTYPE_ARP => return self.detour_arp(eth),
                ETHERTYPE_IPV4 => return self.detour_ipv4(eth),
                _ => (),
            };
        } else {
            METRICS.mmds.rx_bad_eth.inc();
        }
        return false;
    }

    fn detour_arp(&mut self, eth: EthernetFrame<&[u8]>) -> bool {
        if let Ok(arp) = EthIPv4ArpFrame::request_from_bytes(eth.payload()) {
            if arp.tpa() == self.ipv4_addr {
                self.remote_mac_addr = arp.sha();
                self.pending_arp_reply = Some(arp.spa());
                return true;
            }
        }
        false
    }

    fn detour_ipv4(&mut self, eth: EthernetFrame<&[u8]>) -> bool {
        // TODO: We skip verifying the checksum, just in case the device model relies on offloading
        // checksum computation from the guest driver to some other entity. Clear up this entire
        // context at some point!
        if let Ok(ip) = IPv4Packet::from_bytes(eth.payload(), false) {
            if ip.destination_address() == self.ipv4_addr {
                if ip.protocol() == PROTOCOL_TCP {
                    self.remote_mac_addr = eth.src_mac();
                    match self.tcp_handler.receive_packet(&ip) {
                        Ok(event) => match event {
                            RecvEvent::NewConnectionSuccessful => {
                                METRICS.mmds.connections_created.inc()
                            }
                            RecvEvent::NewConnectionReplacing => {
                                METRICS.mmds.connections_created.inc();
                                METRICS.mmds.connections_destroyed.inc();
                            }
                            RecvEvent::EndpointDone => {
                                METRICS.mmds.connections_destroyed.inc();
                            }
                            _ => (),
                        },
                        Err(_) => METRICS.mmds.rx_accepted_err.inc(),
                    }
                } else {
                    // A non-TCP IPv4 packet heading towards the MMDS; we consider it unusual.
                    METRICS.mmds.rx_accepted_unusual.inc();
                }
                return true;
            }
        }
        false
    }

    // Allows the MMDS network stack to write a frame to the specified buffer. Will return:
    // - None, if the MMDS network stack has no frame to send at this point. The buffer can be
    // used for something else by the device model.
    // - Some(len), if a frame of the given length has been written to the specified buffer.
    pub fn write_next_frame(&mut self, buf: &mut [u8]) -> Option<NonZeroUsize> {
        // We try to send ARP replies first.
        if let Some(spa) = self.pending_arp_reply {
            return match self.write_arp_reply(buf, spa) {
                Ok(something) => {
                    self.pending_arp_reply = None;
                    something
                }
                Err(_) => {
                    METRICS.mmds.tx_errors.inc();
                    None
                }
            };
        } else {
            let call_write = match self.tcp_handler.next_segment_status() {
                NextSegmentStatus::Available => true,
                NextSegmentStatus::Timeout(value) => timestamp_cycles() >= value,
                NextSegmentStatus::Nothing => false,
            };

            if call_write {
                return match self.write_packet(buf) {
                    Ok(something) => something,
                    Err(_) => {
                        METRICS.mmds.tx_errors.inc();
                        None
                    }
                };
            }
        }
        None
    }

    fn prepare_eth_unsized<'a>(
        &self,
        buf: &'a mut [u8],
        ethertype: u16,
    ) -> Result<Incomplete<EthernetFrame<'a, &'a mut [u8]>>, EthernetFrameError> {
        EthernetFrame::write_incomplete(buf, self.remote_mac_addr, self.mac_addr, ethertype)
    }

    fn write_arp_reply(
        &self,
        buf: &mut [u8],
        dst_ipv4: Ipv4Addr,
    ) -> Result<Option<NonZeroUsize>, WriteArpFrameError> {
        let mut eth_unsized = self
            .prepare_eth_unsized(buf, ETHERTYPE_ARP)
            .map_err(WriteArpFrameError::Ethernet)?;

        let arp_len = EthIPv4ArpFrame::write_reply(
            eth_unsized
                .inner_mut()
                .payload_mut()
                .split_at_mut(ETH_IPV4_FRAME_LEN)
                .0,
            self.mac_addr,
            self.ipv4_addr,
            self.remote_mac_addr,
            dst_ipv4,
        )
        .map_err(WriteArpFrameError::Arp)?
        .len();

        Ok(Some(
            // The unwrap() is safe because arp_len > 0.
            NonZeroUsize::new(eth_unsized.with_payload_len_unchecked(arp_len).len()).unwrap(),
        ))
    }

    fn write_packet(&mut self, buf: &mut [u8]) -> Result<Option<NonZeroUsize>, WritePacketError> {
        let mut eth_unsized = self
            .prepare_eth_unsized(buf, ETHERTYPE_IPV4)
            .map_err(WritePacketError::Ethernet)?;

        let (maybe_len, event) = self
            .tcp_handler
            .write_next_packet(eth_unsized.inner_mut().payload_mut())?;

        match event {
            WriteEvent::EndpointDone => METRICS.mmds.connections_destroyed.inc(),
            _ => (),
        }

        if let Some(packet_len) = maybe_len {
            return Ok(Some(
                // The unwrap() is safe because packet_len > 0.
                NonZeroUsize::new(
                    eth_unsized
                        .with_payload_len_unchecked(packet_len.get())
                        .len(),
                )
                .unwrap(),
            ));
        }
        Ok(None)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use pdu::tcp::{Flags as TcpFlags, TcpSegment};
    use std::str::FromStr;

    // We use LOCALHOST here because const new() is not stable yet, so just reuse this const, since
    // all we're interested in is having some address different from the MMDS one.
    const REMOTE_ADDR: Ipv4Addr = Ipv4Addr::LOCALHOST;
    const REMOTE_MAC_STR: &str = "11:11:11:22:22:22";
    const MMDS_PORT: u16 = 80;
    const REMOTE_PORT: u16 = 1235;
    const SEQ_NUMBER: u32 = 123;

    // Helper methods which only make sense for testing.
    impl MmdsNetworkStack {
        fn write_arp_request(&self, buf: &mut [u8], for_mmds: bool) -> usize {
            // We write a reply, and then modify it into a request.
            let len = self
                .write_arp_reply(buf, REMOTE_ADDR)
                .unwrap()
                .unwrap()
                .get();

            let mut eth = EthernetFrame::from_bytes_unchecked(&mut buf[..len]);
            let mut arp = EthIPv4ArpFrame::from_bytes_unchecked(eth.payload_mut());

            // Set the operation to REQUEST.
            arp.set_operation(1);
            arp.set_sha(MacAddr::parse_str(REMOTE_MAC_STR).unwrap());
            arp.set_spa(REMOTE_ADDR);

            // The tpa remains REMOTE_ADDR otherwise, and is thus invalid for the MMDS.
            if for_mmds {
                arp.set_tpa(self.ipv4_addr);
            }
            len
        }

        fn write_incoming_tcp_segment(
            &self,
            buf: &mut [u8],
            addr: Ipv4Addr,
            flags: TcpFlags,
        ) -> usize {
            let mut eth_unsized = self.prepare_eth_unsized(buf, ETHERTYPE_IPV4).unwrap();
            let packet_len = {
                let mut packet = IPv4Packet::write_header(
                    eth_unsized.inner_mut().payload_mut(),
                    PROTOCOL_TCP,
                    REMOTE_ADDR,
                    addr,
                )
                .unwrap();

                let mut segment_len = TcpSegment::write_incomplete_segment::<[u8]>(
                    packet.inner_mut().payload_mut(),
                    SEQ_NUMBER,
                    1234,
                    flags,
                    10000,
                    None,
                    0,
                    None,
                )
                .unwrap()
                .finalize(REMOTE_PORT, MMDS_PORT, Some((REMOTE_ADDR, addr)))
                .len();

                packet.with_payload_len_unchecked(segment_len, true).len()
            };

            eth_unsized.with_payload_len_unchecked(packet_len).len()
        }

        fn next_frame_as_ipv4_packet<'a>(&mut self, buf: &'a mut [u8]) -> IPv4Packet<&'a [u8]> {
            let len = self.write_next_frame(buf.as_mut()).unwrap().get();
            let eth = EthernetFrame::from_bytes(&buf[..len]).unwrap();
            IPv4Packet::from_bytes(&buf[eth.payload_offset()..len], true).unwrap()
        }
    }

    #[test]
    fn test_ns() {
        let mut ns = MmdsNetworkStack::new_with_defaults();
        assert_eq!(ns.mac_addr, MacAddr::parse_str(DEFAULT_MAC_ADDR).unwrap());
        assert_eq!(ns.ipv4_addr, Ipv4Addr::from(DEFAULT_IPV4_ADDR));

        let mut buf = [0u8; 2000];
        let mut bad_buf = [0u8; 1];

        let remote_mac = MacAddr::parse_str(REMOTE_MAC_STR).unwrap();
        let mmds_addr = ns.ipv4_addr;
        let bad_mmds_addr = Ipv4Addr::from_str("1.2.3.4").unwrap();

        // Buffer is too small.
        assert!(!ns.detour_frame(bad_buf.as_ref()));

        // There's nothing to send right now.
        assert!(ns.write_next_frame(buf.as_mut()).is_none());

        {
            let len = ns.write_arp_request(buf.as_mut(), false);
            // Not asking for MMDS MAC address.
            assert!(!ns.detour_frame(&buf[..len]));
            // There's still nothing to send.
            assert!(ns.write_next_frame(buf.as_mut()).is_none());
        }

        {
            let len = ns.write_arp_request(buf.as_mut(), true);

            // Asking for MMDS MAC address.
            assert!(ns.detour_frame(&buf[..len]));

            assert_eq!(ns.remote_mac_addr, remote_mac);
        }

        // There should be an ARP reply to send.
        {
            // Buffer is too small.
            assert!(ns.write_next_frame(bad_buf.as_mut()).is_none());

            let len = ns.write_next_frame(buf.as_mut()).unwrap().get();
            let eth = EthernetFrame::from_bytes(&buf[..len]).unwrap();
            let arp_reply = EthIPv4ArpFrame::from_bytes_unchecked(eth.payload());

            // REPLY = 2
            assert_eq!(arp_reply.operation(), 2);
            assert_eq!(arp_reply.sha(), ns.mac_addr);
            assert_eq!(arp_reply.spa(), ns.ipv4_addr);
            assert_eq!(arp_reply.tha(), ns.remote_mac_addr);
            assert_eq!(arp_reply.tpa(), REMOTE_ADDR);
        }

        // Nothing to send anymore.
        assert!(ns.write_next_frame(buf.as_mut()).is_none());

        // Let's send a TCP segment which will be rejected, because it's heading to the wrong
        // address.
        {
            let len = ns.write_incoming_tcp_segment(buf.as_mut(), bad_mmds_addr, TcpFlags::ACK);
            assert!(!ns.detour_frame(&buf[..len]));

            // Nothing to send in response.
            assert!(ns.write_next_frame(buf.as_mut()).is_none());
        }

        // Let's send a TCP segment which will cause a RST to come out of the inner TCP handler.
        {
            let len = ns.write_incoming_tcp_segment(buf.as_mut(), mmds_addr, TcpFlags::ACK);
            assert!(ns.detour_frame(&buf[..len]));
        }

        // Let's check we actually get a RST when writing the next frame.
        {
            assert!(ns.write_next_frame(bad_buf.as_mut()).is_none());

            let ip = ns.next_frame_as_ipv4_packet(buf.as_mut());
            assert_eq!(ip.source_address(), mmds_addr);
            assert_eq!(ip.destination_address(), REMOTE_ADDR);

            let s = TcpSegment::from_bytes(
                ip.payload(),
                Some((ip.source_address(), ip.destination_address())),
            )
            .unwrap();
            assert_eq!(s.flags_after_ns(), TcpFlags::RST);
            assert_eq!(s.source_port(), MMDS_PORT);
            assert_eq!(s.destination_port(), REMOTE_PORT);
        }

        // Nothing else to send.
        assert!(ns.write_next_frame(buf.as_mut()).is_none());

        // Let's send a TCP SYN into the ns.
        {
            let len = ns.write_incoming_tcp_segment(buf.as_mut(), mmds_addr, TcpFlags::SYN);
            assert_eq!(ns.detour_frame(&buf[..len]), true);
        }

        // We should be getting a SYNACK out of the ns in response.
        {
            let ip = ns.next_frame_as_ipv4_packet(buf.as_mut());
            assert_eq!(ip.source_address(), mmds_addr);
            assert_eq!(ip.destination_address(), REMOTE_ADDR);

            let s = TcpSegment::from_bytes(
                ip.payload(),
                Some((ip.source_address(), ip.destination_address())),
            )
            .unwrap();
            assert_eq!(s.flags_after_ns(), TcpFlags::SYN | TcpFlags::ACK);
            assert_eq!(s.source_port(), MMDS_PORT);
            assert_eq!(s.destination_port(), REMOTE_PORT);
            assert_eq!(s.ack_number(), SEQ_NUMBER.wrapping_add(1));
        }

        // Nothing else to send.
        assert!(ns.write_next_frame(buf.as_mut()).is_none());
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate imgui;

extern crate stratis_server as server;
extern crate stratis_shared as shared;

use shared::client::{Client};
use shared::events::Event;
use shared::interface::Interface;

use imgui::{Ui,ImString,ImStr};

use std::sync::mpsc::{Receiver};
use std::sync::{Arc, Mutex};

#[allow(unused_must_use,unused_variables,unused_assignments)]
fn main() {
    let ip_addr = "127.0.0.1:9996";

    // load in client and connect to server
    let client;
    let rx: Receiver<Event>;
    if let Some((mut c, rx_)) = Client::load_file("client.key") { //assumes client key is an admin user
        c.connect(ip_addr);
        client = Arc::new(Mutex::new(c));
        rx = rx_;
        Client::login(&client);
    }
    else {
        let (mut c, rx_) = Client::default();
        Client::save(&c,"client.key");
        c.connect(ip_addr);
        c.register();
        client = Arc::new(Mutex::new(c));
        rx = rx_;
        Client::login(&client);
    }

    let mut ifc = Interface::init("stratis console", [800,600]);
    let mut app = AppState::default();

    'main: loop {
        if !ifc.render(None, |ui| {
            let client = &*client.lock().expect("Client mutex poisoned");
            app.render(&ui,
                       client);
        }) { break 'main }

        ifc.maybe_sleep()
    }
}

struct AppState {
    player_idx: i32,
}

impl Default for AppState {
    fn default() -> AppState {
        AppState {
            player_idx: -1,
        }
    }
}

impl AppState {
    fn render(&mut self, ui: &Ui, client: &Client) {
        ui.main_menu_bar(|| {
            ui.menu(im_str!("Connected:{:?}",client.stream.is_some()))
                .enabled(false)
                .build(|| {})
        });

        ui.window(im_str!("Players"))
            .build(|| {
                let players: Vec<ImString> =
                    client.player_cache.iter().map(|p| {
                        im_str!("{:}", p.0).to_owned()
                    }).collect();

                let players: Vec<&ImStr> = players.iter().map(|p|p.as_ref()).collect();

                ui.list_box(im_str!("UIDS"),
                            &mut self.player_idx,
                            &players[..],
                            50i32);
            });
    }
}
<commit_msg>dont create key<commit_after>#[macro_use]
extern crate imgui;

extern crate stratis_server as server;
extern crate stratis_shared as shared;

use shared::client::{Client};
use shared::events::Event;
use shared::interface::Interface;

use imgui::{Ui,ImString,ImStr};

use std::sync::mpsc::{Receiver};
use std::sync::{Arc, Mutex};

#[allow(unused_must_use,unused_variables,unused_assignments)]
fn main() {
    let ip_addr = "127.0.0.1:9996";

    // load in client and connect to server
    let client;
    let rx: Receiver<Event>;
    if let Some((mut c, rx_)) = Client::load_file("admin.key") { //assumes client key is an admin user
        c.connect(ip_addr);
        client = Arc::new(Mutex::new(c));
        rx = rx_;
        Client::login(&client);
    }
    else { panic!("Cannot find login key") }

    let mut ifc = Interface::init("stratis console", [800,600]);
    let mut app = AppState::default();

    'main: loop {
        if !ifc.render(None, |ui| {
            let client = &*client.lock().expect("Client mutex poisoned");
            app.render(&ui,
                       client);
        }) { break 'main }

        ifc.maybe_sleep()
    }
}

struct AppState {
    player_idx: i32,
}

impl Default for AppState {
    fn default() -> AppState {
        AppState {
            player_idx: -1,
        }
    }
}

impl AppState {
    fn render(&mut self, ui: &Ui, client: &Client) {
        ui.main_menu_bar(|| {
            ui.menu(im_str!("Connected:{:?}",client.stream.is_some()))
                .enabled(false)
                .build(|| {})
        });

        ui.window(im_str!("Players"))
            .build(|| {
                let players: Vec<ImString> =
                    client.player_cache.iter().map(|p| {
                        im_str!("{:}", p.0).to_owned()
                    }).collect();

                let players: Vec<&ImStr> = players.iter().map(|p|p.as_ref()).collect();

                ui.list_box(im_str!("UIDS"),
                            &mut self.player_idx,
                            &players[..],
                            50i32);
            });
    }
}
<|endoftext|>


<commit_before>// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.

use kvproto::kvrpcpb::{Context, IsolationLevel};
use protobuf::Message;
use tipb::select::SelectResponse;

use test_coprocessor::*;
use test_storage::*;

#[test]
fn test_deadline() {
    let _guard = crate::setup();

    let product = ProductTable::new();
    let (_, endpoint) = init_with_data(&product, &[]);
    let req = DAGSelect::from(&product).build();

    fail::cfg("deadline_check_fail", "return()").unwrap();
    let resp = handle_request(&endpoint, req);

    assert!(resp.get_other_error().contains("exceeding max time limit"));
}

#[test]
fn test_deadline_2() {
    // It should not even take any snapshots when request is outdated from the beginning.

    let _guard = crate::setup();

    let product = ProductTable::new();
    let (_, endpoint) = init_with_data(&product, &[]);
    let req = DAGSelect::from(&product).build();

    fail::cfg("rockskv_async_snapshot", "panic").unwrap();
    fail::cfg("deadline_check_fail", "return()").unwrap();
    let resp = handle_request(&endpoint, req);

    assert!(resp.get_other_error().contains("exceeding max time limit"));
}

/// Test deadline exceeded when request is handling
/// Note: only
#[test]
fn test_deadline_3() {
    let _guard = crate::setup();

    let data = vec![
        (1, Some("name:0"), 2),
        (2, Some("name:4"), 3),
        (4, Some("name:3"), 1),
        (5, Some("name:1"), 4),
    ];

    let product = ProductTable::new();
    let (_, endpoint) = {
        let engine = tikv::storage::TestEngineBuilder::new().build().unwrap();
        let mut cfg = tikv::server::Config::default();
        cfg.end_point_request_max_handle_duration = tikv_util::config::ReadableDuration::secs(1);
        // Batch execution will check deadline after the first batch is executed, but our
        // test data is not large enough for the second batch. So let's disable it.
        cfg.end_point_enable_batch_if_possible = false;
        init_data_with_details(Context::default(), engine, &product, &data, true, &cfg)
    };
    let req = DAGSelect::from(&product).build();

    fail::cfg("kv_cursor_seek", "sleep(2000)").unwrap();
    let cop_resp = handle_request(&endpoint, req);
    let mut resp = SelectResponse::default();
    resp.merge_from_bytes(cop_resp.get_data()).unwrap();

    // Errors during evaluation becomes an eval error.
    assert!(resp
        .get_error()
        .get_msg()
        .contains("exceeding max time limit"));
}

#[test]
fn test_parse_request_failed() {
    let _guard = crate::setup();

    let product = ProductTable::new();
    let (_, endpoint) = init_with_data(&product, &[]);
    let req = DAGSelect::from(&product).build();

    fail::cfg("coprocessor_parse_request", "return()").unwrap();
    let resp = handle_request(&endpoint, req);

    assert!(resp.get_other_error().contains("unsupported tp"));
}

#[test]
fn test_parse_request_failed_2() {
    // It should not even take any snapshots when parse failed.

    let _guard = crate::setup();

    let product = ProductTable::new();
    let (_, endpoint) = init_with_data(&product, &[]);
    let req = DAGSelect::from(&product).build();

    fail::cfg("rockskv_async_snapshot", "panic").unwrap();
    fail::cfg("coprocessor_parse_request", "return()").unwrap();
    let resp = handle_request(&endpoint, req);

    assert!(resp.get_other_error().contains("unsupported tp"));
}

#[test]
fn test_readpool_full() {
    let _guard = crate::setup();

    let product = ProductTable::new();
    let (_, endpoint) = init_with_data(&product, &[]);
    let req = DAGSelect::from(&product).build();

    fail::cfg("read_pool_spawn_full", "return()").unwrap();
    let resp = handle_request(&endpoint, req);

    assert!(resp.get_region_error().has_server_is_busy());
}

#[test]
fn test_snapshot_failed() {
    let _guard = crate::setup();

    let product = ProductTable::new();
    let (_, endpoint) = init_with_data(&product, &[]);
    let req = DAGSelect::from(&product).build();

    fail::cfg("rockskv_async_snapshot", "return()").unwrap();
    let resp = handle_request(&endpoint, req);

    assert!(resp.get_other_error().contains("snapshot failed"));
}

#[test]
fn test_snapshot_failed_2() {
    let _guard = crate::setup();

    let product = ProductTable::new();
    let (_, endpoint) = init_with_data(&product, &[]);
    let req = DAGSelect::from(&product).build();

    fail::cfg("rockskv_async_snapshot_not_leader", "return()").unwrap();
    let resp = handle_request(&endpoint, req);

    assert!(resp.get_region_error().has_not_leader());
}

#[test]
fn test_storage_error() {
    let _guard = crate::setup();

    let data = vec![(1, Some("name:0"), 2), (2, Some("name:4"), 3)];

    let product = ProductTable::new();
    let (_, endpoint) = init_with_data(&product, &data);
    let req = DAGSelect::from(&product).build();

    fail::cfg("kv_cursor_seek", "return()").unwrap();
    let resp = handle_request(&endpoint, req);

    assert!(resp.get_other_error().contains("kv cursor seek error"));
}

#[test]
fn test_region_error_in_scan() {
    let _guard = crate::setup();

    let data = vec![
        (1, Some("name:0"), 2),
        (2, Some("name:4"), 3),
        (4, Some("name:3"), 1),
        (5, Some("name:1"), 4),
    ];

    let product = ProductTable::new();
    let (_cluster, raft_engine, mut ctx) = new_raft_engine(1, "");
    ctx.set_isolation_level(IsolationLevel::SI);

    let (_, endpoint) =
        init_data_with_engine_and_commit(ctx.clone(), raft_engine, &product, &data, true);

    fail::cfg("region_snapshot_seek", "return()").unwrap();
    let req = DAGSelect::from(&product).build_with(ctx, &[0]);
    let resp = handle_request(&endpoint, req);

    assert!(resp
        .get_region_error()
        .get_message()
        .contains("region seek error"));
}
<commit_msg>Fix missed tipb import (#5233)<commit_after>// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.

use kvproto::kvrpcpb::{Context, IsolationLevel};
use protobuf::Message;
use tipb::SelectResponse;

use test_coprocessor::*;
use test_storage::*;

#[test]
fn test_deadline() {
    let _guard = crate::setup();

    let product = ProductTable::new();
    let (_, endpoint) = init_with_data(&product, &[]);
    let req = DAGSelect::from(&product).build();

    fail::cfg("deadline_check_fail", "return()").unwrap();
    let resp = handle_request(&endpoint, req);

    assert!(resp.get_other_error().contains("exceeding max time limit"));
}

#[test]
fn test_deadline_2() {
    // It should not even take any snapshots when request is outdated from the beginning.

    let _guard = crate::setup();

    let product = ProductTable::new();
    let (_, endpoint) = init_with_data(&product, &[]);
    let req = DAGSelect::from(&product).build();

    fail::cfg("rockskv_async_snapshot", "panic").unwrap();
    fail::cfg("deadline_check_fail", "return()").unwrap();
    let resp = handle_request(&endpoint, req);

    assert!(resp.get_other_error().contains("exceeding max time limit"));
}

/// Test deadline exceeded when request is handling
/// Note: only
#[test]
fn test_deadline_3() {
    let _guard = crate::setup();

    let data = vec![
        (1, Some("name:0"), 2),
        (2, Some("name:4"), 3),
        (4, Some("name:3"), 1),
        (5, Some("name:1"), 4),
    ];

    let product = ProductTable::new();
    let (_, endpoint) = {
        let engine = tikv::storage::TestEngineBuilder::new().build().unwrap();
        let mut cfg = tikv::server::Config::default();
        cfg.end_point_request_max_handle_duration = tikv_util::config::ReadableDuration::secs(1);
        // Batch execution will check deadline after the first batch is executed, but our
        // test data is not large enough for the second batch. So let's disable it.
        cfg.end_point_enable_batch_if_possible = false;
        init_data_with_details(Context::default(), engine, &product, &data, true, &cfg)
    };
    let req = DAGSelect::from(&product).build();

    fail::cfg("kv_cursor_seek", "sleep(2000)").unwrap();
    let cop_resp = handle_request(&endpoint, req);
    let mut resp = SelectResponse::default();
    resp.merge_from_bytes(cop_resp.get_data()).unwrap();

    // Errors during evaluation becomes an eval error.
    assert!(resp
        .get_error()
        .get_msg()
        .contains("exceeding max time limit"));
}

#[test]
fn test_parse_request_failed() {
    let _guard = crate::setup();

    let product = ProductTable::new();
    let (_, endpoint) = init_with_data(&product, &[]);
    let req = DAGSelect::from(&product).build();

    fail::cfg("coprocessor_parse_request", "return()").unwrap();
    let resp = handle_request(&endpoint, req);

    assert!(resp.get_other_error().contains("unsupported tp"));
}

#[test]
fn test_parse_request_failed_2() {
    // It should not even take any snapshots when parse failed.

    let _guard = crate::setup();

    let product = ProductTable::new();
    let (_, endpoint) = init_with_data(&product, &[]);
    let req = DAGSelect::from(&product).build();

    fail::cfg("rockskv_async_snapshot", "panic").unwrap();
    fail::cfg("coprocessor_parse_request", "return()").unwrap();
    let resp = handle_request(&endpoint, req);

    assert!(resp.get_other_error().contains("unsupported tp"));
}

#[test]
fn test_readpool_full() {
    let _guard = crate::setup();

    let product = ProductTable::new();
    let (_, endpoint) = init_with_data(&product, &[]);
    let req = DAGSelect::from(&product).build();

    fail::cfg("read_pool_spawn_full", "return()").unwrap();
    let resp = handle_request(&endpoint, req);

    assert!(resp.get_region_error().has_server_is_busy());
}

#[test]
fn test_snapshot_failed() {
    let _guard = crate::setup();

    let product = ProductTable::new();
    let (_, endpoint) = init_with_data(&product, &[]);
    let req = DAGSelect::from(&product).build();

    fail::cfg("rockskv_async_snapshot", "return()").unwrap();
    let resp = handle_request(&endpoint, req);

    assert!(resp.get_other_error().contains("snapshot failed"));
}

#[test]
fn test_snapshot_failed_2() {
    let _guard = crate::setup();

    let product = ProductTable::new();
    let (_, endpoint) = init_with_data(&product, &[]);
    let req = DAGSelect::from(&product).build();

    fail::cfg("rockskv_async_snapshot_not_leader", "return()").unwrap();
    let resp = handle_request(&endpoint, req);

    assert!(resp.get_region_error().has_not_leader());
}

#[test]
fn test_storage_error() {
    let _guard = crate::setup();

    let data = vec![(1, Some("name:0"), 2), (2, Some("name:4"), 3)];

    let product = ProductTable::new();
    let (_, endpoint) = init_with_data(&product, &data);
    let req = DAGSelect::from(&product).build();

    fail::cfg("kv_cursor_seek", "return()").unwrap();
    let resp = handle_request(&endpoint, req);

    assert!(resp.get_other_error().contains("kv cursor seek error"));
}

#[test]
fn test_region_error_in_scan() {
    let _guard = crate::setup();

    let data = vec![
        (1, Some("name:0"), 2),
        (2, Some("name:4"), 3),
        (4, Some("name:3"), 1),
        (5, Some("name:1"), 4),
    ];

    let product = ProductTable::new();
    let (_cluster, raft_engine, mut ctx) = new_raft_engine(1, "");
    ctx.set_isolation_level(IsolationLevel::SI);

    let (_, endpoint) =
        init_data_with_engine_and_commit(ctx.clone(), raft_engine, &product, &data, true);

    fail::cfg("region_snapshot_seek", "return()").unwrap();
    let req = DAGSelect::from(&product).build_with(ctx, &[0]);
    let resp = handle_request(&endpoint, req);

    assert!(resp
        .get_region_error()
        .get_message()
        .contains("region seek error"));
}
<|endoftext|>