<commit_before>extern crate chrono;
extern crate clap;
extern crate diesel;
extern crate dotenv;

mod database_error;
#[macro_use]
mod database;

use chrono::*;
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
#[cfg(feature = "postgres")]
use diesel::pg::PgConnection;
#[cfg(feature = "sqlite")]
use diesel::sqlite::SqliteConnection;
use diesel::migrations::schema::*;
use diesel::types::{FromSql, VarChar};
use diesel::{migrations, Connection, Insertable};
use std::error::Error;
use std::io::stdout;
use std::path::{PathBuf, Path};
use std::{env, fs};

use self::database_error::{DatabaseError, DatabaseResult};

fn main() {
    use self::dotenv::dotenv;
    dotenv().ok();

    let database_arg = Arg::with_name("DATABASE_URL")
        .long("database-url")
        .help("Specifies the database URL to connect to. Falls back to \
                   the DATABASE_URL environment variable if unspecified.")
        .global(true)
        .takes_value(true);

    let migration_subcommand = SubCommand::with_name("migration")
        .about("A group of commands for generating, running, and reverting \
                migrations.")
        .setting(AppSettings::VersionlessSubcommands)
        .arg(Arg::with_name("MIGRATION_DIRECTORY")
            .long("migration-dir")
            .help("The location of your migration directory. By default this \
                   will look for a directory called `migrations` in the \
                   current directory and its parents.")
            .takes_value(true)
            .global(true)
        ).subcommand(
            SubCommand::with_name("run")
                .about("Runs all pending migrations")
        ).subcommand(
            SubCommand::with_name("revert")
                .about("Reverts the latest run migration")
        ).subcommand(
            SubCommand::with_name("redo")
                .about("Reverts and re-runs the latest migration. Useful \
                      for testing that a migration can in fact be reverted.")
        ).subcommand(
            SubCommand::with_name("generate")
                .about("Generate a new migration with the given name, and \
                      the current timestamp as the version")
                .arg(Arg::with_name("MIGRATION_NAME")
                     .help("The name of the migration to create")
                     .required(true)
                 )
                .arg(Arg::with_name("MIGRATION_VERSION")
                     .long("version")
                     .help("The version number to use when generating the migration. \
                            Defaults to the current timestamp, which should suffice \
                            for most use cases.")
                     .takes_value(true)
                )
        ).setting(AppSettings::SubcommandRequiredElseHelp);

    let setup_subcommand = SubCommand::with_name("setup")
        .about("Creates the migrations directory, creates the database \
                specified in your DATABASE_URL, and runs existing migrations.");

    let database_subcommand = SubCommand::with_name("database")
        .about("A group of commands for setting up and resetting your database.")
        .setting(AppSettings::VersionlessSubcommands)
        .subcommand(
            SubCommand::with_name("setup")
                .about("Creates the database specified in your DATABASE_URL, \
                        and then runs any existing migrations.")
        ).subcommand(
            SubCommand::with_name("reset")
                .about("Resets your database by dropping the database specified \
                        in your DATABASE_URL and then running `diesel database setup`.")
        ).subcommand(
            SubCommand::with_name("drop")
                .about("Drops the database specified in your DATABASE_URL.")
                .setting(AppSettings::Hidden)
        ).setting(AppSettings::SubcommandRequiredElseHelp);

    let matches = App::new("diesel")
        .version(env!("CARGO_PKG_VERSION"))
        .setting(AppSettings::VersionlessSubcommands)
        .after_help("You can also run `diesel SUBCOMMAND -h` to get more information about that subcommand.")
        .arg(database_arg)
        .subcommand(migration_subcommand)
        .subcommand(setup_subcommand)
        .subcommand(database_subcommand)
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .get_matches();

    match matches.subcommand() {
        ("migration", Some(matches)) => run_migration_command(matches),
        ("setup", Some(matches)) => run_setup_command(matches),
        ("database", Some(matches)) => run_database_command(matches),
        _ => unreachable!("The cli parser should prevent reaching here"),
    }
}

fn run_migration_command(matches: &ArgMatches) {
    match matches.subcommand() {
        ("run", Some(_)) => {
            let database_url = database::database_url(matches);
            call_with_conn!(database_url, migrations::run_pending_migrations)
                .unwrap_or_else(handle_error);
        }
        ("revert", Some(_)) => {
            let database_url = database::database_url(matches);
            call_with_conn!(database_url, migrations::revert_latest_migration)
                .unwrap_or_else(handle_error);
        }
        ("redo", Some(_)) => {
            let database_url = database::database_url(matches);
            call_with_conn!(database_url, redo_latest_migration);
        }
        ("generate", Some(args)) => {
            let migration_name = args.value_of("MIGRATION_NAME").unwrap();
            let version = migration_version(args);
            let versioned_name = format!("{}_{}", version, migration_name);
            let migration_dir = migrations_dir(args).join(versioned_name);
            fs::create_dir(&migration_dir).unwrap();

            let migration_dir_relative = convert_absolute_path_to_relative(
                &migration_dir,
                &env::current_dir().unwrap()
            );

            let up_path = migration_dir.join("up.sql");
            println!("Creating {}", migration_dir_relative.join("up.sql").display());
            fs::File::create(up_path).unwrap();
            let down_path = migration_dir.join("down.sql");
            println!("Creating {}", migration_dir_relative.join("down.sql").display());
            fs::File::create(down_path).unwrap();
        }
        _ => unreachable!("The cli parser should prevent reaching here"),
    }
}

use std::fmt::Display;
fn migration_version<'a>(matches: &'a ArgMatches) -> Box<Display + 'a> {
    matches.value_of("MIGRATION_VERSION").map(|s| Box::new(s) as Box<Display>)
        .unwrap_or_else(|| Box::new(Local::now().format("%Y%m%d%H%M%S")))
}

fn migrations_dir(matches: &ArgMatches) -> PathBuf {
    matches.value_of("MIGRATION_DIRECTORY")
        .map(PathBuf::from)
        .or_else(|| {
            env::var("MIGRATION_DIRECTORY").map(PathBuf::from).ok()
        }).unwrap_or_else(|| {
            migrations::find_migrations_directory()
                .unwrap_or_else(handle_error)
        })
}

fn run_setup_command(matches: &ArgMatches) {
    migrations::find_migrations_directory()
        .unwrap_or_else(|_| {
            create_migrations_directory()
                .unwrap_or_else(handle_error)
        });

    database::setup_database(matches).unwrap_or_else(handle_error);
}

fn run_database_command(matches: &ArgMatches) {
    match matches.subcommand() {
        ("setup", Some(args)) => database::setup_database(args).unwrap_or_else(handle_error),
        ("reset", Some(args)) => database::reset_database(args).unwrap_or_else(handle_error),
        ("drop", Some(args)) => database::drop_database_command(args).unwrap_or_else(handle_error),
        _ => unreachable!("The cli parser should prevent reaching here"),
    };
}

/// Looks for a migrations directory in the current path and all parent paths,
/// and creates one in the same directory as the Cargo.toml if it can't find
/// one. It also sticks a .gitkeep in the directory so git will pick it up.
/// Returns a `DatabaseError::CargoTomlNotFound` if no Cargo.toml is found.
fn create_migrations_directory() -> DatabaseResult<PathBuf> {
    let project_root = try!(find_project_root());
    println!("Creating migrations/ directory at: {}", project_root
                                                        .join("migrations")
                                                        .display());
    try!(fs::create_dir(project_root.join("migrations")));
    try!(fs::File::create(project_root.join("migrations/.gitkeep")));
    Ok(project_root)
}

fn find_project_root() -> DatabaseResult<PathBuf> {
    search_for_cargo_toml_directory(&try!(env::current_dir()))
}

/// Searches for the directory that holds the project's Cargo.toml, and returns
/// the path if it found it, or returns a `DatabaseError::CargoTomlNotFound`.
fn search_for_cargo_toml_directory(path: &Path) -> DatabaseResult<PathBuf> {
    let toml_path = path.join("Cargo.toml");
    if toml_path.is_file() {
        Ok(path.to_owned())
    } else {
        path.parent().map(search_for_cargo_toml_directory)
            .unwrap_or(Err(DatabaseError::CargoTomlNotFound))
    }
}

/// Reverts the most recent migration, and then runs it again, all in a
/// transaction. If either part fails, the transaction is not committed.
fn redo_latest_migration<Conn>(conn: &Conn) where
        Conn: Connection,
        String: FromSql<VarChar, Conn::Backend>,
        for<'a> &'a NewMigration<'a>:
            Insertable<__diesel_schema_migrations::table, Conn::Backend>,
{
    conn.transaction(|| {
        let reverted_version = try!(migrations::revert_latest_migration(conn));
        migrations::run_migration_with_version(conn, &reverted_version, &mut stdout())
    }).unwrap_or_else(handle_error);
}

fn handle_error<E: Error, T>(error: E) -> T {
    println!("{}", error);
    ::std::process::exit(1);
}

// Converts an absolute path to a relative path, with the restriction that the
// target path must be in the same directory or above the current path.
fn convert_absolute_path_to_relative(target_path: &Path, mut current_path: &Path)
    -> PathBuf
{
    let mut result = PathBuf::new();

    while !target_path.starts_with(current_path) {
        result.push("..");
        match current_path.parent() {
            Some(parent) => current_path = parent,
            None => return target_path.into(),
        }
    }

    result.join(target_path.strip_prefix(current_path).unwrap())
}

#[cfg(test)]
mod tests {
    extern crate tempdir;

    use database_error::DatabaseError;

    use self::tempdir::TempDir;

    use std::fs;
    use std::path::PathBuf;

    use super::convert_absolute_path_to_relative;
    use super::search_for_cargo_toml_directory;

    #[test]
    fn toml_directory_find_cargo_toml() {
        let dir = TempDir::new("diesel").unwrap();
        let temp_path = dir.path().canonicalize().unwrap();
        let toml_path = temp_path.join("Cargo.toml");

        fs::File::create(&toml_path).unwrap();

        assert_eq!(Ok(temp_path.clone()), search_for_cargo_toml_directory(&temp_path));
    }

    #[test]
    fn cargo_toml_not_found_if_no_cargo_toml() {
        let dir = TempDir::new("diesel").unwrap();
        let temp_path = dir.path().canonicalize().unwrap();

        assert_eq!(Err(DatabaseError::CargoTomlNotFound),
            search_for_cargo_toml_directory(&temp_path));
    }

    #[test]
    fn convert_absolute_path_to_relative_works() {
        assert_eq!(PathBuf::from("migrations/12345_create_user"),
                   convert_absolute_path_to_relative(&PathBuf::from("projects/foo/migrations/12345_create_user"),
                                                     &PathBuf::from("projects/foo")));
        assert_eq!(PathBuf::from("../migrations/12345_create_user"),
                   convert_absolute_path_to_relative(&PathBuf::from("projects/foo/migrations/12345_create_user"),
                                                     &PathBuf::from("projects/foo/src")));
        assert_eq!(PathBuf::from("../../../migrations/12345_create_user"),
                   convert_absolute_path_to_relative(&PathBuf::from("projects/foo/migrations/12345_create_user"),
                                                     &PathBuf::from("projects/foo/src/controllers/errors")));
        assert_eq!(PathBuf::from("12345_create_user"),
                   convert_absolute_path_to_relative(&PathBuf::from("projects/foo/migrations/12345_create_user"),
                                                     &PathBuf::from("projects/foo/migrations")));
        assert_eq!(PathBuf::from("../12345_create_user"),
                   convert_absolute_path_to_relative(&PathBuf::from("projects/foo/migrations/12345_create_user"),
                                                     &PathBuf::from("projects/foo/migrations/67890_create_post")));
    }
}
<commit_msg>cli: Use UTC for default migration version<commit_after>extern crate chrono;
extern crate clap;
extern crate diesel;
extern crate dotenv;

mod database_error;
#[macro_use]
mod database;

use chrono::*;
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
#[cfg(feature = "postgres")]
use diesel::pg::PgConnection;
#[cfg(feature = "sqlite")]
use diesel::sqlite::SqliteConnection;
use diesel::migrations::schema::*;
use diesel::types::{FromSql, VarChar};
use diesel::{migrations, Connection, Insertable};
use std::error::Error;
use std::io::stdout;
use std::path::{PathBuf, Path};
use std::{env, fs};

use self::database_error::{DatabaseError, DatabaseResult};

fn main() {
    use self::dotenv::dotenv;
    dotenv().ok();

    let database_arg = Arg::with_name("DATABASE_URL")
        .long("database-url")
        .help("Specifies the database URL to connect to. Falls back to \
                   the DATABASE_URL environment variable if unspecified.")
        .global(true)
        .takes_value(true);

    let migration_subcommand = SubCommand::with_name("migration")
        .about("A group of commands for generating, running, and reverting \
                migrations.")
        .setting(AppSettings::VersionlessSubcommands)
        .arg(Arg::with_name("MIGRATION_DIRECTORY")
            .long("migration-dir")
            .help("The location of your migration directory. By default this \
                   will look for a directory called `migrations` in the \
                   current directory and its parents.")
            .takes_value(true)
            .global(true)
        ).subcommand(
            SubCommand::with_name("run")
                .about("Runs all pending migrations")
        ).subcommand(
            SubCommand::with_name("revert")
                .about("Reverts the latest run migration")
        ).subcommand(
            SubCommand::with_name("redo")
                .about("Reverts and re-runs the latest migration. Useful \
                      for testing that a migration can in fact be reverted.")
        ).subcommand(
            SubCommand::with_name("generate")
                .about("Generate a new migration with the given name, and \
                      the current timestamp as the version")
                .arg(Arg::with_name("MIGRATION_NAME")
                     .help("The name of the migration to create")
                     .required(true)
                 )
                .arg(Arg::with_name("MIGRATION_VERSION")
                     .long("version")
                     .help("The version number to use when generating the migration. \
                            Defaults to the current timestamp, which should suffice \
                            for most use cases.")
                     .takes_value(true)
                )
        ).setting(AppSettings::SubcommandRequiredElseHelp);

    let setup_subcommand = SubCommand::with_name("setup")
        .about("Creates the migrations directory, creates the database \
                specified in your DATABASE_URL, and runs existing migrations.");

    let database_subcommand = SubCommand::with_name("database")
        .about("A group of commands for setting up and resetting your database.")
        .setting(AppSettings::VersionlessSubcommands)
        .subcommand(
            SubCommand::with_name("setup")
                .about("Creates the database specified in your DATABASE_URL, \
                        and then runs any existing migrations.")
        ).subcommand(
            SubCommand::with_name("reset")
                .about("Resets your database by dropping the database specified \
                        in your DATABASE_URL and then running `diesel database setup`.")
        ).subcommand(
            SubCommand::with_name("drop")
                .about("Drops the database specified in your DATABASE_URL.")
                .setting(AppSettings::Hidden)
        ).setting(AppSettings::SubcommandRequiredElseHelp);

    let matches = App::new("diesel")
        .version(env!("CARGO_PKG_VERSION"))
        .setting(AppSettings::VersionlessSubcommands)
        .after_help("You can also run `diesel SUBCOMMAND -h` to get more information about that subcommand.")
        .arg(database_arg)
        .subcommand(migration_subcommand)
        .subcommand(setup_subcommand)
        .subcommand(database_subcommand)
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .get_matches();

    match matches.subcommand() {
        ("migration", Some(matches)) => run_migration_command(matches),
        ("setup", Some(matches)) => run_setup_command(matches),
        ("database", Some(matches)) => run_database_command(matches),
        _ => unreachable!("The cli parser should prevent reaching here"),
    }
}

fn run_migration_command(matches: &ArgMatches) {
    match matches.subcommand() {
        ("run", Some(_)) => {
            let database_url = database::database_url(matches);
            call_with_conn!(database_url, migrations::run_pending_migrations)
                .unwrap_or_else(handle_error);
        }
        ("revert", Some(_)) => {
            let database_url = database::database_url(matches);
            call_with_conn!(database_url, migrations::revert_latest_migration)
                .unwrap_or_else(handle_error);
        }
        ("redo", Some(_)) => {
            let database_url = database::database_url(matches);
            call_with_conn!(database_url, redo_latest_migration);
        }
        ("generate", Some(args)) => {
            let migration_name = args.value_of("MIGRATION_NAME").unwrap();
            let version = migration_version(args);
            let versioned_name = format!("{}_{}", version, migration_name);
            let migration_dir = migrations_dir(args).join(versioned_name);
            fs::create_dir(&migration_dir).unwrap();

            let migration_dir_relative = convert_absolute_path_to_relative(
                &migration_dir,
                &env::current_dir().unwrap()
            );

            let up_path = migration_dir.join("up.sql");
            println!("Creating {}", migration_dir_relative.join("up.sql").display());
            fs::File::create(up_path).unwrap();
            let down_path = migration_dir.join("down.sql");
            println!("Creating {}", migration_dir_relative.join("down.sql").display());
            fs::File::create(down_path).unwrap();
        }
        _ => unreachable!("The cli parser should prevent reaching here"),
    }
}

use std::fmt::Display;
fn migration_version<'a>(matches: &'a ArgMatches) -> Box<Display + 'a> {
    matches.value_of("MIGRATION_VERSION").map(|s| Box::new(s) as Box<Display>)
        .unwrap_or_else(|| Box::new(UTC::now().format("%Y%m%d%H%M%S")))
}

fn migrations_dir(matches: &ArgMatches) -> PathBuf {
    matches.value_of("MIGRATION_DIRECTORY")
        .map(PathBuf::from)
        .or_else(|| {
            env::var("MIGRATION_DIRECTORY").map(PathBuf::from).ok()
        }).unwrap_or_else(|| {
            migrations::find_migrations_directory()
                .unwrap_or_else(handle_error)
        })
}

fn run_setup_command(matches: &ArgMatches) {
    migrations::find_migrations_directory()
        .unwrap_or_else(|_| {
            create_migrations_directory()
                .unwrap_or_else(handle_error)
        });

    database::setup_database(matches).unwrap_or_else(handle_error);
}

fn run_database_command(matches: &ArgMatches) {
    match matches.subcommand() {
        ("setup", Some(args)) => database::setup_database(args).unwrap_or_else(handle_error),
        ("reset", Some(args)) => database::reset_database(args).unwrap_or_else(handle_error),
        ("drop", Some(args)) => database::drop_database_command(args).unwrap_or_else(handle_error),
        _ => unreachable!("The cli parser should prevent reaching here"),
    };
}

/// Looks for a migrations directory in the current path and all parent paths,
/// and creates one in the same directory as the Cargo.toml if it can't find
/// one. It also sticks a .gitkeep in the directory so git will pick it up.
/// Returns a `DatabaseError::CargoTomlNotFound` if no Cargo.toml is found.
fn create_migrations_directory() -> DatabaseResult<PathBuf> {
    let project_root = try!(find_project_root());
    println!("Creating migrations/ directory at: {}", project_root
                                                        .join("migrations")
                                                        .display());
    try!(fs::create_dir(project_root.join("migrations")));
    try!(fs::File::create(project_root.join("migrations/.gitkeep")));
    Ok(project_root)
}

fn find_project_root() -> DatabaseResult<PathBuf> {
    search_for_cargo_toml_directory(&try!(env::current_dir()))
}

/// Searches for the directory that holds the project's Cargo.toml, and returns
/// the path if it found it, or returns a `DatabaseError::CargoTomlNotFound`.
fn search_for_cargo_toml_directory(path: &Path) -> DatabaseResult<PathBuf> {
    let toml_path = path.join("Cargo.toml");
    if toml_path.is_file() {
        Ok(path.to_owned())
    } else {
        path.parent().map(search_for_cargo_toml_directory)
            .unwrap_or(Err(DatabaseError::CargoTomlNotFound))
    }
}

/// Reverts the most recent migration, and then runs it again, all in a
/// transaction. If either part fails, the transaction is not committed.
fn redo_latest_migration<Conn>(conn: &Conn) where
        Conn: Connection,
        String: FromSql<VarChar, Conn::Backend>,
        for<'a> &'a NewMigration<'a>:
            Insertable<__diesel_schema_migrations::table, Conn::Backend>,
{
    conn.transaction(|| {
        let reverted_version = try!(migrations::revert_latest_migration(conn));
        migrations::run_migration_with_version(conn, &reverted_version, &mut stdout())
    }).unwrap_or_else(handle_error);
}

fn handle_error<E: Error, T>(error: E) -> T {
    println!("{}", error);
    ::std::process::exit(1);
}

// Converts an absolute path to a relative path, with the restriction that the
// target path must be in the same directory or above the current path.
fn convert_absolute_path_to_relative(target_path: &Path, mut current_path: &Path)
    -> PathBuf
{
    let mut result = PathBuf::new();

    while !target_path.starts_with(current_path) {
        result.push("..");
        match current_path.parent() {
            Some(parent) => current_path = parent,
            None => return target_path.into(),
        }
    }

    result.join(target_path.strip_prefix(current_path).unwrap())
}

#[cfg(test)]
mod tests {
    extern crate tempdir;

    use database_error::DatabaseError;

    use self::tempdir::TempDir;

    use std::fs;
    use std::path::PathBuf;

    use super::convert_absolute_path_to_relative;
    use super::search_for_cargo_toml_directory;

    #[test]
    fn toml_directory_find_cargo_toml() {
        let dir = TempDir::new("diesel").unwrap();
        let temp_path = dir.path().canonicalize().unwrap();
        let toml_path = temp_path.join("Cargo.toml");

        fs::File::create(&toml_path).unwrap();

        assert_eq!(Ok(temp_path.clone()), search_for_cargo_toml_directory(&temp_path));
    }

    #[test]
    fn cargo_toml_not_found_if_no_cargo_toml() {
        let dir = TempDir::new("diesel").unwrap();
        let temp_path = dir.path().canonicalize().unwrap();

        assert_eq!(Err(DatabaseError::CargoTomlNotFound),
            search_for_cargo_toml_directory(&temp_path));
    }

    #[test]
    fn convert_absolute_path_to_relative_works() {
        assert_eq!(PathBuf::from("migrations/12345_create_user"),
                   convert_absolute_path_to_relative(&PathBuf::from("projects/foo/migrations/12345_create_user"),
                                                     &PathBuf::from("projects/foo")));
        assert_eq!(PathBuf::from("../migrations/12345_create_user"),
                   convert_absolute_path_to_relative(&PathBuf::from("projects/foo/migrations/12345_create_user"),
                                                     &PathBuf::from("projects/foo/src")));
        assert_eq!(PathBuf::from("../../../migrations/12345_create_user"),
                   convert_absolute_path_to_relative(&PathBuf::from("projects/foo/migrations/12345_create_user"),
                                                     &PathBuf::from("projects/foo/src/controllers/errors")));
        assert_eq!(PathBuf::from("12345_create_user"),
                   convert_absolute_path_to_relative(&PathBuf::from("projects/foo/migrations/12345_create_user"),
                                                     &PathBuf::from("projects/foo/migrations")));
        assert_eq!(PathBuf::from("../12345_create_user"),
                   convert_absolute_path_to_relative(&PathBuf::from("projects/foo/migrations/12345_create_user"),
                                                     &PathBuf::from("projects/foo/migrations/67890_create_post")));
    }
}
<|endoftext|>


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

//! Kata OS Security Coordinator component support.

// Code here binds the camkes component to the rust code.
#![no_std]
#![allow(clippy::missing_safety_doc)]

use core::slice;
use kata_os_common::camkes::Camkes;
use kata_os_common::cspace_slot::CSpaceSlot;
use kata_os_common::sel4_sys;
use kata_os_common::slot_allocator::KATA_CSPACE_SLOTS;
use kata_security_coordinator::KATA_SECURITY;
use kata_security_interface::*;
use log::trace;

use SecurityRequestError::*;

use sel4_sys::seL4_CPtr;

static mut CAMKES: Camkes = Camkes::new("SecurityCoordinator");
static mut SECURITY_RECV_SLOT: seL4_CPtr = 0;

#[no_mangle]
pub unsafe extern "C" fn pre_init() {
    static mut HEAP_MEMORY: [u8; 8 * 1024] = [0; 8 * 1024];
    // NB: set to max; the LoggerInterface will filter
    CAMKES.pre_init(log::LevelFilter::Trace, &mut HEAP_MEMORY);

    // Complete KATA_SECURITY setup after Global allocator is setup.
    KATA_SECURITY.init();

    SECURITY_RECV_SLOT = KATA_CSPACE_SLOTS.alloc(1).unwrap();
}

#[no_mangle]
pub unsafe extern "C" fn security__init() {
    // Point the receive path to the well-known empty slot. This will be
    // used to receive CNode's from clients for install requests.
    //
    // NB: this must be done here (rather than someplace like pre_init)
    // so it's in the context of the SecurityCoordinatorInterface thread
    // (so we write the correct ipc buffer).
    let path = &Camkes::top_level_path(SECURITY_RECV_SLOT);
    CAMKES.init_recv_path(path);
    Camkes::debug_assert_slot_empty("security__init", path);
}

fn serialize_failure(e: postcard::Error) -> SecurityRequestError {
    trace!("serialize failed: {:?}", e);
    SreBundleDataInvalid
}
fn deserialize_failure(e: postcard::Error) -> SecurityRequestError {
    trace!("deserialize failed: {:?}", e);
    SreDeserializeFailed
}

fn echo_request(
    request_buffer: &[u8],
    reply_buffer: &mut [u8],
) -> Result<(), SecurityRequestError> {
    let request =
        postcard::from_bytes::<EchoRequest>(request_buffer).map_err(deserialize_failure)?;

    trace!("ECHO {:?}", request.value);
    // NB: cheat, bypass serde
    reply_buffer[0..request.value.len()].copy_from_slice(request.value);
    Ok(())
}

fn install_request(
    request_buffer: &[u8],
    reply_buffer: &mut [u8],
) -> Result<(), SecurityRequestError> {
    let recv_path = unsafe { CAMKES.get_current_recv_path() };
    Camkes::debug_assert_slot_cnode("install_request", &recv_path);

    let mut request =
        postcard::from_bytes::<InstallRequest>(request_buffer).map_err(deserialize_failure)?; // XXX clear_path

    // Move the container CNode so it's not clobbered.
    // XXX who should be responsible for this
    let mut container_slot = CSpaceSlot::new();
    container_slot
        .move_to(recv_path.0, recv_path.1, recv_path.2 as u8)
        .map_err(|_| SecurityRequestError::SreCapMoveFailed)?; // XXX expect?
    request.set_container_cap(container_slot.slot);
    container_slot.release();

    let bundle_id = unsafe { KATA_SECURITY.install(&request.pkg_contents) }?;
    let _ = postcard::to_slice(
        &InstallResponse {
            bundle_id: &bundle_id,
        },
        reply_buffer,
    )
    .map_err(serialize_failure)?;
    Ok(())
}

fn uninstall_request(
    request_buffer: &[u8],
    _reply_buffer: &mut [u8],
) -> Result<(), SecurityRequestError> {
    let request =
        postcard::from_bytes::<UninstallRequest>(request_buffer).map_err(deserialize_failure)?;

    trace!("UNINSTALL {}", request.bundle_id);
    unsafe { KATA_SECURITY.uninstall(request.bundle_id) }
}

fn size_buffer_request(
    request_buffer: &[u8],
    reply_buffer: &mut [u8],
) -> Result<(), SecurityRequestError> {
    let request =
        postcard::from_bytes::<SizeBufferRequest>(request_buffer).map_err(deserialize_failure)?;

    trace!("SIZE BUFFER bundle_id {}", request.bundle_id);
    let buffer_size = unsafe { KATA_SECURITY.size_buffer(request.bundle_id) }?;
    let _ = postcard::to_slice(&SizeBufferResponse { buffer_size }, reply_buffer)
        .map_err(serialize_failure)?;
    Ok(())
}

fn get_manifest_request(
    request_buffer: &[u8],
    reply_buffer: &mut [u8],
) -> Result<(), SecurityRequestError> {
    let request =
        postcard::from_bytes::<GetManifestRequest>(request_buffer).map_err(deserialize_failure)?;

    trace!("GET MANIFEST bundle_id {}", request.bundle_id);
    let manifest = unsafe { KATA_SECURITY.get_manifest(request.bundle_id) }?;
    let _ = postcard::to_slice(
        &GetManifestResponse {
            manifest: &manifest,
        },
        reply_buffer,
    )
    .map_err(serialize_failure)?;
    Ok(())
}

fn load_application_request(
    request_buffer: &[u8],
    reply_buffer: &mut [u8],
) -> Result<(), SecurityRequestError> {
    let request = postcard::from_bytes::<LoadApplicationRequest>(request_buffer)
        .map_err(deserialize_failure)?;

    trace!("LOAD APPLICATION bundle_id {}", request.bundle_id);
    let bundle_frames = unsafe { KATA_SECURITY.load_application(request.bundle_id) }?;
    // TODO(sleffler): maybe rearrange to eliminate clone
    postcard::to_slice(
        &LoadApplicationResponse {
            bundle_frames: bundle_frames.clone(),
        },
        reply_buffer,
    )
    .map_err(serialize_failure)?;
    trace!("LOAD APPLICATION -> {}", bundle_frames);
    // Cleanup allocated slot & mark cap for release after reply completes.
    Camkes::set_reply_cap_release(bundle_frames.cnode);
    Ok(())
}

fn load_model_request(
    request_buffer: &[u8],
    reply_buffer: &mut [u8],
) -> Result<(), SecurityRequestError> {
    let request =
        postcard::from_bytes::<LoadModelRequest>(request_buffer).map_err(deserialize_failure)?;

    let model_frames = unsafe { KATA_SECURITY.load_model(request.bundle_id, request.model_id) }?;
    // TODO(sleffler): maybe rearrange to eliminate clone
    let _ = postcard::to_slice(
        &LoadApplicationResponse {
            bundle_frames: model_frames.clone(),
        },
        reply_buffer,
    )
    .map_err(serialize_failure)?;
    trace!("LOAD MODEL -> {}", model_frames);
    // Cleanup allocated slot & mark cap for release after reply completes.
    Camkes::set_reply_cap_release(model_frames.cnode);
    Ok(())
}

fn read_key_request(
    request_buffer: &[u8],
    reply_buffer: &mut [u8],
) -> Result<(), SecurityRequestError> {
    let request =
        postcard::from_bytes::<ReadKeyRequest>(request_buffer).map_err(deserialize_failure)?;

    trace!("READ KEY bundle_id {} key {}", request.bundle_id, request.key);
    let value = unsafe { KATA_SECURITY.read_key(request.bundle_id, request.key) }?;
    let _ = postcard::to_slice(&ReadKeyResponse { value }, reply_buffer).map_err(serialize_failure);
    Ok(())
}

fn write_key_request(
    request_buffer: &[u8],
    _reply_buffer: &mut [u8],
) -> Result<(), SecurityRequestError> {
    let request =
        postcard::from_bytes::<WriteKeyRequest>(request_buffer).map_err(deserialize_failure)?;

    trace!(
        "WRITE KEY bundle_id {} key {} value {:?}",
        request.bundle_id,
        request.key,
        request.value
    );
    // NB: the serialized data are variable length so copy to convert
    let mut keyval = [0u8; KEY_VALUE_DATA_SIZE];
    keyval[..request.value.len()].copy_from_slice(request.value);
    unsafe { KATA_SECURITY.write_key(request.bundle_id, request.key, &keyval) }
}

fn delete_key_request(
    request_buffer: &[u8],
    _reply_buffer: &mut [u8],
) -> Result<(), SecurityRequestError> {
    let request =
        postcard::from_bytes::<DeleteKeyRequest>(request_buffer).map_err(deserialize_failure)?;

    trace!("DELETE KEY bundle_id {} key {}", request.bundle_id, request.key);
    unsafe { KATA_SECURITY.delete_key(request.bundle_id, request.key) }
}

fn test_mailbox_request() -> Result<(), SecurityRequestError> {
    trace!("TEST MAILBOX");
    unsafe { KATA_SECURITY.test_mailbox() }
}

fn capscan_request() -> Result<(), SecurityRequestError> {
    let _ = Camkes::capscan();
    Ok(())
}

#[no_mangle]
pub unsafe extern "C" fn security_request(
    c_request: SecurityRequest,
    c_request_buffer_len: u32,
    c_request_buffer: *const u8,
    c_reply_buffer: *mut SecurityReplyData,
) -> SecurityRequestError {
    let request_buffer = slice::from_raw_parts(c_request_buffer, c_request_buffer_len as usize);
    let reply_buffer = &mut (*c_reply_buffer)[..];
    match c_request {
        SecurityRequest::SrEcho => echo_request(request_buffer, reply_buffer),
        SecurityRequest::SrInstall => install_request(request_buffer, reply_buffer),
        SecurityRequest::SrUninstall => uninstall_request(request_buffer, reply_buffer),
        SecurityRequest::SrSizeBuffer => size_buffer_request(request_buffer, reply_buffer),
        SecurityRequest::SrGetManifest => get_manifest_request(request_buffer, reply_buffer),
        SecurityRequest::SrLoadApplication => {
            load_application_request(request_buffer, reply_buffer)
        }
        SecurityRequest::SrLoadModel => load_model_request(request_buffer, reply_buffer),
        SecurityRequest::SrReadKey => read_key_request(request_buffer, reply_buffer),
        SecurityRequest::SrWriteKey => write_key_request(request_buffer, reply_buffer),
        SecurityRequest::SrDeleteKey => delete_key_request(request_buffer, reply_buffer),
        SecurityRequest::SrTestMailbox => test_mailbox_request(),
        SecurityRequest::SrCapScan => capscan_request(),
    }
    .map_or_else(|e| e, |_v| SecurityRequestError::SreSuccess)
}
<commit_msg>kata-security-component: minor cleanups<commit_after>// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Kata OS Security Coordinator component support.

// Code here binds the camkes component to the rust code.
#![no_std]
#![allow(clippy::missing_safety_doc)]

use core::slice;
use kata_os_common::camkes::Camkes;
use kata_os_common::cspace_slot::CSpaceSlot;
use kata_os_common::sel4_sys;
use kata_security_coordinator::KATA_SECURITY;
use kata_security_interface::*;
use log::trace;

use SecurityRequestError::*;

use sel4_sys::seL4_CPtr;

static mut CAMKES: Camkes = Camkes::new("SecurityCoordinator");
static mut SECURITY_RECV_SLOT: seL4_CPtr = 0;

#[no_mangle]
pub unsafe extern "C" fn pre_init() {
    static mut HEAP_MEMORY: [u8; 8 * 1024] = [0; 8 * 1024];
    // NB: set to max; the LoggerInterface will filter
    CAMKES.pre_init(log::LevelFilter::Trace, &mut HEAP_MEMORY);

    // Complete KATA_SECURITY setup after Global allocator is setup.
    KATA_SECURITY.init();

    SECURITY_RECV_SLOT = CSpaceSlot::new().release();
}

#[no_mangle]
pub unsafe extern "C" fn security__init() {
    // Point the receive path to the well-known empty slot. This will be
    // used to receive CNode's from clients for install requests.
    //
    // NB: this must be done here (rather than someplace like pre_init)
    // so it's in the context of the SecurityCoordinatorInterface thread
    // (so we write the correct ipc buffer).
    let path = &Camkes::top_level_path(SECURITY_RECV_SLOT);
    CAMKES.init_recv_path(path);
    Camkes::debug_assert_slot_empty("security__init", path);
}

fn serialize_failure(e: postcard::Error) -> SecurityRequestError {
    trace!("serialize failed: {:?}", e);
    SreBundleDataInvalid
}
fn deserialize_failure(e: postcard::Error) -> SecurityRequestError {
    trace!("deserialize failed: {:?}", e);
    SreDeserializeFailed
}

fn echo_request(
    request_buffer: &[u8],
    reply_buffer: &mut [u8],
) -> Result<(), SecurityRequestError> {
    let request =
        postcard::from_bytes::<EchoRequest>(request_buffer).map_err(deserialize_failure)?;

    trace!("ECHO {:?}", request.value);
    // NB: cheat, bypass serde
    reply_buffer[0..request.value.len()].copy_from_slice(request.value);
    Ok(())
}

fn install_request(
    request_buffer: &[u8],
    reply_buffer: &mut [u8],
) -> Result<(), SecurityRequestError> {
    let recv_path = unsafe { CAMKES.get_current_recv_path() };
    Camkes::debug_assert_slot_cnode("install_request", &recv_path);

    let mut request =
        postcard::from_bytes::<InstallRequest>(request_buffer).map_err(deserialize_failure)?; // XXX clear_path

    // Move the container CNode so it's not clobbered.
    let mut container_slot = CSpaceSlot::new();
    container_slot
        .move_to(recv_path.0, recv_path.1, recv_path.2 as u8)
        .map_err(|_| SecurityRequestError::SreCapMoveFailed)?; // XXX expect?
    request.set_container_cap(container_slot.release());

    let bundle_id = unsafe { KATA_SECURITY.install(&request.pkg_contents) }?;
    let _ = postcard::to_slice(
        &InstallResponse {
            bundle_id: &bundle_id,
        },
        reply_buffer,
    )
    .map_err(serialize_failure)?;
    Ok(())
}

fn uninstall_request(
    request_buffer: &[u8],
    _reply_buffer: &mut [u8],
) -> Result<(), SecurityRequestError> {
    let request =
        postcard::from_bytes::<UninstallRequest>(request_buffer).map_err(deserialize_failure)?;

    trace!("UNINSTALL {}", request.bundle_id);
    unsafe { KATA_SECURITY.uninstall(request.bundle_id) }
}

fn size_buffer_request(
    request_buffer: &[u8],
    reply_buffer: &mut [u8],
) -> Result<(), SecurityRequestError> {
    let request =
        postcard::from_bytes::<SizeBufferRequest>(request_buffer).map_err(deserialize_failure)?;

    trace!("SIZE BUFFER bundle_id {}", request.bundle_id);
    let buffer_size = unsafe { KATA_SECURITY.size_buffer(request.bundle_id) }?;
    let _ = postcard::to_slice(&SizeBufferResponse { buffer_size }, reply_buffer)
        .map_err(serialize_failure)?;
    Ok(())
}

fn get_manifest_request(
    request_buffer: &[u8],
    reply_buffer: &mut [u8],
) -> Result<(), SecurityRequestError> {
    let request =
        postcard::from_bytes::<GetManifestRequest>(request_buffer).map_err(deserialize_failure)?;

    trace!("GET MANIFEST bundle_id {}", request.bundle_id);
    let manifest = unsafe { KATA_SECURITY.get_manifest(request.bundle_id) }?;
    let _ = postcard::to_slice(
        &GetManifestResponse {
            manifest: &manifest,
        },
        reply_buffer,
    )
    .map_err(serialize_failure)?;
    Ok(())
}

fn load_application_request(
    request_buffer: &[u8],
    reply_buffer: &mut [u8],
) -> Result<(), SecurityRequestError> {
    let request = postcard::from_bytes::<LoadApplicationRequest>(request_buffer)
        .map_err(deserialize_failure)?;

    trace!("LOAD APPLICATION bundle_id {}", request.bundle_id);
    let bundle_frames = unsafe { KATA_SECURITY.load_application(request.bundle_id) }?;
    // TODO(sleffler): maybe rearrange to eliminate clone
    postcard::to_slice(
        &LoadApplicationResponse {
            bundle_frames: bundle_frames.clone(),
        },
        reply_buffer,
    )
    .map_err(serialize_failure)?;
    trace!("LOAD APPLICATION -> {}", bundle_frames);
    // Cleanup allocated slot & mark cap for release after reply completes.
    Camkes::set_reply_cap_release(bundle_frames.cnode);
    Ok(())
}

fn load_model_request(
    request_buffer: &[u8],
    reply_buffer: &mut [u8],
) -> Result<(), SecurityRequestError> {
    let request =
        postcard::from_bytes::<LoadModelRequest>(request_buffer).map_err(deserialize_failure)?;

    let model_frames = unsafe { KATA_SECURITY.load_model(request.bundle_id, request.model_id) }?;
    // TODO(sleffler): maybe rearrange to eliminate clone
    let _ = postcard::to_slice(
        &LoadApplicationResponse {
            bundle_frames: model_frames.clone(),
        },
        reply_buffer,
    )
    .map_err(serialize_failure)?;
    trace!("LOAD MODEL -> {}", model_frames);
    // Cleanup allocated slot & mark cap for release after reply completes.
    Camkes::set_reply_cap_release(model_frames.cnode);
    Ok(())
}

fn read_key_request(
    request_buffer: &[u8],
    reply_buffer: &mut [u8],
) -> Result<(), SecurityRequestError> {
    let request =
        postcard::from_bytes::<ReadKeyRequest>(request_buffer).map_err(deserialize_failure)?;

    trace!("READ KEY bundle_id {} key {}", request.bundle_id, request.key);
    let value = unsafe { KATA_SECURITY.read_key(request.bundle_id, request.key) }?;
    let _ = postcard::to_slice(&ReadKeyResponse { value }, reply_buffer).map_err(serialize_failure);
    Ok(())
}

fn write_key_request(
    request_buffer: &[u8],
    _reply_buffer: &mut [u8],
) -> Result<(), SecurityRequestError> {
    let request =
        postcard::from_bytes::<WriteKeyRequest>(request_buffer).map_err(deserialize_failure)?;

    trace!(
        "WRITE KEY bundle_id {} key {} value {:?}",
        request.bundle_id,
        request.key,
        request.value
    );
    // NB: the serialized data are variable length so copy to convert
    let mut keyval = [0u8; KEY_VALUE_DATA_SIZE];
    keyval[..request.value.len()].copy_from_slice(request.value);
    unsafe { KATA_SECURITY.write_key(request.bundle_id, request.key, &keyval) }
}

fn delete_key_request(
    request_buffer: &[u8],
    _reply_buffer: &mut [u8],
) -> Result<(), SecurityRequestError> {
    let request =
        postcard::from_bytes::<DeleteKeyRequest>(request_buffer).map_err(deserialize_failure)?;

    trace!("DELETE KEY bundle_id {} key {}", request.bundle_id, request.key);
    unsafe { KATA_SECURITY.delete_key(request.bundle_id, request.key) }
}

fn test_mailbox_request() -> Result<(), SecurityRequestError> {
    trace!("TEST MAILBOX");
    unsafe { KATA_SECURITY.test_mailbox() }
}

fn capscan_request() -> Result<(), SecurityRequestError> {
    let _ = Camkes::capscan();
    Ok(())
}

#[no_mangle]
pub unsafe extern "C" fn security_request(
    c_request: SecurityRequest,
    c_request_buffer_len: u32,
    c_request_buffer: *const u8,
    c_reply_buffer: *mut SecurityReplyData,
) -> SecurityRequestError {
    let request_buffer = slice::from_raw_parts(c_request_buffer, c_request_buffer_len as usize);
    let reply_buffer = &mut (*c_reply_buffer)[..];
    match c_request {
        SecurityRequest::SrEcho => echo_request(request_buffer, reply_buffer),
        SecurityRequest::SrInstall => install_request(request_buffer, reply_buffer),
        SecurityRequest::SrUninstall => uninstall_request(request_buffer, reply_buffer),
        SecurityRequest::SrSizeBuffer => size_buffer_request(request_buffer, reply_buffer),
        SecurityRequest::SrGetManifest => get_manifest_request(request_buffer, reply_buffer),
        SecurityRequest::SrLoadApplication => {
            load_application_request(request_buffer, reply_buffer)
        }
        SecurityRequest::SrLoadModel => load_model_request(request_buffer, reply_buffer),
        SecurityRequest::SrReadKey => read_key_request(request_buffer, reply_buffer),
        SecurityRequest::SrWriteKey => write_key_request(request_buffer, reply_buffer),
        SecurityRequest::SrDeleteKey => delete_key_request(request_buffer, reply_buffer),
        SecurityRequest::SrTestMailbox => test_mailbox_request(),
        SecurityRequest::SrCapScan => capscan_request(),
    }
    .map_or_else(|e| e, |_v| SecurityRequestError::SreSuccess)
}
<|endoftext|>


<commit_before>use std::path::PathBuf;

use git2::Repository;
use toml::Value;

use libimagerror::into::IntoError;
use libimagerror::trace::trace_error;
use libimagstore::hook::error::CustomData;
use libimagstore::hook::error::HookErrorKind as HEK;
use libimagstore::hook::result::HookResult;
use libimagutil::debug_result::*;

use vcs::git::action::StoreAction;
use vcs::git::result::Result;
use vcs::git::error::{MapErrInto, GitHookErrorKind as GHEK};

/// Runtime object for git hook implementations.
///
/// Contains some utility functionality to hold the repository and the configuration for the hooks.
pub struct Runtime {
    repository: Option<Repository>,
    config: Option<Value>,
}

impl Runtime {

    /// Build a `Runtime` object, pass the store path to build the `Repository` instance the
    /// `Runtime` has to contain.
    ///
    /// If the building of the `Repository` fails, this function `trace_error()`s the error and
    /// returns a `Runtime` object that does _not_ contain a `Repository`.
    pub fn new(storepath: &PathBuf) -> Runtime {
        Runtime {
            repository: match Repository::open(storepath) {
                Ok(r) => Some(r),
                Err(e) => {
                    trace_error(&e);
                    None
                },
            },

            config: None,
        }
    }

    /// Set the configuration for the `Runtime`. Always returns `Ok(())`.
    pub fn set_config(&mut self, cfg: &Value) -> Result<()> {
        self.config = Some(cfg.clone());
        Ok(())
    }

    /// Check whether the `Runtime` has a `Repository`
    pub fn has_repository(&self) -> bool {
        self.repository.is_some()
    }

    /// Check whether the `Runtime` has a configuration
    pub fn has_config(&self) -> bool {
        self.config.is_some()
    }

    /// Get the the config value by reference or get an `Err()` which can be returned to the callee
    /// of the Hook.
    ///
    /// The `action` Argument is required in case of `Err()` so the error message can be build
    /// correctly.
    pub fn config_value_or_err(&self, action: &StoreAction) -> HookResult<&Value> {
        self.config
            .as_ref()
            .ok_or(GHEK::NoConfigError.into_error())
            .map_err_into(GHEK::ConfigError)
            .map_err(Box::new)
            .map_err(|e| HEK::HookExecutionError.into_error_with_cause(e))
            .map_err(|mut e| e.with_custom_data(CustomData::default().aborting(false)))
            .map_dbg_err(|_| {
                format!("[GIT {} HOOK]: Couldn't get Value object from config", action.uppercase())
            })
    }

    /// Get the `Repository` object from the `Runtime` or an `Err()` that can be returned to the
    /// callee of the Hook.
    ///
    /// The `action` Argument is required in case of `Err()` so the error message can be build
    /// correctly.
    pub fn repository(&self, action: &StoreAction) -> HookResult<&Repository> {
        use vcs::git::error::MapIntoHookError;

        debug!("[GIT {} HOOK]: Getting repository", action.uppercase());
        self.repository
            .as_ref()
            .ok_or(GHEK::MkRepo.into_error())
            .map_err_into(GHEK::RepositoryError)
            .map_into_hook_error()
            .map_dbg_err(|_| format!("[GIT {} HOOK]: Couldn't fetch Repository", action.uppercase()))
            .map_dbg(|_| format!("[GIT {} HOOK]: Repository object fetched", action.uppercase()))
    }

    /// Ensure that the branch that is put in the configuration file is checked out, if any.
    pub fn ensure_cfg_branch_is_checked_out(&self, action: &StoreAction) -> HookResult<()> {
        use vcs::git::config::ensure_branch;

        debug!("[GIT CREATE HOOK]: Ensuring branch checkout");
        let head = try!(self
                        .repository(action)
                        .and_then(|r| {
                            debug!("Repository fetched, getting head");
                            r.head()
                                .map_dbg_err_str("Couldn't fetch HEAD")
                                .map_dbg_err(|e| format!("\tbecause = {:?}", e))
                                .map_err_into(GHEK::HeadFetchError)
                                .map_err(|e| e.into())
                        }));
        debug!("HEAD fetched");

        // TODO: Fail if not on branch? hmmh... I'm not sure
        if !head.is_branch() {
            debug!("HEAD is not a branch");
            return Err(GHEK::NotOnBranch.into_error().into());
        }
        debug!("HEAD is a branch");

        // Check out appropriate branch ... or fail
        match ensure_branch(self.config.as_ref()) {
            Ok(Some(s)) => {
                debug!("We have to ensure branch: {}", s);
                match head.name().map(|name| {
                    debug!("{} == {}", name, s);
                    name == s
                }) {
                    Some(b) => {
                        if b {
                            debug!("Branch already checked out.");
                            Ok(())
                        } else {
                            debug!("Branch not checked out.");
                            unimplemented!()
                        }
                    },

                    None => Err(GHEK::RepositoryBranchNameFetchingError.into_error())
                        .map_err_into(GHEK::RepositoryBranchError)
                        .map_err_into(GHEK::RepositoryError),
                }
            },
            Ok(None) => {
                debug!("No branch to checkout");
                Ok(())
            },

            Err(e) => Err(e).map_err_into(GHEK::RepositoryError),
        }
        .map_err(Box::new)
        .map_err(|e| HEK::HookExecutionError.into_error_with_cause(e))
        .map_dbg_str("[GIT CREATE HOOK]: Branch checked out")
    }

}

<commit_msg>Add helper fn to check whether the repository WD is dirty<commit_after>use std::path::PathBuf;

use git2::{Index, Repository};
use toml::Value;

use libimagerror::into::IntoError;
use libimagerror::trace::trace_error;
use libimagstore::hook::error::CustomData;
use libimagstore::hook::error::HookErrorKind as HEK;
use libimagstore::hook::result::HookResult;
use libimagutil::debug_result::*;

use vcs::git::action::StoreAction;
use vcs::git::result::Result;
use vcs::git::error::{MapErrInto, GitHookErrorKind as GHEK};

/// Runtime object for git hook implementations.
///
/// Contains some utility functionality to hold the repository and the configuration for the hooks.
pub struct Runtime {
    repository: Option<Repository>,
    config: Option<Value>,
}

impl Runtime {

    /// Build a `Runtime` object, pass the store path to build the `Repository` instance the
    /// `Runtime` has to contain.
    ///
    /// If the building of the `Repository` fails, this function `trace_error()`s the error and
    /// returns a `Runtime` object that does _not_ contain a `Repository`.
    pub fn new(storepath: &PathBuf) -> Runtime {
        Runtime {
            repository: match Repository::open(storepath) {
                Ok(r) => Some(r),
                Err(e) => {
                    trace_error(&e);
                    None
                },
            },

            config: None,
        }
    }

    /// Set the configuration for the `Runtime`. Always returns `Ok(())`.
    pub fn set_config(&mut self, cfg: &Value) -> Result<()> {
        self.config = Some(cfg.clone());
        Ok(())
    }

    /// Check whether the `Runtime` has a `Repository`
    pub fn has_repository(&self) -> bool {
        self.repository.is_some()
    }

    /// Check whether the `Runtime` has a configuration
    pub fn has_config(&self) -> bool {
        self.config.is_some()
    }

    /// Get the the config value by reference or get an `Err()` which can be returned to the callee
    /// of the Hook.
    ///
    /// The `action` Argument is required in case of `Err()` so the error message can be build
    /// correctly.
    pub fn config_value_or_err(&self, action: &StoreAction) -> HookResult<&Value> {
        self.config
            .as_ref()
            .ok_or(GHEK::NoConfigError.into_error())
            .map_err_into(GHEK::ConfigError)
            .map_err(Box::new)
            .map_err(|e| HEK::HookExecutionError.into_error_with_cause(e))
            .map_err(|mut e| e.with_custom_data(CustomData::default().aborting(false)))
            .map_dbg_err(|_| {
                format!("[GIT {} HOOK]: Couldn't get Value object from config", action.uppercase())
            })
    }

    /// Get the `Repository` object from the `Runtime` or an `Err()` that can be returned to the
    /// callee of the Hook.
    ///
    /// The `action` Argument is required in case of `Err()` so the error message can be build
    /// correctly.
    pub fn repository(&self, action: &StoreAction) -> HookResult<&Repository> {
        use vcs::git::error::MapIntoHookError;

        debug!("[GIT {} HOOK]: Getting repository", action.uppercase());
        self.repository
            .as_ref()
            .ok_or(GHEK::MkRepo.into_error())
            .map_err_into(GHEK::RepositoryError)
            .map_into_hook_error()
            .map_dbg_err(|_| format!("[GIT {} HOOK]: Couldn't fetch Repository", action.uppercase()))
            .map_dbg(|_| format!("[GIT {} HOOK]: Repository object fetched", action.uppercase()))
    }

    /// Ensure that the branch that is put in the configuration file is checked out, if any.
    pub fn ensure_cfg_branch_is_checked_out(&self, action: &StoreAction) -> HookResult<()> {
        use vcs::git::config::ensure_branch;

        debug!("[GIT CREATE HOOK]: Ensuring branch checkout");
        let head = try!(self
                        .repository(action)
                        .and_then(|r| {
                            debug!("Repository fetched, getting head");
                            r.head()
                                .map_dbg_err_str("Couldn't fetch HEAD")
                                .map_dbg_err(|e| format!("\tbecause = {:?}", e))
                                .map_err_into(GHEK::HeadFetchError)
                                .map_err(|e| e.into())
                        }));
        debug!("HEAD fetched");

        // TODO: Fail if not on branch? hmmh... I'm not sure
        if !head.is_branch() {
            debug!("HEAD is not a branch");
            return Err(GHEK::NotOnBranch.into_error().into());
        }
        debug!("HEAD is a branch");

        // Check out appropriate branch ... or fail
        match ensure_branch(self.config.as_ref()) {
            Ok(Some(s)) => {
                debug!("We have to ensure branch: {}", s);
                match head.name().map(|name| {
                    debug!("{} == {}", name, s);
                    name == s
                }) {
                    Some(b) => {
                        if b {
                            debug!("Branch already checked out.");
                            Ok(())
                        } else {
                            debug!("Branch not checked out.");
                            unimplemented!()
                        }
                    },

                    None => Err(GHEK::RepositoryBranchNameFetchingError.into_error())
                        .map_err_into(GHEK::RepositoryBranchError)
                        .map_err_into(GHEK::RepositoryError),
                }
            },
            Ok(None) => {
                debug!("No branch to checkout");
                Ok(())
            },

            Err(e) => Err(e).map_err_into(GHEK::RepositoryError),
        }
        .map_err(Box::new)
        .map_err(|e| HEK::HookExecutionError.into_error_with_cause(e))
        .map_dbg_str("[GIT CREATE HOOK]: Branch checked out")
    }

    /// Check whether the WD is "dirty" - whether there is a diff to the repository
    /// This function returns false if there is no `Repository` object in the `Runtime`
    pub fn repo_is_dirty(&self, index: &Index) -> bool {
        match self.repository.as_ref() {
            Some(repo) => {
                repo.diff_index_to_workdir(Some(index), None)
                    .map_dbg_str("Fetched diff: Index <-> WD")
                    .map_dbg_err_str("Failed to fetch diff: Index <-> WD")
                    .map(|diff| diff.deltas().count() != 0)
                    .unwrap_or(false)
            },

            None => {
                debug!("No repository: Cannot fetch diff: Index <-> WD");
                false
            }
        }

    }

}

<|endoftext|>


<commit_before>use alloc::boxed::Box;

use collections::String;
use collections::Vec;

use common::event::{self, Event, EventOption};

use core::{cmp, mem};

use drivers::io::{Io, Pio};

use graphics::color::Color;
use graphics::display::Display;

use sync::WaitQueue;

fn ansi_color(value: u8) -> Color {
    match value {
        0 => Color::new(0x00, 0x00, 0x00),
        1 => Color::new(0x80, 0x00, 0x00),
        2 => Color::new(0x00, 0x80, 0x00),
        3 => Color::new(0x80, 0x80, 0x00),
        4 => Color::new(0x00, 0x00, 0x80),
        5 => Color::new(0x80, 0x00, 0x80),
        6 => Color::new(0x00, 0x80, 0x80),
        7 => Color::new(0xc0, 0xc0, 0xc0),
        8 => Color::new(0x80, 0x80, 0x80),
        9 => Color::new(0xff, 0x00, 0x00),
        10 => Color::new(0x00, 0xff, 0x00),
        11 => Color::new(0xff, 0xff, 0x00),
        12 => Color::new(0x00, 0x00, 0xff),
        13 => Color::new(0xff, 0x00, 0xff),
        14 => Color::new(0x00, 0xff, 0xff),
        15 => Color::new(0xff, 0xff, 0xff),
        16 ... 231 => {
            let convert = |value: u8| -> u8 {
                match value {
                    0 => 0,
                    _ => value * 0x28 + 0x28
                }
            };

            let r = convert((value - 16)/36 % 6);
            let g = convert((value - 16)/6 % 6);
            let b = convert((value - 16) % 6);
            Color::new(r, g, b)
        },
        232 ... 255 => {
            let gray = (value - 232) * 10 + 8;
            Color::new(gray, gray, gray)
        },
        _ => Color::new(0, 0, 0)
    }
}

pub struct Console {
    pub display: Option<Box<Display>>,
    pub point_x: usize,
    pub point_y: usize,
    pub foreground: Color,
    pub background: Color,
    pub draw: bool,
    pub redraw: bool,
    pub command: String,
    pub commands: WaitQueue<String>,
    pub escape: bool,
    pub escape_sequence: bool,
    pub sequence: Vec<String>,
    pub raw_mode: bool,
}

impl Console {
    pub fn new() -> Console {
        Console {
            display: Display::root(),
            point_x: 0,
            point_y: 0,
            foreground: ansi_color(7),
            background: ansi_color(0),
            draw: false,
            redraw: true,
            command: String::new(),
            commands: WaitQueue::new(),
            escape: false,
            escape_sequence: false,
            sequence: Vec::new(),
            raw_mode: false,
        }
    }

    pub fn code(&mut self, c: char) {
        if self.escape_sequence {
            match c {
                '0' ... '9' => {
                    // Add a number to the sequence list
                    if let Some(mut value) = self.sequence.last_mut() {
                        value.push(c);
                    }
                },
                ';' => {
                    // Split sequence into list
                    self.sequence.push(String::new());
                },
                'm' => {
                    // Display attributes
                    let mut value_iter = self.sequence.iter();
                    while let Some(value_str) = value_iter.next() {
                        let value = value_str.parse::<u8>().unwrap_or(0);
                        match value {
                            0 => {
                                self.foreground = ansi_color(7);
                                self.background = ansi_color(0);
                            },
                            30 ... 37 => self.foreground = ansi_color(value - 30),
                            38 => match value_iter.next().map_or("", |s| &s).parse::<usize>().unwrap_or(0) {
                                2 => {
                                    //True color
                                    let r = value_iter.next().map_or("", |s| &s).parse::<u8>().unwrap_or(0);
                                    let g = value_iter.next().map_or("", |s| &s).parse::<u8>().unwrap_or(0);
                                    let b = value_iter.next().map_or("", |s| &s).parse::<u8>().unwrap_or(0);
                                    self.foreground = Color::new(r, g, b);
                                },
                                5 => {
                                    //256 color
                                    let color_value = value_iter.next().map_or("", |s| &s).parse::<u8>().unwrap_or(0);
                                    self.foreground = ansi_color(color_value);
                                },
                                _ => {}
                            },
                            40 ... 47 => self.background = ansi_color(value - 40),
                            48 => match value_iter.next().map_or("", |s| &s).parse::<usize>().unwrap_or(0) {
                                2 => {
                                    //True color
                                    let r = value_iter.next().map_or("", |s| &s).parse::<u8>().unwrap_or(0);
                                    let g = value_iter.next().map_or("", |s| &s).parse::<u8>().unwrap_or(0);
                                    let b = value_iter.next().map_or("", |s| &s).parse::<u8>().unwrap_or(0);
                                    self.background = Color::new(r, g, b);
                                },
                                5 => {
                                    //256 color
                                    let color_value = value_iter.next().map_or("", |s| &s).parse::<u8>().unwrap_or(0);
                                    self.background = ansi_color(color_value);
                                },
                                _ => {}
                            },
                            _ => {},
                        }
                    }

                    self.escape_sequence = false;
                },
                'J' => {
                    match self.sequence.get(0).map_or("", |p| &p).parse::<usize>().unwrap_or(0) {
                        0 => {
                            //TODO: Erase down
                        },
                        1 => {
                            //TODO: Erase up
                        },
                        2 => {
                            // Erase all
                            self.point_x = 0;
                            self.point_y = 0;
                            if let Some(ref mut display) = self.display {
                                display.set(self.background);
                            }
                            if ! self.raw_mode {
                                self.redraw = true;
                            }
                        },
                        _ => {}
                    }

                    self.escape_sequence = false;
                },
                'H' | 'f' => {
                    if let Some(ref mut display) = self.display {
                        display.rect(self.point_x, self.point_y, 8, 16, self.background);
                    }

                    let row = self.sequence.get(0).map_or("", |p| &p).parse::<isize>().unwrap_or(1);
                    self.point_y = cmp::max(0, row - 1) as usize * 16;

                    let col = self.sequence.get(1).map_or("", |p| &p).parse::<isize>().unwrap_or(1);
                    self.point_x = cmp::max(0, col - 1) as usize * 8;

                    if let Some(ref mut display) = self.display {
                        display.rect(self.point_x, self.point_y, 8, 16, self.foreground);
                    }

                    self.escape_sequence = false;
                },
/*
@MANSTART{terminal-raw-mode}
INTRODUCTION
    Since Redox has no ioctl syscall, it uses escape codes for switching to raw mode.

ENTERING AND EXITING RAW MODE
    Entering raw mode is done using CSI-r (^[r). Unsetting raw mode is done by CSI-R (^[R).

RAW MODE
    Raw mode means that the stdin must be handled solely by the program itself. It will not automatically be printed nor will it be modified in any way (modulo escape codes).

    This means that:
        - stdin is not printed.
        - newlines are interpreted as carriage returns in stdin.
        - stdin is not buffered, meaning that the stream of bytes goes directly to the program, without the user having to press enter.
@MANEND
*/
                'r' => {
                    self.raw_mode = true;
                    self.escape_sequence = false;
                },
                'R' => {
                    self.raw_mode = false;
                    self.escape_sequence = false;
                },
                _ => self.escape_sequence = false,
            }

            if !self.escape_sequence {
                self.sequence.clear();
                self.escape = false;
            }
        } else {
            match c {
                '[' => {
                    // Control sequence initiator

                    self.escape_sequence = true;
                    self.sequence.push(String::new());
                },
                'c' => {
                    // Reset
                    self.point_x = 0;
                    self.point_y = 0;
                    self.raw_mode = false;
                    self.foreground = ansi_color(7);
                    self.background = ansi_color(0);
                    if let Some(ref mut display) = self.display {
                        display.set(self.background);
                    }
                    self.redraw = true;

                    self.escape = false;
                }
                _ => self.escape = false,
            }
        }
    }

    pub fn character(&mut self, c: char) {
        let (width, height) = if let Some(ref mut display) = self.display {
            (display.width, display.height)
        } else {
            (80, 30)
        };

        if let Some(ref mut display) = self.display {
            display.rect(self.point_x, self.point_y, 8, 16, self.background);
        }

        match c {
            '\0' => {},
            '\x1B' => self.escape = true,
            '\n' => {
                self.point_x = 0;
                self.point_y += 16;
                if ! self.raw_mode {
                    self.redraw = true;
                }
            },
            '\t' => self.point_x = ((self.point_x / 64) + 1) * 64,
            '\r' => self.point_x = 0,
            '\x08' => {
                if self.point_x >= 8 {
                    self.point_x -= 8;
                }

                if let Some(ref mut display) = self.display {
                    display.rect(self.point_x, self.point_y, 8, 16, self.background);
                }
            },
            _ => {
                if let Some(ref mut display) = self.display {
                    display.char(self.point_x, self.point_y, c, self.foreground);
                }

                self.point_x += 8;
            }
        }

        if self.point_x >= width {
            self.point_x = 0;
            self.point_y += 16;
        }

        while self.point_y + 16 > height {
            if let Some(ref mut display) = self.display {
                display.scroll(16, self.background);
            }
            self.point_y -= 16;
        }

        if let Some(ref mut display) = self.display {
            display.rect(self.point_x, self.point_y, 8, 16, self.foreground);
        }
    }

    pub fn event(&mut self, event: Event) {
        match event.to_option() {
            EventOption::Key(key_event) => {
                if key_event.pressed {
                    if self.raw_mode {
                        match key_event.scancode {
                            event::K_BKSP => self.command.push_str("\x08"),
                            event::K_UP => self.command.push_str("\x1B[A"),
                            event::K_DOWN => self.command.push_str("\x1B[B"),
                            event::K_RIGHT => self.command.push_str("\x1B[C"),
                            event::K_LEFT => self.command.push_str("\x1B[D"),
                            _ => match key_event.character {
                                '\0' => {},
                                c => {
                                    self.command.push(c);
                                }
                            },
                        }

                        if ! self.command.is_empty() {
                            let mut command = String::new();
                            mem::swap(&mut self.command, &mut command);
                            self.commands.send(command);
                        }
                    } else {
                        match key_event.scancode {
                            event::K_BKSP => if ! self.command.is_empty() {
                                self.redraw = true;

                                self.write(&[8]);
                                self.command.pop();
                            },
                            _ => match key_event.character {
                                '\0' => (),
                                c => {
                                    self.redraw = true;

                                    self.write(&[c as u8]);
                                    self.command.push(c);

                                    if c == '\n' {
                                        let mut command = String::new();
                                        mem::swap(&mut self.command, &mut command);
                                        self.commands.send(command);
                                    }
                                }
                            },
                        }
                    }
                }
            }
            _ => (),
        }
    }

    pub fn write(&mut self, bytes: &[u8]) {
        for byte in bytes.iter() {
            let c = *byte as char;

            if self.escape {
                self.code(c);
            } else {
                self.character(c);
            }

            if self.display.is_none() || ! self.draw {
                let serial_status = Pio::<u8>::new(0x3F8 + 5);
                let mut serial_data = Pio::<u8>::new(0x3F8);

                while !serial_status.readf(0x20) {}
                serial_data.write(*byte);

                if *byte == 8 {
                    while !serial_status.readf(0x20) {}
                    serial_data.write(0x20);

                    while !serial_status.readf(0x20) {}
                    serial_data.write(8);
                }
            }
        }

        if self.draw && self.redraw {
            self.redraw = false;
            if let Some(ref mut display) = self.display {
                display.flip();
            }
        }
    }
}
<commit_msg>Fix issue in console: returning 8 instead of 7F for backspace in raw mode<commit_after>use alloc::boxed::Box;

use collections::String;
use collections::Vec;

use common::event::{self, Event, EventOption};

use core::{cmp, mem};

use drivers::io::{Io, Pio};

use graphics::color::Color;
use graphics::display::Display;

use sync::WaitQueue;

fn ansi_color(value: u8) -> Color {
    match value {
        0 => Color::new(0x00, 0x00, 0x00),
        1 => Color::new(0x80, 0x00, 0x00),
        2 => Color::new(0x00, 0x80, 0x00),
        3 => Color::new(0x80, 0x80, 0x00),
        4 => Color::new(0x00, 0x00, 0x80),
        5 => Color::new(0x80, 0x00, 0x80),
        6 => Color::new(0x00, 0x80, 0x80),
        7 => Color::new(0xc0, 0xc0, 0xc0),
        8 => Color::new(0x80, 0x80, 0x80),
        9 => Color::new(0xff, 0x00, 0x00),
        10 => Color::new(0x00, 0xff, 0x00),
        11 => Color::new(0xff, 0xff, 0x00),
        12 => Color::new(0x00, 0x00, 0xff),
        13 => Color::new(0xff, 0x00, 0xff),
        14 => Color::new(0x00, 0xff, 0xff),
        15 => Color::new(0xff, 0xff, 0xff),
        16 ... 231 => {
            let convert = |value: u8| -> u8 {
                match value {
                    0 => 0,
                    _ => value * 0x28 + 0x28
                }
            };

            let r = convert((value - 16)/36 % 6);
            let g = convert((value - 16)/6 % 6);
            let b = convert((value - 16) % 6);
            Color::new(r, g, b)
        },
        232 ... 255 => {
            let gray = (value - 232) * 10 + 8;
            Color::new(gray, gray, gray)
        },
        _ => Color::new(0, 0, 0)
    }
}

pub struct Console {
    pub display: Option<Box<Display>>,
    pub point_x: usize,
    pub point_y: usize,
    pub foreground: Color,
    pub background: Color,
    pub draw: bool,
    pub redraw: bool,
    pub command: String,
    pub commands: WaitQueue<String>,
    pub escape: bool,
    pub escape_sequence: bool,
    pub sequence: Vec<String>,
    pub raw_mode: bool,
}

impl Console {
    pub fn new() -> Console {
        Console {
            display: Display::root(),
            point_x: 0,
            point_y: 0,
            foreground: ansi_color(7),
            background: ansi_color(0),
            draw: false,
            redraw: true,
            command: String::new(),
            commands: WaitQueue::new(),
            escape: false,
            escape_sequence: false,
            sequence: Vec::new(),
            raw_mode: false,
        }
    }

    pub fn code(&mut self, c: char) {
        if self.escape_sequence {
            match c {
                '0' ... '9' => {
                    // Add a number to the sequence list
                    if let Some(mut value) = self.sequence.last_mut() {
                        value.push(c);
                    }
                },
                ';' => {
                    // Split sequence into list
                    self.sequence.push(String::new());
                },
                'm' => {
                    // Display attributes
                    let mut value_iter = self.sequence.iter();
                    while let Some(value_str) = value_iter.next() {
                        let value = value_str.parse::<u8>().unwrap_or(0);
                        match value {
                            0 => {
                                self.foreground = ansi_color(7);
                                self.background = ansi_color(0);
                            },
                            30 ... 37 => self.foreground = ansi_color(value - 30),
                            38 => match value_iter.next().map_or("", |s| &s).parse::<usize>().unwrap_or(0) {
                                2 => {
                                    //True color
                                    let r = value_iter.next().map_or("", |s| &s).parse::<u8>().unwrap_or(0);
                                    let g = value_iter.next().map_or("", |s| &s).parse::<u8>().unwrap_or(0);
                                    let b = value_iter.next().map_or("", |s| &s).parse::<u8>().unwrap_or(0);
                                    self.foreground = Color::new(r, g, b);
                                },
                                5 => {
                                    //256 color
                                    let color_value = value_iter.next().map_or("", |s| &s).parse::<u8>().unwrap_or(0);
                                    self.foreground = ansi_color(color_value);
                                },
                                _ => {}
                            },
                            40 ... 47 => self.background = ansi_color(value - 40),
                            48 => match value_iter.next().map_or("", |s| &s).parse::<usize>().unwrap_or(0) {
                                2 => {
                                    //True color
                                    let r = value_iter.next().map_or("", |s| &s).parse::<u8>().unwrap_or(0);
                                    let g = value_iter.next().map_or("", |s| &s).parse::<u8>().unwrap_or(0);
                                    let b = value_iter.next().map_or("", |s| &s).parse::<u8>().unwrap_or(0);
                                    self.background = Color::new(r, g, b);
                                },
                                5 => {
                                    //256 color
                                    let color_value = value_iter.next().map_or("", |s| &s).parse::<u8>().unwrap_or(0);
                                    self.background = ansi_color(color_value);
                                },
                                _ => {}
                            },
                            _ => {},
                        }
                    }

                    self.escape_sequence = false;
                },
                'J' => {
                    match self.sequence.get(0).map_or("", |p| &p).parse::<usize>().unwrap_or(0) {
                        0 => {
                            //TODO: Erase down
                        },
                        1 => {
                            //TODO: Erase up
                        },
                        2 => {
                            // Erase all
                            self.point_x = 0;
                            self.point_y = 0;
                            if let Some(ref mut display) = self.display {
                                display.set(self.background);
                            }
                            if ! self.raw_mode {
                                self.redraw = true;
                            }
                        },
                        _ => {}
                    }

                    self.escape_sequence = false;
                },
                'H' | 'f' => {
                    if let Some(ref mut display) = self.display {
                        display.rect(self.point_x, self.point_y, 8, 16, self.background);
                    }

                    let row = self.sequence.get(0).map_or("", |p| &p).parse::<isize>().unwrap_or(1);
                    self.point_y = cmp::max(0, row - 1) as usize * 16;

                    let col = self.sequence.get(1).map_or("", |p| &p).parse::<isize>().unwrap_or(1);
                    self.point_x = cmp::max(0, col - 1) as usize * 8;

                    if let Some(ref mut display) = self.display {
                        display.rect(self.point_x, self.point_y, 8, 16, self.foreground);
                    }

                    self.escape_sequence = false;
                },
/*
@MANSTART{terminal-raw-mode}
INTRODUCTION
    Since Redox has no ioctl syscall, it uses escape codes for switching to raw mode.

ENTERING AND EXITING RAW MODE
    Entering raw mode is done using CSI-r (^[r). Unsetting raw mode is done by CSI-R (^[R).

RAW MODE
    Raw mode means that the stdin must be handled solely by the program itself. It will not automatically be printed nor will it be modified in any way (modulo escape codes).

    This means that:
        - stdin is not printed.
        - newlines are interpreted as carriage returns in stdin.
        - stdin is not buffered, meaning that the stream of bytes goes directly to the program, without the user having to press enter.
@MANEND
*/
                'r' => {
                    self.raw_mode = true;
                    self.escape_sequence = false;
                },
                'R' => {
                    self.raw_mode = false;
                    self.escape_sequence = false;
                },
                _ => self.escape_sequence = false,
            }

            if !self.escape_sequence {
                self.sequence.clear();
                self.escape = false;
            }
        } else {
            match c {
                '[' => {
                    // Control sequence initiator

                    self.escape_sequence = true;
                    self.sequence.push(String::new());
                },
                'c' => {
                    // Reset
                    self.point_x = 0;
                    self.point_y = 0;
                    self.raw_mode = false;
                    self.foreground = ansi_color(7);
                    self.background = ansi_color(0);
                    if let Some(ref mut display) = self.display {
                        display.set(self.background);
                    }
                    self.redraw = true;

                    self.escape = false;
                }
                _ => self.escape = false,
            }
        }
    }

    pub fn character(&mut self, c: char) {
        let (width, height) = if let Some(ref mut display) = self.display {
            (display.width, display.height)
        } else {
            (80, 30)
        };

        if let Some(ref mut display) = self.display {
            display.rect(self.point_x, self.point_y, 8, 16, self.background);
        }

        match c {
            '\0' => {},
            '\x1B' => self.escape = true,
            '\n' => {
                self.point_x = 0;
                self.point_y += 16;
                if ! self.raw_mode {
                    self.redraw = true;
                }
            },
            '\t' => self.point_x = ((self.point_x / 64) + 1) * 64,
            '\r' => self.point_x = 0,
            '\x08' => {
                if self.point_x >= 8 {
                    self.point_x -= 8;
                }

                if let Some(ref mut display) = self.display {
                    display.rect(self.point_x, self.point_y, 8, 16, self.background);
                }
            },
            _ => {
                if let Some(ref mut display) = self.display {
                    display.char(self.point_x, self.point_y, c, self.foreground);
                }

                self.point_x += 8;
            }
        }

        if self.point_x >= width {
            self.point_x = 0;
            self.point_y += 16;
        }

        while self.point_y + 16 > height {
            if let Some(ref mut display) = self.display {
                display.scroll(16, self.background);
            }
            self.point_y -= 16;
        }

        if let Some(ref mut display) = self.display {
            display.rect(self.point_x, self.point_y, 8, 16, self.foreground);
        }
    }

    pub fn event(&mut self, event: Event) {
        match event.to_option() {
            EventOption::Key(key_event) => {
                if key_event.pressed {
                    if self.raw_mode {
                        match key_event.scancode {
                            event::K_BKSP => self.command.push_str("\x7F"),
                            event::K_UP => self.command.push_str("\x1B[A"),
                            event::K_DOWN => self.command.push_str("\x1B[B"),
                            event::K_RIGHT => self.command.push_str("\x1B[C"),
                            event::K_LEFT => self.command.push_str("\x1B[D"),
                            _ => match key_event.character {
                                '\0' => {},
                                c => {
                                    self.command.push(c);
                                }
                            },
                        }

                        if ! self.command.is_empty() {
                            let mut command = String::new();
                            mem::swap(&mut self.command, &mut command);
                            self.commands.send(command);
                        }
                    } else {
                        match key_event.scancode {
                            event::K_BKSP => if ! self.command.is_empty() {
                                self.redraw = true;

                                self.write(&[8]);
                                self.command.pop();
                            },
                            _ => match key_event.character {
                                '\0' => (),
                                c => {
                                    self.redraw = true;

                                    self.write(&[c as u8]);
                                    self.command.push(c);

                                    if c == '\n' {
                                        let mut command = String::new();
                                        mem::swap(&mut self.command, &mut command);
                                        self.commands.send(command);
                                    }
                                }
                            },
                        }
                    }
                }
            }
            _ => (),
        }
    }

    pub fn write(&mut self, bytes: &[u8]) {
        for byte in bytes.iter() {
            let c = *byte as char;

            if self.escape {
                self.code(c);
            } else {
                self.character(c);
            }

            if self.display.is_none() || ! self.draw {
                let serial_status = Pio::<u8>::new(0x3F8 + 5);
                let mut serial_data = Pio::<u8>::new(0x3F8);

                while !serial_status.readf(0x20) {}
                serial_data.write(*byte);

                if *byte == 8 {
                    while !serial_status.readf(0x20) {}
                    serial_data.write(0x20);

                    while !serial_status.readf(0x20) {}
                    serial_data.write(8);
                }
            }
        }

        if self.draw && self.redraw {
            self.redraw = false;
            if let Some(ref mut display) = self.display {
                display.flip();
            }
        }
    }
}
<|endoftext|>