1extern crate errno;
3extern crate libc;
4
5use libc::{c_int, mkfifo, mode_t, EACCES, EEXIST, ENOENT};
6use std::ffi::CString;
7use std::fs::{File, OpenOptions};
8use std::io;
9use std::os::unix::fs::OpenOptionsExt;
10use std::path::Path;
11
12mod ext;
13pub use self::ext::*;
14
15pub fn create<P: AsRef<Path>>(path: P, mode: Option<u32>) -> io::Result<()> {
40 let path = CString::new(path.as_ref().to_str().unwrap())?;
41 let mode = mode.unwrap_or(0o644);
42 let result: c_int = unsafe { mkfifo(path.as_ptr(), mode as mode_t) };
43
44 let result: i32 = result.into();
45 if result == 0 {
46 return Ok(());
47 }
48
49 let error = errno::errno();
50 match error.0 {
51 EACCES => {
52 return Err(io::Error::new(
53 io::ErrorKind::PermissionDenied,
54 format!("could not open {:?}: {}", path, error),
55 ));
56 }
57 EEXIST => {
58 return Err(io::Error::new(
59 io::ErrorKind::AlreadyExists,
60 format!("could not open {:?}: {}", path, error),
61 ));
62 }
63 ENOENT => {
64 return Err(io::Error::new(
65 io::ErrorKind::NotFound,
66 format!("could not open {:?}: {}", path, error),
67 ));
68 }
69 _ => {
70 return Err(io::Error::new(
71 io::ErrorKind::Other,
72 format!("could not open {:?}: {}", path, error),
73 ));
74 }
75 }
76}
77
78pub fn open_read<P: AsRef<Path>>(path: P) -> io::Result<File> {
92 OpenOptions::new()
93 .read(true)
94 .custom_flags(libc::O_NONBLOCK)
95 .open(path)
96}
97
98pub fn open_write<P: AsRef<Path>>(path: P) -> io::Result<File> {
119 OpenOptions::new()
120 .write(true)
121 .append(true)
122 .custom_flags(libc::O_NONBLOCK)
123 .open(path)
124}
125
126#[cfg(test)]
127mod tests {
128 extern crate fs2;
129
130 use super::*;
131 use fs2::FileExt;
132 use std::fs;
133 use std::io::{self, Error, ErrorKind, Read, Write};
134
135 fn lock_active_test() -> io::Result<fs::File> {
136 let file = File::create("/tmp/unix-named-pipe_tests.lock")?;
137 file.lock_exclusive()?;
138
139 Ok(file)
140 }
141
142 #[test]
143 fn create_new_pipe() {
144 let lock = lock_active_test().unwrap();
145
146 let filename = "/tmp/pipe";
147 let _ = create(filename, None).expect("could not create pipe");
148
149 fs::remove_file(filename).expect("could not remove test pipe");
150 lock.unlock().unwrap();
151 }
152
153 #[test]
154 fn create_pipe_eexists() {
155 let lock = lock_active_test().unwrap();
156
157 let filename = "/tmp/pipe";
158 fs::write(filename, "").expect("could not write test file");
159
160 let pipe = create(filename, None);
161 assert_eq!(pipe.is_err(), true);
162
163 let err: Error = pipe.unwrap_err();
164 assert_eq!(err.kind(), ErrorKind::AlreadyExists);
165
166 fs::remove_file(filename).expect("could not remove test file");
167 lock.unlock().unwrap();
168 }
169
170 #[test]
171 fn create_pipe_enoent() {
172 let filename = "/notadir/pipe";
173 let pipe = create(filename, None);
174 assert_eq!(pipe.is_err(), true);
175
176 let err: Error = pipe.unwrap_err();
177 assert_eq!(err.kind(), ErrorKind::NotFound);
178 }
179
180 #[test]
181 fn open_pipe_read() {
182 let lock = lock_active_test().unwrap();
183
184 let filename = "/tmp/test.pipe";
185 let _ = create(filename, None).expect("could not make test pipe");
186
187 let contents: [u8; 4] = [0xca, 0xfe, 0xba, 0xbe];
188 let mut actual: [u8; 4] = [0; 4];
189
190 let mut read_file = open_read(filename).expect("could not open test pipe for reading");
192
193 {
195 let mut write_file =
196 open_write(filename).expect("could not open test pipe for writing");
197 write_file
198 .write(&contents)
199 .expect("could not write test data to pipe");
200 write_file.flush().expect("could not flush test pipe");
201 }
202
203 read_file
205 .read_exact(&mut actual)
206 .expect("could not read test data from pipe");
207 assert_eq!(contents, actual);
208
209 fs::remove_file(filename).expect("could not remove test file");
210 lock.unlock().unwrap();
211 }
212}