Problems with nix::sched::clone


#1

Hi.
I’m trying to use the nix::sched::clone function, but It returns an error sais:
thread ‘main’ paniched at ‘Box Any’. (brackets omitted around Any).
The main problem must be my implementation of the callback.
The code I’ve written is the following:

fn child() -> isize {
    Command::new("ip")
        .arg("link")
        .spawn()
        .expect("ip command failed to start");
    thread::sleep(time::Duration::from_secs(1));
    2
}

fn main() {
    const STACK_SIZE: usize = 1024 * 1024;
    let ref mut stack: [u8; STACK_SIZE] = [0; STACK_SIZE];
    let cbk = Box::new(|| child());
    let p = sched::clone(cbk, stack, sched::CLONE_NEWNET, None);
    let p = match p {
        Ok(p) => p,
        Err(err) => panic!(err),
    };
    println!("{}", p);
    if (p as i32) == -1 {
        println!("clone error");
        println!("{:?}", Error::last_os_error());
        exit(1)
    }
    println!("exited: {}", p as i32);
    exit(0)
}

Where I wrong?


#2

The reason your code is not working is because unprivileged processes are not allowed to use CLONE_NEWNET – if you run it as root, it works.

Next, when you paste code, please paste the full thing so that others don’t have to guess which uses are necessary for it to compile. They are:

use std::time;
use std::thread;
use std::process::{Command, exit};
use std::io::Error;

extern crate nix;
use nix::sched;

Instead of

 let p = match p {
    Ok(p) => p,
    Err(err) => panic!(err),
};

you can use:

let p = p.unwrap();
// or better yet
let p = p.expect("Some useful error message here, e.g. failed to clone");

You don’t have to additionally check for (p as i32) == -1. The underlying system call and the libc function libc::clone return -ERRNO or -1 on errors. The nix crate conveniently checks for that and wraps it into a neat Result for you. This way, you can use Rust’s native error handling patterns (match/unwrap/expect/?) instead of C-style ones (return_value == -1). nix calls Error::last_os_error() for you and puts that into the Result it returns.

What I’m saying is that your first match (or the unwrap/expect that I’m suggesting) is enough and after that p is guaranteed to be positive. No need to implement additional check yourself.

You don’t need to wrap the child() function in a closure to use it as FnMut – plain functions implement the functional traits too.

I’ve also made a couple of other small changes to your program:

use std::time;
use std::thread;
use std::process::Command;

extern crate nix;
use nix::sched;

fn child() -> isize {
    Command::new("ip")
        .arg("link")
        .spawn()
        .expect("ip command failed to start");
    thread::sleep(time::Duration::from_secs(1));
    // no idea why we're returning two here
    2
}

fn main() {
    const STACK_SIZE: usize = 1024 * 1024;
    let ref mut stack = [0; STACK_SIZE];
    let p = sched::clone(Box::new(child), stack, sched::CLONE_NEWNET, None)
                .expect("Failed to spawn the child");
    println!("{}", p);
    // not sure why the following line is even needed
    println!("exited: {}", p as i32);
}

When run as a regular user, it fails with the following error message: thread 'main' panicked at 'Failed to spawn the child: Sys(EPERM)' (see how nix extracted the error and wrapped it up for us?), and when run under root (e.g. with sudo), it works as expected.