<commit_before>use cli::{clear_screen, Args};
use env_logger;
use error::Result;
use gitignore;
use log;
use notification_filter::NotificationFilter;
#[cfg(target_os = "linux")]
use notify;
use pathop::PathOp;
use process::{self, Process};
use signal::{self, Signal};
use std::{
    collections::HashMap,
    io::Write,
    sync::{
        mpsc::{channel, Receiver},
        Arc, RwLock,
    },
    time::Duration,
};
use watcher::{Event, Watcher};

fn init_logger(debug: bool) {
    let mut log_builder = env_logger::Builder::new();
    let level = if debug {
        log::LevelFilter::Debug
    } else {
        log::LevelFilter::Warn
    };

    log_builder
        .format(|buf, r| writeln!(buf, "*** {}", r.args()))
        .filter(None, level)
        .init();
}

pub trait Handler {
    /// Initialises the `Handler` with a copy of the arguments.
    fn new(args: Args) -> Result<Self>
    where
        Self: Sized;

    /// Called through a manual request, such as an initial run.
    ///
    /// # Returns
    ///
    /// A `Result` which means:
    ///
    /// - `Err`: an error has occurred while processing, quit.
    /// - `Ok(false)`: everything is fine and the loop can continue.
    /// - `Ok(true)`: everything is fine but we should gracefully stop.
    fn on_manual(&mut self) -> Result<bool>;

    /// Called through a file-update request.
    ///
    /// # Parameters
    ///
    /// - `ops`: The list of events that triggered this update.
    ///
    /// # Returns
    ///
    /// A `Result` which means:
    ///
    /// - `Err`: an error has occurred while processing, quit.
    /// - `Ok(true)`: everything is fine and the loop can continue.
    /// - `Ok(false)`: everything is fine but we should gracefully stop.
    fn on_update(&mut self, ops: &[PathOp]) -> Result<bool>;
}

/// Starts watching, and calls a handler when something happens.
///
/// Given an argument structure and a `Handler` type, starts the watcher
/// loop (blocking until done).
pub fn watch<H>(args: Args) -> Result<()>
where
    H: Handler,
{
    init_logger(args.debug);
    let mut handler = H::new(args.clone())?;

    let gitignore = gitignore::load(if args.no_vcs_ignore { &[] } else { &args.paths });
    let filter = NotificationFilter::new(&args.filters, &args.ignores, gitignore)?;

    let (tx, rx) = channel();
    let poll = args.poll.clone();
    #[cfg(target_os = "linux")]
    let poll_interval = args.poll_interval.clone();
    let watcher = Watcher::new(tx.clone(), &args.paths, args.poll, args.poll_interval).or_else(|err| {
        if poll {
            return Err(err);
        }

        #[cfg(target_os = "linux")]
        {
            use nix::libc;
            let mut fallback = false;
            if let notify::Error::Io(ref e) = err {
                if e.raw_os_error() == Some(libc::ENOSPC) {
                    warn!("System notification limit is too small, falling back to polling mode. For better performance increase system limit:\n\tsysctl fs.inotify.max_user_watches=524288");
                    fallback = true;
                }
            }

            if fallback {
                return Watcher::new(tx, &args.paths, true, poll_interval);
            }
        }

        Err(err)
    })?;

    if watcher.is_polling() {
        warn!("Polling for changes every {} ms", args.poll_interval);
    }

    // Call handler initially, if necessary
    if args.run_initially && !args.once {
        if args.clear_screen {
            clear_screen();
        }

        if !handler.on_manual()? {
            return Ok(());
        }
    }

    loop {
        debug!("Waiting for filesystem activity");
        let paths = wait_fs(&rx, &filter, args.debounce);
        debug!("Paths updated: {:?}", paths);

        if args.clear_screen {
            clear_screen();
        }

        if !handler.on_update(&paths)? {
            break;
        }
    }

    Ok(())
}

pub struct ExecHandler {
    args: Args,
    signal: Option<Signal>,
    child_process: Arc<RwLock<Option<Process>>>,
}

impl ExecHandler {
    fn spawn(&mut self, ops: &[PathOp]) -> Result<()> {
        if self.args.clear_screen {
            clear_screen();
        }

        debug!("Launching child process");
        let mut guard = self.child_process.write()?;
        *guard = Some(process::spawn(&self.args.cmd, ops, self.args.no_shell)?);

        Ok(())
    }
}

impl Handler for ExecHandler {
    fn new(args: Args) -> Result<Self> {
        let child_process: Arc<RwLock<Option<Process>>> = Arc::new(RwLock::new(None));
        let weak_child = Arc::downgrade(&child_process);

        // Convert signal string to the corresponding integer
        let signal = signal::new(args.signal.clone());

        signal::install_handler(move |sig: Signal| {
            if let Some(lock) = weak_child.upgrade() {
                let strong = lock.read().unwrap();
                if let Some(ref child) = *strong {
                    match sig {
                        Signal::SIGCHLD => child.reap(), // SIGCHLD is special, initiate reap()
                        _ => child.signal(sig),
                    }
                }
            }
        });

        Ok(Self {
            args,
            signal,
            child_process,
        })
    }

    // Only returns Err() on lock poisoning.
    fn on_manual(&mut self) -> Result<bool> {
        let cls = self.args.clear_screen;
        self.args.clear_screen = false;
        self.spawn(&[])?;
        self.args.clear_screen = cls;
        Ok(true)
    }

    // Only returns Err() on lock poisoning.
    fn on_update(&mut self, ops: &[PathOp]) -> Result<bool> {
        // We have three scenarios here:
        //
        // 1. Make sure the previous run was ended, then run the command again
        // 2. Just send a specified signal to the child, do nothing more
        // 3. Send SIGTERM to the child, wait for it to exit, then run the command again
        // 4. Send a specified signal to the child, wait for it to exit, then run the command again
        //
        let scenario = (self.args.restart, self.signal.is_some());

        match scenario {
            // Custom restart behaviour (--restart was given, and --signal specified):
            // Send specified signal to the child, wait for it to exit, then run the command again
            (true, true) => {
                signal_process(&self.child_process, self.signal, true);
                self.spawn(ops)?;
            }

            // Default restart behaviour (--restart was given, but --signal wasn't specified):
            // Send SIGTERM to the child, wait for it to exit, then run the command again
            (true, false) => {
                let sigterm = signal::new(Some("SIGTERM".into()));
                signal_process(&self.child_process, sigterm, true);
                self.spawn(ops)?;
            }

            // SIGHUP scenario: --signal was given, but --restart was not
            // Just send a signal (e.g. SIGHUP) to the child, do nothing more
            (false, true) => signal_process(&self.child_process, self.signal, false),

            // Default behaviour (neither --signal nor --restart specified):
            // Make sure the previous run was ended, then run the command again
            (false, false) => {
                signal_process(&self.child_process, None, true);
                self.spawn(ops)?;
            }
        }

        // Handle once option for integration testing
        if self.args.once {
            signal_process(&self.child_process, self.signal, false);
            return Ok(false);
        }

        Ok(true)
    }
}

pub fn run(args: Args) -> Result<()> {
    watch::<ExecHandler>(args)
}

fn wait_fs(rx: &Receiver<Event>, filter: &NotificationFilter, debounce: u64) -> Vec<PathOp> {
    let mut paths = vec![];
    let mut cache = HashMap::new();

    loop {
        let e = rx.recv().expect("error when reading event");

        if let Some(ref path) = e.path {
            let pathop = PathOp::new(path, e.op.ok(), e.cookie);
            // Ignore cache for the initial file. Otherwise, in
            // debug mode it's hard to track what's going on
            let excluded = filter.is_excluded(path);
            if !cache.contains_key(&pathop) {
                cache.insert(pathop.clone(), excluded);
            }

            if !excluded {
                paths.push(pathop);
                break;
            }
        }
    }

    // Wait for filesystem activity to cool off
    let timeout = Duration::from_millis(debounce);
    while let Ok(e) = rx.recv_timeout(timeout) {
        if let Some(ref path) = e.path {
            let pathop = PathOp::new(path, e.op.ok(), e.cookie);
            if cache.contains_key(&pathop) {
                continue;
            }

            let excluded = filter.is_excluded(path);

            cache.insert(pathop.clone(), excluded);

            if !excluded {
                paths.push(pathop);
            }
        }
    }

    paths
}

// signal_process sends signal to process. It waits for the process to exit if wait is true
fn signal_process(process: &RwLock<Option<Process>>, signal: Option<Signal>, wait: bool) {
    let guard = process.read().unwrap();

    if let Some(ref child) = *guard {
        if let Some(s) = signal {
            child.signal(s);
        }

        if wait {
            debug!("Waiting for process to exit...");
            child.wait();
        }
    }
}
<commit_msg>[run] Fix outdated comment in ExecHandler<commit_after>use cli::{clear_screen, Args};
use env_logger;
use error::Result;
use gitignore;
use log;
use notification_filter::NotificationFilter;
#[cfg(target_os = "linux")]
use notify;
use pathop::PathOp;
use process::{self, Process};
use signal::{self, Signal};
use std::{
    collections::HashMap,
    io::Write,
    sync::{
        mpsc::{channel, Receiver},
        Arc, RwLock,
    },
    time::Duration,
};
use watcher::{Event, Watcher};

fn init_logger(debug: bool) {
    let mut log_builder = env_logger::Builder::new();
    let level = if debug {
        log::LevelFilter::Debug
    } else {
        log::LevelFilter::Warn
    };

    log_builder
        .format(|buf, r| writeln!(buf, "*** {}", r.args()))
        .filter(None, level)
        .init();
}

pub trait Handler {
    /// Initialises the `Handler` with a copy of the arguments.
    fn new(args: Args) -> Result<Self>
    where
        Self: Sized;

    /// Called through a manual request, such as an initial run.
    ///
    /// # Returns
    ///
    /// A `Result` which means:
    ///
    /// - `Err`: an error has occurred while processing, quit.
    /// - `Ok(false)`: everything is fine and the loop can continue.
    /// - `Ok(true)`: everything is fine but we should gracefully stop.
    fn on_manual(&mut self) -> Result<bool>;

    /// Called through a file-update request.
    ///
    /// # Parameters
    ///
    /// - `ops`: The list of events that triggered this update.
    ///
    /// # Returns
    ///
    /// A `Result` which means:
    ///
    /// - `Err`: an error has occurred while processing, quit.
    /// - `Ok(true)`: everything is fine and the loop can continue.
    /// - `Ok(false)`: everything is fine but we should gracefully stop.
    fn on_update(&mut self, ops: &[PathOp]) -> Result<bool>;
}

/// Starts watching, and calls a handler when something happens.
///
/// Given an argument structure and a `Handler` type, starts the watcher
/// loop (blocking until done).
pub fn watch<H>(args: Args) -> Result<()>
where
    H: Handler,
{
    init_logger(args.debug);
    let mut handler = H::new(args.clone())?;

    let gitignore = gitignore::load(if args.no_vcs_ignore { &[] } else { &args.paths });
    let filter = NotificationFilter::new(&args.filters, &args.ignores, gitignore)?;

    let (tx, rx) = channel();
    let poll = args.poll.clone();
    #[cfg(target_os = "linux")]
    let poll_interval = args.poll_interval.clone();
    let watcher = Watcher::new(tx.clone(), &args.paths, args.poll, args.poll_interval).or_else(|err| {
        if poll {
            return Err(err);
        }

        #[cfg(target_os = "linux")]
        {
            use nix::libc;
            let mut fallback = false;
            if let notify::Error::Io(ref e) = err {
                if e.raw_os_error() == Some(libc::ENOSPC) {
                    warn!("System notification limit is too small, falling back to polling mode. For better performance increase system limit:\n\tsysctl fs.inotify.max_user_watches=524288");
                    fallback = true;
                }
            }

            if fallback {
                return Watcher::new(tx, &args.paths, true, poll_interval);
            }
        }

        Err(err)
    })?;

    if watcher.is_polling() {
        warn!("Polling for changes every {} ms", args.poll_interval);
    }

    // Call handler initially, if necessary
    if args.run_initially && !args.once {
        if !handler.on_manual()? {
            return Ok(());
        }
    }

    loop {
        debug!("Waiting for filesystem activity");
        let paths = wait_fs(&rx, &filter, args.debounce);
        debug!("Paths updated: {:?}", paths);

        if !handler.on_update(&paths)? {
            break;
        }
    }

    Ok(())
}

pub struct ExecHandler {
    args: Args,
    signal: Option<Signal>,
    child_process: Arc<RwLock<Option<Process>>>,
}

impl ExecHandler {
    fn spawn(&mut self, ops: &[PathOp]) -> Result<()> {
        if self.args.clear_screen {
            clear_screen();
        }

        debug!("Launching child process");
        let mut guard = self.child_process.write()?;
        *guard = Some(process::spawn(&self.args.cmd, ops, self.args.no_shell)?);

        Ok(())
    }
}

impl Handler for ExecHandler {
    fn new(args: Args) -> Result<Self> {
        let child_process: Arc<RwLock<Option<Process>>> = Arc::new(RwLock::new(None));
        let weak_child = Arc::downgrade(&child_process);

        // Convert signal string to the corresponding integer
        let signal = signal::new(args.signal.clone());

        signal::install_handler(move |sig: Signal| {
            if let Some(lock) = weak_child.upgrade() {
                let strong = lock.read().unwrap();
                if let Some(ref child) = *strong {
                    match sig {
                        Signal::SIGCHLD => child.reap(), // SIGCHLD is special, initiate reap()
                        _ => child.signal(sig),
                    }
                }
            }
        });

        Ok(Self {
            args,
            signal,
            child_process,
        })
    }

    // Only returns Err() on lock poisoning.
    fn on_manual(&mut self) -> Result<bool> {
        let cls = self.args.clear_screen;
        self.args.clear_screen = false;
        self.spawn(&[])?;
        self.args.clear_screen = cls;
        Ok(true)
    }

    // Only returns Err() on lock poisoning.
    fn on_update(&mut self, ops: &[PathOp]) -> Result<bool> {
        // We have four scenarios here:
        //
        // 1. Send a specified signal to the child, wait for it to exit, then run the command again
        // 2. Send SIGTERM to the child, wait for it to exit, then run the command again
        // 3. Just send a specified signal to the child, do nothing more
        // 4. Make sure the previous run was ended, then run the command again
        //
        let scenario = (self.args.restart, self.signal.is_some());

        match scenario {
            // Custom restart behaviour (--restart was given, and --signal specified):
            // Send specified signal to the child, wait for it to exit, then run the command again
            (true, true) => {
                signal_process(&self.child_process, self.signal, true);
                self.spawn(ops)?;
            }

            // Default restart behaviour (--restart was given, but --signal wasn't specified):
            // Send SIGTERM to the child, wait for it to exit, then run the command again
            (true, false) => {
                let sigterm = signal::new(Some("SIGTERM".into()));
                signal_process(&self.child_process, sigterm, true);
                self.spawn(ops)?;
            }

            // SIGHUP scenario: --signal was given, but --restart was not
            // Just send a signal (e.g. SIGHUP) to the child, do nothing more
            (false, true) => signal_process(&self.child_process, self.signal, false),

            // Default behaviour (neither --signal nor --restart specified):
            // Make sure the previous run was ended, then run the command again
            (false, false) => {
                signal_process(&self.child_process, None, true);
                self.spawn(ops)?;
            }
        }

        // Handle once option for integration testing
        if self.args.once {
            signal_process(&self.child_process, self.signal, false);
            return Ok(false);
        }

        Ok(true)
    }
}

pub fn run(args: Args) -> Result<()> {
    watch::<ExecHandler>(args)
}

fn wait_fs(rx: &Receiver<Event>, filter: &NotificationFilter, debounce: u64) -> Vec<PathOp> {
    let mut paths = Vec::new();
    let mut cache = HashMap::new();

    loop {
        let e = rx.recv().expect("error when reading event");

        if let Some(ref path) = e.path {
            let pathop = PathOp::new(path, e.op.ok(), e.cookie);
            // Ignore cache for the initial file. Otherwise, in
            // debug mode it's hard to track what's going on
            let excluded = filter.is_excluded(path);
            if !cache.contains_key(&pathop) {
                cache.insert(pathop.clone(), excluded);
            }

            if !excluded {
                paths.push(pathop);
                break;
            }
        }
    }

    // Wait for filesystem activity to cool off
    let timeout = Duration::from_millis(debounce);
    while let Ok(e) = rx.recv_timeout(timeout) {
        if let Some(ref path) = e.path {
            let pathop = PathOp::new(path, e.op.ok(), e.cookie);
            if cache.contains_key(&pathop) {
                continue;
            }

            let excluded = filter.is_excluded(path);

            cache.insert(pathop.clone(), excluded);

            if !excluded {
                paths.push(pathop);
            }
        }
    }

    paths
}

// signal_process sends signal to process. It waits for the process to exit if wait is true
fn signal_process(process: &RwLock<Option<Process>>, signal: Option<Signal>, wait: bool) {
    let guard = process.read().unwrap();

    if let Some(ref child) = *guard {
        if let Some(s) = signal {
            child.signal(s);
        }

        if wait {
            debug!("Waiting for process to exit...");
            child.wait();
        }
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::to_str::ToStr;
use std::os::{pipe, change_dir};
use std::io::{Open, Write};
use std::io::fs::{mkdir, File};
use std::io::pipe::PipeStream;
use std::io::stdio::{stdout, stderr};

use argparse::{ArgumentParser, Store, List};

use super::env::Environ;
use super::config::Config;
use super::linux::{make_namespace, change_root, bind_mount, mount_pseudofs};
use super::linux::{execute, forkme, wait_process};
use libc::funcs::posix88::unistd::getuid;

fn mount_all(root: &Path, mount_dir: &Path, project_root: &Path)
    -> Result<(), String>
{
    let vagga_marker = mount_dir.join_many(["tmp", ".vagga"]);
    try!(bind_mount(root, mount_dir, false));
    try!(bind_mount(&Path::new("/sys"), &mount_dir.join("sys"), false));
    // TODO(tailhook) use dev in /var/lib/container-dev
    try!(bind_mount(&Path::new("/dev"), &mount_dir.join("dev"), false));
    try!(bind_mount(project_root, &mount_dir.join("work"), true));
    try!(mount_pseudofs("proc", &mount_dir.join("proc"), ""));
    // TODO(tailhook) allow customize size of tmpfs
    try!(mount_pseudofs("tmpfs", &mount_dir.join("tmp"),
                        "size=102400k,mode=1777"));
    try!(ensure_dir(&vagga_marker));
    try!(bind_mount(&vagga_marker,
                    &mount_dir.join_many(["work", ".vagga"]),
                    false));
    match File::open_mode(&vagga_marker.join("CONTAINED.txt"), Open, Write)
        .write_line("You are running in vagga container.") {
        Ok(()) => {}
        Err(e) => return Err(format!("Can't write CONTAINED.txt: {}", e)),
    }
    return Ok(());
}

fn ensure_dir(p: &Path) -> Result<(),String> {
    if p.exists() {
        return Ok(());
    }
    return mkdir(p, io::UserRWX).map_err(|e| { e.to_str() });
}

pub fn run_chroot(env: &Environ, container_root: &Path, mount_dir: &Path,
    command: &String, args: &Vec<String>)
    -> Result<(),String>
{
    try!(mount_all(container_root, mount_dir, &env.project_root));
    debug!("Changing directory to {}", mount_dir.display());
    change_dir(mount_dir);
    try!(change_root(mount_dir));
    let reldir = match env.work_dir.path_relative_from(&env.project_root) {
        Some(path) => path,
        None => Path::new(""),
    };
    let path = Path::new("/work").join(reldir);
    debug!("Changing directory to {}", path.display());
    change_dir(&path);

    // TODO(tailhook) set environment from config
    let environ = vec!(
        "PATH=/bin:/usr/bin:/usr/local/bin".to_string(),
        "HOME=/non-existent".to_string(),
        );
    try!(execute(command, args, &environ));
    unreachable!();
}

pub fn run_user_command(env: &Environ, config: &Config,
    cmdname: &String, args: Vec<String>)
    -> Result<int, String>
{
    let command = match config.commands.find(cmdname) {
        Some(c) => c,
        None => {
            return Err(format!("Can't find command {} in config",
                               cmdname));
        }
    };
    let cname = match command.container {
        Some(ref name) => name.clone(),
        None => unimplemented!(),
    };
    match config.containers.find(&cname) {
        Some(c) => c,
        None => {
            return Err(format!("Can't find container {} for command {}",
                               command.container, cmdname));
        }
    };
    return match command.run {
        Some(ref cmdline) => _run(env, config, &cname,
            &"/bin/sh".to_string(),
            &(vec!("-c".to_string(), cmdline.clone()) + args.slice_from(1))),
        None => unimplemented!()
    }
}

pub fn run_command(env: &Environ, config: &Config, args: Vec<String>)
    -> Result<int, String>
{
    let mut cname = "devel".to_string();
    let mut command = "".to_string();
    let mut cmdargs = Vec::new();
    {
        let mut ap = ArgumentParser::new();
        ap.refer(&mut cname)
            .add_argument("container", box Store::<String>,
                "A name of the container to build")
            .required();
        ap.refer(&mut command)
            .add_argument("command", box Store::<String>,
                "A command to run inside container")
            .required();
        ap.refer(&mut cmdargs)
            .add_argument("arguments", box List::<String>,
                "Arguments for the command")
            .required();
        match ap.parse(args, &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(0) => return Ok(0),
            Err(_) => return Ok(122),
        }
    }
    match config.containers.find(&cname) {
        Some(c) => c,
        None => {
            return Err(format!("Can't find container {} in config",
                               cname));
        }
    };
    _run(env, config, &cname, &command, &cmdargs)
}

pub fn _run(env: &Environ, config: &Config, container: &String,
    command: &String, cmdargs: &Vec<String>)
    -> Result<int, String>
{
    info!("Running {}: {} {}", container, command, cmdargs);
    let container_dir = env.project_root.join_many(
        [".vagga", container.as_slice()]);
    let container_root = container_dir.join("root");
    let uid = unsafe { getuid() };

    for dir in ["proc", "sys", "dev", "work", "tmp"].iter() {
        try!(ensure_dir(&container_root.join(*dir)));
    }

    let mount_dir = env.project_root.join_many([".vagga", "mnt"]);
    try!(ensure_dir(&mount_dir));
    try!(make_namespace());

    let mut pipe = match PipeStream::pair() {
        Ok(pipe) => pipe,
        Err(x) => return Err(format!("Can't create pipe: {}", x)),
    };

    let pid = try!(forkme());
    if pid == 0 {
        let mut buf: [u8,..1] = [0];
        match pipe.reader.read(buf) {
            Ok(_) => {}
            Err(x) => return Err(format!("Can't read from pipe: {}", x)),
        }
        drop(pipe);
        try!(run_chroot(env, &container_root, &mount_dir, command, cmdargs));
        unreachable!();
    } else {
        // TODO(tailhook) set uid map from config
        let uid_map = format!("0 {} 1", uid);
        debug!("Writing uid_map: {}", uid_map);
        match File::open_mode(&Path::new("/proc")
                          .join(pid.to_str())
                          .join("uid_map"), Open, Write)
                .write_str(uid_map.as_slice()) {
            Ok(()) => {}
            Err(e) => return Err(format!(
                "Error writing uid mapping: {}", e)),
        }

        match pipe.writer.write_char('x') {
            Ok(_) => {}
            Err(e) => return Err(format!(
                "Error writing to pipe: {}", e)),
        }
        drop(pipe);

        return wait_process(pid);
    }
}
<commit_msg>Fix parsing arguments of `_run`<commit_after>use std::io;
use std::to_str::ToStr;
use std::os::{pipe, change_dir};
use std::io::{Open, Write};
use std::io::fs::{mkdir, File};
use std::io::pipe::PipeStream;
use std::io::stdio::{stdout, stderr};

use argparse::{ArgumentParser, Store, List};

use super::env::Environ;
use super::config::Config;
use super::linux::{make_namespace, change_root, bind_mount, mount_pseudofs};
use super::linux::{execute, forkme, wait_process};
use libc::funcs::posix88::unistd::getuid;

fn mount_all(root: &Path, mount_dir: &Path, project_root: &Path)
    -> Result<(), String>
{
    let vagga_marker = mount_dir.join_many(["tmp", ".vagga"]);
    try!(bind_mount(root, mount_dir, false));
    try!(bind_mount(&Path::new("/sys"), &mount_dir.join("sys"), false));
    // TODO(tailhook) use dev in /var/lib/container-dev
    try!(bind_mount(&Path::new("/dev"), &mount_dir.join("dev"), false));
    try!(bind_mount(project_root, &mount_dir.join("work"), true));
    try!(mount_pseudofs("proc", &mount_dir.join("proc"), ""));
    // TODO(tailhook) allow customize size of tmpfs
    try!(mount_pseudofs("tmpfs", &mount_dir.join("tmp"),
                        "size=102400k,mode=1777"));
    try!(ensure_dir(&vagga_marker));
    try!(bind_mount(&vagga_marker,
                    &mount_dir.join_many(["work", ".vagga"]),
                    false));
    match File::open_mode(&vagga_marker.join("CONTAINED.txt"), Open, Write)
        .write_line("You are running in vagga container.") {
        Ok(()) => {}
        Err(e) => return Err(format!("Can't write CONTAINED.txt: {}", e)),
    }
    return Ok(());
}

fn ensure_dir(p: &Path) -> Result<(),String> {
    if p.exists() {
        return Ok(());
    }
    return mkdir(p, io::UserRWX).map_err(|e| { e.to_str() });
}

pub fn run_chroot(env: &Environ, container_root: &Path, mount_dir: &Path,
    command: &String, args: &Vec<String>)
    -> Result<(),String>
{
    try!(mount_all(container_root, mount_dir, &env.project_root));
    debug!("Changing directory to {}", mount_dir.display());
    change_dir(mount_dir);
    try!(change_root(mount_dir));
    let reldir = match env.work_dir.path_relative_from(&env.project_root) {
        Some(path) => path,
        None => Path::new(""),
    };
    let path = Path::new("/work").join(reldir);
    debug!("Changing directory to {}", path.display());
    change_dir(&path);

    // TODO(tailhook) set environment from config
    let environ = vec!(
        "PATH=/bin:/usr/bin:/usr/local/bin".to_string(),
        "HOME=/non-existent".to_string(),
        );
    try!(execute(command, args, &environ));
    unreachable!();
}

pub fn run_user_command(env: &Environ, config: &Config,
    cmdname: &String, args: Vec<String>)
    -> Result<int, String>
{
    let command = match config.commands.find(cmdname) {
        Some(c) => c,
        None => {
            return Err(format!("Can't find command {} in config",
                               cmdname));
        }
    };
    let cname = match command.container {
        Some(ref name) => name.clone(),
        None => unimplemented!(),
    };
    match config.containers.find(&cname) {
        Some(c) => c,
        None => {
            return Err(format!("Can't find container {} for command {}",
                               command.container, cmdname));
        }
    };
    return match command.run {
        Some(ref cmdline) => _run(env, config, &cname,
            &"/bin/sh".to_string(),
            &(vec!("-c".to_string(), cmdline.clone()) + args.slice_from(1))),
        None => unimplemented!()
    }
}

pub fn run_command(env: &Environ, config: &Config, args: Vec<String>)
    -> Result<int, String>
{
    let mut cname = "devel".to_string();
    let mut command = "".to_string();
    let mut cmdargs = Vec::new();
    {
        let mut ap = ArgumentParser::new();
        ap.refer(&mut cname)
            .add_argument("container", box Store::<String>,
                "A name of the container to build")
            .required();
        ap.refer(&mut command)
            .add_argument("command", box Store::<String>,
                "A command to run inside container")
            .required();
        ap.refer(&mut cmdargs)
            .add_argument("arguments", box List::<String>,
                "Arguments for the command")
            .required();
        ap.stop_on_first_argument(true);
        match ap.parse(args, &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(0) => return Ok(0),
            Err(_) => return Ok(122),
        }
    }
    match config.containers.find(&cname) {
        Some(c) => c,
        None => {
            return Err(format!("Can't find container {} in config",
                               cname));
        }
    };
    _run(env, config, &cname, &command, &cmdargs)
}

pub fn _run(env: &Environ, config: &Config, container: &String,
    command: &String, cmdargs: &Vec<String>)
    -> Result<int, String>
{
    info!("Running {}: {} {}", container, command, cmdargs);
    let container_dir = env.project_root.join_many(
        [".vagga", container.as_slice()]);
    let container_root = container_dir.join("root");
    let uid = unsafe { getuid() };

    for dir in ["proc", "sys", "dev", "work", "tmp"].iter() {
        try!(ensure_dir(&container_root.join(*dir)));
    }

    let mount_dir = env.project_root.join_many([".vagga", "mnt"]);
    try!(ensure_dir(&mount_dir));
    try!(make_namespace());

    let mut pipe = match PipeStream::pair() {
        Ok(pipe) => pipe,
        Err(x) => return Err(format!("Can't create pipe: {}", x)),
    };

    let pid = try!(forkme());
    if pid == 0 {
        let mut buf: [u8,..1] = [0];
        match pipe.reader.read(buf) {
            Ok(_) => {}
            Err(x) => return Err(format!("Can't read from pipe: {}", x)),
        }
        drop(pipe);
        try!(run_chroot(env, &container_root, &mount_dir, command, cmdargs));
        unreachable!();
    } else {
        // TODO(tailhook) set uid map from config
        let uid_map = format!("0 {} 1", uid);
        debug!("Writing uid_map: {}", uid_map);
        match File::open_mode(&Path::new("/proc")
                          .join(pid.to_str())
                          .join("uid_map"), Open, Write)
                .write_str(uid_map.as_slice()) {
            Ok(()) => {}
            Err(e) => return Err(format!(
                "Error writing uid mapping: {}", e)),
        }

        match pipe.writer.write_char('x') {
            Ok(_) => {}
            Err(e) => return Err(format!(
                "Error writing to pipe: {}", e)),
        }
        drop(pipe);

        return wait_process(pid);
    }
}
<|endoftext|>


<commit_before>use cookie_factory::*;
use nom::{be_u8,be_u16,be_u32,IResult};
use method::{method,Method};
use format::field::*;
use generated::*;

use std::collections::HashMap;

named!(pub protocol_header<&[u8]>,
  preceded!(
    tag!(b"AMQP\0"),
    tag!(&[0, 9, 1])
  )
);

pub fn gen_protocol_header<'a>(x:(&'a mut [u8],usize)) -> Result<(&'a mut [u8],usize),GenError> {
  do_gen!(
    x,
    gen_slice!(b"AMQP") >>
    gen_slice!(&[0, 0, 9, 1])
  )
}

// 0      1         3             7                    size+7 size+8
// +------+---------+-------------+   +------------+   +-----------+
// | type | channel |    size     |   | payload    |   | frame-end |
// +------+---------+-------------+   +------------+   +-----------+
//   octet  short        long        size octets           octet

#[derive(Clone,Copy,Debug,PartialEq,Eq)]
pub enum FrameType {
  Method,
  Header,
  Body,
  Heartbeat
}

named!(pub frame_type<FrameType>,
  switch!(be_u8,
    1 => value!(FrameType::Method)
  | 2 => value!(FrameType::Header)
  | 3 => value!(FrameType::Body)
  | 4 => value!(FrameType::Heartbeat)
  )
);

#[derive(Clone,Copy,Debug,PartialEq,Eq)]
pub enum Channel {
  Global,
  Id(u16),
}

named!(pub channel_id<Channel>,
  switch!(be_u16,
    0 => value!(Channel::Global)
  | i => value!(Channel::Id(i))
  )
);

//FIXME: maybe parse the frame header, and leave the payload for later
#[derive(Clone,Copy,Debug,PartialEq,Eq)]
pub struct RawFrame<'a> {
  pub frame_type: FrameType,
  pub channel_id: u16,
  pub size:       u32,
  pub payload:    &'a[u8],
}

named!(pub raw_frame<RawFrame>,
  do_parse!(
    frame:   frame_type    >>
    channel: be_u16        >>
    size:    be_u32        >>
    payload: take!(size)   >>
             tag!(&[0xCE]) >>
    (RawFrame {
      frame_type: frame,
      channel_id: channel,
      size:       size,
      payload:    payload,
    })
  )
);

#[derive(Clone,Debug,PartialEq)]
pub enum Frame<'a> {
  Method(u16, Method),
  //content header
  Header(u16, ()),
  //content Body
  Body(u16, &'a[u8]),
  Heartbeat(u16)
}

pub fn frame(input: &[u8]) -> IResult<&[u8], Frame> {
  let (remaining, raw) = try_parse!(input, raw_frame);
  match raw.frame_type {
    FrameType::Header    => IResult::Done(remaining, Frame::Header(raw.channel_id, ())),
    FrameType::Body      => IResult::Done(remaining, Frame::Body(raw.channel_id, raw.payload)),
    FrameType::Heartbeat => IResult::Done(remaining, Frame::Heartbeat(raw.channel_id)),
    FrameType::Method    => {
      println!("will try to parse a method");
      let (remaining2, m) = try_parse!(raw.payload, method);
      IResult::Done(remaining2, Frame::Method(raw.channel_id, m))
    },
  }
}

pub fn gen_method_frame<'a>(input:(&'a mut [u8],usize), channel: u16, class: &Class) -> Result<(&'a mut [u8],usize),GenError> {
  if let Ok(input1) = gen_be_u8!(input, 1u8) {
    if let Ok((sl2, index2)) = gen_be_u16!(input1, channel) {
      if let Ok((sl3, index3)) = gen_class((sl2, index2 + 4), class) {
        if let Ok((sl4, index4)) = gen_be_u32!((sl3, index2), index3 - index2 - 4) {
          gen_be_u8!((sl4, index3), 0xCE)
          //if let Ok((sl5, index5)) = gen_be_u8!((sl4, index3), 0xCE)
          //{
          //}
        } else {
          Err(GenError::CustomError(42))
        }
      } else {
        Err(GenError::CustomError(42))
      }
    } else {
      Err(GenError::CustomError(42))
    }
  } else {
    Err(GenError::CustomError(42))
  }
}

pub fn gen_heartbeat_frame<'a>(input:(&'a mut [u8],usize)) -> Result<(&'a mut [u8],usize),GenError> {
  do_gen!(input, gen_slice!(&[4, 0, 0, 0xCE]))
}

pub fn gen_content_header_frame<'a>(input:(&'a mut [u8],usize), channel_id: u16, class_id: u16, length: u64) -> Result<(&'a mut [u8],usize),GenError> {
  do_gen!(input,
    gen_be_u8!( 2 ) >>
    gen_be_u16!(channel_id) >>

    gen_be_u32!( 42 ) >> //calculate static size

    gen_be_u16!(class_id) >>
    gen_be_u16!(0) >> // weight
    gen_be_u64!(length) >>
    gen_be_u8!(0) >> // properties
    gen_field_table(&HashMap::new()) >>

    gen_be_u8!(0xCE)
  )
}

pub fn gen_content_body_frame<'a>(input:(&'a mut [u8],usize), channel_id: u16, slice: &[u8]) -> Result<(&'a mut [u8],usize),GenError> {
  do_gen!(input,
    gen_be_u8!( 3 ) >>
    gen_be_u16!(channel_id) >>

    gen_slice!(slice) >>

    gen_be_u8!(0xCE)
  )
}
<commit_msg>hearbeat frames begin with an 8<commit_after>use cookie_factory::*;
use nom::{be_u8,be_u16,be_u32,IResult};
use method::{method,Method};
use format::field::*;
use generated::*;

use std::collections::HashMap;

named!(pub protocol_header<&[u8]>,
  preceded!(
    tag!(b"AMQP\0"),
    tag!(&[0, 9, 1])
  )
);

pub fn gen_protocol_header<'a>(x:(&'a mut [u8],usize)) -> Result<(&'a mut [u8],usize),GenError> {
  do_gen!(
    x,
    gen_slice!(b"AMQP") >>
    gen_slice!(&[0, 0, 9, 1])
  )
}

// 0      1         3             7                    size+7 size+8
// +------+---------+-------------+   +------------+   +-----------+
// | type | channel |    size     |   | payload    |   | frame-end |
// +------+---------+-------------+   +------------+   +-----------+
//   octet  short        long        size octets           octet

#[derive(Clone,Copy,Debug,PartialEq,Eq)]
pub enum FrameType {
  Method,
  Header,
  Body,
  Heartbeat
}

named!(pub frame_type<FrameType>,
  switch!(be_u8,
    1 => value!(FrameType::Method)
  | 2 => value!(FrameType::Header)
  | 3 => value!(FrameType::Body)
  | 8 => value!(FrameType::Heartbeat)
  )
);

#[derive(Clone,Copy,Debug,PartialEq,Eq)]
pub enum Channel {
  Global,
  Id(u16),
}

named!(pub channel_id<Channel>,
  switch!(be_u16,
    0 => value!(Channel::Global)
  | i => value!(Channel::Id(i))
  )
);

//FIXME: maybe parse the frame header, and leave the payload for later
#[derive(Clone,Copy,Debug,PartialEq,Eq)]
pub struct RawFrame<'a> {
  pub frame_type: FrameType,
  pub channel_id: u16,
  pub size:       u32,
  pub payload:    &'a[u8],
}

named!(pub raw_frame<RawFrame>,
  do_parse!(
    frame:   frame_type    >>
    channel: be_u16        >>
    size:    be_u32        >>
    payload: take!(size)   >>
             tag!(&[0xCE]) >>
    (RawFrame {
      frame_type: frame,
      channel_id: channel,
      size:       size,
      payload:    payload,
    })
  )
);

#[derive(Clone,Debug,PartialEq)]
pub enum Frame<'a> {
  Method(u16, Method),
  //content header
  Header(u16, ()),
  //content Body
  Body(u16, &'a[u8]),
  Heartbeat(u16)
}

pub fn frame(input: &[u8]) -> IResult<&[u8], Frame> {
  let (remaining, raw) = try_parse!(input, raw_frame);
  match raw.frame_type {
    FrameType::Header    => IResult::Done(remaining, Frame::Header(raw.channel_id, ())),
    FrameType::Body      => IResult::Done(remaining, Frame::Body(raw.channel_id, raw.payload)),
    FrameType::Heartbeat => IResult::Done(remaining, Frame::Heartbeat(raw.channel_id)),
    FrameType::Method    => {
      println!("will try to parse a method");
      let (remaining2, m) = try_parse!(raw.payload, method);
      IResult::Done(remaining2, Frame::Method(raw.channel_id, m))
    },
  }
}

pub fn gen_method_frame<'a>(input:(&'a mut [u8],usize), channel: u16, class: &Class) -> Result<(&'a mut [u8],usize),GenError> {
  if let Ok(input1) = gen_be_u8!(input, 1u8) {
    if let Ok((sl2, index2)) = gen_be_u16!(input1, channel) {
      if let Ok((sl3, index3)) = gen_class((sl2, index2 + 4), class) {
        if let Ok((sl4, index4)) = gen_be_u32!((sl3, index2), index3 - index2 - 4) {
          gen_be_u8!((sl4, index3), 0xCE)
          //if let Ok((sl5, index5)) = gen_be_u8!((sl4, index3), 0xCE)
          //{
          //}
        } else {
          Err(GenError::CustomError(42))
        }
      } else {
        Err(GenError::CustomError(42))
      }
    } else {
      Err(GenError::CustomError(42))
    }
  } else {
    Err(GenError::CustomError(42))
  }
}

pub fn gen_heartbeat_frame<'a>(input:(&'a mut [u8],usize)) -> Result<(&'a mut [u8],usize),GenError> {
  do_gen!(input, gen_slice!(&[8, 0, 0, 0xCE]))
}

pub fn gen_content_header_frame<'a>(input:(&'a mut [u8],usize), channel_id: u16, class_id: u16, length: u64) -> Result<(&'a mut [u8],usize),GenError> {
  do_gen!(input,
    gen_be_u8!( 2 ) >>
    gen_be_u16!(channel_id) >>

    gen_be_u32!( 42 ) >> //calculate static size

    gen_be_u16!(class_id) >>
    gen_be_u16!(0) >> // weight
    gen_be_u64!(length) >>
    gen_be_u8!(0) >> // properties
    gen_field_table(&HashMap::new()) >>

    gen_be_u8!(0xCE)
  )
}

pub fn gen_content_body_frame<'a>(input:(&'a mut [u8],usize), channel_id: u16, slice: &[u8]) -> Result<(&'a mut [u8],usize),GenError> {
  do_gen!(input,
    gen_be_u8!( 3 ) >>
    gen_be_u16!(channel_id) >>

    gen_slice!(slice) >>

    gen_be_u8!(0xCE)
  )
}
<|endoftext|>


<commit_before>mod format;
pub mod source;

use source::Source;
use error::*;
use value::Value;
use std::collections::HashMap;
use std::path::Path;

use self::source::FileSource;
pub use self::format::FileFormat;

pub struct File<T>
    where T: FileSource
{
    source: T,

    /// Namespace to restrict configuration from the file
    namespace: Option<String>,

    /// Format of file (which dictates what driver to use).
    format: Option<FileFormat>,

    /// A required File will error if it cannot be found
    required: bool,
}

impl File<source::string::FileSourceString> {
    pub fn from_str(s: &str, format: FileFormat) -> Self {
        File {
            format: Some(format),
            required: true,
            namespace: None,
            source: s.into(),
        }
    }
}

impl File<source::file::FileSourceFile> {
    pub fn new(name: &str, format: FileFormat) -> Self {
        File {
            format: Some(format),
            required: true,
            namespace: None,
            source: source::file::FileSourceFile::new(name),
        }
    }

    /// Given the basename of a file, will attempt to locate a file by setting its
    /// extension to a registered format.
    pub fn with_name(name: &str) -> Self {
        File {
            format: None,
            required: true,
            namespace: None,
            source: source::file::FileSourceFile::new(name),
        }
    }
}

impl<T: FileSource> File<T> {
    pub fn required(mut self, required: bool) -> Self {
        self.required = required;
        self
    }

    pub fn namespace(mut self, namespace: &str) -> Self {
        self.namespace = Some(namespace.into());
        self
    }
}

impl<T: FileSource> Source for File<T> {
    fn collect(&self) -> Result<HashMap<String, Value>> {
        // Coerce the file contents to a string
        let (uri, contents, format) = match self.source.resolve(self.format).map_err(|err| {
            ConfigError::Foreign(err)
        }) {
            Ok((uri, contents, format)) => (uri, contents, format),

            Err(error) => {
                if !self.required {
                    return Ok(HashMap::new());
                }

                return Err(error);
            }
        };

        // Parse the string using the given format
        let result = format.parse(uri.as_ref(), &contents, self.namespace.as_ref()).map_err(|cause| {
            ConfigError::FileParse {
                uri: uri,
                cause: cause
            }
        });

        if result.is_err() && !self.required {
            // Ignore fails and just go with it if its not required
            Ok(HashMap::new())
        } else {
            result
        }
    }
}
<commit_msg>Parsing errors even for non required file<commit_after>mod format;
pub mod source;

use source::Source;
use error::*;
use value::Value;
use std::collections::HashMap;
use std::path::Path;

use self::source::FileSource;
pub use self::format::FileFormat;

pub struct File<T>
    where T: FileSource
{
    source: T,

    /// Namespace to restrict configuration from the file
    namespace: Option<String>,

    /// Format of file (which dictates what driver to use).
    format: Option<FileFormat>,

    /// A required File will error if it cannot be found
    required: bool,
}

impl File<source::string::FileSourceString> {
    pub fn from_str(s: &str, format: FileFormat) -> Self {
        File {
            format: Some(format),
            required: true,
            namespace: None,
            source: s.into(),
        }
    }
}

impl File<source::file::FileSourceFile> {
    pub fn new(name: &str, format: FileFormat) -> Self {
        File {
            format: Some(format),
            required: true,
            namespace: None,
            source: source::file::FileSourceFile::new(name),
        }
    }

    /// Given the basename of a file, will attempt to locate a file by setting its
    /// extension to a registered format.
    pub fn with_name(name: &str) -> Self {
        File {
            format: None,
            required: true,
            namespace: None,
            source: source::file::FileSourceFile::new(name),
        }
    }
}

impl<T: FileSource> File<T> {
    pub fn required(mut self, required: bool) -> Self {
        self.required = required;
        self
    }

    pub fn namespace(mut self, namespace: &str) -> Self {
        self.namespace = Some(namespace.into());
        self
    }
}

impl<T: FileSource> Source for File<T> {
    fn collect(&self) -> Result<HashMap<String, Value>> {
        // Coerce the file contents to a string
        let (uri, contents, format) = match self.source.resolve(self.format).map_err(|err| {
            ConfigError::Foreign(err)
        }) {
            Ok((uri, contents, format)) => (uri, contents, format),

            Err(error) => {
                if !self.required {
                    return Ok(HashMap::new());
                }

                return Err(error);
            }
        };

        // Parse the string using the given format
        let result = format.parse(uri.as_ref(), &contents, self.namespace.as_ref()).map_err(|cause| {
            ConfigError::FileParse {
                uri: uri,
                cause: cause
            }
        });

        result
    }
}
<|endoftext|>


<commit_before>use crate::*;
use tree_hash::{SignedRoot, TreeHash};
use types::*;

#[derive(Debug, PartialEq)]
pub enum Error {
    BeaconStateError(BeaconStateError),
    EpochProcessingError(EpochProcessingError),
}

/// Advances a state forward by one slot, performing per-epoch processing if required.
///
/// Spec v0.5.1
pub fn per_slot_processing(state: &mut BeaconState, spec: &ChainSpec) -> Result<(), Error> {
    cache_state(state, spec)?;

    if (state.slot + 1) % spec.slots_per_epoch == 0 {
        per_epoch_processing(state, spec)?;
    }

    state.slot += 1;

    Ok(())
}

fn cache_state(state: &mut BeaconState, spec: &ChainSpec) -> Result<(), Error> {
    let previous_slot_state_root = Hash256::from_slice(&state.tree_hash_root()[..]);

    // Note: increment the state slot here to allow use of our `state_root` and `block_root`
    // getter/setter functions.
    //
    // This is a bit hacky, however it gets the job safely without lots of code.
    let previous_slot = state.slot;
    state.slot += 1;

    // Store the previous slot's post-state transition root.
    if state.latest_block_header.state_root == spec.zero_hash {
        state.latest_block_header.state_root = previous_slot_state_root
    }

    let latest_block_root = Hash256::from_slice(&state.latest_block_header.signed_root()[..]);
    state.set_block_root(previous_slot, latest_block_root, spec)?;

    // Set the state slot back to what it should be.
    state.slot -= 1;

    Ok(())
}

impl From<BeaconStateError> for Error {
    fn from(e: BeaconStateError) -> Error {
        Error::BeaconStateError(e)
    }
}

impl From<EpochProcessingError> for Error {
    fn from(e: EpochProcessingError) -> Error {
        Error::EpochProcessingError(e)
    }
}
<commit_msg>Store state roots during slot processing<commit_after>use crate::*;
use tree_hash::{SignedRoot, TreeHash};
use types::*;

#[derive(Debug, PartialEq)]
pub enum Error {
    BeaconStateError(BeaconStateError),
    EpochProcessingError(EpochProcessingError),
}

/// Advances a state forward by one slot, performing per-epoch processing if required.
///
/// Spec v0.5.1
pub fn per_slot_processing(state: &mut BeaconState, spec: &ChainSpec) -> Result<(), Error> {
    cache_state(state, spec)?;

    if (state.slot + 1) % spec.slots_per_epoch == 0 {
        per_epoch_processing(state, spec)?;
    }

    state.slot += 1;

    Ok(())
}

fn cache_state(state: &mut BeaconState, spec: &ChainSpec) -> Result<(), Error> {
    let previous_slot_state_root = Hash256::from_slice(&state.tree_hash_root()[..]);

    // Note: increment the state slot here to allow use of our `state_root` and `block_root`
    // getter/setter functions.
    //
    // This is a bit hacky, however it gets the job safely without lots of code.
    let previous_slot = state.slot;
    state.slot += 1;

    // Store the previous slot's post-state transition root.
    if state.latest_block_header.state_root == spec.zero_hash {
        state.latest_block_header.state_root = previous_slot_state_root
    }

    // Store the previous slot's post state transition root.
    state.set_state_root(previous_slot, previous_slot_state_root, spec)?;

    let latest_block_root = Hash256::from_slice(&state.latest_block_header.signed_root()[..]);
    state.set_block_root(previous_slot, latest_block_root, spec)?;

    // Set the state slot back to what it should be.
    state.slot -= 1;

    Ok(())
}

impl From<BeaconStateError> for Error {
    fn from(e: BeaconStateError) -> Error {
        Error::BeaconStateError(e)
    }
}

impl From<EpochProcessingError> for Error {
    fn from(e: EpochProcessingError) -> Error {
        Error::EpochProcessingError(e)
    }
}
<|endoftext|>


<commit_before>use std::iter::IntoIterator;

use syntax::ast;
use syntax::codemap::{DUMMY_SP, Span};
use syntax::owned_slice::OwnedSlice;

use ident::ToIdent;
use invoke::{Invoke, Identity};
use lifetime::{IntoLifetime, IntoLifetimeDef, LifetimeDefBuilder};
use name::ToName;
use path::IntoPath;
use ty_param::TyParamBuilder;

//////////////////////////////////////////////////////////////////////////////

pub struct GenericsBuilder<F=Identity> {
    callback: F,
    span: Span,
    lifetimes: Vec<ast::LifetimeDef>,
    ty_params: Vec<ast::TyParam>,
    predicates: Vec<ast::WherePredicate>,
}

impl GenericsBuilder {
    pub fn new() -> Self {
        GenericsBuilder::new_with_callback(Identity)
    }

    pub fn from_generics(generics: ast::Generics) -> Self {
        GenericsBuilder::from_generics_with_callback(generics, Identity)
    }
}

impl<F> GenericsBuilder<F>
    where F: Invoke<ast::Generics>,
{
    pub fn new_with_callback(callback: F) -> Self {
        GenericsBuilder {
            callback: callback,
            span: DUMMY_SP,
            lifetimes: Vec::new(),
            ty_params: Vec::new(),
            predicates: Vec::new(),
        }
    }

    pub fn from_generics_with_callback(generics: ast::Generics, callback: F) -> Self {
        GenericsBuilder {
            callback: callback,
            span: DUMMY_SP,
            lifetimes: generics.lifetimes,
            ty_params: generics.ty_params.into_vec(),
            predicates: generics.where_clause.predicates,
        }
    }

    pub fn with(self, generics: ast::Generics) -> Self {
        self.with_lifetimes(generics.lifetimes.into_iter())
            .with_ty_params(generics.ty_params.move_iter())
            .with_predicates(generics.where_clause.predicates.into_iter())
    }

    pub fn span(mut self, span: Span) -> Self {
        self.span = span;
        self
    }

    pub fn with_lifetimes<I, L>(mut self, iter: I) -> Self
        where I: IntoIterator<Item=L>,
              L: IntoLifetimeDef,
    {
        let iter = iter.into_iter().map(|lifetime_def| lifetime_def.into_lifetime_def());
        self.lifetimes.extend(iter);
        self
    }

    pub fn with_lifetime_names<I, N>(mut self, iter: I) -> Self
        where I: IntoIterator<Item=N>,
              N: ToName,
    {
        for name in iter {
            self = self.lifetime_name(name);
        }
        self
    }

    pub fn with_lifetime(mut self, lifetime: ast::LifetimeDef) -> Self {
        self.lifetimes.push(lifetime);
        self
    }

    pub fn lifetime_name<N>(self, name: N) -> Self
        where N: ToName,
    {
        self.lifetime(name).build()
    }

    pub fn lifetime<N>(self, name: N) -> LifetimeDefBuilder<Self>
        where N: ToName,
    {
        LifetimeDefBuilder::new_with_callback(name, self)
    }

    pub fn with_ty_params<I>(mut self, iter: I) -> Self
        where I: IntoIterator<Item=ast::TyParam>,
    {
        self.ty_params.extend(iter);
        self
    }

    pub fn with_ty_param_ids<I, T>(mut self, iter: I) -> Self
        where I: IntoIterator<Item=T>,
              T: ToIdent,
    {
        for id in iter {
            self = self.ty_param_id(id);
        }
        self
    }

    pub fn with_ty_param(mut self, ty_param: ast::TyParam) -> Self {
        self.ty_params.push(ty_param);
        self
    }

    pub fn ty_param_id<I>(self, id: I) -> Self
        where I: ToIdent,
    {
        self.ty_param(id).build()
    }

    pub fn ty_param<I>(self, id: I) -> TyParamBuilder<Self>
        where I: ToIdent,
    {
        let span = self.span;
        TyParamBuilder::new_with_callback(id, self).span(span)
    }

    pub fn with_predicates<I>(mut self, iter: I) -> Self
        where I: IntoIterator<Item=ast::WherePredicate>,
    {
        self.predicates.extend(iter);
        self
    }

    pub fn with_predicate(mut self, predicate: ast::WherePredicate) -> Self {
        self.predicates.push(predicate);
        self
    }

    pub fn add_lifetime_bound<L>(mut self, lifetime: L) -> Self
        where L: IntoLifetime,
    {
        let lifetime = lifetime.into_lifetime();

        for lifetime_def in self.lifetimes.iter_mut() {
            lifetime_def.bounds.push(lifetime.clone());
        }

        for ty_param in self.ty_params.iter_mut() {
            *ty_param = TyParamBuilder::from_ty_param(ty_param.clone())
                .lifetime_bound(lifetime.clone())
                .build();
        }

        self
    }

    pub fn add_ty_param_bound<P>(mut self, path: P) -> Self
        where P: IntoPath,
    {
        let path = path.into_path();

        for ty_param in self.ty_params.iter_mut() {
            *ty_param = TyParamBuilder::from_ty_param(ty_param.clone())
                .trait_bound(path.clone()).build()
                .build();
        }

        self
    }

    pub fn strip_bounds(self) -> Self {
        self.strip_lifetimes()
            .strip_ty_params()
            .strip_predicates()
    }

    pub fn strip_lifetimes(mut self) -> Self {
        for lifetime in self.lifetimes.iter_mut() {
            lifetime.bounds = vec![];
        }
        self
    }

    pub fn strip_ty_params(mut self) -> Self {
        for ty_param in self.ty_params.iter_mut() {
            ty_param.bounds = OwnedSlice::empty();
        }
        self
    }

    pub fn strip_predicates(mut self) -> Self {
        self.predicates = vec![];
        self
    }

    pub fn build(self) -> F::Result {
        self.callback.invoke(ast::Generics {
            lifetimes: self.lifetimes,
            ty_params: OwnedSlice::from_vec(self.ty_params),
            where_clause: ast::WhereClause {
                id: ast::DUMMY_NODE_ID,
                predicates: self.predicates,
            },
        })
    }
}

impl<F> Invoke<ast::LifetimeDef> for GenericsBuilder<F>
    where F: Invoke<ast::Generics>,
{
    type Result = Self;

    fn invoke(self, lifetime: ast::LifetimeDef) -> Self {
        self.with_lifetime(lifetime)
    }
}

impl<F> Invoke<ast::TyParam> for GenericsBuilder<F>
    where F: Invoke<ast::Generics>,
{
    type Result = Self;

    fn invoke(self, ty_param: ast::TyParam) -> Self {
        self.with_ty_param(ty_param)
    }
}
<commit_msg>refactor(generics): Switch to ideomatic iteration style<commit_after>use std::iter::IntoIterator;

use syntax::ast;
use syntax::codemap::{DUMMY_SP, Span};
use syntax::owned_slice::OwnedSlice;

use ident::ToIdent;
use invoke::{Invoke, Identity};
use lifetime::{IntoLifetime, IntoLifetimeDef, LifetimeDefBuilder};
use name::ToName;
use path::IntoPath;
use ty_param::TyParamBuilder;

//////////////////////////////////////////////////////////////////////////////

pub struct GenericsBuilder<F=Identity> {
    callback: F,
    span: Span,
    lifetimes: Vec<ast::LifetimeDef>,
    ty_params: Vec<ast::TyParam>,
    predicates: Vec<ast::WherePredicate>,
}

impl GenericsBuilder {
    pub fn new() -> Self {
        GenericsBuilder::new_with_callback(Identity)
    }

    pub fn from_generics(generics: ast::Generics) -> Self {
        GenericsBuilder::from_generics_with_callback(generics, Identity)
    }
}

impl<F> GenericsBuilder<F>
    where F: Invoke<ast::Generics>,
{
    pub fn new_with_callback(callback: F) -> Self {
        GenericsBuilder {
            callback: callback,
            span: DUMMY_SP,
            lifetimes: Vec::new(),
            ty_params: Vec::new(),
            predicates: Vec::new(),
        }
    }

    pub fn from_generics_with_callback(generics: ast::Generics, callback: F) -> Self {
        GenericsBuilder {
            callback: callback,
            span: DUMMY_SP,
            lifetimes: generics.lifetimes,
            ty_params: generics.ty_params.into_vec(),
            predicates: generics.where_clause.predicates,
        }
    }

    pub fn with(self, generics: ast::Generics) -> Self {
        self.with_lifetimes(generics.lifetimes.into_iter())
            .with_ty_params(generics.ty_params.move_iter())
            .with_predicates(generics.where_clause.predicates.into_iter())
    }

    pub fn span(mut self, span: Span) -> Self {
        self.span = span;
        self
    }

    pub fn with_lifetimes<I, L>(mut self, iter: I) -> Self
        where I: IntoIterator<Item=L>,
              L: IntoLifetimeDef,
    {
        let iter = iter.into_iter().map(|lifetime_def| lifetime_def.into_lifetime_def());
        self.lifetimes.extend(iter);
        self
    }

    pub fn with_lifetime_names<I, N>(mut self, iter: I) -> Self
        where I: IntoIterator<Item=N>,
              N: ToName,
    {
        for name in iter {
            self = self.lifetime_name(name);
        }
        self
    }

    pub fn with_lifetime(mut self, lifetime: ast::LifetimeDef) -> Self {
        self.lifetimes.push(lifetime);
        self
    }

    pub fn lifetime_name<N>(self, name: N) -> Self
        where N: ToName,
    {
        self.lifetime(name).build()
    }

    pub fn lifetime<N>(self, name: N) -> LifetimeDefBuilder<Self>
        where N: ToName,
    {
        LifetimeDefBuilder::new_with_callback(name, self)
    }

    pub fn with_ty_params<I>(mut self, iter: I) -> Self
        where I: IntoIterator<Item=ast::TyParam>,
    {
        self.ty_params.extend(iter);
        self
    }

    pub fn with_ty_param_ids<I, T>(mut self, iter: I) -> Self
        where I: IntoIterator<Item=T>,
              T: ToIdent,
    {
        for id in iter {
            self = self.ty_param_id(id);
        }
        self
    }

    pub fn with_ty_param(mut self, ty_param: ast::TyParam) -> Self {
        self.ty_params.push(ty_param);
        self
    }

    pub fn ty_param_id<I>(self, id: I) -> Self
        where I: ToIdent,
    {
        self.ty_param(id).build()
    }

    pub fn ty_param<I>(self, id: I) -> TyParamBuilder<Self>
        where I: ToIdent,
    {
        let span = self.span;
        TyParamBuilder::new_with_callback(id, self).span(span)
    }

    pub fn with_predicates<I>(mut self, iter: I) -> Self
        where I: IntoIterator<Item=ast::WherePredicate>,
    {
        self.predicates.extend(iter);
        self
    }

    pub fn with_predicate(mut self, predicate: ast::WherePredicate) -> Self {
        self.predicates.push(predicate);
        self
    }

    pub fn add_lifetime_bound<L>(mut self, lifetime: L) -> Self
        where L: IntoLifetime,
    {
        let lifetime = lifetime.into_lifetime();

        for lifetime_def in &mut self.lifetimes {
            lifetime_def.bounds.push(lifetime.clone());
        }

        for ty_param in &mut self.ty_params {
            *ty_param = TyParamBuilder::from_ty_param(ty_param.clone())
                .lifetime_bound(lifetime.clone())
                .build();
        }

        self
    }

    pub fn add_ty_param_bound<P>(mut self, path: P) -> Self
        where P: IntoPath,
    {
        let path = path.into_path();

        for ty_param in &mut self.ty_params {
            *ty_param = TyParamBuilder::from_ty_param(ty_param.clone())
                .trait_bound(path.clone()).build()
                .build();
        }

        self
    }

    pub fn strip_bounds(self) -> Self {
        self.strip_lifetimes()
            .strip_ty_params()
            .strip_predicates()
    }

    pub fn strip_lifetimes(mut self) -> Self {
        for lifetime in &mut self.lifetimes {
            lifetime.bounds = vec![];
        }
        self
    }

    pub fn strip_ty_params(mut self) -> Self {
        for ty_param in &mut self.ty_params {
            ty_param.bounds = OwnedSlice::empty();
        }
        self
    }

    pub fn strip_predicates(mut self) -> Self {
        self.predicates = vec![];
        self
    }

    pub fn build(self) -> F::Result {
        self.callback.invoke(ast::Generics {
            lifetimes: self.lifetimes,
            ty_params: OwnedSlice::from_vec(self.ty_params),
            where_clause: ast::WhereClause {
                id: ast::DUMMY_NODE_ID,
                predicates: self.predicates,
            },
        })
    }
}

impl<F> Invoke<ast::LifetimeDef> for GenericsBuilder<F>
    where F: Invoke<ast::Generics>,
{
    type Result = Self;

    fn invoke(self, lifetime: ast::LifetimeDef) -> Self {
        self.with_lifetime(lifetime)
    }
}

impl<F> Invoke<ast::TyParam> for GenericsBuilder<F>
    where F: Invoke<ast::Generics>,
{
    type Result = Self;

    fn invoke(self, ty_param: ast::TyParam) -> Self {
        self.with_ty_param(ty_param)
    }
}
<|endoftext|>


<commit_before>use libc;
use std::cmp;

#[allow(non_camel_case_types)]
pub type size_t = ::std::os::raw::c_ulong;

#[allow(non_camel_case_types)]
#[derive(Debug)]
pub enum Struct___locale_map { }

#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Debug)]
pub struct Struct___locale_struct {
    pub cat: [*const Struct___locale_map; 6usize],
}

#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Debug)]
pub struct tls_module {
    pub next: *mut tls_module,
    pub image: *mut ::std::os::raw::c_void,
    pub len: size_t,
    pub size: size_t,
    pub align: size_t,
    pub offset: size_t,
}

#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Debug)]
pub struct __libc {
    pub can_do_threads: ::std::os::raw::c_int,
    pub threaded: ::std::os::raw::c_int,
    pub secure: ::std::os::raw::c_int,
    pub threads_minus_1: ::std::os::raw::c_int,
    pub auxv: *mut size_t,
    pub tls_head: *mut tls_module,
    pub tls_size: size_t,
    pub tls_align: size_t,
    pub tls_cnt: size_t,
    pub page_size: size_t,
    pub global_locale: Struct___locale_struct,
}

extern {
    pub static mut __libc: __libc;
}

// example of tls init structure for libc
/*
l_tls_initimage = 0x7ffff7bb2788,
l_tls_initimage_size = 16,
l_tls_blocksize = 120,
l_tls_align = 8,
l_tls_firstbyte_offset = 0,
l_tls_offset = 128,
l_tls_modid = 2,
l_tls_dtor_count = 0,
*/

#[derive(Debug, Clone, Copy)]
pub struct TlsInfo {
    pub blocksize: usize, // actual memory size ph_memsz
    pub align: usize,
    pub offset: isize,
    pub modid: u32,
    pub firstbyte_offset: usize,
    pub image: usize,
    pub image_size: usize,
}

impl TlsInfo {
    pub fn new (){
        unimplemented!();
    }
}

/// Memset yo!
unsafe fn memset(ptr: *mut u8, byte: u8, size: usize) {
    let mut i = 0;
    while i < size {
        *ptr.offset(i as isize) = byte;
        i += 1;
    }
}

// glibc api
// always remember, for x86_64: TLS_TCB_AT_TP
// the TCB follows the TLS blocks

#[repr(C)]
#[derive(Debug)]
struct Pointer {
    val: *mut libc::c_void,
    is_static: bool
}

// this is a union :/, either counter or pointer
#[repr(C)]
#[derive(Debug)]
struct Dtv {
    counter: usize,
    pointer: Pointer
}

pub const SIZEOF_DTV: usize = 0x10;

#[inline(always)]
unsafe fn get_dtv(tls_storage: *mut libc::c_void) -> *mut Dtv {
    let descr_dtv = tls_storage.offset(1) as *mut *mut Dtv;
    dbgc!(purple_bold: true, "tls", "get_dtv: {:?}", **descr_dtv);
    *descr_dtv
}

// TODO: rename this to something better, like ModuleInfo or SlotInfo
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct DtvInfo {
    generation: u32,
    info: TlsInfo
}

#[inline(always)]
/// Installs the dtv in the tcbhead_t struct, which is the second element; we hack it for now by simply casting and setting the offset; see `sysdeps/x86_64/nptl/tls.h:128`
unsafe fn install_dtv(tls_storage: *mut libc::c_void, dtv: *mut Dtv) {
    let descr_dtv = tls_storage.offset(1) as *mut *mut Dtv;
    *descr_dtv = dtv.offset(1);
}

unsafe fn allocate_dtv (tls_storage: *mut libc::c_void) -> *mut libc::c_void {
    let dtv_len = 2 + DTV_SURPLUS;
    dbgc!(purple_bold: true, "tls", "allocate_dtv dtv_len: {:?}", dtv_len);
    let dtv = libc::calloc(dtv_len + 2, SIZEOF_DTV as libc::size_t) as *mut Dtv;
    dbgc!(purple_bold: true, "tls", "allocate_dtv calloc dtv: {:?}", dtv);
    if !dtv.is_null() {
        // hehe surries!
        (*dtv.offset(0)).counter = dtv_len;
        dbgc!(purple_bold: true, "tls", "allocate_dtv calloc dtv: {:?}", *dtv);
        install_dtv(tls_storage, dtv);
        dbgc!(purple_bold: true, "tls", "allocate_dtv calloc post install: {:?} dtv: {:?}", **(tls_storage.offset(1) as *mut *mut Dtv), *dtv);
        tls_storage
    } else {
        dbgc!(purple_bold: true, "tls", "allocate_dtv dtv is NULL");
        ::std::ptr::null_mut::<libc::c_void>()
    }
}

pub unsafe fn _dl_allocate_tls_storage () -> *mut libc::c_void {
    let mut result = libc::memalign(TLS_STATIC_SIZE, TLS_STATIC_ALIGN);
    dbgc!(purple_bold: true, "tls", "_dl_allocate_tls_storage result: {:?}", result);
    let allocated = result; // to be used by free in case fails, unimplemented
    result = result.offset(TLS_STATIC_SIZE as isize - TLS_TCB_SIZE as isize);
    dbgc!(purple_bold: true, "tls", "_dl_allocate_tls_storage post result: {:?}", result);
    memset(result as *mut u8, 0x0, TLS_TCB_SIZE);
    dbgc!(purple_bold: true, "tls", "_dl_allocate_tls_storage memset result: {:?}", result);
    allocate_dtv(result)
}

// TODO: finish
// dl-tls.c:448 _dl_allocate_tls_init (void *result)
// TODO: replace max_dtv_idx/modid and tls_clients with mut self
pub unsafe fn allocate_tls (max_dtv_idx: u32, modules: &[DtvInfo]) -> *mut libc::c_void {
    let tls_storage = _dl_allocate_tls_storage ();
    if tls_storage.is_null() {
        // TODO: actually don't think I can panic, because no TLS yet :/
        panic!("Error: tls storage failed to allocate");
    }
    let dtv = get_dtv(tls_storage);
    // TODO: check if current dtv is large enough
    // do this by examining the len (which is 14 + whatever modules we loaded) and checking if it's smaller than our dtv_idx counter (basically last modid)
    // need to write a resize_dtv routine of course, too
    // and then call install_dtv
    let total = 0;
    let mut maxgen = 0;
    loop {
        let cnt = if total == 0 { 1 } else { 0 };
        for cnt in cnt..modules.len() {
            if total + cnt > max_dtv_idx as usize {
                break;
            }
            let info = modules[cnt].info;
            maxgen = cmp::max(maxgen, modules[cnt].generation);
            // need to transmute here due to union...
//            dtv[info.modid]
        }
        // TODO: remove after fixed
        break;
    }
    unimplemented!();
//    tls_storage
}

#[inline(always)]
/// misc/sys/param.h
/// assuming __GNUC__ not defined...
fn roundup(x: usize, y: usize) -> usize {
    ((x + (y - 1)) / y) * y
}

/// Implements:
/// dl-tls.c:137 _dl_determine_tlsoffset (void)
fn determine_offset(static_align: &mut usize, static_used: &mut usize, static_size: &mut usize, modules: &mut[DtvInfo]) {
    let mut max_align = TLS_TCB_ALIGN;
    let mut freetop = 0;
    let mut freebottom = 0;
    assert!(modules.len() == 1);

    let mut offset = 0;

    for (i, slot_info) in modules.iter().enumerate() {
        let mut info = slot_info.info;
        let mut off;
        // todo: verify ! = -
        let firstbyte = (!info.firstbyte_offset) & (info.align - 1);
        max_align = cmp::max(max_align, info.align);

        if (freebottom - freetop) >= info.blocksize {
            off = roundup ((freetop + info.blocksize) - firstbyte, info.align + firstbyte);
            if off <= freebottom {
                freetop = off;
                info.offset = off as isize;
                continue;
            }
        }

        off = roundup (offset + info.blocksize - firstbyte, info.align) + firstbyte;
        if off > offset + info.blocksize + (freebottom - freetop) {
            freetop = offset;
            freebottom = off - info.blocksize;
        }
        offset = off;
        info.offset = off as isize;
    }

    *static_used = offset;
    *static_size = roundup(offset + TLS_STATIC_SURPLUS, max_align) + TLS_TCB_SIZE;

    *static_align = max_align;

   dbgc!(purple_bold: true, "tls", "determine_offset final static_used: {} static_size: {} static_align: {} freetop: {} freebottom: {}", static_used, static_size, static_align, freetop, freebottom);
}

/// Implements: sysdeps/x86_64/nptl/tls.h:148 TLS_INIT_TP(thrdescr)
/// sets up the `fs` thread pointer on x86_64
#[inline(always)]
unsafe fn tls_init_tp (tcbp: *mut libc::c_void) {
    syscall!(ARCH_PRCTL, 0x1002, tcbp as usize);
}

// for special tls just for local process i believe, so it can access errno, etc.
// TODO: rtld.c:572 init_tls (void)
// 1. allocates the necessary tcbp
// 2. allocates the slot info list
// 3. sets the appropriate data into the slot info list (determine_offset)
// 4. and installs it via syscall tls_init_tp to the main thread
unsafe fn init_tls() {
    unimplemented!();
}

// final process
// 1. init_tls in main thread = dryad
// 2. load, relocate, etc.
// 3. allocate_tls_init (tcbp)
// 4. call TLS_INIT_TP

pub const TLS_STATIC_SIZE: libc::size_t = 0x1000;
pub const TLS_STATIC_ALIGN: libc::size_t = 0x40;
pub const TLS_STATIC_ALIGN_MASK: libc::size_t = !(TLS_STATIC_ALIGN - 1);
// sizeof (struct pthread)
pub const TLS_TCB_SIZE: libc::size_t = 0x900;
// sysdeps/generic/ldsodefs.h:402
pub const DTV_SURPLUS: libc::size_t = 14;
// sysdeps/generic/ldsodefs.h:399
pub const TLS_SLOTINFO_SURPLUS: libc::size_t = 62;

// this was the most miserable thing in the world to figure out
// __alignof__ (struct pthread)
pub const TLS_TCB_ALIGN: libc::size_t = 8;

// "Non-shared code has no support for multiple namespaces."
// sysdeps/generic/ldsodefs.h:276
pub const DL_NNS: libc::size_t = 16;
// dl-tls.c:34
pub const TLS_STATIC_SURPLUS: libc::size_t = 64 + DL_NNS * 100;

/// https://en.wikipedia.org/wiki/Lachesis_(mythology)
pub struct Lachesis {
    pub modules: Vec<DtvInfo>
}
<commit_msg>tls: DtvInfo -> SlotInfo, "finish" _allocate_tls_init and tls_init<commit_after>use libc;
use std::cmp;

#[allow(non_camel_case_types)]
pub type size_t = ::std::os::raw::c_ulong;

#[allow(non_camel_case_types)]
#[derive(Debug)]
pub enum Struct___locale_map { }

#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Debug)]
pub struct Struct___locale_struct {
    pub cat: [*const Struct___locale_map; 6usize],
}

#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Debug)]
pub struct tls_module {
    pub next: *mut tls_module,
    pub image: *mut ::std::os::raw::c_void,
    pub len: size_t,
    pub size: size_t,
    pub align: size_t,
    pub offset: size_t,
}

#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Debug)]
pub struct __libc {
    pub can_do_threads: ::std::os::raw::c_int,
    pub threaded: ::std::os::raw::c_int,
    pub secure: ::std::os::raw::c_int,
    pub threads_minus_1: ::std::os::raw::c_int,
    pub auxv: *mut size_t,
    pub tls_head: *mut tls_module,
    pub tls_size: size_t,
    pub tls_align: size_t,
    pub tls_cnt: size_t,
    pub page_size: size_t,
    pub global_locale: Struct___locale_struct,
}

extern {
    pub static mut __libc: __libc;
}

// example of tls init structure for libc
/*
l_tls_initimage = 0x7ffff7bb2788,
l_tls_initimage_size = 16,
l_tls_blocksize = 120,
l_tls_align = 8,
l_tls_firstbyte_offset = 0,
l_tls_offset = 128,
l_tls_modid = 2,
l_tls_dtor_count = 0,
*/

#[derive(Debug, Clone, Copy)]
pub struct TlsInfo {
    pub blocksize: usize, // actual memory size ph_memsz
    pub align: usize,
    pub offset: isize,
    pub modid: u32,
    pub firstbyte_offset: usize,
    pub image: usize,
    pub image_size: usize,
}

impl TlsInfo {
    pub fn new (){
        unimplemented!();
    }
}

/// Memset yo!
unsafe fn memset(ptr: *mut u8, byte: u8, size: usize) {
    let mut i = 0;
    while i < size {
        *ptr.offset(i as isize) = byte;
        i += 1;
    }
}

// glibc api
// always remember, for x86_64: TLS_TCB_AT_TP
// the TCB follows the TLS blocks

#[repr(C)]
#[derive(Debug, Clone)]
struct DtvHead {
    counter: usize,
    _padding: [u8; 4],
}

// this is a union :/, either counter or pointer
// the counter is only used for the first element in the dtv (DtvHead), because C programmers are dicks
#[repr(C)]
#[derive(Debug, Clone)]
struct Dtv {
    val: *mut libc::c_void,
    is_static: bool
}

pub const SIZEOF_DTV: usize = 0x10;

#[inline(always)]
unsafe fn get_dtv(tls_storage: *mut libc::c_void) -> *mut DtvHead {
    let descr_dtv = tls_storage.offset(1) as *mut *mut DtvHead;
    dbgc!(purple_bold: true, "tls", "get_dtv: {:?}", **descr_dtv);
    *descr_dtv
}

// TODO: rename this to something better, like ModuleInfo or SlotInfo
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct SlotInfo {
    generation: u32,
    info: TlsInfo
}

#[inline(always)]
/// Installs the dtv in the tcbhead_t struct, which is the second element; we hack it for now by simply casting and setting the offset; see `sysdeps/x86_64/nptl/tls.h:128`
unsafe fn install_dtv(tls_storage: *mut libc::c_void, dtv: *mut DtvHead) {
    let descr_dtv = tls_storage.offset(1) as *mut *mut Dtv;
    *descr_dtv = dtv.offset(1) as *mut Dtv;
}

unsafe fn allocate_dtv (tls_storage: *mut libc::c_void) -> *mut libc::c_void {
    let dtv_len = 2 + DTV_SURPLUS;
    dbgc!(purple_bold: true, "tls", "allocate_dtv dtv_len: {:?}", dtv_len);
    let dtv = libc::calloc(dtv_len + 2, SIZEOF_DTV as libc::size_t) as *mut DtvHead;
    dbgc!(purple_bold: true, "tls", "allocate_dtv calloc dtv: {:?}", dtv);
    if !dtv.is_null() {
        // hehe surries!
        (*dtv.offset(0)).counter = dtv_len;
        dbgc!(purple_bold: true, "tls", "allocate_dtv calloc dtv: {:?}", *dtv);
        install_dtv(tls_storage, dtv);
        dbgc!(purple_bold: true, "tls", "allocate_dtv calloc post install: {:?} dtv: {:?}", **(tls_storage.offset(1) as *mut *mut Dtv), *dtv);
        tls_storage
    } else {
        dbgc!(purple_bold: true, "tls", "allocate_dtv dtv is NULL");
        ::std::ptr::null_mut::<libc::c_void>()
    }
}

pub unsafe fn _dl_allocate_tls_storage () -> *mut libc::c_void {
    let mut result = libc::memalign(TLS_STATIC_SIZE, TLS_STATIC_ALIGN);
    dbgc!(purple_bold: true, "tls", "_dl_allocate_tls_storage result: {:?}", result);
    let allocated = result; // to be used by free in case fails, unimplemented
    result = result.offset(TLS_STATIC_SIZE as isize - TLS_TCB_SIZE as isize);
    dbgc!(purple_bold: true, "tls", "_dl_allocate_tls_storage post result: {:?}", result);
    memset(result as *mut u8, 0x0, TLS_TCB_SIZE);
    dbgc!(purple_bold: true, "tls", "_dl_allocate_tls_storage memset result: {:?}", result);
    allocate_dtv(result)
}

// TODO: finish
// dl-tls.c:448 _dl_allocate_tls_init (void *result)
// TODO: replace max_dtv_idx/modid and tls_clients with mut self
pub unsafe fn allocate_tls_init (max_dtv_idx: u32, tls_storage: *mut libc::c_void, modules: &[SlotInfo]) -> *mut libc::c_void {
    if tls_storage.is_null() {
        // TODO: actually don't think I can panic, because no TLS yet :/
        panic!("Error: tls storage failed to allocate");
    }
    let dtv = get_dtv(tls_storage);
    // TODO: check if current dtv is large enough
    // do this by examining the len (which is 14 + whatever modules we loaded) and checking if it's smaller than our dtv_idx counter (basically last modid)
    // need to write a resize_dtv routine of course, too
    // and then call install_dtv
    let mut total = 0;
    let mut maxgen = 0;
    dbgc!(purple_bold: true, "tls", "allocate_tls entering loop with modules: {:?}", modules);
    // TODO: fix this, broken w.r.t. glibc implementation because
    // slotinfo_list is a linked list of slotinfo_list, with len, next, and slotinfo[]; but we're just using the slotinfo[] here, because for simple test programs the linked list is always one element...
    loop {
        let cnt = if total == 0 { 1 } else { 0 };
        for cnt in cnt..modules.len() {
            if total + cnt > max_dtv_idx as usize {
                break;
            }
            dbgc!(purple_bold: true, "tls", "allocate_tls cnt: {:?}", cnt);
            let info = modules[cnt].info;
            maxgen = cmp::max(maxgen, modules[cnt].generation);
            let mut dtv_ = dtv.offset(info.modid as isize) as *mut Dtv;
            *dtv_ = Dtv { val: TLS_DTV_UNALLOCATED, is_static: false};

            // cfg TLS_TCB_AT_TP
            let dest = (tls_storage.offset(-info.offset as isize)) as *mut u8;
            let size = info.blocksize - info.image_size;
            dbgc!(purple_bold: true, "tls", "memset + memcpy: {:?} with size: {} and info.image {} info.image_size {}", dest, size, info.image, info.image_size);
            ::std::ptr::copy_nonoverlapping(dest, info.image as *mut u8, info.image_size);
//            libc::memcpy(dest, info.image, info.image_size);
            memset(dest, 0u8, size);
        }

        total += cnt;
        dbgc!(purple_bold: true, "tls", "allocate_tls new total: {:?}", total);
        if total >= max_dtv_idx as usize {
            break;
        }
    }

    dbgc!(purple_bold: true, "tls", "allocate_tls finished, setting dtv head to maxgen {} with tls storage: {:?}", maxgen, tls_storage);
    *dtv = DtvHead { counter: maxgen as usize, _padding: [0u8; 4]};
    tls_storage
}

#[inline(always)]
/// misc/sys/param.h
/// this is not the __GNUC__ version, but simpler; maybe it'll work?
fn roundup(x: usize, y: usize) -> usize {
    ((x + (y - 1)) / y) * y
}

/// Implements:
/// dl-tls.c:137 _dl_determine_tlsoffset (void)
fn determine_offset(static_align: &mut usize, static_used: &mut usize, static_size: &mut usize, modules: &mut[SlotInfo]) {
    let mut max_align = TLS_TCB_ALIGN;
    let mut freetop = 0;
    let mut freebottom = 0;
    assert!(modules.len() == 1);

    let mut offset = 0;

    for (i, slot_info) in modules.iter().enumerate() {
        let mut info = slot_info.info;
        let mut off;
        // todo: verify ! = -
        let firstbyte = (!info.firstbyte_offset) & (info.align - 1);
        max_align = cmp::max(max_align, info.align);

        if (freebottom - freetop) >= info.blocksize {
            off = roundup ((freetop + info.blocksize) - firstbyte, info.align + firstbyte);
            if off <= freebottom {
                freetop = off;
                info.offset = off as isize;
                continue;
            }
        }

        off = roundup (offset + info.blocksize - firstbyte, info.align) + firstbyte;
        if off > offset + info.blocksize + (freebottom - freetop) {
            freetop = offset;
            freebottom = off - info.blocksize;
        }
        offset = off;
        info.offset = off as isize;
    }

    *static_used = offset;
    *static_size = roundup(offset + TLS_STATIC_SURPLUS, max_align) + TLS_TCB_SIZE;

    *static_align = max_align;

   dbgc!(purple_bold: true, "tls", "determine_offset final static_used: {} static_size: {} static_align: {} freetop: {} freebottom: {}", static_used, static_size, static_align, freetop, freebottom);
}

/// Implements: sysdeps/x86_64/nptl/tls.h:148 TLS_INIT_TP(thrdescr)
/// sets up the `fs` thread pointer on x86_64
#[inline(always)]
unsafe fn tls_init_tp (tcbp: *mut libc::c_void) {
    syscall!(ARCH_PRCTL, 0x1002, tcbp as usize);
}

// for special tls just for local process i believe, so it can access errno, etc.
// TODO: rtld.c:572 init_tls (void)
// 1. allocates the necessary tcbp
// 2. allocates the slot info list
// 3. sets the appropriate data into the slot info list (determine_offset)
// 4. and installs it via syscall tls_init_tp to the main thread
// 5. EXTRA: we immediately call allocate_tls_init for testing, which is normally called after the module information are all grabbed and accumulated into _rtld_global by the dynamic linker
unsafe fn init_tls(max_dtv_idx: u32, modules: &mut [SlotInfo]) {
    // this will be 64
    let nelem = max_dtv_idx + 1 + TLS_SLOTINFO_SURPLUS as u32;
    // TODO: this should probably be 0?
//    let mut static_align = 64;
//    let mut static_size = 4096;
//    let mut static_used = 120;

    let mut static_align = 0;
    let mut static_size = 0;
    let mut static_used = 0;

    determine_offset(&mut static_align, &mut static_used, &mut static_size, modules);

    dbgc!(purple_bold: true, "tls", "init_tls determine_offset {} {} {}", static_align, static_size, static_used);

    let tcbp = _dl_allocate_tls_storage();
    dbgc!(purple_bold: true, "tls", "init_tls allocating tls storage {:?}", tcbp);
    // make the syscall
    tls_init_tp(tcbp);
    dbgc!(purple_bold: true, "tls", "init_tls installed into thread");
    // glibc sets global tls_init_tp_called = true now and returns the tcbp for later use in allocate_tls_init(tcbp);
    allocate_tls_init(max_dtv_idx, tcbp, modules);
    dbgc!(purple_bold: true, "tls", "init_tls allocate_tls_init call finished");
}

// final process
// 1. init_tls in main thread = dryad
// 2. load, relocate, etc.
// 3. allocate_tls_init (tcbp)

pub const TLS_STATIC_SIZE: libc::size_t = 0x1000;
pub const TLS_STATIC_ALIGN: libc::size_t = 0x40;
pub const TLS_STATIC_ALIGN_MASK: libc::size_t = !(TLS_STATIC_ALIGN - 1);
// sizeof (struct pthread)
pub const TLS_TCB_SIZE: libc::size_t = 0x900;
// sysdeps/generic/ldsodefs.h:402
pub const DTV_SURPLUS: libc::size_t = 14;
// sysdeps/generic/ldsodefs.h:399
pub const TLS_SLOTINFO_SURPLUS: libc::size_t = 62;

// this was the most miserable thing in the world to figure out
// __alignof__ (struct pthread)
pub const TLS_TCB_ALIGN: libc::size_t = 8;

// "Non-shared code has no support for multiple namespaces."
// sysdeps/generic/ldsodefs.h:276
pub const DL_NNS: libc::size_t = 16;
// dl-tls.c:34
pub const TLS_STATIC_SURPLUS: libc::size_t = 64 + DL_NNS * 100;
pub const TLS_DTV_UNALLOCATED: *mut libc::c_void = 0xffffffffffffffff as *mut libc::c_void;

/// https://en.wikipedia.org/wiki/Lachesis_(mythology)
pub struct Lachesis {
    pub modules: Vec<SlotInfo>
}
<|endoftext|>