Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Does not seem to work with pseudo-terminals on OS X #53

Open
TechnoSam opened this issue Jun 15, 2018 · 5 comments
Open

Does not seem to work with pseudo-terminals on OS X #53

TechnoSam opened this issue Jun 15, 2018 · 5 comments
Labels

Comments

@TechnoSam
Copy link

I considered this library for a project, but I couldn't get it to work in my test environment using pseudo-terminals on OS X (e.g. /dev/ptyp3, /dev/ttyp3).

I was able to do what I needed just fine with pySerial, so I don't think my understanding of pseudo-terminals is lacking.

I have no idea if this would work for a real connection, but without a reliable way to test, I'd waste too much time. If this issue could be fixed, I may revisit the project.

@dcuddeback
Copy link
Owner

dcuddeback commented Jun 18, 2018

@TechnoSam I'm sorry to hear it's not working for you. I'm able to use PTYs successfully on OS X, so I need more details in order to reproduce your issue. Would you mind providing the following details?

  1. The exact error message that you get.
  2. Precise steps to reproduce the bug, including a snippet of code to illustrate what fails. (Does it fail when opening the device, reading/writing settings, reading/writing data, or something else?)
  3. Any other information that may be relevant to your issue, e.g., OS version, tools you're using, anything you might be doing or have configured out of the ordinary, device permissions, etc.

I need to be able to reproduce the issue before I can do anything about it. Based on your description, I'm not even sure what bug to look for.

@TechnoSam
Copy link
Author

Ah yes, that was a failure on my part.

OS is macOS 10.12.6. I'm not aware of any out of the ordinary configurations.

Here is my main.rs:

extern crate serial;

use std::io;
use std::time::Duration;
use std::ffi::OsString;
use serial::prelude::*;

fn main() {

    let mut port = serial::open(&OsString::from("/dev/ttypa")).unwrap();
    listen(&mut port).unwrap();

}

fn listen<T: SerialPort>(port: &mut T) -> io::Result<()> {
    port.reconfigure(&|settings| {
        settings.set_baud_rate(serial::Baud115200)?;
        settings.set_char_size(serial::Bits8);
        settings.set_parity(serial::ParityNone);
        settings.set_stop_bits(serial::Stop1);
        settings.set_flow_control(serial::FlowNone);
        Ok(())
    })?;
    port.set_timeout(Duration::from_secs(10))?;

    let mut buf: Vec<u8> = vec![];
    loop {
        let count = port.read(&mut buf[..])?;
        for byte in 0..count {
            println!("{}", buf[byte])
        }
    }
}

My attempt:

  1. Open a terminal and run screen /dev/ptypa
  2. Open a new terminal and run the program (cargo run in my case)

On the rust terminal, I see:

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Custom { kind: BrokenPipe, error: StringError("Broken pipe") }', libcore/result.rs:945:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.

On the screen terminal, I just see [screen is terminating].

With backtrace:

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Custom { kind: BrokenPipe, error: StringError("Broken pipe") }', libcore/result.rs:945:5
stack backtrace:
   0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
             at libstd/sys/unix/backtrace/tracing/gcc_s.rs:49
   1: std::sys_common::backtrace::print
             at libstd/sys_common/backtrace.rs:71
             at libstd/sys_common/backtrace.rs:59
   2: std::panicking::default_hook::{{closure}}
             at libstd/panicking.rs:207
   3: std::panicking::default_hook
             at libstd/panicking.rs:223
   4: std::panicking::begin_panic
             at libstd/panicking.rs:402
   5: std::panicking::try::do_call
             at libstd/panicking.rs:349
   6: std::panicking::try::do_call
             at libstd/panicking.rs:325
   7: core::ptr::drop_in_place
             at libcore/panicking.rs:72
   8: core::result::unwrap_failed
             at /Users/travis/build/rust-lang/rust/src/libcore/macros.rs:26
   9: <core::result::Result<T, E>>::unwrap
             at /Users/travis/build/rust-lang/rust/src/libcore/result.rs:782
  10: pi_serial::main
             at src/main.rs:12
  11: std::rt::lang_start::{{closure}}
             at /Users/travis/build/rust-lang/rust/src/libstd/rt.rs:74
  12: std::panicking::try::do_call
             at libstd/rt.rs:59
             at libstd/panicking.rs:306
  13: panic_unwind::dwarf::eh::read_encoded_pointer
             at libpanic_unwind/lib.rs:102
  14: <std::io::Write::write_fmt::Adaptor<'a, T> as core::fmt::Write>::write_str
             at libstd/panicking.rs:285
             at libstd/panic.rs:361
             at libstd/rt.rs:58
  15: std::rt::lang_start
             at /Users/travis/build/rust-lang/rust/src/libstd/rt.rs:74
  16: pi_serial::main

So it looks like it's failing to open.

If I change it to connect to /dev/ptypa while the screen session is running, I get a "Resource busy" error instead of "Broken pipe", so that at least makes sense.

I'm happy to try anything else or provide you with more info. Thanks for getting back to me!

@dcuddeback
Copy link
Owner

@TechnoSam Thanks. I reproduced the issue. I'll see if I can get to the bottom of what's happening. Another way to connect two PTYs that you may consider using as a workaround until I've fixed this is to use socat:

$ socat -d -d pty,raw,echo=0,crnl pty,raw,echo=0,crnl

That will print out the paths to two connected PTY devices. Your procedure works with PTYs created by socat, except you need to fix a bug here:

let mut buf: Vec<u8> = vec![];

That line allocates an empty vector, so there's no memory available for read() to fill in. I changed it to vec![0, 0, 0, 0, 0, 0, 0, 0], just to make sure some storage is allocated for testing.

I'm not sure why creating PTYs with socat would behave differently from the /dev/*typ* devices, but I'm looking into it.

@TechnoSam
Copy link
Author

Sweet, I will try socat. As long as I have a way to test remotely, I don't really care what it is, so if this works, I think I will be good.

Thanks for looking into this all the same!

Also, thanks for catching the bug, I didn't realize it wouldn't resize.

@dcuddeback dcuddeback added the bug label Jun 18, 2018
@dcuddeback
Copy link
Owner

I tracked the issue down to poll() returning POLLNVAL in revents. I actually get the same behavior if I use pySerial's PosixPollSerial instead of the regular Serial class, which the docstring seems to suggest isn't supported well on all platforms: https://github.com/pyserial/pyserial/blob/d7ae8f668f0d55abe2808144a1ee6c8e1254f13b/serial/serialposix.py#L743-L748. In fact the poll(2) manpage on OS X says:

BUGS
The poll() system call currently does not support devices.

🤦‍♂️

(It does work with some devices though, because I've used USB to serial devices with my Macbook.)

Thanks for reporting this issue. I'll look for another solution on OS X (I'm thinking kqueue(2)).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants