conradkleinespel / rpassword

Cross platform Rust library to read a password in the terminal (Linux, BSD, OSX, Windows, WASM).
Apache License 2.0
244 stars 38 forks source link

fails to build for wasm32-wasi #53

Closed AnharMiah closed 4 years ago

AnharMiah commented 4 years ago

hi, firstly thanks for creating this crate its highly appreciated!

Just some background, as part of a weekend hack I was playing around with the wasm32-wasi target, and I had a demo project that uses rpassword, however when using wasm32-wasi rpassword fails to build

would this take much to get to build under that target?

I get a multiple errors saying that certain function names don't exist e.g

258 |           None => read_password_from_stdin(false),
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^ help: a function with a similar name exists: `read_password_from_tty`
...
263 | / pub fn read_password_from_tty(prompt: Option<&str>)
264 | |                               -> ::std::io::Result<String> {
265 | |     if let Some(prompt) = prompt {
266 | |         display_on_tty(prompt)?;
267 | |     }
268 | |     read_password_from_stdin(true)
269 | | }
    | |_- similarly named function `read_password_from_tty` defined here

I'm not too clued about the cfg annotation, but in the source it appears you are doing some conditional code paths based on either Windows or Unix, would this be the case that this somehow needs to include the wasm32 as well?

conradkleinespel commented 4 years ago

Hey @AnharMiah ! Thanks for reporting this.

I'm not familiar with wasm32-wasi. Does it has specific APIs for reading data from stdin or from a TTY? Maybe you'd need to use those APIs to implement read_password_from_stdin specifically for wasi :thinking:

In any case, if you do find a fix, I'm happy to review and merge it. The goal is for rpassword to be as compatible as possible. If you do submit a PR, please include instructions as to how I can test the code on my machine (eg. tutorial to how to compile wasm32-wasi code).

AnharMiah commented 4 years ago

hi @conradkleinespel

thanks for fast response!

according to the WASI official repo (https://github.com/bytecodealliance/wasi):

image

So from that since WASI supports Rust's standard library, STDIN should be totally fine to use.

I created a dummy hello world that took args from STDIN and it compiled fine with no issues (for both x86 as well as WASI)

conradkleinespel commented 4 years ago

@AnharMiah OK, sounds good!

From the looks of it, libc supports wasi, so that's a good start because libc is used in rpassword's code for the unix target. Have you tried replacing #[cfg(unix)] with #[cfg(unix,wasm32-wasi)] in src/lib.rs ? Does that work ?

conradkleinespel commented 4 years ago

@AnharMiah Sorry, that might actually not be what you need. Maybe you need #cfg(any(unix, target_arch=wasm32)). I'm not sure, but playing with these things might get you what you want.

More information about that is here: https://doc.rust-lang.org/reference/conditional-compilation.html

AnharMiah commented 4 years ago

@conradkleinespel thanks for the suggestion!

well I tried replacing #[cfg(unix)] with #[cfg(any(unix, target_arch="wasm32"))] and now is given far more build errors!:

error[E0433]: failed to resolve: could not find `unix` in `os`
  --> src/lib.rs:52:18
   |
52 |     use std::os::unix::io::AsRawFd;
   |                  ^^^^ could not find `unix` in `os`

error[E0432]: unresolved imports `libc::termios`, `libc::tcsetattr`, `libc::TCSANOW`, `libc::ECHO`, `libc::ECHONL`
  --> src/lib.rs:49:23
   |
49 |     use libc::{c_int, termios, isatty, tcsetattr, TCSANOW, ECHO, ECHONL, STDIN_FILENO};
   |                       ^^^^^^^          ^^^^^^^^^  ^^^^^^^  ^^^^  ^^^^^^ no `ECHONL` in the root
   |                       |                |          |        |
   |                       |                |          |        no `ECHO` in the root
   |                       |                |          no `TCSANOW` in the root
   |                       |                no `tcsetattr` in the root
   |                       no `termios` in the root

error[E0425]: cannot find function `tcgetattr` in crate `libc`
  --> src/lib.rs:64:36
   |
64 |         io_result(unsafe { ::libc::tcgetattr(fd, term.as_mut_ptr()) })?;
   |                                    ^^^^^^^^^ not found in `libc`

<SNIPPED>

error: aborting due to 12 previous errors

Some errors have detailed explanations: E0425, E0432, E0433, E0599, E0658.
For more information about an error, try `rustc --explain E0425`.
error: could not compile `rpassword`.
AnharMiah commented 4 years ago

sorry when I use #[cfg(any(unix, target_arch="wasm32-wasi"))] I get only 3 errors, about not finding those specific function names:

   --> src/lib.rs:254:17
    |
254 |           None => read_password_from_stdin(false),
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^ help: a function with a similar name exists: `read_password_from_tty`
...
259 | / pub fn read_password_from_tty(prompt: Option<&str>)
260 | |                               -> ::std::io::Result<String> {
261 | |     if let Some(prompt) = prompt {
262 | |         display_on_tty(prompt)?;
263 | |     }
264 | |     read_password_from_stdin(true)
265 | | }
    | |_- similarly named function `read_password_from_tty` defined here

error[E0425]: cannot find function `display_on_tty` in this scope
   --> src/lib.rs:262:9
    |
262 |         display_on_tty(prompt)?;
    |         ^^^^^^^^^^^^^^ not found in this scope

error[E0425]: cannot find function `read_password_from_stdin` in this scope
   --> src/lib.rs:264:5
    |
259 | / pub fn read_password_from_tty(prompt: Option<&str>)
260 | |                               -> ::std::io::Result<String> {
261 | |     if let Some(prompt) = prompt {
262 | |         display_on_tty(prompt)?;
263 | |     }
264 | |     read_password_from_stdin(true)
    | |     ^^^^^^^^^^^^^^^^^^^^^^^^ help: a function with a similar name exists: `read_password_from_tty`
265 | | }
    | |_- similarly named function `read_password_from_tty` defined here

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0425`.
error: could not compile `rpassword`.
conradkleinespel commented 4 years ago

OK, from what you're saying #[cfg(any(unix, target_arch="wasm32"))] seems to be the way to go about debugging this. Most errors have a reason, I'll get to that in a minute.

#[cfg(any(unix, target_arch="wasm32-wasi"))) doesn't work because wasm32-wasi is not an "arch", so "target_arch" doesn't work with "wasm32-wasi" it only work with "wasm32" (which is an arch).

Can you tell me something : does wasm32-wasi work in a web browser only ? never in a terminal ?

Also, feel free to hop onto Discord if you want a bit more speed in the discussion: https://discord.com/invite/9zXtYqQ

AnharMiah commented 4 years ago

oh ok yes that makes sense,

Can you tell me something : does wasm32-wasi work in a web browser only ? never in a terminal ?

so WASI basically allows WASM to run on the server or basically anything that implements a WASM runtime with "sys calls", there are many projects that use WASI to basically run on many platform see:

https://wasmer.io/

https://github.com/bytecodealliance/wasmtime

https://github.com/wasm3/wasm3

What this means is that if you can compile your Rust to wasm, then you can use the WASM(I) runtime across many many architecture without any further compilation (a new kind modern JVM if you will) so from everything from a tiny ESP8266 microcontoller all the way to satellite STBs etc

conradkleinespel commented 4 years ago

So, back to the first output, the one with #[cfg(any(unix, target_arch="wasm32"))].

The first error is about use std::os::unix::io::AsRawFd. Probably because there is no RawFd for WASI: have a look here => https://github.com/rust-lang/libc/blob/master/src/wasi.rs

If wasm32-wasi is meant to work in a web browser, try to remove all code from src/lib.rs that has to do with tty. The read_password_from_stdin function would look somewhat like this:

/// Reads a password from stdin
    pub fn read_password_from_stdin(open_tty: bool) -> ::std::io::Result<String> {
        let mut password = super::ZeroOnDrop::new();

        io::stdin().read_line(&mut password)?

        super::fixes_newline(&mut password);

        Ok(password.into_inner())
    }
AnharMiah commented 4 years ago

ok thanks, I'll have a play, WASI is not intended for the browser its the opposite (i.e running WASM on the server)

UPDATE:

so after looking into the libc upstream repo, I think that's basically the problem, they just don't have bindings for those things for WASI hence the build failure, probably best to open an issue with the libc repo :)

Thanks again for all your help!

conradkleinespel commented 4 years ago

@AnharMiah Oh OK ! I'll go ahead and close this issue for now then. Feel free to re-open later or make a PR if you have a fix for this. Really sorry that you're unable to use rpassword for now but I hope you find a way to create what you want.

Until then, feel free to copy any piece of code form rpassword as long as you comply with the Apache license: image

Best of luck to you!