7

I need to write to file descriptor 3. I have been searching for it but the documentation is quite poor. The only thing that I have found is the use of libc library and the fdopen method, but I haven't found any example about how to use it or write on it.

Can anyone provide me an example of writing to a file descriptor in Rust?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
rdiaz82
  • 996
  • 12
  • 31
  • 1
    There are several crates on [crates.io](https://crates.io/search?q=file%20descriptor). Did you try any of them yet? – Peter Hall Feb 25 '19 at 00:47

2 Answers2

13

You can use FromRawFd to create a File from a specific file descriptor, but only on UNIX-like operating systems:

use std::{
    fs::File,
    io::{self, Write},
    os::unix::io::FromRawFd,
};

fn main() -> io::Result<()> {
    let mut f = unsafe { File::from_raw_fd(3) };
    write!(&mut f, "Hello, world!")?;
    Ok(())
}
$ target/debug/example 3> /tmp/output
$ cat /tmp/output
Hello, world!

from_raw_fd is unsafe because there's no guarantee that the file descriptor is valid or who is actually responsible for that file descriptor.

The created File will assume ownership of the file descriptor: when the File goes out of scope, the file descriptor will be closed. You can avoid this by using either IntoRawFd or mem::forget.

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • This works like a charm!! really easy and clean solution! – rdiaz82 Feb 25 '19 at 12:10
  • It is worth adding that if you do not want to close the _fd_ when `f` is dropped, you can just call `f.into_raw_fd()` (from trait `IntoRawFd`) and discard the return value, that will just be `3`. – rodrigo Feb 25 '19 at 14:37
  • 1
    @rodrigo yep, or `mem::forget` – Shepmaster Feb 25 '19 at 14:38
  • 2
    @Shepmaster: `mem::forget` will work, but I think that if you want to keep the _fd_ opened, just because it was opened when you arrived, then `into_raw_fd()` is the proper way: `mem::forget` might have other undesirable effects, such as not freeing internal buffers or who knows what. – rodrigo Feb 25 '19 at 17:07
3

The libc crate is "just" a wrapping library to interface between C and Rust so to know how to use a function one should read the manual of the C function, there are many source for that, here one for fdopen():

The fdopen() function associates a stream with the existing file descriptor, fd. The mode of the stream (one of the values "r", "r+", "w", "w+", "a", "a+") must be compatible with the mode of the file descriptor. The file position indicator of the new stream is set to that belonging to fd, and the error and end-of-file indicators are cleared. Modes "w" or "w+" do not cause truncation of the file. The file descriptor is not dup'ed, and will be closed when the stream created by fdopen() is closed. The result of applying fdopen() to a shared memory object is undefined.

Basic use is so:

use libc::fdopen;
use std::ffi::CString;

fn main() {
    let mode = CString::new("w").unwrap();
    unsafe {
        let _ = fdopen(3, mode.as_ptr());
    }
}

To use it, you could use fwrite():

The function fwrite() writes nmemb elements of data, each size bytes long, to the stream pointed to by stream, obtaining them from the location given by ptr.

So, complete example:

use libc::{c_void, fdopen, fwrite};
use std::ffi::CString;

fn main() {
    let mode = CString::new("w").unwrap();
    let file = unsafe {
        let file = fdopen(3, mode.as_ptr());
        if file.is_null() {
            panic!("can't open file");
        }
        file
    };

    let welcome = "Hello world!";

    let result = unsafe { fwrite(welcome.as_ptr() as *const c_void, 1, welcome.len(), file) };
    if result != welcome.len() {
        panic!("write not successful");
    }
}
Stargateur
  • 24,473
  • 8
  • 65
  • 91