<commit_before>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#![cfg(test)]

extern crate capnp;
extern crate capnp_rpc;

#[macro_use]
extern crate gj;

use gj::{EventLoop, Promise};
use gj::io::unix;
use capnp::Error;
use capnp_rpc::{RpcSystem, rpc_twoparty_capnp, twoparty};

pub mod test_capnp {
  include!(concat!(env!("OUT_DIR"), "/test_capnp.rs"));
}

pub mod impls;
pub mod test_util;

#[test]
fn drop_rpc_system() {
    EventLoop::top_level(|wait_scope| {
        let (instream, _outstream) = try!(unix::Stream::new_pair());
        let (reader, writer) = instream.split();
        let network =
            Box::new(twoparty::VatNetwork::new(reader, writer,
                                               rpc_twoparty_capnp::Side::Client,
                                               Default::default()));
        let rpc_system = RpcSystem::new(network, None);
        drop(rpc_system);
        try!(Promise::<(),Error>::ok(()).wait(wait_scope));
        Ok(())
    }).expect("top level error");
}

fn set_up_rpc<F>(main: F)
    where F: FnOnce(test_capnp::bootstrap::Client) -> Promise<(), Error>,
          F: Send + 'static
{
    EventLoop::top_level(|wait_scope| {
        let (join_handle, stream) = try!(unix::spawn(|stream, wait_scope| {

            let (reader, writer) = stream.split();
            let mut network =
                Box::new(twoparty::VatNetwork::new(reader, writer,
                                                   rpc_twoparty_capnp::Side::Server,
                                                   Default::default()));
            let disconnect_promise = network.on_disconnect();
            let bootstrap =
                test_capnp::bootstrap::ToClient::new(impls::Bootstrap).from_server::<::capnp_rpc::Server>();

            let _rpc_system = RpcSystem::new(network, Some(bootstrap.client));
            try!(disconnect_promise.wait(wait_scope));
            Ok(())
        }));

        let (reader, writer) = stream.split();
        let network =
            Box::new(twoparty::VatNetwork::new(reader, writer,
                                               rpc_twoparty_capnp::Side::Client,
                                               Default::default()));

        let mut rpc_system = RpcSystem::new(network, None);
        let client: test_capnp::bootstrap::Client = rpc_system.bootstrap(rpc_twoparty_capnp::Side::Server);

        try!(main(client).wait(wait_scope));
        drop(rpc_system);
        join_handle.join().expect("thread exited unsuccessfully");
        Ok(())
    }).expect("top level error");
}

#[test]
fn do_nothing() {
    set_up_rpc(|_client| {
        Promise::ok(())
    });
}

#[test]
fn basic() {
    set_up_rpc(|client| {
        client.test_interface_request().send().promise.then(|response| {

            let client = pry!(pry!(response.get()).get_cap());
            let mut request1 = client.foo_request();
            request1.get().set_i(123);
            request1.get().set_j(true);
            let promise1 = request1.send().promise.then(|response| {
                if "foo" == pry!(pry!(response.get()).get_x()) {
                    Promise::ok(())
                } else {
                    Promise::err(Error::failed("expected X to equal 'foo'".to_string()))
                }
            });

            let request3 = client.bar_request();
            let promise3 = request3.send().promise.then_else(|result| {
                // We expect this call to fail.
                match result {
                    Ok(_) => {
                        Promise::err(Error::failed("expected bar() to fail".to_string()))
                    }
                    Err(_) => {
                        Promise::ok(())
                    }
                }
            });

            let mut request2 = client.baz_request();
            ::test_util::init_test_message(pry!(request2.get().get_s()));
            let promise2 = request2.send().promise.map(|_| Ok(()));

            Promise::all(vec![promise1, promise2, promise3].into_iter()).map(|_| Ok(()))
        })
    });
}

#[test]
fn pipelining() {
    set_up_rpc(|client| {
        client.test_pipeline_request().send().promise.then(|response| {
            let client = pry!(pry!(response.get()).get_cap());
            let mut request = client.get_cap_request();
            request.get().set_n(234);
            let server = impls::TestInterface::new();
            let chained_call_count = server.get_call_count();
            request.get().set_in_cap(
                ::test_capnp::test_interface::ToClient::new(server).from_server::<::capnp_rpc::Server>());

            let promise = request.send();

            let mut pipeline_request = promise.pipeline.get_out_box().get_cap().foo_request();
            pipeline_request.get().set_i(321);
            let pipeline_promise = pipeline_request.send();

            let pipeline_request2 = {
                let extends_client =
                    ::test_capnp::test_extends::Client { client: promise.pipeline.get_out_box().get_cap().client };
                extends_client.grault_request()
            };
            let pipeline_promise2 = pipeline_request2.send();

            drop(promise); // Just to be annoying, drop the original promise.

            if chained_call_count.get() != 0 {
                return Promise::err(Error::failed("expeced chained_call_count to equal 0".to_string()));
            }

            pipeline_promise.promise.then(move |response| {
                if pry!(pry!(response.get()).get_x()) != "bar" {
                    return Promise::err(Error::failed("expected x to equal 'bar'".to_string()));
                }
                pipeline_promise2.promise.then(move |response| {
                    ::test_util::CheckTestMessage::check_test_message(pry!(response.get()));
                    assert_eq!(chained_call_count.get(), 1);
                    Promise::ok(())
                })
            })
        })
    });
}

#[test]
fn null_capability() {
    let mut message = ::capnp::message::Builder::new_default();
    let root: ::test_capnp::test_all_types::Builder = message.get_root().unwrap();

    // In capnproto-c++, this would return a BrokenCap. Here, it returns a decode error.
    // Would it be worthwhile to try to match the C++ behavior here? We would need something
    // like the BrokenCapFactory singleton.
    assert!(root.get_interface_field().is_err());
}

#[test]
fn release_simple() {
    set_up_rpc(|client| {
        client.test_more_stuff_request().send().promise.then(|response| {
            let client = pry!(pry!(response.get()).get_cap());

            Promise::all(vec![
                client.get_handle_request().send().promise,
                client.get_handle_request().send().promise].into_iter()).then(move |responses| {
                let handle1 = pry!(pry!(responses[0].get()).get_handle());
                let handle2 = pry!(pry!(responses[1].get()).get_handle());

                let client1 = client.clone();
                let client2 = client.clone();
                client.get_handle_count_request().send().promise.map(|response| {
                    if try!(response.get()).get_count() != 2 {
                        Err(Error::failed("expected handle count to equal 2".to_string()))
                    } else {
                        Ok(())
                    }
                }).then(move |()| {
                    drop(handle1);
                    client1.get_handle_count_request().send().promise.map(|response| {
                        if try!(response.get()).get_count() != 1 {
                            Err(Error::failed("expected handle count to equal 1".to_string()))
                        } else {
                            Ok(())
                        }
                    })
                }).then(move |()| {
                    drop(handle2);
                    client2.get_handle_count_request().send().promise.map(|response| {
                        if try!(response.get()).get_count() != 0 {
                            Err(Error::failed("expected handle count to equal 0".to_string()))
                        } else {
                            Ok(())
                        }
                    })
                })
            })
        })
    });
}

#[test]
fn promise_resolve() {
    set_up_rpc(|client| {
        client.test_more_stuff_request().send().promise.then(|response| {
            let client = pry!(pry!(response.get()).get_cap());

            let mut request = client.call_foo_request();
            let mut request2 = client.call_foo_when_resolved_request();

            let (paf_promise, paf_fulfiller) =
                Promise::<::test_capnp::test_interface::Client, Error>::and_fulfiller();

            {
                let mut fork = paf_promise.fork();
                let cap1 = ::capnp_rpc::new_promise_client(fork.add_branch().map(|c| Ok(c.client)));
                let cap2 = ::capnp_rpc::new_promise_client(fork.add_branch().map(|c| Ok(c.client)));
                request.get().set_cap(cap1);
                request2.get().set_cap(cap2);
            }

            let promise = request.send();
            let promise2 = request2.send();

            // Make sure getCap() has been called on the server side by sending another call and waiting
            // for it.
            let client2 = ::test_capnp::test_call_order::Client { client: client.clone().client };
            client2.get_call_sequence_request().send().promise.then(move |response| {
                Promise::ok(())
            })
        })
    });
}

fn get_call_sequence(client: &::test_capnp::test_call_order::Client, expected: u32)
                     -> ::capnp::capability::RemotePromise<::test_capnp::test_call_order::get_call_sequence_results::Owned>
{
    let mut req = client.get_call_sequence_request();
    req.get().set_expected(expected);
    req.send()
}

#[test]
fn embargo() {
    set_up_rpc(|client| {
        client.test_more_stuff_request().send().promise.then(|response| {
            let client = pry!(pry!(response.get()).get_cap());

            let server = ::impls::TestCallOrder::new();

            // ugh, we need upcasting.
            let client2 = ::test_capnp::test_call_order::Client { client: client.clone().client };
            let early_call = client2.get_call_sequence_request().send();
            drop(client2);

            let mut echo_request = client.echo_request();
            echo_request.get().set_cap(
                ::test_capnp::test_call_order::ToClient::new(server).from_server::<::capnp_rpc::Server>());
            let echo = echo_request.send();

            let pipeline = echo.pipeline.get_cap();

            let call0 = get_call_sequence(&pipeline, 0);

            let call1 = get_call_sequence(&pipeline, 1);

            early_call.promise.then(move |_early_call_response| {
                let call2 = get_call_sequence(&pipeline, 2);
                echo.promise.then(move |_echo_response| {
                    let call3 = get_call_sequence(&pipeline, 3);
                    let call4 = get_call_sequence(&pipeline, 4);
                    let call5 = get_call_sequence(&pipeline, 5);

                    Promise::all(vec![call0.promise,
                                      call1.promise,
                                      call2.promise,
                                      call3.promise,
                                      call4.promise,
                                      call5.promise].into_iter()).map(|responses| {
                        let mut counter = 0;
                        for r in responses.into_iter() {
                            if counter != try!(r.get()).get_n() {
                                return Err(Error::failed(
                                    "calls arrived out of order".to_string()))
                            }
                            counter += 1;
                        }
                        Ok(())
                    })
                })
            })
        })
    });
}
<commit_msg>add more to the test<commit_after>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#![cfg(test)]

extern crate capnp;
extern crate capnp_rpc;

#[macro_use]
extern crate gj;

use gj::{EventLoop, Promise};
use gj::io::unix;
use capnp::Error;
use capnp_rpc::{RpcSystem, rpc_twoparty_capnp, twoparty};

pub mod test_capnp {
  include!(concat!(env!("OUT_DIR"), "/test_capnp.rs"));
}

pub mod impls;
pub mod test_util;

#[test]
fn drop_rpc_system() {
    EventLoop::top_level(|wait_scope| {
        let (instream, _outstream) = try!(unix::Stream::new_pair());
        let (reader, writer) = instream.split();
        let network =
            Box::new(twoparty::VatNetwork::new(reader, writer,
                                               rpc_twoparty_capnp::Side::Client,
                                               Default::default()));
        let rpc_system = RpcSystem::new(network, None);
        drop(rpc_system);
        try!(Promise::<(),Error>::ok(()).wait(wait_scope));
        Ok(())
    }).expect("top level error");
}

fn set_up_rpc<F>(main: F)
    where F: FnOnce(test_capnp::bootstrap::Client) -> Promise<(), Error>,
          F: Send + 'static
{
    EventLoop::top_level(|wait_scope| {
        let (join_handle, stream) = try!(unix::spawn(|stream, wait_scope| {

            let (reader, writer) = stream.split();
            let mut network =
                Box::new(twoparty::VatNetwork::new(reader, writer,
                                                   rpc_twoparty_capnp::Side::Server,
                                                   Default::default()));
            let disconnect_promise = network.on_disconnect();
            let bootstrap =
                test_capnp::bootstrap::ToClient::new(impls::Bootstrap).from_server::<::capnp_rpc::Server>();

            let _rpc_system = RpcSystem::new(network, Some(bootstrap.client));
            try!(disconnect_promise.wait(wait_scope));
            Ok(())
        }));

        let (reader, writer) = stream.split();
        let network =
            Box::new(twoparty::VatNetwork::new(reader, writer,
                                               rpc_twoparty_capnp::Side::Client,
                                               Default::default()));

        let mut rpc_system = RpcSystem::new(network, None);
        let client: test_capnp::bootstrap::Client = rpc_system.bootstrap(rpc_twoparty_capnp::Side::Server);

        try!(main(client).wait(wait_scope));
        drop(rpc_system);
        join_handle.join().expect("thread exited unsuccessfully");
        Ok(())
    }).expect("top level error");
}

#[test]
fn do_nothing() {
    set_up_rpc(|_client| {
        Promise::ok(())
    });
}

#[test]
fn basic() {
    set_up_rpc(|client| {
        client.test_interface_request().send().promise.then(|response| {

            let client = pry!(pry!(response.get()).get_cap());
            let mut request1 = client.foo_request();
            request1.get().set_i(123);
            request1.get().set_j(true);
            let promise1 = request1.send().promise.then(|response| {
                if "foo" == pry!(pry!(response.get()).get_x()) {
                    Promise::ok(())
                } else {
                    Promise::err(Error::failed("expected X to equal 'foo'".to_string()))
                }
            });

            let request3 = client.bar_request();
            let promise3 = request3.send().promise.then_else(|result| {
                // We expect this call to fail.
                match result {
                    Ok(_) => {
                        Promise::err(Error::failed("expected bar() to fail".to_string()))
                    }
                    Err(_) => {
                        Promise::ok(())
                    }
                }
            });

            let mut request2 = client.baz_request();
            ::test_util::init_test_message(pry!(request2.get().get_s()));
            let promise2 = request2.send().promise.map(|_| Ok(()));

            Promise::all(vec![promise1, promise2, promise3].into_iter()).map(|_| Ok(()))
        })
    });
}

#[test]
fn pipelining() {
    set_up_rpc(|client| {
        client.test_pipeline_request().send().promise.then(|response| {
            let client = pry!(pry!(response.get()).get_cap());
            let mut request = client.get_cap_request();
            request.get().set_n(234);
            let server = impls::TestInterface::new();
            let chained_call_count = server.get_call_count();
            request.get().set_in_cap(
                ::test_capnp::test_interface::ToClient::new(server).from_server::<::capnp_rpc::Server>());

            let promise = request.send();

            let mut pipeline_request = promise.pipeline.get_out_box().get_cap().foo_request();
            pipeline_request.get().set_i(321);
            let pipeline_promise = pipeline_request.send();

            let pipeline_request2 = {
                let extends_client =
                    ::test_capnp::test_extends::Client { client: promise.pipeline.get_out_box().get_cap().client };
                extends_client.grault_request()
            };
            let pipeline_promise2 = pipeline_request2.send();

            drop(promise); // Just to be annoying, drop the original promise.

            if chained_call_count.get() != 0 {
                return Promise::err(Error::failed("expeced chained_call_count to equal 0".to_string()));
            }

            pipeline_promise.promise.then(move |response| {
                if pry!(pry!(response.get()).get_x()) != "bar" {
                    return Promise::err(Error::failed("expected x to equal 'bar'".to_string()));
                }
                pipeline_promise2.promise.then(move |response| {
                    ::test_util::CheckTestMessage::check_test_message(pry!(response.get()));
                    assert_eq!(chained_call_count.get(), 1);
                    Promise::ok(())
                })
            })
        })
    });
}

#[test]
fn null_capability() {
    let mut message = ::capnp::message::Builder::new_default();
    let root: ::test_capnp::test_all_types::Builder = message.get_root().unwrap();

    // In capnproto-c++, this would return a BrokenCap. Here, it returns a decode error.
    // Would it be worthwhile to try to match the C++ behavior here? We would need something
    // like the BrokenCapFactory singleton.
    assert!(root.get_interface_field().is_err());
}

#[test]
fn release_simple() {
    set_up_rpc(|client| {
        client.test_more_stuff_request().send().promise.then(|response| {
            let client = pry!(pry!(response.get()).get_cap());

            Promise::all(vec![
                client.get_handle_request().send().promise,
                client.get_handle_request().send().promise].into_iter()).then(move |responses| {
                let handle1 = pry!(pry!(responses[0].get()).get_handle());
                let handle2 = pry!(pry!(responses[1].get()).get_handle());

                let client1 = client.clone();
                let client2 = client.clone();
                client.get_handle_count_request().send().promise.map(|response| {
                    if try!(response.get()).get_count() != 2 {
                        Err(Error::failed("expected handle count to equal 2".to_string()))
                    } else {
                        Ok(())
                    }
                }).then(move |()| {
                    drop(handle1);
                    client1.get_handle_count_request().send().promise.map(|response| {
                        if try!(response.get()).get_count() != 1 {
                            Err(Error::failed("expected handle count to equal 1".to_string()))
                        } else {
                            Ok(())
                        }
                    })
                }).then(move |()| {
                    drop(handle2);
                    client2.get_handle_count_request().send().promise.map(|response| {
                        if try!(response.get()).get_count() != 0 {
                            Err(Error::failed("expected handle count to equal 0".to_string()))
                        } else {
                            Ok(())
                        }
                    })
                })
            })
        })
    });
}

#[test]
fn promise_resolve() {
    set_up_rpc(|client| {
        client.test_more_stuff_request().send().promise.then(|response| {
            let client = pry!(pry!(response.get()).get_cap());

            let mut request = client.call_foo_request();
            let mut request2 = client.call_foo_when_resolved_request();

            let (paf_promise, paf_fulfiller) =
                Promise::<::test_capnp::test_interface::Client, Error>::and_fulfiller();

            {
                let mut fork = paf_promise.fork();
                let cap1 = ::capnp_rpc::new_promise_client(fork.add_branch().map(|c| Ok(c.client)));
                let cap2 = ::capnp_rpc::new_promise_client(fork.add_branch().map(|c| Ok(c.client)));
                request.get().set_cap(cap1);
                request2.get().set_cap(cap2);
            }

            let promise = request.send().promise;
            let promise2 = request2.send().promise;

            // Make sure getCap() has been called on the server side by sending another call and waiting
            // for it.
            let client2 = ::test_capnp::test_call_order::Client { client: client.clone().client };
            client2.get_call_sequence_request().send().promise.then(move |_response| {
                let server = impls::TestInterface::new();

                paf_fulfiller.fulfill(
                    ::test_capnp::test_interface::ToClient::new(server).from_server::<::capnp_rpc::Server>());

                promise.then(move |response| {
                    if pry!(pry!(response.get()).get_s()) != "bar" {
                        return Promise::err(Error::failed("expected s to equal 'bar'".to_string()));
                    }
                    Promise::ok(())
/*                    promise2.then(move |response| {
                        if pry!(pry!(response.get()).get_s()) != "bar" {
                            return Promise::err(Error::failed("expected s to equal 'bar'".to_string()));
                        }

                        Promise::ok(())
                    })*/
                })
            })
        })
    });
}

fn get_call_sequence(client: &::test_capnp::test_call_order::Client, expected: u32)
                     -> ::capnp::capability::RemotePromise<::test_capnp::test_call_order::get_call_sequence_results::Owned>
{
    let mut req = client.get_call_sequence_request();
    req.get().set_expected(expected);
    req.send()
}

#[test]
fn embargo() {
    set_up_rpc(|client| {
        client.test_more_stuff_request().send().promise.then(|response| {
            let client = pry!(pry!(response.get()).get_cap());

            let server = ::impls::TestCallOrder::new();

            // ugh, we need upcasting.
            let client2 = ::test_capnp::test_call_order::Client { client: client.clone().client };
            let early_call = client2.get_call_sequence_request().send();
            drop(client2);

            let mut echo_request = client.echo_request();
            echo_request.get().set_cap(
                ::test_capnp::test_call_order::ToClient::new(server).from_server::<::capnp_rpc::Server>());
            let echo = echo_request.send();

            let pipeline = echo.pipeline.get_cap();

            let call0 = get_call_sequence(&pipeline, 0);

            let call1 = get_call_sequence(&pipeline, 1);

            early_call.promise.then(move |_early_call_response| {
                let call2 = get_call_sequence(&pipeline, 2);
                echo.promise.then(move |_echo_response| {
                    let call3 = get_call_sequence(&pipeline, 3);
                    let call4 = get_call_sequence(&pipeline, 4);
                    let call5 = get_call_sequence(&pipeline, 5);

                    Promise::all(vec![call0.promise,
                                      call1.promise,
                                      call2.promise,
                                      call3.promise,
                                      call4.promise,
                                      call5.promise].into_iter()).map(|responses| {
                        let mut counter = 0;
                        for r in responses.into_iter() {
                            if counter != try!(r.get()).get_n() {
                                return Err(Error::failed(
                                    "calls arrived out of order".to_string()))
                            }
                            counter += 1;
                        }
                        Ok(())
                    })
                })
            })
        })
    });
}
<|endoftext|>


<commit_before>use std::io;
use std::num::{FPNaN, FPInfinite};
use std::f64;

use ser;

/// A structure for implementing serialization to JSON.
pub struct Serializer<W> {
    writer: W,
}

impl<W: Writer> Serializer<W> {
    /// Creates a new JSON serializer whose output will be written to the writer
    /// specified.
    #[inline]
    pub fn new(writer: W) -> Serializer<W> {
        Serializer {
            writer: writer,
        }
    }

    /// Unwrap the Writer from the Serializer.
    #[inline]
    pub fn unwrap(self) -> W {
        self.writer
    }
}

impl<W: Writer> ser::VisitorState<io::IoResult<()>> for Serializer<W> {
    #[inline]
    fn visit_null(&mut self) -> io::IoResult<()> {
        self.writer.write_str("null")
    }

    #[inline]
    fn visit_bool(&mut self, value: bool) -> io::IoResult<()> {
        if value {
            self.writer.write_str("true")
        } else {
            self.writer.write_str("false")
        }
    }

    #[inline]
    fn visit_int(&mut self, value: int) -> io::IoResult<()> {
        write!(self.writer, "{}", value)
    }

    #[inline]
    fn visit_i8(&mut self, value: i8) -> io::IoResult<()> {
        write!(self.writer, "{}", value)
    }

    #[inline]
    fn visit_i16(&mut self, value: i16) -> io::IoResult<()> {
        write!(self.writer, "{}", value)
    }

    #[inline]
    fn visit_i32(&mut self, value: i32) -> io::IoResult<()> {
        write!(self.writer, "{}", value)
    }

    #[inline]
    fn visit_i64(&mut self, value: i64) -> io::IoResult<()> {
        write!(self.writer, "{}", value)
    }

    #[inline]
    fn visit_uint(&mut self, value: uint) -> io::IoResult<()> {
        write!(self.writer, "{}", value)
    }

    #[inline]
    fn visit_u8(&mut self, value: u8) -> io::IoResult<()> {
        write!(self.writer, "{}", value)
    }

    #[inline]
    fn visit_u16(&mut self, value: u16) -> io::IoResult<()> {
        write!(self.writer, "{}", value)
    }

    #[inline]
    fn visit_u32(&mut self, value: u32) -> io::IoResult<()> {
        write!(self.writer, "{}", value)
    }

    #[inline]
    fn visit_u64(&mut self, value: u64) -> io::IoResult<()> {
        write!(self.writer, "{}", value)
    }

    #[inline]
    fn visit_f64(&mut self, value: f64) -> io::IoResult<()> {
        fmt_f64_or_null(&mut self.writer, value)
    }

    #[inline]
    fn visit_char(&mut self, v: char) -> io::IoResult<()> {
        escape_char(&mut self.writer, v)
    }

    #[inline]
    fn visit_str(&mut self, value: &str) -> io::IoResult<()> {
        escape_str(&mut self.writer, value)
    }

    #[inline]
    fn visit_seq<
        V: ser::Visitor<Serializer<W>, io::IoResult<()>>
    >(&mut self, mut visitor: V) -> io::IoResult<()> {
        try!(self.writer.write_str("["));

        loop {
            match visitor.visit(self) {
                Some(Ok(())) => { }
                Some(Err(err)) => { return Err(err); }
                None => { break; }
            }
        }

        self.writer.write_str("]")
    }

    #[inline]
    fn visit_seq_elt<
        T: ser::Serialize<Serializer<W>, io::IoResult<()>>
    >(&mut self, first: bool, value: T) -> io::IoResult<()> {
        if !first {
            try!(self.writer.write_str(","));
        }

        value.serialize(self)
    }

    #[inline]
    fn visit_map<
        V: ser::Visitor<Serializer<W>, io::IoResult<()>>
    >(&mut self, mut visitor: V) -> io::IoResult<()> {
        try!(self.writer.write_str("{{"));

        loop {
            match visitor.visit(self) {
                Some(Ok(())) => { }
                Some(Err(err)) => { return Err(err); }
                None => { break; }
            }
        }

        self.writer.write_str("}}")
    }

    #[inline]
    fn visit_map_elt<
        K: ser::Serialize<Serializer<W>, io::IoResult<()>>,
        V: ser::Serialize<Serializer<W>, io::IoResult<()>>
    >(&mut self, first: bool, key: K, value: V) -> io::IoResult<()> {
        if !first {
            try!(self.writer.write_str(","));
        }

        try!(key.serialize(self));
        try!(self.writer.write_str(":"));
        value.serialize(self)
    }
}

fn escape_bytes<W: Writer>(wr: &mut W, bytes: &[u8]) -> io::IoResult<()> {
    try!(wr.write_str("\""));

    let mut start = 0;

    for (i, byte) in bytes.iter().enumerate() {
        let escaped = match *byte {
            b'"' => "\\\"",
            b'\\' => "\\\\",
            b'\x08' => "\\b",
            b'\x0c' => "\\f",
            b'\n' => "\\n",
            b'\r' => "\\r",
            b'\t' => "\\t",
            _ => { continue; }
        };

        if start < i {
            try!(wr.write(bytes.slice(start, i)));
        }

        try!(wr.write_str(escaped));

        start = i + 1;
    }

    if start != bytes.len() {
        try!(wr.write(bytes.slice_from(start)));
    }

    wr.write_str("\"")
}

fn escape_str<W: Writer>(wr: &mut W, value: &str) -> io::IoResult<()> {
    escape_bytes(wr, value.as_bytes())
}

pub fn escape_char<W: Writer>(wr: &mut W, value: char) -> io::IoResult<()> {
    let mut buf = [0, .. 4];
    value.encode_utf8(buf);
    escape_bytes(wr, buf)
}

fn fmt_f64_or_null<W: Writer>(wr: &mut W, value: f64) -> io::IoResult<()> {
    match value.classify() {
        FPNaN | FPInfinite => wr.write_str("null"),
        _ => wr.write_str(f64::to_str_digits(value, 6).as_slice()),
    }
}

#[inline]
pub fn to_vec<
    T: ser::Serialize<Serializer<io::MemWriter>, io::IoResult<()>>
>(value: &T) -> io::IoResult<Vec<u8>> {
    let writer = io::MemWriter::new();
    let mut state = Serializer::new(writer);
    try!(value.serialize(&mut state));
    Ok(state.unwrap().unwrap())
}

#[inline]
pub fn to_string<
    T: ser::Serialize<Serializer<io::MemWriter>, io::IoResult<()>>
>(value: &T) -> io::IoResult<Result<String, Vec<u8>>> {
    let vec = try!(to_vec(value));
    Ok(String::from_utf8(vec))
}
<commit_msg>Bump up the json::to_vec mem writer buf size<commit_after>use std::io;
use std::num::{FPNaN, FPInfinite};
use std::f64;

use ser;

/// A structure for implementing serialization to JSON.
pub struct Serializer<W> {
    writer: W,
}

impl<W: Writer> Serializer<W> {
    /// Creates a new JSON serializer whose output will be written to the writer
    /// specified.
    #[inline]
    pub fn new(writer: W) -> Serializer<W> {
        Serializer {
            writer: writer,
        }
    }

    /// Unwrap the Writer from the Serializer.
    #[inline]
    pub fn unwrap(self) -> W {
        self.writer
    }
}

impl<W: Writer> ser::VisitorState<io::IoResult<()>> for Serializer<W> {
    #[inline]
    fn visit_null(&mut self) -> io::IoResult<()> {
        self.writer.write_str("null")
    }

    #[inline]
    fn visit_bool(&mut self, value: bool) -> io::IoResult<()> {
        if value {
            self.writer.write_str("true")
        } else {
            self.writer.write_str("false")
        }
    }

    #[inline]
    fn visit_int(&mut self, value: int) -> io::IoResult<()> {
        write!(self.writer, "{}", value)
    }

    #[inline]
    fn visit_i8(&mut self, value: i8) -> io::IoResult<()> {
        write!(self.writer, "{}", value)
    }

    #[inline]
    fn visit_i16(&mut self, value: i16) -> io::IoResult<()> {
        write!(self.writer, "{}", value)
    }

    #[inline]
    fn visit_i32(&mut self, value: i32) -> io::IoResult<()> {
        write!(self.writer, "{}", value)
    }

    #[inline]
    fn visit_i64(&mut self, value: i64) -> io::IoResult<()> {
        write!(self.writer, "{}", value)
    }

    #[inline]
    fn visit_uint(&mut self, value: uint) -> io::IoResult<()> {
        write!(self.writer, "{}", value)
    }

    #[inline]
    fn visit_u8(&mut self, value: u8) -> io::IoResult<()> {
        write!(self.writer, "{}", value)
    }

    #[inline]
    fn visit_u16(&mut self, value: u16) -> io::IoResult<()> {
        write!(self.writer, "{}", value)
    }

    #[inline]
    fn visit_u32(&mut self, value: u32) -> io::IoResult<()> {
        write!(self.writer, "{}", value)
    }

    #[inline]
    fn visit_u64(&mut self, value: u64) -> io::IoResult<()> {
        write!(self.writer, "{}", value)
    }

    #[inline]
    fn visit_f64(&mut self, value: f64) -> io::IoResult<()> {
        fmt_f64_or_null(&mut self.writer, value)
    }

    #[inline]
    fn visit_char(&mut self, v: char) -> io::IoResult<()> {
        escape_char(&mut self.writer, v)
    }

    #[inline]
    fn visit_str(&mut self, value: &str) -> io::IoResult<()> {
        escape_str(&mut self.writer, value)
    }

    #[inline]
    fn visit_seq<
        V: ser::Visitor<Serializer<W>, io::IoResult<()>>
    >(&mut self, mut visitor: V) -> io::IoResult<()> {
        try!(self.writer.write_str("["));

        loop {
            match visitor.visit(self) {
                Some(Ok(())) => { }
                Some(Err(err)) => { return Err(err); }
                None => { break; }
            }
        }

        self.writer.write_str("]")
    }

    #[inline]
    fn visit_seq_elt<
        T: ser::Serialize<Serializer<W>, io::IoResult<()>>
    >(&mut self, first: bool, value: T) -> io::IoResult<()> {
        if !first {
            try!(self.writer.write_str(","));
        }

        value.serialize(self)
    }

    #[inline]
    fn visit_map<
        V: ser::Visitor<Serializer<W>, io::IoResult<()>>
    >(&mut self, mut visitor: V) -> io::IoResult<()> {
        try!(self.writer.write_str("{{"));

        loop {
            match visitor.visit(self) {
                Some(Ok(())) => { }
                Some(Err(err)) => { return Err(err); }
                None => { break; }
            }
        }

        self.writer.write_str("}}")
    }

    #[inline]
    fn visit_map_elt<
        K: ser::Serialize<Serializer<W>, io::IoResult<()>>,
        V: ser::Serialize<Serializer<W>, io::IoResult<()>>
    >(&mut self, first: bool, key: K, value: V) -> io::IoResult<()> {
        if !first {
            try!(self.writer.write_str(","));
        }

        try!(key.serialize(self));
        try!(self.writer.write_str(":"));
        value.serialize(self)
    }
}

fn escape_bytes<W: Writer>(wr: &mut W, bytes: &[u8]) -> io::IoResult<()> {
    try!(wr.write_str("\""));

    let mut start = 0;

    for (i, byte) in bytes.iter().enumerate() {
        let escaped = match *byte {
            b'"' => "\\\"",
            b'\\' => "\\\\",
            b'\x08' => "\\b",
            b'\x0c' => "\\f",
            b'\n' => "\\n",
            b'\r' => "\\r",
            b'\t' => "\\t",
            _ => { continue; }
        };

        if start < i {
            try!(wr.write(bytes.slice(start, i)));
        }

        try!(wr.write_str(escaped));

        start = i + 1;
    }

    if start != bytes.len() {
        try!(wr.write(bytes.slice_from(start)));
    }

    wr.write_str("\"")
}

fn escape_str<W: Writer>(wr: &mut W, value: &str) -> io::IoResult<()> {
    escape_bytes(wr, value.as_bytes())
}

pub fn escape_char<W: Writer>(wr: &mut W, value: char) -> io::IoResult<()> {
    let mut buf = [0, .. 4];
    value.encode_utf8(buf);
    escape_bytes(wr, buf)
}

fn fmt_f64_or_null<W: Writer>(wr: &mut W, value: f64) -> io::IoResult<()> {
    match value.classify() {
        FPNaN | FPInfinite => wr.write_str("null"),
        _ => wr.write_str(f64::to_str_digits(value, 6).as_slice()),
    }
}

#[inline]
pub fn to_vec<
    T: ser::Serialize<Serializer<io::MemWriter>, io::IoResult<()>>
>(value: &T) -> io::IoResult<Vec<u8>> {
    let writer = io::MemWriter::with_capacity(1024);
    let mut state = Serializer::new(writer);
    try!(value.serialize(&mut state));
    Ok(state.unwrap().unwrap())
}

#[inline]
pub fn to_string<
    T: ser::Serialize<Serializer<io::MemWriter>, io::IoResult<()>>
>(value: &T) -> io::IoResult<Result<String, Vec<u8>>> {
    let vec = try!(to_vec(value));
    Ok(String::from_utf8(vec))
}
<|endoftext|>


<commit_before>use command_executor::{Command, CommandMetadata, Group as GroupTrait, GroupMetadata};
use super::super::IndyContext;

use libindy::wallet::Wallet;

use std::collections::HashMap;
use std::rc::Rc;

pub struct Group {
    metadata: GroupMetadata
}

impl Group {
    pub fn new() -> Group {
        Group {
            metadata: GroupMetadata::new("wallet", "Wallet management commands")
        }
    }
}

impl GroupTrait for Group {
    fn metadata(&self) -> &GroupMetadata {
        &self.metadata
    }
}

pub struct CreateCommand {
    ctx: Rc<IndyContext>,
    metadata: CommandMetadata,
}

pub struct OpenCommand {
    ctx: Rc<IndyContext>,
    metadata: CommandMetadata,
}

pub struct CloseCommand {
    ctx: Rc<IndyContext>,
    metadata: CommandMetadata,
}

impl CreateCommand {
    pub fn new(ctx: Rc<IndyContext>) -> CreateCommand {
        CreateCommand {
            ctx,
            metadata: CommandMetadata::build("create", "Create new wallet with specified name")
                .add_main_param("name", "The name of new wallet")
                .finalize()
        }
    }
}

impl Command for CreateCommand {
    fn execute(&self, params: &HashMap<&'static str, &str>) -> Result<(), ()> {
        println!("wallet create: >>> execute {:?} while context {:?}", params, self.ctx);
        let pool_name = params.get("pool_name").ok_or((/* TODO error */))?;
        let wallet_name = params.get("name").ok_or((/* TODO error */))?;
        let config: Option<String> = params.get("key").map(|key|
            json!({
                "key": key
            }).to_string());
        Wallet::create_wallet(pool_name,
                              wallet_name,
                              None,
                              config.as_ref().map(String::as_str))
            .map_err(|_| ())?;
        Ok(())
    }

    fn metadata(&self) -> &CommandMetadata {
        &self.metadata
    }
}

impl OpenCommand {
    pub fn new(ctx: Rc<IndyContext>) -> OpenCommand {
        OpenCommand {
            ctx,
            metadata: CommandMetadata::build("open", "Open wallet with specified name. Also close previously opened.")
                .add_main_param("name", "The name of wallet")
                .finalize()
        }
    }
}

impl Command for OpenCommand {
    fn metadata(&self) -> &CommandMetadata {
        &self.metadata
    }

    fn execute(&self, params: &HashMap<&'static str, &str>) -> Result<(), ()> {
        println!("wallet open: >>> execute {:?} while context {:?}", params, self.ctx);
        let wallet_name = params.get("name").ok_or((/* TODO error */))?;
        let config = params.get("config").map(|cfg| *cfg);
        let wallet_handle = Wallet::open_wallet(wallet_name, config)
            .map_err(|_| ())?;
        //TODO close previously opened wallet
        self.ctx.set_current_wallet(wallet_name, wallet_handle);
        Ok(())
    }
}

impl CloseCommand {
    pub fn new(ctx: Rc<IndyContext>) -> CloseCommand {
        CloseCommand {
            ctx,
            metadata: CommandMetadata::build("close", "Close wallet with specified handle.")
                .add_main_param("handle", "The handle of wallet")
                .finalize()
        }
    }
}

impl Command for CloseCommand {
    fn metadata(&self) -> &CommandMetadata {
        &self.metadata
    }

    fn execute(&self, params: &HashMap<&'static str, &str>) -> Result<(), ()> {
        println!("wallet close: >>> execute {:?} while context {:?}", params, self.ctx);
        let wallet_handle = self.ctx.get_current_wallet_handle().ok_or((/* TODO error */))?;
        Wallet::close_wallet(wallet_handle).map_err(|_| ())?;
        self.ctx.reset_current_wallet();
        Ok(())
    }
}

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

    use std::cell::RefCell;

    mod create {
        use super::*;

        #[test]
        pub fn exec_works() {
            TestUtils::cleanup_storage();
            let ctx = Rc::new((IndyContext { cur_wallet: RefCell::new(None) }));
            let cmd = CreateCommand::new(ctx);
            cmd.metadata().help();
            let mut params = HashMap::new();
            params.insert("name", "wallet");
            params.insert("pool_name", "pool");
            cmd.execute(&params).unwrap();
            TestUtils::cleanup_storage();
        }
    }

    mod open {
        use super::*;

        #[test]
        pub fn exec_works() {
            TestUtils::cleanup_storage();
            let ctx = Rc::new((IndyContext { cur_wallet: RefCell::new(None) }));
            let cmd = OpenCommand::new(ctx);
            let mut params = HashMap::new();
            cmd.metadata().help();
            params.insert("name", "wallet");
            cmd.execute(&params).unwrap_err(); //open not created wallet
            TestUtils::cleanup_storage();
        }

        //TODO add open_for_created_works
    }

    mod close {
        use super::*;

        #[test]
        pub fn exec_for_opened_works() {
            TestUtils::cleanup_storage();
            let ctx = Rc::new((IndyContext { cur_wallet: RefCell::new(None) }));

            let cmd = CreateCommand::new(ctx.clone());
            let mut params = HashMap::new();
            params.insert("name", "wallet");
            params.insert("pool_name", "pool");
            cmd.execute(&params).unwrap();

            let cmd = OpenCommand::new(ctx.clone());
            let mut params = HashMap::new();
            params.insert("name", "wallet");
            cmd.execute(&params).unwrap();

            let cmd = CloseCommand::new(ctx.clone());
            let params = HashMap::new();
            cmd.execute(&params).unwrap();

            TestUtils::cleanup_storage();
        }
    }
}<commit_msg>[cli] Fix open wallet params.<commit_after>use command_executor::{Command, CommandMetadata, Group as GroupTrait, GroupMetadata};
use super::super::{IndyContext, serde_json};

use libindy::wallet::Wallet;

use std::collections::HashMap;
use std::rc::Rc;

pub struct Group {
    metadata: GroupMetadata
}

impl Group {
    pub fn new() -> Group {
        Group {
            metadata: GroupMetadata::new("wallet", "Wallet management commands")
        }
    }
}

impl GroupTrait for Group {
    fn metadata(&self) -> &GroupMetadata {
        &self.metadata
    }
}

pub struct CreateCommand {
    ctx: Rc<IndyContext>,
    metadata: CommandMetadata,
}

pub struct OpenCommand {
    ctx: Rc<IndyContext>,
    metadata: CommandMetadata,
}

pub struct CloseCommand {
    ctx: Rc<IndyContext>,
    metadata: CommandMetadata,
}

impl CreateCommand {
    pub fn new(ctx: Rc<IndyContext>) -> CreateCommand {
        CreateCommand {
            ctx,
            metadata: CommandMetadata::build("create", "Create new wallet with specified name")
                .add_main_param("name", "The name of new wallet")
                .finalize()
        }
    }
}

impl Command for CreateCommand {
    fn execute(&self, params: &HashMap<&'static str, &str>) -> Result<(), ()> {
        println!("wallet create: >>> execute {:?} while context {:?}", params, self.ctx);
        let pool_name = params.get("pool_name").ok_or((/* TODO error */))?;
        let wallet_name = params.get("name").ok_or((/* TODO error */))?;
        let config: Option<String> = params.get("key").map(|key|
            json!({
                "key": key
            }).to_string());
        Wallet::create_wallet(pool_name,
                              wallet_name,
                              None,
                              config.as_ref().map(String::as_str))
            .map_err(|_| ())?;
        Ok(())
    }

    fn metadata(&self) -> &CommandMetadata {
        &self.metadata
    }
}

impl OpenCommand {
    pub fn new(ctx: Rc<IndyContext>) -> OpenCommand {
        OpenCommand {
            ctx,
            metadata: CommandMetadata::build("open", "Open wallet with specified name. Also close previously opened.")
                .add_main_param("name", "The name of wallet")
                .finalize()
        }
    }
}

impl Command for OpenCommand {
    fn metadata(&self) -> &CommandMetadata {
        &self.metadata
    }

    fn execute(&self, params: &HashMap<&'static str, &str>) -> Result<(), ()> {
        println!("wallet open: >>> execute {:?} while context {:?}", params, self.ctx);

        let wallet_name = params.get("name").ok_or((/* TODO error */))?;

        let config: Option<String> =
            if params.contains_key("key")
                || params.contains_key("rekey")
                || params.contains_key("freshness_time") {
                let mut config = serde_json::Map::new();

                for str_config_option in &["key", "rekey"] {
                    if let Some(config_option) = params.get(str_config_option) {
                        config.insert(config_option.to_string(), serde_json::Value::String(config_option.to_string()));
                    }
                }

                if let Some(freshness_time) = params.get("freshness_time") {
                    let freshness_time: i64 = freshness_time.parse().map_err(|_| (/* TODO error */))?;
                    config.insert("freshness_time".to_string(), serde_json::Value::from(freshness_time));
                }

                Some(serde_json::to_string(&serde_json::Value::from(config)).unwrap())
            } else {
                None
            };

        let wallet_handle = Wallet::open_wallet(wallet_name, config.as_ref().map(String::as_str))
            .map_err(|_| ())?;
        //TODO close previously opened wallet
        self.ctx.set_current_wallet(wallet_name, wallet_handle);

        Ok(())
    }
}

impl CloseCommand {
    pub fn new(ctx: Rc<IndyContext>) -> CloseCommand {
        CloseCommand {
            ctx,
            metadata: CommandMetadata::build("close", "Close wallet with specified handle.")
                .add_main_param("handle", "The handle of wallet")
                .finalize()
        }
    }
}

impl Command for CloseCommand {
    fn metadata(&self) -> &CommandMetadata {
        &self.metadata
    }

    fn execute(&self, params: &HashMap<&'static str, &str>) -> Result<(), ()> {
        println!("wallet close: >>> execute {:?} while context {:?}", params, self.ctx);
        let wallet_handle = self.ctx.get_current_wallet_handle().ok_or((/* TODO error */))?;
        Wallet::close_wallet(wallet_handle).map_err(|_| ())?;
        self.ctx.reset_current_wallet();
        Ok(())
    }
}

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

    use std::cell::RefCell;

    mod create {
        use super::*;

        #[test]
        pub fn exec_works() {
            TestUtils::cleanup_storage();
            let ctx = Rc::new((IndyContext { cur_wallet: RefCell::new(None) }));
            let cmd = CreateCommand::new(ctx);
            cmd.metadata().help();
            let mut params = HashMap::new();
            params.insert("name", "wallet");
            params.insert("pool_name", "pool");
            cmd.execute(&params).unwrap();
            TestUtils::cleanup_storage();
        }
    }

    mod open {
        use super::*;

        #[test]
        pub fn exec_works() {
            TestUtils::cleanup_storage();
            let ctx = Rc::new((IndyContext { cur_wallet: RefCell::new(None) }));
            let cmd = OpenCommand::new(ctx);
            let mut params = HashMap::new();
            cmd.metadata().help();
            params.insert("name", "wallet");
            cmd.execute(&params).unwrap_err(); //open not created wallet
            TestUtils::cleanup_storage();
        }

        //TODO add open_for_created_works
    }

    mod close {
        use super::*;

        #[test]
        pub fn exec_for_opened_works() {
            TestUtils::cleanup_storage();
            let ctx = Rc::new((IndyContext { cur_wallet: RefCell::new(None) }));

            let cmd = CreateCommand::new(ctx.clone());
            let mut params = HashMap::new();
            params.insert("name", "wallet");
            params.insert("pool_name", "pool");
            cmd.execute(&params).unwrap();

            let cmd = OpenCommand::new(ctx.clone());
            let mut params = HashMap::new();
            params.insert("name", "wallet");
            cmd.execute(&params).unwrap();

            let cmd = CloseCommand::new(ctx.clone());
            let params = HashMap::new();
            cmd.execute(&params).unwrap();

            TestUtils::cleanup_storage();
        }
    }
}<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

mod blocking;
mod client;
mod response;

pub use blocking::JsonRpcClient;
pub use client::{
    get_response_from_batch, process_batch_response, JsonRpcAsyncClient, JsonRpcBatch,
};
pub use libra_json_rpc_types::{errors, views};
pub use response::{JsonRpcResponse, ResponseAsView};
<commit_msg>[json-rpc] Expose AccountAddress and SignedTransaction<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

mod blocking;
mod client;
mod response;

pub use blocking::JsonRpcClient;
pub use client::{
    get_response_from_batch, process_batch_response, JsonRpcAsyncClient, JsonRpcBatch,
};
pub use libra_json_rpc_types::{errors, views};
pub use libra_types::{account_address::AccountAddress, transaction::SignedTransaction};
pub use response::{JsonRpcResponse, ResponseAsView};
<|endoftext|>


<commit_before>use crate::context::Context;
use crate::instance::{
    siginfo_ext::SiginfoExt, FaultDetails, Instance, State, TerminationDetails, CURRENT_INSTANCE,
    HOST_CTX,
};
use crate::sysdeps::UContextPtr;
use failure::Error;
use lazy_static::lazy_static;
use libc::{c_int, c_void, siginfo_t, SIGBUS, SIGSEGV};
use lucet_module_data::TrapCode;
use nix::sys::signal::{
    pthread_sigmask, raise, sigaction, SaFlags, SigAction, SigHandler, SigSet, SigmaskHow, Signal,
};
use std::sync::Mutex;

lazy_static! {
    // TODO: work out an alternative to this that is signal-safe for `reraise_host_signal_in_handler`
    static ref LUCET_SIGNAL_STATE: Mutex<Option<SignalState>> = Mutex::new(None);
}

/// The value returned by
/// [`Instance.signal_handler`](struct.Instance.html#structfield.signal_handler) to determine the
/// outcome of a handled signal.
pub enum SignalBehavior {
    /// Use default behavior, which switches back to the host with `State::Fault` populated.
    Default,
    /// Override default behavior and cause the instance to continue.
    Continue,
    /// Override default behavior and cause the instance to terminate.
    Terminate,
}

pub type SignalHandler = dyn Fn(
    &Instance,
    &Option<TrapCode>,
    libc::c_int,
    *const siginfo_t,
    *const c_void,
) -> SignalBehavior;

pub fn signal_handler_none(
    _inst: &Instance,
    _trapcode: &Option<TrapCode>,
    _signum: libc::c_int,
    _siginfo_ptr: *const siginfo_t,
    _ucontext_ptr: *const c_void,
) -> SignalBehavior {
    SignalBehavior::Default
}

impl Instance {
    pub(crate) fn with_signals_on<F, R>(&mut self, f: F) -> Result<R, Error>
    where
        F: FnOnce(&mut Instance) -> Result<R, Error>,
    {
        // setup signal stack for this thread
        let guest_sigstack = SigStack::new(
            self.alloc.slot().sigstack,
            SigStackFlags::empty(),
            libc::SIGSTKSZ,
        );
        let previous_sigstack = unsafe { sigaltstack(Some(guest_sigstack)) }
            .expect("enabling or changing the signal stack succeeds");
        if let Some(previous_sigstack) = previous_sigstack {
            assert!(
                !previous_sigstack
                    .flags()
                    .contains(SigStackFlags::SS_ONSTACK),
                "an instance was created with a signal stack"
            );
        }
        let mut ostate = LUCET_SIGNAL_STATE.lock().unwrap();
        if let Some(ref mut state) = *ostate {
            state.counter += 1;
        } else {
            unsafe {
                setup_guest_signal_state(&mut ostate);
            }
        }
        drop(ostate);

        // run the body
        let res = f(self);

        let mut ostate = LUCET_SIGNAL_STATE.lock().unwrap();
        let counter_zero = if let Some(ref mut state) = *ostate {
            state.counter -= 1;
            if state.counter == 0 {
                unsafe {
                    // restore the host signal stack
                    if !altstack_flags()
                        .expect("the current stack flags could be retrieved")
                        .contains(SigStackFlags::SS_ONSTACK)
                    {
                        sigaltstack(previous_sigstack).expect("sigaltstack restoration succeeds");
                    }
                    restore_host_signal_state(state);
                }
                true
            } else {
                false
            }
        } else {
            panic!("signal handlers weren't installed at instance exit");
        };
        if counter_zero {
            *ostate = None;
        }

        res
    }
}

/// Signal handler installed during instance execution.
///
/// This function is only designed to handle signals that are the direct result of execution of a
/// hardware instruction from the faulting WASM thread. It thus safely assumes the signal is
/// directed specifically at this thread (i.e. not a different thread or the process as a whole).
extern "C" fn handle_signal(signum: c_int, siginfo_ptr: *mut siginfo_t, ucontext_ptr: *mut c_void) {
    let signal = Signal::from_c_int(signum).expect("signum is a valid signal");
    if !(signal == Signal::SIGBUS
        || signal == Signal::SIGSEGV
        || signal == Signal::SIGILL
        || signal == Signal::SIGFPE)
    {
        panic!("unexpected signal in guest signal handler: {:?}", signal);
    }
    assert!(!siginfo_ptr.is_null(), "siginfo must not be null");

    // Safety: when using a SA_SIGINFO sigaction, the third argument can be cast to a `ucontext_t`
    // pointer per the manpage
    assert!(!ucontext_ptr.is_null(), "ucontext_ptr must not be null");
    let ctx = UContextPtr::new(ucontext_ptr);
    let rip = ctx.get_ip();

    let switch_to_host = CURRENT_INSTANCE.with(|current_instance| {
        let mut current_instance = current_instance.borrow_mut();

        if current_instance.is_none() {
            // If there is no current instance, we've caught a signal raised by a thread that's not
            // running a lucet instance. Restore the host signal handler and reraise the signal,
            // then return if the host handler returns
            unsafe {
                reraise_host_signal_in_handler(signal, signum, siginfo_ptr, ucontext_ptr);
            }
            // don't try context-switching
            return false;
        }

        // Safety: the memory pointed to by CURRENT_INSTANCE should be a valid instance. This is not
        // a trivial property, but relies on the compiler not emitting guest programs that can
        // overwrite the instance.
        let inst = unsafe {
            current_instance
                .as_mut()
                .expect("current instance exists")
                .as_mut()
        };

        let trapcode = inst.module.lookup_trapcode(rip);

        let behavior = (inst.signal_handler)(inst, &trapcode, signum, siginfo_ptr, ucontext_ptr);
        match behavior {
            SignalBehavior::Continue => {
                // return to the guest context without making any modifications to the instance
                false
            }
            SignalBehavior::Terminate => {
                // set the state before jumping back to the host context
                inst.state = State::Terminated {
                    details: TerminationDetails::Signal,
                };
                true
            }
            SignalBehavior::Default => {
                // safety: pointer is checked for null at the top of the function, and the
                // manpage guarantees that a siginfo_t will be passed as the second argument
                let siginfo = unsafe { *siginfo_ptr };
                let rip_addr = rip as usize;
                // If the trap table lookup returned unknown, it is a fatal error
                let unknown_fault = trapcode.is_none();

                // If the trap was a segv or bus fault and the addressed memory was outside the
                // guard pages, it is also a fatal error
                let outside_guard = (siginfo.si_signo == SIGSEGV || siginfo.si_signo == SIGBUS)
                    && !inst.alloc.addr_in_heap_guard(siginfo.si_addr());

                // record the fault and jump back to the host context
                inst.state = State::Fault {
                    details: FaultDetails {
                        fatal: unknown_fault || outside_guard,
                        trapcode: trapcode,
                        rip_addr,
                        // Details set to `None` here: have to wait until `verify_trap_safety` to
                        // fill in these details, because access may not be signal safe.
                        rip_addr_details: None,
                    },
                    siginfo,
                    context: ctx.into(),
                };
                true
            }
        }
    });

    if switch_to_host {
        HOST_CTX.with(|host_ctx| unsafe {
            Context::set_from_signal(&*host_ctx.get())
                .expect("can successfully switch back to the host context");
        });
        unreachable!()
    }
}

struct SignalState {
    counter: usize,
    saved_sigbus: SigAction,
    saved_sigfpe: SigAction,
    saved_sigill: SigAction,
    saved_sigsegv: SigAction,
}

// raw pointers in the saved types
unsafe impl Send for SignalState {}

unsafe fn setup_guest_signal_state(ostate: &mut Option<SignalState>) {
    let mut masked_signals = SigSet::empty();
    masked_signals.add(Signal::SIGBUS);
    masked_signals.add(Signal::SIGFPE);
    masked_signals.add(Signal::SIGILL);
    masked_signals.add(Signal::SIGSEGV);

    // setup signal handlers
    let sa = SigAction::new(
        SigHandler::SigAction(handle_signal),
        SaFlags::SA_RESTART | SaFlags::SA_SIGINFO | SaFlags::SA_ONSTACK,
        masked_signals,
    );
    let saved_sigbus = sigaction(Signal::SIGBUS, &sa).expect("sigaction succeeds");
    let saved_sigfpe = sigaction(Signal::SIGFPE, &sa).expect("sigaction succeeds");
    let saved_sigill = sigaction(Signal::SIGILL, &sa).expect("sigaction succeeds");
    let saved_sigsegv = sigaction(Signal::SIGSEGV, &sa).expect("sigaction succeeds");

    *ostate = Some(SignalState {
        counter: 1,
        saved_sigbus,
        saved_sigfpe,
        saved_sigill,
        saved_sigsegv,
    });
}

unsafe fn restore_host_signal_state(state: &mut SignalState) {
    // restore signal handlers
    sigaction(Signal::SIGBUS, &state.saved_sigbus).expect("sigaction succeeds");
    sigaction(Signal::SIGFPE, &state.saved_sigfpe).expect("sigaction succeeds");
    sigaction(Signal::SIGILL, &state.saved_sigill).expect("sigaction succeeds");
    sigaction(Signal::SIGSEGV, &state.saved_sigsegv).expect("sigaction succeeds");
}

unsafe fn reraise_host_signal_in_handler(
    sig: Signal,
    signum: libc::c_int,
    siginfo_ptr: *mut libc::siginfo_t,
    ucontext_ptr: *mut c_void,
) {
    let saved_handler = {
        // TODO: avoid taking a mutex here, probably by having some static muts just for this
        // function
        if let Some(ref state) = *LUCET_SIGNAL_STATE.lock().unwrap() {
            match sig {
                Signal::SIGBUS => state.saved_sigbus.clone(),
                Signal::SIGFPE => state.saved_sigfpe.clone(),
                Signal::SIGILL => state.saved_sigill.clone(),
                Signal::SIGSEGV => state.saved_sigsegv.clone(),
                sig => panic!(
                    "unexpected signal in reraise_host_signal_in_handler: {:?}",
                    sig
                ),
            }
        } else {
            // this case is very fishy; it can arise when the last lucet instance spins down and
            // uninstalls the lucet handlers while a signal handler is running on this thread, but
            // before taking the mutex above. The theory is that if this has happened, the host
            // handler has been reinstalled, so we shouldn't end up back here if we reraise

            // unmask the signal to reraise; we don't have to restore it because the handler will return
            // after this. If it signals again between here and now, that's a double fault and the
            // process is going to die anyway
            let mut unmask = SigSet::empty();
            unmask.add(sig);
            pthread_sigmask(SigmaskHow::SIG_UNBLOCK, Some(&unmask), None)
                .expect("pthread_sigmask succeeds");
            // if there's no current signal state, just re-raise and hope for the best
            raise(sig).expect("raise succeeds");
            return;
        }
    };

    match saved_handler.handler() {
        SigHandler::SigDfl => {
            // reinstall default signal handler and reraise the signal; this should terminate the
            // program
            sigaction(sig, &saved_handler).expect("sigaction succeeds");
            let mut unmask = SigSet::empty();
            unmask.add(sig);
            pthread_sigmask(SigmaskHow::SIG_UNBLOCK, Some(&unmask), None)
                .expect("pthread_sigmask succeeds");
            raise(sig).expect("raise succeeds");
        }
        SigHandler::SigIgn => {
            // don't do anything; if we hit this case, whatever program is hosting us is almost
            // certainly doing something wrong, because our set of signals requires intervention to
            // proceed
            return;
        }
        SigHandler::Handler(f) => {
            // call the saved handler directly so there is no altstack confusion
            f(signum)
        }
        SigHandler::SigAction(f) => {
            // call the saved handler directly so there is no altstack confusion
            f(signum, siginfo_ptr, ucontext_ptr)
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// A collection of wrappers that will be upstreamed to the `nix` crate eventually.
////////////////////////////////////////////////////////////////////////////////////////////////////

use bitflags::bitflags;

#[derive(Copy, Clone)]
pub struct SigStack {
    stack: libc::stack_t,
}

impl SigStack {
    pub fn new(sp: *mut libc::c_void, flags: SigStackFlags, size: libc::size_t) -> SigStack {
        let mut stack = unsafe { std::mem::uninitialized::<libc::stack_t>() };
        stack.ss_sp = sp;
        stack.ss_flags = flags.bits();
        stack.ss_size = size;
        SigStack { stack }
    }

    pub fn disabled() -> SigStack {
        let mut stack = unsafe { std::mem::uninitialized::<libc::stack_t>() };
        stack.ss_sp = std::ptr::null_mut();
        stack.ss_flags = SigStackFlags::SS_DISABLE.bits();
        stack.ss_size = libc::SIGSTKSZ;
        SigStack { stack }
    }

    pub fn flags(&self) -> SigStackFlags {
        SigStackFlags::from_bits_truncate(self.stack.ss_flags)
    }
}

impl AsRef<libc::stack_t> for SigStack {
    fn as_ref(&self) -> &libc::stack_t {
        &self.stack
    }
}

impl AsMut<libc::stack_t> for SigStack {
    fn as_mut(&mut self) -> &mut libc::stack_t {
        &mut self.stack
    }
}

bitflags! {
    pub struct SigStackFlags: libc::c_int {
        const SS_ONSTACK = libc::SS_ONSTACK;
        const SS_DISABLE = libc::SS_DISABLE;
    }
}

pub unsafe fn sigaltstack(new_sigstack: Option<SigStack>) -> nix::Result<Option<SigStack>> {
    let mut previous_stack = std::mem::uninitialized::<libc::stack_t>();
    let disabled_sigstack = SigStack::disabled();
    let new_stack = match new_sigstack {
        None => &disabled_sigstack.stack,
        Some(ref new_stack) => &new_stack.stack,
    };
    let res = libc::sigaltstack(
        new_stack as *const libc::stack_t,
        &mut previous_stack as *mut libc::stack_t,
    );
    nix::errno::Errno::result(res).map(|_| {
        let sigstack = SigStack {
            stack: previous_stack,
        };
        if sigstack.flags().contains(SigStackFlags::SS_DISABLE) {
            None
        } else {
            Some(sigstack)
        }
    })
}

pub unsafe fn altstack_flags() -> nix::Result<SigStackFlags> {
    let mut current_stack = std::mem::uninitialized::<libc::stack_t>();
    let res = libc::sigaltstack(
        std::ptr::null_mut(),
        &mut current_stack as *mut libc::stack_t,
    );
    nix::errno::Errno::result(res)
        .map(|_| SigStackFlags::from_bits_truncate(current_stack.ss_flags))
}
<commit_msg>[lucet-runtime] make sigaltstack restore on a per-thread basis<commit_after>use crate::context::Context;
use crate::instance::{
    siginfo_ext::SiginfoExt, FaultDetails, Instance, State, TerminationDetails, CURRENT_INSTANCE,
    HOST_CTX,
};
use crate::sysdeps::UContextPtr;
use failure::Error;
use lazy_static::lazy_static;
use libc::{c_int, c_void, siginfo_t, SIGBUS, SIGSEGV};
use lucet_module_data::TrapCode;
use nix::sys::signal::{
    pthread_sigmask, raise, sigaction, SaFlags, SigAction, SigHandler, SigSet, SigmaskHow, Signal,
};
use std::sync::Mutex;

lazy_static! {
    // TODO: work out an alternative to this that is signal-safe for `reraise_host_signal_in_handler`
    static ref LUCET_SIGNAL_STATE: Mutex<Option<SignalState>> = Mutex::new(None);
}

/// The value returned by
/// [`Instance.signal_handler`](struct.Instance.html#structfield.signal_handler) to determine the
/// outcome of a handled signal.
pub enum SignalBehavior {
    /// Use default behavior, which switches back to the host with `State::Fault` populated.
    Default,
    /// Override default behavior and cause the instance to continue.
    Continue,
    /// Override default behavior and cause the instance to terminate.
    Terminate,
}

pub type SignalHandler = dyn Fn(
    &Instance,
    &Option<TrapCode>,
    libc::c_int,
    *const siginfo_t,
    *const c_void,
) -> SignalBehavior;

pub fn signal_handler_none(
    _inst: &Instance,
    _trapcode: &Option<TrapCode>,
    _signum: libc::c_int,
    _siginfo_ptr: *const siginfo_t,
    _ucontext_ptr: *const c_void,
) -> SignalBehavior {
    SignalBehavior::Default
}

impl Instance {
    pub(crate) fn with_signals_on<F, R>(&mut self, f: F) -> Result<R, Error>
    where
        F: FnOnce(&mut Instance) -> Result<R, Error>,
    {
        // Set up the signal stack for this thread. Note that because signal stacks are per-thread,
        // rather than per-process, we do this for every run, while the signal handler is installed
        // only once per process.
        let guest_sigstack = SigStack::new(
            self.alloc.slot().sigstack,
            SigStackFlags::empty(),
            libc::SIGSTKSZ,
        );
        let previous_sigstack = unsafe { sigaltstack(Some(guest_sigstack)) }
            .expect("enabling or changing the signal stack succeeds");
        if let Some(previous_sigstack) = previous_sigstack {
            assert!(
                !previous_sigstack
                    .flags()
                    .contains(SigStackFlags::SS_ONSTACK),
                "an instance was created with a signal stack"
            );
        }
        let mut ostate = LUCET_SIGNAL_STATE.lock().unwrap();
        if let Some(ref mut state) = *ostate {
            state.counter += 1;
        } else {
            unsafe {
                setup_guest_signal_state(&mut ostate);
            }
        }
        drop(ostate);

        // run the body
        let res = f(self);

        let mut ostate = LUCET_SIGNAL_STATE.lock().unwrap();
        let counter_zero = if let Some(ref mut state) = *ostate {
            state.counter -= 1;
            if state.counter == 0 {
                unsafe {
                    restore_host_signal_state(state);
                }
                true
            } else {
                false
            }
        } else {
            panic!("signal handlers weren't installed at instance exit");
        };
        if counter_zero {
            *ostate = None;
        }

        unsafe {
            // restore the host signal stack for this thread
            if !altstack_flags()
                .expect("the current stack flags could be retrieved")
                .contains(SigStackFlags::SS_ONSTACK)
            {
                sigaltstack(previous_sigstack).expect("sigaltstack restoration succeeds");
            }
        }

        res
    }
}

/// Signal handler installed during instance execution.
///
/// This function is only designed to handle signals that are the direct result of execution of a
/// hardware instruction from the faulting WASM thread. It thus safely assumes the signal is
/// directed specifically at this thread (i.e. not a different thread or the process as a whole).
extern "C" fn handle_signal(signum: c_int, siginfo_ptr: *mut siginfo_t, ucontext_ptr: *mut c_void) {
    let signal = Signal::from_c_int(signum).expect("signum is a valid signal");
    if !(signal == Signal::SIGBUS
        || signal == Signal::SIGSEGV
        || signal == Signal::SIGILL
        || signal == Signal::SIGFPE)
    {
        panic!("unexpected signal in guest signal handler: {:?}", signal);
    }
    assert!(!siginfo_ptr.is_null(), "siginfo must not be null");

    // Safety: when using a SA_SIGINFO sigaction, the third argument can be cast to a `ucontext_t`
    // pointer per the manpage
    assert!(!ucontext_ptr.is_null(), "ucontext_ptr must not be null");
    let ctx = UContextPtr::new(ucontext_ptr);
    let rip = ctx.get_ip();

    let switch_to_host = CURRENT_INSTANCE.with(|current_instance| {
        let mut current_instance = current_instance.borrow_mut();

        if current_instance.is_none() {
            // If there is no current instance, we've caught a signal raised by a thread that's not
            // running a lucet instance. Restore the host signal handler and reraise the signal,
            // then return if the host handler returns
            unsafe {
                reraise_host_signal_in_handler(signal, signum, siginfo_ptr, ucontext_ptr);
            }
            // don't try context-switching
            return false;
        }

        // Safety: the memory pointed to by CURRENT_INSTANCE should be a valid instance. This is not
        // a trivial property, but relies on the compiler not emitting guest programs that can
        // overwrite the instance.
        let inst = unsafe {
            current_instance
                .as_mut()
                .expect("current instance exists")
                .as_mut()
        };

        let trapcode = inst.module.lookup_trapcode(rip);

        let behavior = (inst.signal_handler)(inst, &trapcode, signum, siginfo_ptr, ucontext_ptr);
        match behavior {
            SignalBehavior::Continue => {
                // return to the guest context without making any modifications to the instance
                false
            }
            SignalBehavior::Terminate => {
                // set the state before jumping back to the host context
                inst.state = State::Terminated {
                    details: TerminationDetails::Signal,
                };
                true
            }
            SignalBehavior::Default => {
                // safety: pointer is checked for null at the top of the function, and the
                // manpage guarantees that a siginfo_t will be passed as the second argument
                let siginfo = unsafe { *siginfo_ptr };
                let rip_addr = rip as usize;
                // If the trap table lookup returned unknown, it is a fatal error
                let unknown_fault = trapcode.is_none();

                // If the trap was a segv or bus fault and the addressed memory was outside the
                // guard pages, it is also a fatal error
                let outside_guard = (siginfo.si_signo == SIGSEGV || siginfo.si_signo == SIGBUS)
                    && !inst.alloc.addr_in_heap_guard(siginfo.si_addr());

                // record the fault and jump back to the host context
                inst.state = State::Fault {
                    details: FaultDetails {
                        fatal: unknown_fault || outside_guard,
                        trapcode: trapcode,
                        rip_addr,
                        // Details set to `None` here: have to wait until `verify_trap_safety` to
                        // fill in these details, because access may not be signal safe.
                        rip_addr_details: None,
                    },
                    siginfo,
                    context: ctx.into(),
                };
                true
            }
        }
    });

    if switch_to_host {
        HOST_CTX.with(|host_ctx| unsafe {
            Context::set_from_signal(&*host_ctx.get())
                .expect("can successfully switch back to the host context");
        });
        unreachable!()
    }
}

struct SignalState {
    counter: usize,
    saved_sigbus: SigAction,
    saved_sigfpe: SigAction,
    saved_sigill: SigAction,
    saved_sigsegv: SigAction,
}

// raw pointers in the saved types
unsafe impl Send for SignalState {}

unsafe fn setup_guest_signal_state(ostate: &mut Option<SignalState>) {
    let mut masked_signals = SigSet::empty();
    masked_signals.add(Signal::SIGBUS);
    masked_signals.add(Signal::SIGFPE);
    masked_signals.add(Signal::SIGILL);
    masked_signals.add(Signal::SIGSEGV);

    // setup signal handlers
    let sa = SigAction::new(
        SigHandler::SigAction(handle_signal),
        SaFlags::SA_RESTART | SaFlags::SA_SIGINFO | SaFlags::SA_ONSTACK,
        masked_signals,
    );
    let saved_sigbus = sigaction(Signal::SIGBUS, &sa).expect("sigaction succeeds");
    let saved_sigfpe = sigaction(Signal::SIGFPE, &sa).expect("sigaction succeeds");
    let saved_sigill = sigaction(Signal::SIGILL, &sa).expect("sigaction succeeds");
    let saved_sigsegv = sigaction(Signal::SIGSEGV, &sa).expect("sigaction succeeds");

    *ostate = Some(SignalState {
        counter: 1,
        saved_sigbus,
        saved_sigfpe,
        saved_sigill,
        saved_sigsegv,
    });
}

unsafe fn restore_host_signal_state(state: &mut SignalState) {
    // restore signal handlers
    sigaction(Signal::SIGBUS, &state.saved_sigbus).expect("sigaction succeeds");
    sigaction(Signal::SIGFPE, &state.saved_sigfpe).expect("sigaction succeeds");
    sigaction(Signal::SIGILL, &state.saved_sigill).expect("sigaction succeeds");
    sigaction(Signal::SIGSEGV, &state.saved_sigsegv).expect("sigaction succeeds");
}

unsafe fn reraise_host_signal_in_handler(
    sig: Signal,
    signum: libc::c_int,
    siginfo_ptr: *mut libc::siginfo_t,
    ucontext_ptr: *mut c_void,
) {
    let saved_handler = {
        // TODO: avoid taking a mutex here, probably by having some static muts just for this
        // function
        if let Some(ref state) = *LUCET_SIGNAL_STATE.lock().unwrap() {
            match sig {
                Signal::SIGBUS => state.saved_sigbus.clone(),
                Signal::SIGFPE => state.saved_sigfpe.clone(),
                Signal::SIGILL => state.saved_sigill.clone(),
                Signal::SIGSEGV => state.saved_sigsegv.clone(),
                sig => panic!(
                    "unexpected signal in reraise_host_signal_in_handler: {:?}",
                    sig
                ),
            }
        } else {
            // this case is very fishy; it can arise when the last lucet instance spins down and
            // uninstalls the lucet handlers while a signal handler is running on this thread, but
            // before taking the mutex above. The theory is that if this has happened, the host
            // handler has been reinstalled, so we shouldn't end up back here if we reraise

            // unmask the signal to reraise; we don't have to restore it because the handler will return
            // after this. If it signals again between here and now, that's a double fault and the
            // process is going to die anyway
            let mut unmask = SigSet::empty();
            unmask.add(sig);
            pthread_sigmask(SigmaskHow::SIG_UNBLOCK, Some(&unmask), None)
                .expect("pthread_sigmask succeeds");
            // if there's no current signal state, just re-raise and hope for the best
            raise(sig).expect("raise succeeds");
            return;
        }
    };

    match saved_handler.handler() {
        SigHandler::SigDfl => {
            // reinstall default signal handler and reraise the signal; this should terminate the
            // program
            sigaction(sig, &saved_handler).expect("sigaction succeeds");
            let mut unmask = SigSet::empty();
            unmask.add(sig);
            pthread_sigmask(SigmaskHow::SIG_UNBLOCK, Some(&unmask), None)
                .expect("pthread_sigmask succeeds");
            raise(sig).expect("raise succeeds");
        }
        SigHandler::SigIgn => {
            // don't do anything; if we hit this case, whatever program is hosting us is almost
            // certainly doing something wrong, because our set of signals requires intervention to
            // proceed
            return;
        }
        SigHandler::Handler(f) => {
            // call the saved handler directly so there is no altstack confusion
            f(signum)
        }
        SigHandler::SigAction(f) => {
            // call the saved handler directly so there is no altstack confusion
            f(signum, siginfo_ptr, ucontext_ptr)
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// A collection of wrappers that will be upstreamed to the `nix` crate eventually.
////////////////////////////////////////////////////////////////////////////////////////////////////

use bitflags::bitflags;

#[derive(Copy, Clone)]
pub struct SigStack {
    stack: libc::stack_t,
}

impl SigStack {
    pub fn new(sp: *mut libc::c_void, flags: SigStackFlags, size: libc::size_t) -> SigStack {
        let mut stack = unsafe { std::mem::uninitialized::<libc::stack_t>() };
        stack.ss_sp = sp;
        stack.ss_flags = flags.bits();
        stack.ss_size = size;
        SigStack { stack }
    }

    pub fn disabled() -> SigStack {
        let mut stack = unsafe { std::mem::uninitialized::<libc::stack_t>() };
        stack.ss_sp = std::ptr::null_mut();
        stack.ss_flags = SigStackFlags::SS_DISABLE.bits();
        stack.ss_size = libc::SIGSTKSZ;
        SigStack { stack }
    }

    pub fn flags(&self) -> SigStackFlags {
        SigStackFlags::from_bits_truncate(self.stack.ss_flags)
    }
}

impl AsRef<libc::stack_t> for SigStack {
    fn as_ref(&self) -> &libc::stack_t {
        &self.stack
    }
}

impl AsMut<libc::stack_t> for SigStack {
    fn as_mut(&mut self) -> &mut libc::stack_t {
        &mut self.stack
    }
}

bitflags! {
    pub struct SigStackFlags: libc::c_int {
        const SS_ONSTACK = libc::SS_ONSTACK;
        const SS_DISABLE = libc::SS_DISABLE;
    }
}

pub unsafe fn sigaltstack(new_sigstack: Option<SigStack>) -> nix::Result<Option<SigStack>> {
    let mut previous_stack = std::mem::uninitialized::<libc::stack_t>();
    let disabled_sigstack = SigStack::disabled();
    let new_stack = match new_sigstack {
        None => &disabled_sigstack.stack,
        Some(ref new_stack) => &new_stack.stack,
    };
    let res = libc::sigaltstack(
        new_stack as *const libc::stack_t,
        &mut previous_stack as *mut libc::stack_t,
    );
    nix::errno::Errno::result(res).map(|_| {
        let sigstack = SigStack {
            stack: previous_stack,
        };
        if sigstack.flags().contains(SigStackFlags::SS_DISABLE) {
            None
        } else {
            Some(sigstack)
        }
    })
}

pub unsafe fn altstack_flags() -> nix::Result<SigStackFlags> {
    let mut current_stack = std::mem::uninitialized::<libc::stack_t>();
    let res = libc::sigaltstack(
        std::ptr::null_mut(),
        &mut current_stack as *mut libc::stack_t,
    );
    nix::errno::Errno::result(res)
        .map(|_| SigStackFlags::from_bits_truncate(current_stack.ss_flags))
}
<|endoftext|>